Archive for the ‘ruby idioms’ Category

Ruby idioms : Avoid check class membership

Avoid use class, is_a? and kind_of? to check class membership. Replace these statement with a message to the object that respond to the message. The code below is an anti-pattern.

if mortgage.class==FixRateMortgage

The code below is more idiomatic in Ruby.

class Mortgage
  attr_accessor :rate

class FixedRateMortgage < Mortgage
  def initialize()
  def rate=(rate)

  def change_rate_not_allowed
     puts "The rate in a fixed rate mortage can not be change"

Sending the method rate= to either kind of classes will result in the appropriate behaviour.

Ruby idioms : Add a method to a concrete instance.

Sometimes can be useful to add a method in a specific instance of a class. We can do that with the next code :

 class Person

 pedro =
 peter =

 # inject a method in the instances
 def pedro.hello_world; puts "Hola Mundo"; end

 def peter.hello_world; puts "Hello World"; end 
 pedro.hello_world #=> Hola Mundo
 peter.hello_world #=> Hello World

A more useful example, an Array that represent a list of employees that must be sortable by any of the employee attributes : (via Neal Ford)

  employees  = []
  def employees.sort_by_attribute(sym)
    sort {|x, y| x.send(sym) <=> y.send(sym)}

Default values and Hash parameters

I had talked before about it. We can use a hash to pass parameter to our method, and que can merge with another hash that contain the defaults values. Why to do that :

  • Set Default Values in Methods : Avoid unexpected behaviour.
  • Simple Hash as Default Method Parameter : This form allows you to add functionality to methods without breaking existing calls
def add_book(p_arg)

  p_arg = {:title=>"no title", 
           :language=>"esperanto"}.merge! p_arg

add_book :title=>"El Qujote",
         :author=>"Miguel De Cervantes",

=> {:language=>"spanish", :title=>"El Qujote", :author=>"Miguel De Cervantes"}
add_book :title=>"El Qujote",
         :author=>"Miguel De Cervantes"

=> {:language=>"esperanto", :title=>"El Qujote", :author=>"Miguel De Cervantes"}
add_book :title=>"El Qujote",
         :author=>"Miguel De Cervantes"

=> {:language=>"esperanto", :foo=>"bar" ,:title=>"El Qujote", :author=>"Miguel De Cervantes"}

Multiples assignmets in a for loop clause.

 for hello, world in [ [:Hola, :Mundo],
                       [:Hallo, :Welt], 
                       [:Foo, :Bar]    ]
   puts "#{hello} #{world}"

Variable/optional parameter methods, symbols and ruby

First if you want only for optional parameter in a method, just see the next two examples :



def foo(bar=:hello)
  puts bar

foo     #=> hello
foo bye #=> bye

The second example :



def add_book(title=>"no title")
  arg={:, :author=>"anonymous", :language=>"esperanto"}
  arg.merge! p_argirb(main):020:1>

add_book :title=>"El Qujote", :author=>"Miguel De Cervantes", :language=>"spanish"
=> {:language=>"spanish", :title=>"El Qujote", :author=>"Miguel De Cervantes"}

add_book :title=>"El Qujote", :author=>"Miguel De Cervantes"
=> {:language=>"esperanto", :title=>"El Qujote", :author=>"Miguel De Cervantes"}

add_book :title=>"El Qujote", :author=>"Miguel De Cervantes" :foo=>:Bar
=> {:language=>"esperanto", :title=>"El Qujote", :author=>"Miguel De Cervantes"}

Sometimes is usefull to have an optional/variable parameters method, . In a recent domain specific languages (DSL) design i have to implement a object that have some optional parameters.

For example we want to add a book to our database, But we don’t want to write every single attribute, is the attribute is not present then is initialize with a default value. The properties are

add_book :title=>"El quijote", :author=> "Miguel de Cervantes"
add_book :title=>"El quijote", :author=> "Miguel de Cervantes", :tag=>"novel"
add_book :title=>"El quijote"
add_book :title=>"hogehoge", :language=>"Japanese" # => raise ArgumentError

Asking in the ruby list, Hidetoshi NAGAI, teach me how to use symbols, variable parameter methods (varargs), to create this clean example. The main idea is use a hash for parameter value preinitializate and check is the parameter is in the list. BTW, he also override the method_missing method to emulate all the getters and setters for the object.



class Book
  def initialize(properties = {})
      @props = {
          'title' => '',
          'author' => '',
          'tag' => nil,
          'price' => nil,
          'ISBN' => nil,

      properties.each{|k, v|
          k = k.to_s
          raise ArgumentError, "unknown property: #{k}" unless @props.key?(k)
          @props[k] = v

  def method_missing(id, *args)
      prop = id.id2name
      case args.length
      when 1

          if prop[-1] == ?= # it will be a setter
              self[prop[0..-2]] = args[0]
          else # Else it will be "".
                                  # Then call "self[] = val"
              self[prop] = args[0]
      when 0 # the getter
      else #continue the search
          super(id, *args)

  def [](prop)
      prop = prop.to_s
      raise ArgumentError, "unknown property: #{prop}" unless @props.key?(prop)

  def []=(prop, val)
      prop = prop.to_s
      raise ArgumentError, "unknown property: #{prop}" unless @props.key?(prop)
      @props[prop] = val

def add_book(props)
  book =
  puts "case1: title : #{book.title} -- Author : #{}"
  puts "case2: title : #{book[:title]} -- Author : #{book[:author]}"
  puts "case3: title : #{book['title']} -- Author : #{book['author']}"

In a next post i explain ho to to improve the optinal parameter using meta-programming.

Ruby idioms : The splat operator.

The split mode :

   pet1, pet2, pet3 = *["duck","dog","cat"]

The collect mode :

  *zoo = pet1, pet2, pet3

The splat operator can be used in a case statement :

BOARD_MEMBERS = ['Jan', 'Julie', 'Archie', 'Stewick']
HISTORIANS = ['Braith', 'Dewey', 'Eduardo']case name
  "You're on the board!  A congratulations is in order."
  "You are busy chronicling every deft play." 
  "We welcome you all to the First International
   Symposium of Board Members and Historians Alike."

We can also use the splat operator to create a Hash object form a array of pairs.



a = [[:planes, 21], [:cars, 36]]
h = Hash[*a]  # => { :planes=>21, :cars=>36}

The ||= and |= operators

We initialize a variable with a value with the operator |=, but the value is only assigned if the variable has not been initialized yet.

  # Forma mas o menos normal
  if not breakfast
     breakfast = :bacon

  # Esta forma es mas concisa y mas parecida al lenguaje humano
  breakfast = :bacon unless breakfast

  #usando ||=
  breakfast ||= :bacon

With the ||= we can add an object to an array, but if the object is already present the array, the operation dosnt introduce repetition in the array.

    animals  = ['dog','cat']

    # usando un condicion
    animlas += ['dog'] unless animals.include? ('dog')

    # using |=
    >> animal = ['dog','cat']
    => ["dog", "cat"]

    >> animal |= ['cat']
    => ["dog", "cat"]

    >> animal |= ['cat']
    => ["dog", "cat"]

    >> animal += ['cat']
    => ["dog", "cat", "cat"]

Ruby idioms : case

How to use the case statement :

case val
  when 5: puts 'integer'
  when 'foo': puts 'string'
  when :a, :b, :c: puts 'list'
  when *[:d, :e, :f]: puts 'splatted list'
  when /d{2,4}/: puts 'regex'
  else puts 'the rest'