Realizt30
Realizt30

Reputation: 183

Ruby self.extended gets called as instance method

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

Upvotes: 1

Views: 1321

Answers (2)

BenKoshy
BenKoshy

Reputation: 35731

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.

Upvotes: 0

Jörg W Mittag
Jörg W Mittag

Reputation: 369624

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.

Upvotes: 3

Related Questions