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
精彩评论