Of Mice and Men


Improved ‘try()’

Posted in Ruby by Anders Engström on the March 2nd, 2008

I recently discovered the nice ‘try()’ hack that chris@ozmm.org created.

It has some resemblance to my Array#to_proc - so I though that I would try to create an extended ‘try()’ :)

It works with “nested methods” and supports a default return value.

Here we go:

class Object
  def try(*args)
      options = {:default => nil}.merge(args.last.is_a?(Hash) ? args.pop : {})
      target = self # Initial target is self.
      while target && mtd = args.shift
        target = target.send(mtd) if target.respond_to?(mtd)
      end

      return target || options[:default]
  end
end


class Person
  attr_accessor :name
  attr_accessor :address

end
class Address
  attr_accessor :street
end

def print_info(person, default = nil)
  puts <<-END
    ==============================================================
    Name: #{person.try(:name, :default => default)}
    Street: #{person.try(:address, :street, :default => default)}
  END
end

@person = Person.new
@person.name = "Frank Black"
@person.address = Address.new
@person.address.street = "xyz"
print_info(@person)

@person.address = nil
print_info(@person)
print_info(@person, "No Info Registered")

Array#to_proc - Nested properties for to_proc hack.

Posted in Ruby by Anders Engström on the April 18th, 2007

Ruby is really elegant when it come to extending core libraries with custom behavior.

I needed a way to apply the standard to_proc hack to nested method calls. Normally the to_proc implementation in Symbol allows you to replace

['abc', 'defg'].map{|x| x.size} #=> [3, 4]

with

['abc, 'defg'].map(&:size) #=> [3, 4]

But this only applies to “first level” methods on the target reference. The following extension of the Array class allows specifying a ‘call chain’ of methods:

class Array
  def to_proc            
    proc{|target|        
      inject(target){|memo, arg| 
        break unless memo
        memo.send(arg)            
      }
    }
  end
end

So, in order to extract the regnr of an optional default_trailer:

class Car < ActiveRecord::Base
    has_one :default_trailer, :class_name => 'Trailer'
end

class Trailer < ActiveRecord::Base
    belongs_to :car
end

you can do:

[car1, car2, car3].map(&[:default_trailer, :regnr]) 
    #=> ['GH3456', nil, 'FF1234']                   
Comments Off