Sonntag, 30. August 2015

Render Rails layout partials!

In Ruby on Rails in general repetitions in views are eliminated by partials.
This works for most cases. Widgets or complex controls also often consist of an HTML skeleton that repeats itself. However, they also contain some content HTML that differs.
As an example, a modal dialog is used (people/_people.html.erb):

A modal dialog for other resources would look alike. They only would differ in the HTML within the modal body. The rest (header, footer, etc.) were just repetition. It therefore makes sense to extract the HTML skeleton into a partial.
The content can consist of any HTML. To pass it to the partial modal as a variable is not an option. But there is an elegant solution:
ActionView#render provides the option :layout. It is often used to render a page in a certain layout. But it also is intended to render a partial in a "Layout" (people/_people.html.erb):
<%= render layout: 'shared/modal' do %>
  <%= render @people %>
<% end %>
und das wiederverwendbare dialog widget (shared/_modal.html.erb):

The entire HTML skeleton was extracted into the partial. The content can be passed as a block and injected with yield to the right place.
Even more complex widgets can be shared with this approach.
For example, if the partial consumers require a more flexible set of buttons (besides the Cancel button), ActionView#content_for comes into play.
The extended partial shared/_modal.html.erb:

and the consumer template people/_people.html.erb:
<%= render layout: 'shared/modal' do %>
  <%= render @people %>
  <% content_for :buttons do %>
    <%= link_to 'print', people_path(format: :pdf) %>
  <% end %>
<% end %>
Further articles of interest:

Supported by Ruby 2.2.1, Ruby on Rails 4.2.1

Sonntag, 23. August 2015

Batch huge ActiveRecord collections!

To process large amounts of data may force the server to its knees. For example, a background job processing statistical data. The same can also apply to data migrations.
Much of the resources are consumed by ActiveRecord object instantiations. If there is no alternative to instantiate the ActiveRecord objects, Ruby on Rails provides the possibility of batch processing with ActiveRecord::Batches#find_in_batches.
Therefore, instead of:
Order.all.each(&:calculate_summer_sale!)
The example is trivial, but in the case of many orders, it is not predictable how much load will be on the production server. In fact the entire set of objects is loaded and held in the memory until the final pass is completed.
However, the risk can be restricted by batching:
Order.where(invoiced: false).find_in_batches(batch_size: 500) do |orders|
  orders.each(&:calculate_summer_sale!)
end
In the example, 500 orders are processed and loaded into memory per batch run.
The :batch_size is optional. The default value is 1000.
The orders collection is processed with each only. Then ActiveRecord::Batches#find_each is a more concise version (this time with a named scope):
Order.not_invoiced.find_each(batch_size: 500) do |order|
  order.calculate_summer_sale!
end
Both versions principle are similar. They divide the objects in batches based on the ID, which is why the query methods ActiveRecord::QueryMethods#order and ActiveRecord::QueryMethods#limit can not be used.
Another option is :start. It allows the batches to be split across multiple processes.
For example, the first 20 batches are processed from the first process (each with 1000):
Order.where(id: 1..20_000).find_each do |order|
  order.calculate_summer_sale!
end
and the remaining from the second process:
Order.find_each(start: 20_000).each do |order|
  order.calculate_summer_sale!
end
ActiveRecord::QueryMethods#select should be used cautiously. The example:
Order.select('total').find_each(batch_size: 500) do |order|
  order.calculate_summer_sale!
end
Smaller objects are instantiated and therefore it generates less burden. But only a batch of 500 objects actually is processed, since the batching is based on the primary key, which was not selected. At least the ID would have to be selected with:
Order.select('id, total').find_each(batch_size: 500) do |order|
  order.calculate_summer_sale!
end

Further articles of interest:

Supported by Ruby 2.2.1, Ruby on Rails 4.2.1

Sonntag, 16. August 2015

Dive into the Hash#default_proc!

Hash without Proc

A Ruby Hash can be initialized in several ways:
config = {}
config = Hash.new
In both cases the associative array returns nil, when accessed with a key which does not exist:
config[:production]
=> nil
The Hash was instantiated without a Proc:
config.default_proc
=> nil
If you want to execute a nested access, an exception is raised:
config = Hash.new
config[:production][:log] = 'log.txt'
NoMethodError: undefined method '[]=' for nil:NilClass
because Nil#[] is not defined.

Hash with Proc

A Hash also can be instantiated with a block:
config = Hash.new{ |hash, key| rand(1..10) }
The access to an unknown key will always return a value (random number between 1 and 10):
config[:production]
=> 4
config[:production]
=> 9
The reason is the Hash object got a default Proc during instantiation. If the accessing key is not included, the Proc is executed. In that example the Hash itself keeps being empty:
config
=> {}
Of course, the Proc also can do more meaningful stuff:
config = Hash.new{ |hash, key| hash[key] = Hash.new }
config[:production][:log] = 'log/production.log'
Although the key :production has never been initialized, no exception is thrown. The Hash was even extended as expected:
config
=> {:production=>{:log=>"log/production.log"}}
The Proc object can also be requested again:
config.default_proc
=> #
However, the Hash still can not be accessed infinitely deep:
config[:production][:log][:path] = 'log/production.log'
NoMethodError: undefined method '[]=' for nil:NilClass

Hash with Proc for infinite access

When a Hash has to provide infinite deep access without raising an exception, a default Proc has to be passed recursively by passing the default_proc to the accessed Hash value:
config = Hash.new do |hash, key| 
  hash[key] = Hash.new(&hash.default_proc)
end
The Hash owns a default Proc:
config.default_proc
=> #
If the accessing key is not available, no matter which depth, it will always create a new Hash object by default:
config[:production][:log][:path] = 'log/production.log'
config
=> {:production=>{:log=>{:path=>"log/production.log"}}}
A default Proc is also assignable after instantiating the Hash:
config = {}
config.default_proc = proc{ |hash, key| hash[key] = Hash.new(&hash.default_proc) }
config[:production][:log][:path] = 'log/production.log'
config
=> {:production=>{:log=>{:path=>"log/production.log"}}}
Not infinite depth, but very deep:
config[0][1][2][3][4][5][6][7][8][9]
=> {}

Further articles of interest:

Supported by Ruby 2.2.1

Sonntag, 9. August 2015

Set associations in FactoryGirl!

Fixtures are not suitable for testing. They are difficult to maintain and require explicit instantiation of test objects. This is particularly true for associations.
Instead, factories should be be used to generate test objects. The widespread gem Factory Girl lends itself to it. For example, the models/person_spec.rb:
require 'active_record_spec_helper'
require 'person.rb'
require 'address.rb

describe Person do
  subject { FactoryGirl.build :person }

  describe "validations" do
    FactoryGirl.create :address
    if { is_expected.to validate_presence_of(:address) }
  end
end
A person shall only be valid if it also has a valid address. The factories/address.rb:
FactoryGirl.define do
  factory :address do
    street 'Main street'
    number 1
    zip 12345
  end
end
The factories/person.rb:
FactoryGirl.define do
  factory :person do
    name 'Christian'
    surname 'Rolle'
    address_id 1
  end
end
The people factory has defined an address_id association by setting a plain ID. That is very fragile. Especially since a valid person is expected to own a valid address object.
The association could be mocked or the FactoryGirl association feature could be used for some reasons (factories/person.rb):
FactoryGirl.define do
  factory :person do
    name 'Christian'
    surname 'Rolle'
    association :address, strategy: :build
  end
end
The address association will not be actively saved. That saves touching the database in each test and therefore speed up the test tremendously (models/person_spec.rb):
require 'active_record_spec_helper'
require 'person.rb'
require 'address.rb

describe Person do
  subject { FactoryGirl.build :person }

  describe "validations" do
    if { is_expected.to validate_presence_of(:address) }
  end
end
In most tests the associations presence is sufficient. If indeed a stored association is required (eg named scopes), that explicit saving approach is way more expressive.
Further articles of interest:

Supported by Ruby 2.2.1, Ruby on Rails 4.2.1, RSpec 3.3.2 and FactoryGirl 4.5.0

Sonntag, 2. August 2015

Speed up ActiveRecord callback tests!

ActiveRecord model tests are often slow when they touch the database.
For example, the simple create callback test in the person_spec.rb:
require 'active_record_spec_helper'
require 'person.rb'

describe Person do
  subject { FactoryGirl.build :person }

  describe "#create" do
    it "should be active" do
      subject.active = nil
      subject.save
      expect(subject.active).to be_truthy
    end
  end
end
It is expected that any new person should be active after creation. The test ensures this.
However, it can be optimized. It is absolutely unimportant to the relevance of the tests that the record is actually stored after passing through the callbacks. The ActiveRecord has already been extensively proven its functionality in the Ruby on Rails test suite.
So there neither has to be opened a connection to the database, nor fired a SQL statement. Therefore, the person_spec.rb also can look like:
require 'active_record_spec_helper'
require 'person.rb'

describe Person do
  subject { FactoryGirl.build :person }

  describe "#create" do
    it "should be active" do
      subject.active = nil
      subject.run_callbacks :create
      expect(subject.active).to be_truthy
    end
  end
end
The test still ensures a new person to be active. And it consumes way less resources. ActiveSupport::Callbacks#run_callbacks calls the callbacks, but without final persistence. A comparison makes the performance difference obvious. With database:
ubuntu$ rspec spec/models/person_spec.rb

Finished in 0.12141 seconds (files took 2 seconds to load)
Without database:
ubuntu$ rspec spec/models/person_spec.rb

Finished in 0.04964 seconds (files took 2 seconds to load)
At first glance it does not look impressive.
But the same test without touching the database is more than twice as fast. The more more callback tests do exist in the project, the more impressive the numbers will be.
Further articles of interest:

Supported by Ruby 2.2.1, Ruby on Rails 4.2.1 and RSpec 3.3.2