Created
February 4, 2009 10:48
-
-
Save matthewrudy/58054 to your computer and use it in GitHub Desktop.
Rails "delegate" with a :default
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Module | |
# Provides a delegate class method to easily expose contained objects' methods | |
# as your own. Pass one or more methods (specified as symbols or strings) | |
# and the name of the target object as the final :to option (also a symbol | |
# or string). At least one method and the :to option are required. | |
# | |
# Delegation is particularly useful with Active Record associations: | |
# | |
# class Greeter < ActiveRecord::Base | |
# def hello() "hello" end | |
# def goodbye() "goodbye" end | |
# end | |
# | |
# class Foo < ActiveRecord::Base | |
# belongs_to :greeter | |
# delegate :hello, :to => :greeter | |
# end | |
# | |
# Foo.new.hello # => "hello" | |
# Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c> | |
# | |
# Multiple delegates to the same target are allowed: | |
# class Foo < ActiveRecord::Base | |
# belongs_to :greeter | |
# delegate :hello, :goodbye, :to => :greeter | |
# end | |
# | |
# Foo.new.goodbye # => "goodbye" | |
# | |
# | |
# Methods can be delegated to instance variables, class variables, or constants | |
# by providing the variable as a symbol: | |
# class Foo | |
# CONSTANT_ARRAY = [0,1,2,3] | |
# @@class_array = [4,5,6,7] | |
# | |
# def initialize | |
# @instance_array = [8,9,10,11] | |
# end | |
# delegate :sum, :to => :CONSTANT_ARRAY | |
# delegate :min, :to => :@@class_array | |
# delegate :max, :to => :@instance_array | |
# end | |
# | |
# Foo.new.sum # => 6 | |
# Foo.new.min # => 4 | |
# Foo.new.max # => 11 | |
# | |
# A default can be added which will be used in the case that the target is not truthy | |
# class Foo | |
# belongs_to :greeter | |
# delegate :said_hello?, :to => :greeter, :default => false | |
# end | |
# | |
# Foo.new.greeter # => nil | |
# Foo.new.said_hello? # => false | |
# | |
def delegate(*methods) | |
options = methods.pop | |
unless options.is_a?(Hash) && to = options[:to] | |
raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)." | |
end | |
methods.each do |method| | |
if options.has_key?(:default) | |
module_eval(<<-EOS, "(__DELEGATION__)", 1) | |
def #{method}(*args, &block) | |
if #{to} | |
#{to}.__send__(#{method.inspect}, *args, &block) | |
else | |
#{options[:default].inspect} | |
end | |
end | |
EOS | |
else | |
module_eval(<<-EOS, "(__DELEGATION__)", 1) | |
def #{method}(*args, &block) | |
#{to}.__send__(#{method.inspect}, *args, &block) | |
end | |
EOS | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Delegated | |
attr_accessor :delegate_to | |
delegate :defaulted_to_nil, :to => :delegate_to, :default => nil | |
delegate :defaulted_to_symbol, :to => :delegate_to, :default => :some_symbol | |
delegate :not_defaulted, :to => :delegate_to | |
end | |
describe Module, "delegation" do | |
before(:each) do | |
@it = Delegated.new | |
end | |
describe "with default" do | |
it "should default to nil if the delegated attribute does not exist" do | |
@it.delegate_to.should == nil | |
@it.defaulted_to_nil.should == nil | |
end | |
it "should default to a given symbol if the delegated attribute does not exist" do | |
@it.delegate_to.should == nil | |
@it.defaulted_to_symbol.should == :some_symbol | |
end | |
it "should delegate to the provided target if it exists" do | |
to = stub("that which it is delegated to") | |
to.should_receive(:defaulted_to_nil).and_return(:something) | |
to.should_receive(:defaulted_to_symbol).and_return(:something_else) | |
@it.delegate_to = to | |
@it.defaulted_to_nil.should == :something | |
@it.defaulted_to_symbol.should == :something_else | |
end | |
end | |
describe "without default" do | |
it "should raise an exception if the target is nil" do | |
@it.delegate_to.should == nil | |
lambda {@it.not_defaulted}.should raise_error(NoMethodError) | |
end | |
it "should delegate to the provided target if it exists" do | |
to = stub("that which it is delegated to") | |
to.should_receive(:not_defaulted).and_return(:something) | |
@it.delegate_to = to | |
@it.not_defaulted.should == :something | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment