Reputation: 125902
Given a module with a singleton method like this:
module Foo
class << self
def bar
puts "method bar from Foo"
end
end
end
How can I override Foo.bar
using another module?
Upvotes: 3
Views: 1877
Reputation: 8293
This code
module Foo
class << self
def bar
puts "method bar from Foo"
end
end
end
is equal to
class << Foo
def bar
puts "method bar from Foo"
end
end
that is also equal to
def Foo.bar
puts "method bar from Foo"
end
So, you can call it to redefine this method everywhere where Foo
is defined (ever withing another module, without including Foo
).
Upvotes: 3
Reputation: 125902
My problem was that I forgot to think through the inheritance chain. I was looking for a way to override the method by modifying the inheritance chain, but that's not possible.
The reason is that bar
is defined on Foo
itself, so it never looks up its inheritance chain for the method. Therefore, to change bar
, I have to change it on Foo
itself.
While I could just re-open Foo, like this:
module Foo
def self.bar
puts "new foo method"
end
end
... I prefer a way to be able to wrap the original bar
method, as though I
were subclassing and could call super
. I can achieve that by setting up an
alias for the old method.
module Foo
class << self
def bar
"method bar from Foo"
end
end
end
puts Foo.bar # => "method bar from Foo"
module FooEnhancement
# Add a hook - whenever a class or module calls `extend FooEnhancement`,
# run this code
def self.extended(base)
# In the context of the extending module or class
# (in this case, it will be Foo), do the following
base.class_eval do
# Define this new method
def self.new_bar
"#{old_bar} - now with more fiber!"
end
# Set up aliases.
# We're already in the context of the class, but there's no
# `self.alias`, so we need to call `alias` inside this block
class << self
# We can call the original `bar` method with `old_bar`
alias :old_bar :bar
# If we call `bar`, now we'll get our `new_bar` method
alias :bar :new_bar
end
end
end
end
# This will fire off the self.extended hook in FooEnhancement
Foo.extend FooEnhancement
# Calls the enhanced version of `bar`
puts Foo.bar # => 'method bar from Foo - now with more fiber!'
Upvotes: 4
Reputation: 3030
You can do the following:
module Foo
class << self
def bar
puts "method bar from Foo"
end
def baz
puts "method baz from Foo"
end
end
end
module Foo2
def Foo.bar
puts "new version of bar"
end
end
include Foo
Foo.baz #=> method baz from Foo
Foo.bar #=> new version of bar
Or instead of naming it Foo2
simply re-open Foo
.
module Foo
class << self
def bar
puts "method bar from Foo"
end
def baz
puts "method baz from Foo"
end
end
end
module Foo
class << self
def bar
puts "new version of bar"
end
end
end
include Foo
Foo.baz #=> method baz from Foo
Foo.bar #=> new version of bar
Upvotes: 0