-1

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.

Eli Rose
  • 6,788
  • 8
  • 35
  • 55
  • _"I want to call foo from bar"_ – if there was an `A.foo` in addition to `M.foo`, which `foo` would you want to call? – Stefan Sep 05 '17 at 07:00
  • 3
    Why don't you want to use `M.foo`? That seems to me the logical thing to do. It does seem odd to have both the class method and instance method in the same module., considering that you want to invoke the first and include the second. – Cary Swoveland Sep 05 '17 at 07:01
  • 1
    There is no relationship whatsoever between an instance of `A` and `M`'s singleton class. In particular, `M`'s singleton class is not in `A`'s inheritance chain. – Jörg W Mittag Sep 05 '17 at 10:37

3 Answers3

1

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.

kiddorails
  • 12,961
  • 2
  • 32
  • 41
  • Thanks, I didn't think of this! Since it seems there is no solution which avoids including `foo` in `A`, and I understand why thanks to Jorg's comment, I'll accept this answer as the closest to what I want. – Eli Rose Sep 06 '17 at 02:22
0

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.

user1934428
  • 19,864
  • 7
  • 42
  • 87
0

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
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100