Rails: I Can't Call a Function in a Module in /Lib - What am I Doing Wrong

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

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.

Rails: Can't access a module in my lib directory

For a really great answer, look at Yehuda Katz' answer referenced in the comment to your question (and really, do look at that).

The short answer in this case is that you probably are not loading your file. See the link that RyanWilcox gave you. You can check this by putting a syntax error in your file - if the syntax error is not raised when starting your app (server or console), you know the file is not being loaded.

If you think you are loading it, please post the code you are using to load it. Again, see the link RyanWilcox gave you for details. It includes this code, which goes into one of your environment config files:

# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]

But really, read Yehuda's answer.

Rails /lib modules and

There are two ways that files get loaded in Rails:

  • It is registered in the autoload process, and you reference a constant that corresponds to the file name. For instance, if you have app/controllers/pages_controller.rb and reference PagesController, app/controllers/pages_controller.rb will automatically be loaded. This happens for a preset list of directories in the load path. This is a feature of Rails, and is not part of the normal Ruby load process.
  • Files are explicitly required. If a file is required, Ruby looks through the entire list of paths in your load paths, and find the first case where the file you required is in the load path. You can see the entire load path by inspecting $LOAD_PATH (an alias for $:).

Since lib is in your load path, you have two options: either name your files with the same names as the constants, so Rails will automatically pick them up when you reference the constant in question, or explicitly require the module.

I also notice that you might be confused about another thing. ApplicationController is not the root object in the system. Observe:

module MyModule
def im_awesome
puts "#{self} is so awesome"
end
end

class ApplicationController < ActionController::Base
include MyModule
end

class AnotherClass
end

AnotherClass.new.im_awesome
# NoMethodError: undefined method `im_awesome' for #<AnotherClass:0x101208ad0>

You will need to include the module into whatever class you want to use it in.

class AnotherClass
include MyModule
end

AnotherClass.new.im_awesome
# AnotherClass is so awesome

Of course, in order to be able to include the module in the first place, you'll need to have it available (using either of the techniques above).

Can I invoke an instance method on a Ruby module without including it?

If a method on a module is turned into a module function you can simply call it off of Mods as if it had been declared as

module Mods
def self.foo
puts "Mods.foo(self)"
end
end

The module_function approach below will avoid breaking any classes which include all of Mods.

module Mods
def foo
puts "Mods.foo"
end
end

class Includer
include Mods
end

Includer.new.foo

Mods.module_eval do
module_function(:foo)
public :foo
end

Includer.new.foo # this would break without public :foo above

class Thing
def bar
Mods.foo
end
end

Thing.new.bar

However, I'm curious why a set of unrelated functions are all contained within the same module in the first place?

Edited to show that includes still work if public :foo is called after module_function :foo

How to call a class method in \lib from model in rails?

Ruby has really powerful functions for manipulating both hashes and arrays.
Typing out duplicate assignments like:

self.strength = @character_stats[:strength]
self.dexterity = @character_stats[:dexterity]
self.constitution = @character_stats[:constitution]

Is pretty dull. So instead we can simply rewrite the methods to pass hashes around.

class RandomStatGenerator

# This is just a constant containing all the stats we want to generate.
STATS = [:strength, :dexterity, :constitution, :intelligence, :wisdom, :charisma]

# Create a hash with random roll values for each stat
def self.roll_stats
# This is kind of scary looking but actually just creates an
# hash from an array of keys
Hash[STATS.map {|k| [k, self.roll ] } ]
end

private

def self.roll
# Create an array with 4 elements (nil)
ary = Array.new(4)
# We then replace the nil value with a random value 1-6
ary = ary.map do
(1 + (rand(6)))
end
# sort it and drop the lowest roll. return the sum of all rolls.
ary.sort.drop(1).sum

# a ruby ninja writes it like this
Array.new(4).map { 1 + rand(6) }.sort.drop(1).sum
end
end

Output:

irb(main):032:0> RandomStatGenerator.roll_stats
=> {:strength=>14, :dexterity=>14, :constitution=>14, :intelligence=>13, :wisdom=>10, :charisma=>9}

But if you don't intend to actually create instances of a class, than you should use a module instead.

Rails models can either be created with a hash or you can replace its values with a hash:

Character.new(RandomStatGenerator.roll_stats)
@character.assign_attributes(RandomStatGenerator.roll_stats)

So we can use this in Character#generate_stats:

def generate_stats
assign_attributes(RandomStatGenerator.roll_stats)
end

You should use ActiveModel callbacks with extreme prejudice. It is often quite a challenge to regulate where in your application and when in the model lifetime. Since before_save runs after validations means that any validations like validates_presence_of :constitution will fail.

In your case it might be better to simply do it in the controller or use:

before_validation :generate_stats, if: -> { new_record? && @rand_stat_gen }

I moved my folder from lib to /app in a rails 6, I can't seem to instantiate an object still because of load path issue

I suspect it's spring, try this

bin/spring stop

And then start rails console, stopping Spring will force Rails to load your app fresh

Also,

if the name of your module is Someappname, then the directory name should be app/someappname and not some_app_name

Hope that helps!

Calling a method inside a do block in ruby

It seems that gemspec files do some weird stuff with scoping and namespacing (in the entire file, not just within the gemspec block itself), but after banging my head against my desk for 15 minutes and downing yet another beer to dull the existential torment induced by my inability to call a function in a gemspec, I figured out a reasonably clean solution: just stick self. in front of the function name in both the definition and invocation, like so:

def self.foo
puts 'It works :)'
end

Gem::Specification.new do |s|
# ...
self.foo # should print 'It works :)' instead of erroring out
# ...
end

Why this is necessary, and why nobody else seems to have asked or answered this question anywhere on the World Wide Web, I haven't the faintest idea, but here we go.

Calling a class-function that gets redefined, from an included Module in Ruby

I think, that you want to implement something like this:

module PrintHeader
def print_header
puts header
end
end

class CoreClass
include PrintHeader

def header
'old header'
end
end

class UseClass < CoreClass
def header
'New header'
end
end

core = CoreClass.new
use = UseClass.new

core.header
# => 'old header'
core.print_header
# old header
# => nil
use.header
# => 'New header'
use.print_header
# New header
# => nil

Namespace module class methods undefined

The file was actually located in /lib/reports/stripe/stripe.rb. It was a mistake I made much earlier, but forgot to fix. Moving the file to /lib/reports/stripe.rb resolved the issue.



Related Topics



Leave a reply



Submit