Ruby self.extended gets called as instance method

喜欢而已 提交于 2020-06-01 07:37:05

问题


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 defs 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!