Ruby self.extended gets called as instance method
Asked Answered
W

2

1
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

Woolf answered 7/5, 2020 at 23:57 Comment(1)
You can define arbitrary methods with extended, but not with def in the block there. You should do base.define_method(...) if you need to do this dynamically.Clemmie
F
3

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.

Footie answered 8/5, 2020 at 6:29 Comment(1)
what about in the def self.include? do i need to do the same thing like def base.cities?Woolf
M
0

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.

Mcmillin answered 8/5, 2020 at 12:44 Comment(5)
You do it if you want to make a "static class method" - it's not strictly speaking accurate, I write it only because you're coming from a java / c# backgroundMcmillin
ok so I believe in the def self.included(base), i do not need to put def base.cities correct?Woolf
@noobyz that is incorrect: if you want to make a static class method, you HAVE TO put: def base.cities inside def self.included(base)Mcmillin
but i thought when we include a module that would make all the function an instance of the class where it's being included, and vice versa for excluding a module. Maybe I'm missing the purpose of having def self.included/self.excluded?Woolf
They do, but you made a mistake in your code. you forgot the word base when defining the static class method, and that word makes all the difference.Mcmillin

© 2022 - 2024 — McMap. All rights reserved.