问题
module Country
def location
puts "location"
end
def self.included(base)
def cities
puts "cities"
end
end
def self.extended(base)
def animals
puts "animals"
end
end
end
class Test
include Country
end
class Test2
extend Country
end
As far as I understand, self.included will be invoked when the module is being included as instance method where as self.extended will be invoked when the module is being extended as static class method.
But when I have two class in the same file, why it's not throwing error
Test.new.animals
=>animals
And If I removed the Test 2 class,
# class Test2
# extend Country
# end
Test.new.animals
=>No method error
回答1:
def bar
without an explicit definee (i.e. def foo.bar
) defines bar
in the closest lexically enclosing module. The closest lexically enclosing module for all three of your def
s is always Country
, so all three methods are defined in the Country
module.
If you want to define a singleton method, you could use
module Country
def self.extended(base)
def base.animals
puts "animals"
end
end
end
See Ruby what class gets a method when there is no explicit receiver? for more details.
回答2:
Perhaps I will make explicitly clear what I feel is not fully and explicitly addressed Jorg's beautiful answer (with the utmost respect) to those not intimately familiar with Ruby's "Object Model":
module Country
def location
puts "location"
end
def self.included(base)
def cities
puts "cities"
end
end
def self.extended(base)
puts "#{self}" ## NOTICE THIS NEW LINE! NEW LINE
def animals
puts "animals"
end
end
end
class Test
include Country
end
class Test2
extend Country
end
Test.new.animals
What is the problem?
We are extending Test2, aren't we? How then is the animals method defined in Test1?
The key is to add a puts "#{self}
line above the animals method.
We can see here that the animals method is defined in the the Country module. So really, when you think you are extending, it, you are are in fact making sure it's added as an instance method, rather than a "static class method" (if you're coming from a c# / java background). That's not strictly speaking, accurate: when you are "extending" like this - and if you are doing it correctly - you are in fact adding the method to Test2's singleton class. Ruby's object model is a little tricky in that regard. A static class method is a method ADDED to a class's singleton class. What's a singleton class? Now you are getting into ruby's object model. It's complicated and a bit of a brain drain, but once you get it, you can do some pretty powerful (and dangerous?) things like: monkey patching.
The Solution:
Jorg says it better than I could. you need to define animals like this: def base.animals
.
来源:https://stackoverflow.com/questions/61669750/ruby-self-extended-gets-called-as-instance-method