I'm trying to create private helper methods for module functions to no avail. I feel like there's something very simple I'm missing.
Updated example with a more understandable use case:
module FancyScorer
module_function
def score(ary)
scores = []
ary.each_slice(2).with_index do |slice, i|
scores <<
case i % 2
when 0
score_eventh(ary)
else
score_oddth(ary)
end
end
scores.inject(:+)
end
private
def score_eventh(ary)
ary.inject(:+) / (ary.size - 1)
end
def score_oddth(ary)
ary.inject(:*) / (ary.size - 1)
end
end
FancyScorer.score([1,2,3,4])
# => `block in score_curiously': undefined method `score_eventh'
# for FancyScorer:Module (NoMethodError)
Note: The private methods should remain private.
Here's the use case: there are several modules that contain various scoring techniques, e.g. FancyScorer
, SimpleScorer
, ComplexScorer
. These functions are tested independently, and then are employed to create a score
method for different classes. For instance:
class A
...
def score
FancyScorer.score(metrics) + 2*SimpleScorer.score(metrics)
end
end
class B
...
def score
0.5*FancyScorer.score(metrics) + 2*SimpleScorer.score(metrics[0,3]) + ComplexScorer.score(metrics)
end
end
Previous example with no provided use case:
module Party
module_function
def pooper
enjoy
end
private
def enjoy
puts "Wahoo!"
end
end
Party.pooper
# => NameError: undefined local variable or method `enjoy' for Party:module
# from (party): in `pooper`
module_function(symbol, ...) → self
says:-
Creates module functions for the named methods. These functions may be called with the module as a receiver, and also become available as instance methods to classes that mix in the module. Module functions are copies of the original, and so may be changed independently. The instance-method versions are made private. If used with no arguments, subsequently defined methods become module functions.
You need to make sure you declare the helper method as a #private_class_method
: private
only affects instance methods.
module Party
def enjoy
puts 'hello'
end
def pooper
enjoy
end
private_class_method :enjoy
module_function :pooper
end
Party.pooper # => 'hello'
Party.enjoy # => private method `enjoy' called for Party:Module (NoMethodError)
Furthermore, you should be careful about the ordering of accessibility keywords like public
, private
, and module_function
. These do not overlap, but rather override.
module Party
module_function # Module function declarations begin
def pooper
bar
end
def bar
enjoy
end
private # Private declarations begin, module function declarations end
module_function # Private declarations end, module function declarations begin
def enjoy # Therefore, this is a module function
"Wahoo!"
end
end
Party.pooper # => "Wahoo!"
Party.bar # => "Wahoo!"
Party.enjoy # => "Wahoo!" <-- No longer private
Note here module_function
overrides the previous private
declaration.
Here some more examples when module_function
will do its job, and when not.
The module_function
definitions stop once another accessibility keyword pops up. In this example, module_function
is interrupted by public
making #pooper
a public instance method. Use of private
would similarly block module_method
.
module Party
module_function
public
def pooper
"i am pooper"
end
end
Party.pooper
# undefined method `pooper' for Party:Module (NoMethodError)
Now if the order is changed:
module Party
public
module_function
def pooper
"i am pooper"
end
end
Party.pooper # => "i am pooper"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With