Reputation: 2186
Suppose I have two classes:
Inner
has a method func
which yields values to the caller.Outer
has an overridden method func
which calls Inner.func
, does something with the yielded values, and yields them in turn to its own caller.When I run this sample code, the block in Outer
is skipped, and I get this weird undefined method
error:
# subclassed.rb
class Inner
def func
puts ' Inner entered func'
yield 1
yield 2
yield 3
puts ' Inner leaving func'
end
end
class Outer < Inner
def func
puts 'Outer entered func'
super.func do |value|
puts " Outer received #{value} !!! this never happens !!!"
yield value
end
puts 'Outer leaving func'
end
end
outer = Outer.new
outer.func do |value|
puts " Script received #{value}"
end
$ ruby subclassed.rb
Outer entered func
Inner entered func
Script received 1
Script received 2
Script received 3
Inner leaving func
C:/Source/temp/rubytest/subclassed.rb:14:in `func': undefined method `func' for nil:NilClass (NoMethodError)
from C:/Source/temp/rubytest/subclassed.rb:23:in `<main>'
However, if I use encapsulation, everything works as expected:
# encapsulated.rb
class Inner
def func
puts ' Inner entered func'
yield 1
yield 2
yield 3
puts ' Inner leaving func'
end
end
class Outer
def initialize
@inner = Inner.new # (use encapsulation)
end
def func
puts 'Outer entered func'
@inner.func do |value| # (use encapsulation)
puts " Outer received #{value}"
yield value
end
puts 'Outer leaving func'
end
end
outer = Outer.new
outer.func do |value|
puts " Script received #{value}"
end
$ ruby encapsulated.rb
Outer entered func
Inner entered func
Outer received 1
Script received 1
Outer received 2
Script received 2
Outer received 3
Script received 3
Inner leaving func
Outer leaving func
As an experiment, I changed def func
to def func(&callback)
, and yield 1
to callback.call(1)
, but I got identical behaviour.
There are some existing posts dealing with subclasses and superclasses, and some others dealing with yields. However, I can't find anything on this particular problem. This is the first time I've actually been surprised by something Ruby did in a long time! Anyone have any ideas?
Upvotes: 2
Views: 519
Reputation: 13014
class Outer < Inner
def func(&block)
puts "outer entered func"
super do |value|
puts " Outer received #{value}"
block.call(value)
end
puts "Outer leaving func"
end
end
I am passing the block with &block
and then calling block in context of parent's method. Also, just use super
instead of super.func
to call parent's method.
Output:
o = Outer.new
o.func do |val|
puts " Script received #{val}"
end
outer entered func
Inner entered func
Outer received 1
Script received 1
Outer received 2
Script received 2
Outer received 3
Script received 3
Inner leaving func
Outer leaving func
Upvotes: 2