public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Add :allow_nil option to delegate [#1127 state:resolved]

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
porras (author)
Sun Dec 07 15:53:38 -0800 2008
lifo (committer)
Sun Dec 21 15:24:06 -0800 2008
commit  e8de7a67a5ef063164da022845a7cae1753da80e
tree    6b9f49120dd9f95b4b5800454520bf1cc4dcf793
parent  f7bd0beb67c5d9d50e37aa596605b91e61197fbe
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *2.3.0 [Edge]*
0
 
0
+* Add :allow_nil option to delegate. #1127 [Sergio Gil]
0
+
0
 * Add Benchmark.ms convenience method to benchmark realtime in milliseconds.  [Jeremy Kemper]
0
 
0
 * 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
73
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
76
77
...
84
85
86
 
 
87
88
89
90
 
91
92
93
...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
...
108
109
110
111
112
113
114
115
 
116
117
118
119
0
@@ -72,6 +72,30 @@ class Module
0
   #   invoice.customer_name    # => "John Doe"
0
   #   invoice.customer_address # => "Vimmersvej 13"
0
   #
0
+  # If the object to which you delegate can be nil, you may want to use the
0
+  # :allow_nil option. In that case, it returns nil instead of raising a
0
+  # NoMethodError exception:
0
+  #
0
+  #  class Foo
0
+  #    attr_accessor :bar
0
+  #    def initialize(bar = nil)
0
+  #      @bar = bar
0
+  #    end
0
+  #    delegate :zoo, :to => :bar
0
+  #  end
0
+  #
0
+  #  Foo.new.zoo   # raises NoMethodError exception (you called nil.zoo)
0
+  #
0
+  #  class Foo
0
+  #    attr_accessor :bar
0
+  #    def initialize(bar = nil)
0
+  #      @bar = bar
0
+  #    end
0
+  #    delegate :zoo, :to => :bar, :allow_nil => true
0
+  #  end
0
+  #
0
+  #  Foo.new.zoo   # returns nil
0
+  #
0
   def delegate(*methods)
0
     options = methods.pop
0
     unless options.is_a?(Hash) && to = options[:to]
0
@@ -84,10 +108,12 @@ class Module
0
 
0
     prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
0
 
0
+    allow_nil = options[:allow_nil] && "#{to} && "
0
+
0
     methods.each do |method|
0
       module_eval(<<-EOS, "(__DELEGATION__)", 1)
0
         def #{prefix}#{method}(*args, &block)
0
-          #{to}.__send__(#{method.inspect}, *args, &block)
0
+          #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block)
0
         end
0
       EOS
0
     end
...
41
42
43
 
 
 
 
44
45
46
...
117
118
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
121
122
...
41
42
43
44
45
46
47
48
49
50
...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
0
@@ -41,6 +41,10 @@ Invoice   = Struct.new(:client) do
0
   delegate :street, :city, :name, :to => :client, :prefix => :customer
0
 end
0
 
0
+Project   = Struct.new(:description, :person) do
0
+  delegate :name, :to => :person, :allow_nil => true
0
+end
0
+
0
 class Name
0
   delegate :upcase, :to => :@full_name
0
 
0
@@ -117,6 +121,29 @@ class ModuleTest < Test::Unit::TestCase
0
     end
0
   end
0
 
0
+  def test_delegation_with_allow_nil
0
+    rails = Project.new("Rails", Someone.new("David"))
0
+    assert_equal rails.name, "David"
0
+  end
0
+
0
+  def test_delegation_with_allow_nil_and_nil_value
0
+    rails = Project.new("Rails")
0
+    assert_nil rails.name
0
+  end
0
+
0
+  def test_delegation_with_allow_nil_and_nil_value_and_prefix
0
+    Project.class_eval do
0
+      delegate :name, :to => :person, :allow_nil => true, :prefix => true
0
+    end
0
+    rails = Project.new("Rails")
0
+    assert_nil rails.person_name
0
+  end
0
+
0
+  def test_delegation_without_allow_nil_and_nil_value
0
+    david = Someone.new("David")
0
+    assert_raises(NoMethodError) { david.street }
0
+  end
0
+
0
   def test_parent
0
     assert_equal Yz::Zy, Yz::Zy::Cd.parent
0
     assert_equal Yz, Yz::Zy.parent

Comments

adzap Sun Dec 21 21:14:43 -0800 2008

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

matthewrudy Tue Dec 23 14:59:56 -0800 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.