Reputation: 12181
I want to provide a consistent interface by inheriting from a base class. But certain methods on that base class I would like to wrap with additional functionality.
Let's say every subclass will have an eat method:
class ClassyAnimal
def self.inherited(base)
base.class_eval do
alias :eat_without_napkin :eat
alias :eat :eat_with_napkin
end
end
def eat_with_napkin
begin
eat_without_napkin
rescue FoodOutOfMouthError
puts 'phew!'
end
end
def eat
raise 'Please implement the #eat method.'
end
end
class ClassyWalrus < ClassyAnimal
def eat
puts 'Eating Cronuts.'
raise FoodOutOfMouthError.new('Damn these tusks!')
puts 'Finishing my meal.'
end
end
class FoodOutOfMouthError < StandardError; end
Now I would hope that ClassyWalrus.new.eat
would output the following:
Eating Cronuts.
phew!
Unfortunately, we get the following:
Eating Cronuts.
FoodOutOfMouthError: Damn these tusks!
At the time of inheritance, and therefore at time of alias
, #eat is defined as the one inside of ClassyAnimal instead of the one inside of ClassyWalrus. The aliased #eat then gets overridden by the subclass, and the wrapping is lost.
What can be done?
Upvotes: 3
Views: 625
Reputation: 12181
One possibility I thought of, which I believe is how ActionMailer does this, is to define the method on the child as an instance variable, but invoke it on the class itself.
This isn't exactly what I'm looking for, so I won't accept this answer, but just to document it.
class ClassyAnimal
def self.eat
if instance_methods.include? :eat
begin
self.new.eat
rescue FoodOutOfMouthError
puts 'phew!'
end
end
end
end
class ClassyWalrus < ClassyAnimal
def eat
puts 'Eating Cronuts.'
raise FoodOutOfMouthError.new('Damn these tusks!')
puts 'Finishing my meal.'
end
end
class FoodOutOfMouthError < StandardError; end
Which would be called as:
ClassyWalrus.eat
# outputs => Eating Cronuts.
# outputs => phew!
Upvotes: 0
Reputation: 16507
Try the following additions to the code.
in class ClassyAnimal
:
def eat_with_napkin
begin
yield
rescue FoodOutOfMouthError
puts 'phew!'
end
end
in class ClassyWalrus
:
def eat
puts 'Eating Cronuts.'
superclass.eat do
raise FoodOutOfMouthError.new('Damn these tusks!')
end
puts 'Finishing my meal.'
end
Upvotes: 1