How do I access a module from an instance method in Ruby?

135 Views Asked by At

Suppose I have a module:

module M
  def self.foo
    ...
  end

  def bar
    ...
  end
end

Module M is included in a class.

class A
  include M
end

I want to call foo from bar, which will eventually be called on an instance of A. What's the best way to do this inside bar?

Of course I can just say M.foo, but that's duplication of the module's name, which feels unnecessary.

3

There are 3 best solutions below

1
On BEST ANSWER

Usually, in a module, it's a good practice to have class-methods and instance-methods separate, like this:

module M
  def bar
    puts "BAR"
    self.class.foo
  end

  module ClassMethods
    def foo
      puts "FOO"
    end
  end
end

Now, in a class, I would ideally want to include this module M in a way that I get A.foo as class method and A.new.bar as instance method. The trick for that is Module.included.

module M
  def bar
    puts "BAR"
    self.class.foo
  end

  module ClassMethods
    def foo
      puts "FOO"
    end
  end

  # when module is included, extend the class with ClassMethods
  def self.included(base)
    base.extend ClassMethods
  end
end

class A
  include M
end

A.singleton_methods #=> [:foo]

A.new.foo
#=> BAR
#=> FOO

With this approach, you can refer the class method with self.class and it will automagically work.

Hope it helps.

0
On

Not really elegant, but

def bar
  Module.nesting.last.foo
end

should do it.

Note that Module#nesting returns an array because one module might be nested into another module. In the general case, you need to apply the correct array index to pick the module you want to have.

1
On

I think using M.foo is best, but as an exercise, one could change M#bar as follows.

module M
  def self.foo
    puts "In self.foo"
  end

  def bar
    puts "In bar"
    method(__method__).owner.foo
  end
end

class A
  include M
end

a = A.new
a.bar
  # In bar
  # In self.foo