Posts Tagged ‘forwardable’

Using delegate in ruby

Sunday, June 28th, 2009

As I have been wandering ruby gems and rails plugin codes, I found delegate is used in many ways. For example, in attachment_fu plugin, 

delegate :content_types, :to => Technoweenie::AttachmentFu

And in mechanize gem,

def_delegator :parser, :search, :search

Things to do with above codes are intuitive. In the first, if it receives content_types request, then invoke Technoweenie::AttachementFu.content_types. And in second one, if it receives search request, then invoke parser.search method. It is what the delegate normally do.

This is handy beacause it reduces the effort to declare an additional function like below.

def content_types
  Technoweenie::AttachmentFu.content_types
end

As you can see in the above examples, there are two ways in using delegate.(Maybe more than two in the whole ruby world, but now I am focusing what I’ve found.) One is using activesupport, and the other way is using Forwardable module in Ruby standard libray.

delegate in ActiveSupport

Activesupport extends Module and adds delegate method to it. Therefore just by requiring activesupport, you can use delegate method at any Class.

irb(main):001:0> Object.respond_to? :delegate
=> false
irb(main):002:0> require 'activesupport'
=> true
irb(main):003:0> Object.respond_to? :delegate
=> true

Using delegate is easy. Just two things are needed. method name to delegate and target object.

require 'activesupport'
class SayHello
  def greet(name)
    "hello #{name}"
  end
end

class Foo
  attr_accessor :greeter
  delegate :greet, :to => :greeter
end

foo = Foo.new
foo.greeter = SayHello.new
foo.greet("Tom")  # => "hello Tom"

After declaring delegate, Foo comes to have instance method ‘greet’, which forwards greet request to greeter. To make delegate smarter, two more options, prefix and allow_nil are possible.

A prefix option adds prefix word to the method. For example ,

class Foo
  attr_accessor :greeter
  delegate :greet, :to => :greeter, :prefix => :hello
end
foo.hello_greet("Tom")  # => "hello Tom"

If allow_nil option is true, delegate method returns nil instead of Exception, when delegate object is absent. For example,

class Foo
  attr_accessor :greeter
  delegate :greet, :to => :greeter, :allow_nil => true
end
foo = Foo.new
foo.greeter = nil
foo.greet("Tom")  # => nil

delegate in Forwardable module

If you are not a big fan of activesupport, you can use Forwardable module in standard ruby library. To do the same thing using forwardable is like below.

require "forwardable"
class SayHello
  def greet(name)
    "hello #{name}"
  end
end

class Foo
  extend Forwardable
  attr_accessor :greeter
  def_delegator :greeter, :greet
end

foo = Foo.new
foo.greeter = SayHello.new
foo.greet("Tom")  # => "hello Tom" 

The parameters of def_delegator is target_object, method, alias_method(optional). An alias_method is the name of method that Foo creates. If alias_method is absent, method is used instead. So In the above, Foo come to have greet method, which calls greeter.greet. Any other options are not allowed.