rubymoduleinclude

Ruby- NoMethodError across modules


I'm having some issues with getting to grips with using modules correctly.

I have:

module Utilities
  def file_search()
    # some code
    return x
  end
end


module Remake_Components
  require 'csv'
  include Utilities

  f = Utilities.file_search()
end

Which gives me the error: #<NoMethodError: undefined method "file_search" for Utilities:Module> I would have expected f to be the result of running the file_search function.

My understanding was I had to use the include keyword to use the functions in the other method but it doesn't seem to be so?


Solution

  • My understanding was I had to use the include keyword to use the functions in the other method but it doesn't seem to be so?

    Module#include will inject the Module into the including Object's hierarchical chain and will make instance methods of the included Module accessible to instances of the including Object.

    In your case file_search is an instance method defined in Utilities and by calling include Utilities you have made this method available to instances of Remake_Components (however Modules do not have instances (per se)).

    The way you are trying to call file_search with the Utilities Module as a receiver does not require a call to include but rather requires the method to be defined as a "class instance method" or a "module function".

    If you would like it to work this way then you have 2 options:

    module Utilities
      def file_search 
        # some code
        return x
      end
      module_function :file_search
    end
    

    Both options will allow you to call Utilities.file_search; however, the latter will also allow you to "include" this method as a private instance method when using Module#include.

    By way of Example:

    module A 
      def self.foo = 'Foo'
      def bar = 'Bar'
      module_function :bar
    end 
    
    class B 
      include A 
      def test_foo = A.foo
      def test_bar = bar
    end 
    
    A.foo
    #=> "Foo"
    A.bar 
    #=> "Bar"
    B.new.test_foo
    #=> "Foo"
    B.new.test_bar
    #=> "Bar" 
    B.new.foo
    #=> undefined method `foo' for #<B:0x00007f0c7785e528> (NoMethodError)
    B.new.bar
    #=> private method `bar' called for #<B:0x00007ff5a5dbe5a0> (NoMethodError)
    

    A few other less consequential notes: