Rails: I can't call a function in a module in /lib - what am I doing wrong?

Ruby on-RailsRuby

Ruby on-Rails Problem Overview


I have a module saved in /lib as test_functions.rb that looks like this

module TestFunctions
  def abc
    puts 123
  end
end

Going into ruby script/runner, I can see that the module is loading automatically (good ol' convention over configuration and all that...)

>> TestFunctions.instance_methods
=> ["abc"]

so the method is known, let's try calling it

>> TestFunctions.abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):3

Nope. How about this?

>> TestFunctions::abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):4

Test Nope again.

defined?(TestFunctions::abc) #=> nil, but
TestFunctions.method_defined? :abc #=> true

Like I said at the top, I know I'm being dumb, can anyone de-dumb me?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

If you want Module-level functions, define them in any of these ways:

module Foo
  def self.method_one
  end

  def Foo.method_two
  end

  class << self
    def method_three
    end
  end
end

All of these ways will make the methods available as Foo.method_one or Foo::method_one etc

As other people have mentioned, instance methods in Modules are the methods which are available in places where you've included the Module

Solution 2 - Ruby on-Rails

I'm going to try to summarise the various answers myself, since each had something valuable to say, but none really got to what I now realise is probably the best response:

I was asking the wrong question because I was doing it wrong.

For reasons I can no longer explain, I wanted a set of completely stand-alone functions in a library, which represented methods I was trying to DRY out of my classes. That can be achieved, using things like

module Foo
  def self.method_one
  end

  def Foo.method_two
  end

  class << self
    def method_three
    end
  end

  def method_four
  end

  module_function :method_four
end

I could also include my module, either within a class, in which case the methods become part of the class or outside, in which case they are defined on whatever class I'm running inside (Object? Kernel? Irb, if I'm interactive? Probably not a great idea, then)

The thing is, there was no good reason not to have a class in the first place - I'd somehow got on to a train of thought that took me down an seldom-used and frankly slightly weird branch line. Probably a flashback to the days before OO became mainstream (I'm old enough that up to today I've spent a lot more years writing procedural code).

So the functions have moved into a class, where they seem pretty happy, and the class methods thus exposed are being cheerfully used wherever necessary.

Solution 3 - Ruby on-Rails

You can also use module_function like so:

module TestFunctions
  def abc
    puts 123
  end

  module_function :abc
end

TestFunctions.abc  # => 123

Now you can include TestFunctions in class and call "abc" from within TestFunctions module.

Solution 4 - Ruby on-Rails

I messed with this for a while and learned several things. Hopefully this will help someone else out. I am running Rails 3.2.8.

My module (utilities.rb) looks like this and is in the /lib directory of my rails app:

module Utilities

  def compute_hello(input_string)
     return "Hello #{input_string}"
  end

end

My test (my_test.rb) looks like this and is in the /test/unit directory of my rails app:

require "test_helper"
require "utilities"

class MyTest < ActiveSupport::TestCase
  include Utilities

  def test_compute_hello
    x = compute_hello(input_string="Miles")
    print x
    assert x=="Hello Miles", "Incorrect Response"
  end

end

Here are a few things to note: My test extends ActiveSupport::TestCase. This is important because ActiveSupport adds /lib to the $LOAD_PATH. (seehttp://stackoverflow.com/questions/1073076/rails-lib-modules-and)

Secondly, I needed to both "require" my module file, and also "include" the module. Lastly, it is important to note that the stuff that gets included from the module essentially gets placed in the test class. So... be careful that the module that you include doesn't start with "test_". Otherwise, Rails will attempt to run your module method as a test.

Solution 5 - Ruby on-Rails

You can't call a method in a Module directly. You need to include it in a class. Try this:

>> class MyTest
>>   include TestFunctions
>> end
=> MyTest
>> MyTest.new.abc
123
=> nil

Solution 6 - Ruby on-Rails

You need to include the module

include Testfunctions

Then 'abc' will return something.

Solution 7 - Ruby on-Rails

You need to prefix your function with the module name because modules are not classes:

Your /lib/test_functions.rb:

module TestFunctions
  def TestFunctions.abc
    puts 123
  end
end

Your code using the module method:

require 'test_functions'
TestFunctions.abc

Solution 8 - Ruby on-Rails

Today you can do it using module_function notation.

module TestFunctions
  def abc
    puts 123
  end
end

Now TestFunctions.abc prints "123"

A little more about module_function: https://apidock.com/ruby/Module/module_function

Solution 9 - Ruby on-Rails

Try this code:

service = Class.new { extend TestFunctions }
service.method_four

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMike WoodhouseView Question on Stackoverflow
Solution 1 - Ruby on-RailsGarethView Answer on Stackoverflow
Solution 2 - Ruby on-RailsMike WoodhouseView Answer on Stackoverflow
Solution 3 - Ruby on-RailsMarcin UrbanskiView Answer on Stackoverflow
Solution 4 - Ruby on-RailsMiles PorterView Answer on Stackoverflow
Solution 5 - Ruby on-RailsSarah MeiView Answer on Stackoverflow
Solution 6 - Ruby on-RailsjamesView Answer on Stackoverflow
Solution 7 - Ruby on-RailsDouglasView Answer on Stackoverflow
Solution 8 - Ruby on-RailsRafael Gomes FranciscoView Answer on Stackoverflow
Solution 9 - Ruby on-RailsleompetersView Answer on Stackoverflow