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