Permalink
Browse files

Add :allow_nil option to delegate [#1127 state:resolved]

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
  • Loading branch information...
porras authored and lifo committed Dec 7, 2008
1 parent f7bd0be commit e8de7a67a5ef063164da022845a7cae1753da80e
View
@@ -1,5 +1,7 @@
*2.3.0 [Edge]*
+* Add :allow_nil option to delegate. #1127 [Sergio Gil]
+
* Add Benchmark.ms convenience method to benchmark realtime in milliseconds. [Jeremy Kemper]
* Updated included memcache-client to the 1.5.0.5 version which includes fixes from fiveruns and 37signals to deal with failover and timeouts #1535 [Joshua Sierles]
@@ -72,6 +72,30 @@ class Module
# invoice.customer_name # => "John Doe"
# invoice.customer_address # => "Vimmersvej 13"
#
+ # If the object to which you delegate can be nil, you may want to use the
+ # :allow_nil option. In that case, it returns nil instead of raising a
+ # NoMethodError exception:
+ #
+ # class Foo
+ # attr_accessor :bar
+ # def initialize(bar = nil)
+ # @bar = bar
+ # end
+ # delegate :zoo, :to => :bar
+ # end
+ #
+ # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
+ #
+ # class Foo
+ # attr_accessor :bar
+ # def initialize(bar = nil)
+ # @bar = bar
+ # end
+ # delegate :zoo, :to => :bar, :allow_nil => true
+ # end
+ #
+ # Foo.new.zoo # returns nil
+ #
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
@@ -84,10 +108,12 @@ def delegate(*methods)
prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
+ allow_nil = options[:allow_nil] && "#{to} && "
+
methods.each do |method|
module_eval(<<-EOS, "(__DELEGATION__)", 1)
def #{prefix}#{method}(*args, &block)
- #{to}.__send__(#{method.inspect}, *args, &block)
+ #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block)
end
EOS
end
@@ -41,6 +41,10 @@ class De
delegate :street, :city, :name, :to => :client, :prefix => :customer
end
+Project = Struct.new(:description, :person) do
+ delegate :name, :to => :person, :allow_nil => true
+end
+
class Name
delegate :upcase, :to => :@full_name
@@ -117,6 +121,29 @@ def initialize(client)
end
end
+ def test_delegation_with_allow_nil
+ rails = Project.new("Rails", Someone.new("David"))
+ assert_equal rails.name, "David"
+ end
+
+ def test_delegation_with_allow_nil_and_nil_value
+ rails = Project.new("Rails")
+ assert_nil rails.name
+ end
+
+ def test_delegation_with_allow_nil_and_nil_value_and_prefix
+ Project.class_eval do
+ delegate :name, :to => :person, :allow_nil => true, :prefix => true
+ end
+ rails = Project.new("Rails")
+ assert_nil rails.person_name
+ end
+
+ def test_delegation_without_allow_nil_and_nil_value
+ david = Someone.new("David")
+ assert_raises(NoMethodError) { david.street }
+ end
+
def test_parent
assert_equal Yz::Zy, Yz::Zy::Cd.parent
assert_equal Yz, Yz::Zy.parent

2 comments on commit e8de7a6

@adzap

This comment has been minimized.

Show comment
Hide comment
@adzap

adzap Dec 22, 2008

Contributor

Cool. I wrote my own implementation of this ages ago. Didn’t think anyone would use it. Nice to have in core!

Contributor

adzap replied Dec 22, 2008

Cool. I wrote my own implementation of this ages ago. Didn’t think anyone would use it. Nice to have in core!

@matthewrudy

This comment has been minimized.

Show comment
Hide comment
@matthewrudy

matthewrudy Dec 23, 2008

Contributor

I took the approach;
delegate :to => “something”, :default => nil

with my own patch,
and it was fine in tests,
but some bug presented itself in development mode.

can’t remember quite what.

Contributor

matthewrudy replied Dec 23, 2008

I took the approach;
delegate :to => “something”, :default => nil

with my own patch,
and it was fine in tests,
but some bug presented itself in development mode.

can’t remember quite what.

Please sign in to comment.