开发者

Is it possible to call a Module function from inside a class that is also in that module

In this Ruby code:

Module M
  Class C < Struct.new(:param)
    def work
      M::helper(param)
    开发者_StackOverflow中文版end
  end

  def helper(param)
    puts "hello #{param}"
  end
end

I get a "undefined method 'helper' for 'M:Module'" error when I try to run

c = M::C.new("world")
c.work

but calling M::helper("world") directly from another class works fine. Can classes not call Module functions that are defined in the same Module they are defined in? Is there a way around this other than moving the class outside of the module?


In order to invoke M::helper you need to define it as def self.helper; end For the sake of comparison, take a look at helper and helper2 in the following modified snippet

module M
  class C < Struct.new(:param)
    include M     # include module to get helper2 mixed in
    def work
      M::helper(param)
      helper2(param)
    end
  end

  def self.helper(param)
    puts "hello #{param}"
  end

  def helper2(param)
    puts "Mixed in hello #{param}"
  end
end

c = M::C.new("world")
c.work


you should prepend module method with self:

module M
  class C < Struct.new(:param)
    def work
      M::helper(param)
    end
  end

  def self.helper(param)
    puts "hello #{param}"
  end
end


C is trying to call helper on M when helper is not in M's singleton class. Also, you keep saying that helper is a module function, when it is only a method. Making helper a module function will make the code work:

module M
  class C < Struct.new(:param)
    def work
      M::helper(param)
    end
  end

  module_function

  def helper(param)
    puts "hello #{param}"
  end
end

Including the module in the class will also work:

module M
  class C < Struct.new(:param)
    include M

    def work
      helper(param)
    end
  end

  def helper(param)
    puts "hello #{param}"
  end
end

In the first example, helper is put into M's singleton class with module_function. The second example imports M's methods into C so that C can use them. Another difference is that in the first, you will be able to call M.helper from anywhere in your code. In the second, you will be able to call helper from any instance of C in your code. To fix this, make helper private:

module M
  class C < Struct.new(:param)
    include M

    def work
      helper(param)
    end
  end

  private
  def helper(param)
    puts "hello #{param}"
  end
end


Here is some explanation:

From ruby docs.

A Module is a collection of methods and constants. The methods in a module may be instance methods or module methods. Instance methods appear as methods in a class when the module is included, module methods do not. Conversely, module methods may be called without creating an encapsulating object, while instance methods may not. (See Module#module_function.)

self.methodname inside a module creates a module method.

In this case when you invoke M::helper you actually doing M.helper when you look at it from a C++ developer's point of view. The receiver is Module object (an instance of ruby builtin type Module) in this case.


Other way to looking at this is to understand a receiver concept, every method call consists of a receiver and method name (+ optionally params and code block). Receiver can be a Module object, Class object or instance of a user defined class.

You can only invoke Module (or Class) methods on a Module (or Class) object. You can invoke any method (Module/Class/instance) on an instance.

If you want to invoke an instance method defined in a module then you must give it a receiver by including that module in some class and creating an instance of it.

So in this case another solution can be:

module MM
  def helper(param)
    puts "hello #{param}"
  end

  class ReceiverClass
    include MM           # add helper() to ReceiverClass
  end

  class C < Struct.new(:param)
    def work
      ReceiverClass.new.helper(param)
    end
  end
end

c = MM::C.new("world")
c.work
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜