Here is what my code looks like:
module A
def foo
puts "A"
end
end
module B
include A
def bar
foo
end
end
开发者_运维问答class MyClass
include B
def foo
puts "X"
end
def self.test
puts bar
end
end
When I call "C.test" I get "X" instead of "A" (which is what I want) because the local definition of foo has overridden that in A. I can't change the signature of either foo's. I can only mainly edit my own class; I can edit modules A and B but lots of existing code use them and they are (so no changing foo to A.foo for instance). I am thinking of doing
class MyClass
module MyModules
include B
end
....
MyModules.bar
....
end
But this does not work.
How can I "localize" the namespace when doing include B?
From what i understand, you want to override A#foo
(which is also used by B#bar
) inside MyClass
. However, you only want to override it inside MyClass
, and not for the code in the B
mixin.
The thing to understand here, is that when the code of the A
and B
mixins are run, they will be part of a MyClass
instance. Therefore, it is impossible to override the instance methods of MyClass
without affecting the mixins. It is as if the methods of the mixins were thrown into a bucket (the MyClass
instance), and then run. They do not have separate scopes. Therefore, the simple answer is: no, you can not do it.
There could be several possible solutions though, many of which smell like pasta. Running into problems like this one could be an indication that the overall design decisions need some refactoring.
Going over the basics of modules:
module Numbered
DEFAULT = "1234-AWESOME"
def serial_number
DEFAULT
end
def self.awesome?
true
end
end
Numbered.awesome? # => true
Numbered.serial_number # whoops! NoMethodError.
o = Object.new
o.extend(Numbered)
o.serial_number #=> "1234-AWESOME"
Numbered.extend(Numbered)
Numbered.serial_number # => "1234-AWESOME"
class Dog
extend Numbered
end
Dog.serial_number # => "1234-AWESOME"
Dog::DEFAULT # => "1234-AWESOME"
fido = Dog.new
fido.serial_number # NoMethodError!
This is because the method was added as a class method. include
to the rescue:
class Fish
include Numbered
def serial_number
super + '-FSH'
end
end
cod = Fish.new
cod.serial_number # => "1234-AWESOME-FSH"
So, include
heaps all the methods together into one common instance, but we can still use super
to call an included method that we were overriding. If you go for a solution on the pasta side, you might be able to use super inside the bar
method, conditionally invoking the method you were overriding.
Also, depending on the case, you could set up a new module and extend
it with the module containing the methods you need access too, like in the answer you added.
I found a solution for what I wanted, might not be the best answer but works exactly the way I want it to.
class MyClass
class MyModules
extend B
end
....
MyModules.bar
....
end
This effectively allows me to limit the scope of B and all other included modules in the local class MyModules. In my code it actually has a meaning because all the included modules handle expression manipulation so I can call the local class "Expr" (and they are seriously overusing the names 'eval', 'bind' and 'free' right now).
精彩评论