Today I Learned

This project exists to catalogue the sharing & accumulation of knowledge as it happens day-to-day. Posts have a 200-word limit, and posting is open to any Rocketeer as well as selected friends of JetRockets. We hope you enjoy learning along with us.

7 posts by Dmitry Voronov @dmdropkick

Create factory with an uploaded file in Rails rspec

To create a factory with an uploaded file in Rails rspec, needs to be included ActionDispatch::TestProcess module in the rails_helper.rb file to use the #fixture_file_upload method in the factories.

# spec/rails_helper.rb
include ActionDispatch::TestProcess

In a factory, when setting the attribute value, use the method #fixture_file_upload, specifying the path and file type.

# spec/factories/import_file.rb
FactoryBot.define do
  factory :import_file, class: ImportFile do
    data { fixture_file_upload 'spec/fixtures/test_file.pdf', 'application/pdf' }

How to delete polymorphic models cascade

If you use a polymorphic model in your Rails application, like in example

class Trade < ActiveRecord::Base
  has_many :gl_entries, as: :source, dependent: :destroy

class GlEntry < ActiveRecord::Base
  belongs_to :source, polymorphic: true

You will not be able to add the usual foreign keys for cascading delete records. But this can be implemented using a database.

To do this, you need to write a trigger in the database that will run the delete function for each record.

CREATE FUNCTION deleteGlEntriesOfTrade()
  SET SCHEMA 'public'
  LANGUAGE plpgsql 
  AS $$
    DELETE FROM gl_entries WHERE source_id = AND source_type = 'Trade';
    RETURN OLD;   
CREATE TRIGGER deleteTradesGlEntriesTrigger 

Create a migration and use :)

Use hash or case-statement in Ruby?

Often, when we need to get a value based on the other one, we’re using a case-statement. Like this

def realizing_trade_type(realizable_trade_type)
  case realizable_trade_type
  when 'buy'
  when 'short'
  when 'buy_contract'
  when 'short_contract'

But, if the conditions and the results are simple values, why don’t we use hash for this? We can :)

  'buy'            => 'sell',
  'short'          => 'cover',
  'buy_contract'   => 'sell_contract',
  'short_contract' => 'cover_contract'

Here is the benchmark of both options, executed 10_000_000 times. It shows that a hash is faster in times for such the kind of usage.

>> require 'benchmark'
>> do |x|'hash') { 10_000_000.times { REALIZING_TRADE_TYPES['buy'] } }'case-statement') { 10_000_000.times { realizing_trade_type 'buy' } }'empty') { 10_000_000.times {} }
                      user     system      total        real
hash              0.990423   0.003412   0.993835 (  1.057612)
case-statement    1.752263   0.004531   1.756794 (  1.762030)
empty             0.380810   0.000728   0.381538 (  0.382153)

So, it’s better to use a hash when you are just retrieving some values (like in the example above). If there is additional logic to execute, a case-statement is still a way to go.

Render and combine PDF files into one

Render PDF files from HTML templates in Rails can be done using WickedPDF.

First you need to use ActionView to render HTML to a string:

def render_to_string(data)
  action_view =
  action_view.view_paths = ActionController::Base.view_paths

  action_view.class_eval do
    include ApplicationHelper
    include PDFHelper
    # or other helpers

  action_view.render template: 'pdf/template.html',
                     layout: 'layout/pdf.html',
                     locals: { data: data }

Then you can render pdf with the desired settings like this:

# first pdf file with some view settings and values from data1...
pdf1 =
        render_to_string(data1), {
            pdf: 'report1',
            page_size: 'Letter',
            orientation: 'Portrait'
# ...and second pdf file with some view settings and values from data2
pdf2 =
        render_to_string(data2), {
            pdf: 'report2',
            page_size: 'Letter',
            orientation: 'Landscape'

And now you can combine it with CombinePDF gem, that provide you parse method to get PDF content and to_pdfmethod to render the result back to PDF.

combiner =
combiner << CombinePDF.parse(pdf1)
combiner << CombinePDF.parse(pdf2)

Use image files from S3 in WickedPdf

If you need to use image files from S3 in your generated pdf file using WickedPdf, then you need first to download the image. You can create method that does this and add it to the helper.

require 'open-uri'

module PdfHelper
  def embed_remote_image(url, content_type)
    asset = open(url, "r:UTF-8", &:read)
    base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "")

And use image_tag instead of wicked_pdf_image_tag

= image_tag embed_remote_image(file.logo_url, 'image/jpeg')

Manage Elixir versions like RVM & Rbenv

You can install different versions of the Elixir with help of Kiex, like in Ruby with a RVM and Rbenv.

Download and install Kiex

\curl -sSL | bash -s

In .bashrc (or .zshrc if you use z shell), add the following

[[ -s "$HOME/.kiex/scripts/kiex" ]] && source "$HOME/.kiex/scripts/kiex"

Install required Elixir

kiex install 1.7 # or another version

And then you can use any version

kiex use 1.7

How to create zip files on the fly w/o Tempfile

There are many articles about how to archive files from the server and send a zip-file to a client without persisting it on the server. But usually they don’t literally do it, because they use temporary files.

There is a simple way to do it without creating any file though. You just have to put files directly to Zip::OutputStream and then read from it. Btw pay attention: you must rewind the stream before reading it.

# some files objects
def download(files)
  zip_stream = Zip::OutputStream.write_buffer do |zip|
    files.each.with_index(1) do |file, index|
     # file name must be uniq in archive
  # important - rewind the steam
            type: 'application/zip', 
            disposition: 'attachment', 
            filename: ''