Sonntag, 28. Dezember 2014

Reduce Ruby method parameter complexity!

In Ruby land it is approved that more than 3 method parameters are far too much. Such bloated parameter lists are a code smell. And they smell like a logic overload, which by the way is hard to test. So looking for breaking them apart into smaller pieces is definitely the first step. But even if there is only a small thing going on, there are chances to decrease parameter complexity. A small parameter list is fundamental for a maintainable method. The smaller the parameter list, the less it is likely to change and the greater the chances the method keeps on being stable. Aside from that, a long API parameter list is harder to satisfy and harder to read:
  def foo name, value, state, start_at, end_at, is_new
    # something weird is going on
  end
Never waste time (money) and cheerfulness by trying to deal with code like the above. Refactor it! There are several options, depending on the use case.

1. Deal with parameter objects!

If the method signature consists of parameters, which are likely to be properties of the same object, it is healthy to assign the entire object itself.
class Food
  attr_accessor :name, :kcal, :freshness_date
end

class Consumer < Person
  def eats food_name, kcal, freshness_date
    return if self.allergies.includes?(food_name) or 
      freshness_date.eql?(Date.today)
    self.kcal += kcal
  end
end
can be refactored to:
class Consumer < Person
  def eats food
    return if self.allergies.includes?(food.name) or 
      food.freshness_date < Date.today
    self.kcal += food.kcal
  end
end
With parameter objects, the method signature is way more resistant against changes and therefore more stable in the long run. Furthermore if the logic needs to be reused, the method can be extracted into a module easily.

2. Introduce named parameters!

The more generalized the methods logic or the lower the abstraction level, the more the methods parameter list is likely to consist of optional parameters. Sometimes such methods look like:
class Food
  def initialize name, kcal=0, freshness_date=nil, preservative=nil
    @name = name
    @kcal = kcal
    @freshness_date = freshness_date
    @preservative = preservative
  end
end
Food.new 'Apple', nil, nil, false
which is awkward (since nil is always a bad choice). It should be refactored to:
class Food
  def initialize name, options={}
    @name = name
    @kcal = options[:kcal]
    @freshness_date = options[:freshness_date]
    @preservative = options[:preservative]
  end
end
Food.new 'Apple', preservative: false
Please note the way more expressive object instantiation. Besides adding new optional parameters to the options Hash is easy and does not affect existing code.

3. Replace Parameter with Explicit Methods!

Some use cases do not require a parameter list at all and those parameters should be set explicitly. The object instantiation example again:
class Food
  def initialize name, kcal=0, freshness_date=nil, preservative=nil
    @name = name
    @kcal = kcal
    @freshness_date = freshness_date
    @preservative = preservative
  end
end
Food.new 'Apple', nil, nil, false
but his time defining values with explicit messages:
class Food
  attr_accessor :kcal, :freshness_date, :preservative
  def initialize name
    @name = name
  end
end
apple = Food.new 'Apple'
apple.preservative = false

Further articles of interest:

Supported by Ruby 2.1.1

Sonntag, 21. Dezember 2014

Structure Ruby objects with Structs!

Decomposing fatty degenerated classes into several concerning classes makes sense in any case. Breaking them apart following the single responsibility pattern leads to new classes, each one easy to read, maintain and a pleasure to deal with others.
Sometimes it is very convenient to define one dynamically or it just should be a structure able to receive a few messages. Then the Ruby Struct comes into play.
Creating a Struct object is as easy as:
Struct.new 'Food', :name, :kcal
food = Struct::Food.new 'Apple', 52
=> #<struct Struct::Food name="Apple", kcal=52>
and it has the 2 defined accessors name and kcal:
food.name
=> "Apple"
food.name = 'Pea'
=> "Pea"
food[:kcal] = 57
=> 57
Adding a receiver message goes like:
Struct.new 'Food', :name, :kcal do
  def to_s
    "#{name} (#{kcal})"
  end
end
food = Struct::Food.new 'Apple', 52
food.to_s
=> "Apple (52)"
More practice-focused is inheriting from Struct:
class Food < Struct.new(:name, kcal)
  def to_s
    "#{name} (#{kcal})"
  end
end
and:
food = Food.new 'Apple', 52
food.to_s
=> "Apple (52)"
Whoever argues inheriting from an instance can not be possible: Struct#new returns a class. That is why this also works nice:
Food = Struct.new :name, :kcal
food = Food.new 'Apple'
=> #<struct Food name="Foo", kcal=nil>
food.kcal = 52
A Struct class was assigned to the constant Food. It is possible because classes are just constants.
Please note the difference between the namespaced Struct::Food class in the first example and this Food class.
Structs can be incredible convenient.

Supported by Ruby 2.1.1

Sonntag, 14. Dezember 2014

Create a custom ActiveModel validator!

There are use cases, when the Ruby on Rails standard validators do not satisfy the requirements. Creating a custom validator often times look like:
class Brand < ActiveRecord::Base
  validate :name_likeness
private
  def name_likeness
    like_conditions = name.split.map { |term| 
      self.class.arel_table[:name].matches("%#{term}%") 
    }.inject(&:or)
    return if self.class.where(like_conditions).count.zero?
    errors.add :name, "has already been taken likewise"
  end
end
which ensures the brands name has no term, which already was used in any brands name component.
and using it:
Brand.where("name LIKE ?", "%Fox%")
=> [<Brand id:1, name: 'Fox Media']
brand = Brand.new name: 'Fox Racing'
brand.save
=> false
brand.full_messages_for :name
=> ["Name has already been taken likewise"]
which works but there are various reasons why it should be refactored:
  1. when the custom validation has to be shared on several model classes
  2. because complex validation logic should be extracted into a concerning class
The custom validation logic should be moved into the custom validation class app/validators/likeness_validator.rb:
class LikenessValidator < ActiveModel::EachValidator
  def validate_each record, attribute, value
    like_conditions = value.split.map { |term|
      record.class.arel_table[:name].matches("%#{term}%")
    }.inject(&:or)
    return if record.class.where(like_conditions).count.zero?
    record.errors.add :name, "has already been taken likewise"
  end 
end   
which inherits from ActiveModel::EachValidator and overwrites its validate_each. The logic mostly was copied from the former validating method with the exception of moving the scope from self to record. The former name is passed by the parameter value.
Of course the autoload_paths should be expanded (config/application.rb):
config.autoload_paths += %W["#{config.root}/app/validators/"]
Then the Brand model class can be refactored:
class Brand < ActiveRecord::Base
  validates :name, likeness: true
end
Please note the naming convention: the validation class was named LikenessValidator and is pointed to with the option likeness.
Validating again:
Brand.where("name LIKE ?", "%Fox%")
=> [<Brand id:1, name: 'Fox Media']
brand = Brand.new name: 'Fox Racing'
brand.save
=> false
brand.full_messages_for :name
=> ["Name has already been taken likewise"]
works as expected.
Additional parameters can be assigned like:
class Brand < ActiveRecord::Base
  validates :name, likeness: { min_length: 5 }
end
and used in the custom validation class:
class LikenessValidator < ActiveModel::EachValidator
  def validate_each record, attribute, value
    terms = terms_with_min_length value, options[:min_length]
    return if terms.empty?
    like_conditions = terms.map { |term| 
      record.class.arel_table[:name].matches("%#{term}%") 
      }.inject(&:or)
    return if record.class.where(like_conditions).count.zero?
    record.errors.add :name, "has already been taken likewise"
  end
private
  def terms_with_min_length term, min_length=nil
    terms = term.split
    return terms if min_length.nil?
    term.split.select { |term| 
      term.length >= min_length 
    }
  end
end   
Brand name terms with less than 5 characters satisfy the validation:
Brand.where("name LIKE ?", "%Fox%")
=> [<Brand id:1, name: 'Fox Media']
brand = Brand.new name: 'Fox Racing'
brand.save
=> true

Further articles of interest:

Supported by Ruby 2.1.1 and Ruby on Rails 4.1.8

Sonntag, 7. Dezember 2014

Generate SQL with Arel the Ruby way!

As long as ActiveRecord::Base queries are simple, it is easy to stick to ActiveRecord::QueryMethods like:
Food.where name: 'Peanut butter'
# SELECT "foods".* FROM "foods"  WHERE "foods"."name" = 'Peanut butter'
=> [#<Food id: 1, name: "Peanut butter">]
But what about like-searching by several terms:
class Food < ActiveRecord::Base
  def self.search *terms
    conditions = terms.inject([]) { |conditions, word|
      conditions << "name LIKE ?"
    }.join(" OR ")
    where(conditions, *terms)
  end
end
and querying:
Food.search "Butter", "Apple"
=> [
  #<Food id: 1, name: "Peanut butter">
  #<Food id: 2, name: "Apple juice">
]
works, but is awkward because:
  1. it deals with raw SQL fragments
  2. confuses arguments with question marks
  3. is by far not object oriented
  4. suffers the lack of syntax check
The Food model scope can be refactored easily using Arel:
class Food < ActiveRecord::Base
  def self.search *terms
    conditions = terms.map { |term| 
      arel_table[:name].matches("%#{term}%") 
    }.inject(&:or)
    where(conditions)
  end 
end
The ActiveRecord::Core::ClassMethods#arel_table returns an Arel::Table object. It offers access to all model attributes. For a better understanding: it helps to send to_sql to Arel:
Food.arel_table[:name].matches("%Butter%").to_sql
=> "\"foods\".\"name\" LIKE '%Butter%'"
or the entire example conditions:
%w(Butter Apple).map { |term| 
  Food.arel_table[:name].matches("%#{term}%") 
}.inject(&:or).to_sql
=> "(\"foods\".\"name\" LIKE '%Butter%' OR \"foods\".\"name\" LIKE '%Apple%')"
which is awesome and reveals the power of Arel. No more writing SQL manually required.
It works:
Food.search "Butter", "Apple"
=> [
  #<Food id: 1, name: "Peanut butter">
  #<Food id: 2, name: "Apple juice">
]
The entire flexibility of Arel is documented at Arel API.
Further articles of interest:

Supported by Ruby 2.1.1 and Ruby on Rails 4.1.8