85

I am writing a custom wrapper for open_flash_chart plugin. It's placed in /lib and load it as a module in ApplicationController.

However, I have either a problem with the Class hierarchy or some other problem.

From any controller I can access open_flash_chart functions as OpenFlashChart, Line etc.

However, in a class in a /lib module, it doesnt work!

Any ideas?

ggorlen
  • 44,755
  • 7
  • 76
  • 106
Mantas
  • 5,691
  • 6
  • 27
  • 24
  • hope this can help you https://stackoverflow.com/questions/17304110/is-it-possible-to-include-modules-in-rake-task-and-make-its-methods-available-fo/54250302#54250302 – S.Yadav Jan 18 '19 at 08:45

5 Answers5

149

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).

Yehuda Katz
  • 28,535
  • 12
  • 89
  • 91
  • 2
    Just wanted to add : If one of your module in /lib (or in one of the autoload directories) is already define ; exemple you overload ActiveRecord or String, you'll have to explicitly require it or it won't be loaded – Mike Dec 07 '10 at 10:50
  • 1
    strangely, I'm getting: uninitialized constant GaClient (NameError), unless I require 'ga_client' beforehand (the class is defined in lib/ga_client.rb). Is there documentation for the autoload naming scheme? – mkirk Apr 15 '11 at 20:58
87

In Rails 3 /lib modules are not loaded automatically.

This is because the line:

# config.autoload_paths += %W(#{config.root}/extras)

inside config/application.rb is commented.

You can try to uncomment this line or, (it worked even better for me), leave this commented (for future reference) and add this two lines:

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Nicolai Reuschling
  • 2,558
  • 2
  • 20
  • 24
diegopau
  • 1,324
  • 10
  • 22
  • This will duplicate `../lib` path in `ApplicationName::Application.config.autoload_paths` array. – jibiel Jan 23 '12 at 11:30
  • 1
    @jibiel so what's the resolution here? – ylluminate Feb 15 '12 at 19:13
  • 4
    why did the latter option work better for you? Also, why was the default changed...there must be a reason despite lots of people finding work arounds. – ckarbass Jun 15 '12 at 06:01
  • @ylluminate So single `config.autoload_paths += Dir["#{config.root}/lib/**/"]` should be enough. @ckarbass Second line gives you the freedom to organize your utilities into _subfolders_ and thus niftify your modules with _namespaces_. That's pretty much it. And [here's why](https://rails.lighthouseapp.com/projects/8994/tickets/5218#ticket-5218-7) the default was changed. Better late than never :) – jibiel May 30 '14 at 09:37
  • This seems a bit messy to me. Wouldn't this unnecessarily auto-load rake tasks? – Dennis Oct 14 '14 at 15:01
25

What worked for me, besides uncommenting config.autoload_paths (I’m on Rails 3.1.3), was to create a initializer like this:

#config/initializers/myapp_init.rb
require 'my_module'    
include MyModule

This way I can call mymodule methods from anywhere and as class methods Model.mymodule_method or as instance methods mymodel.mymodule_method

Maybe some expert may explain the implications of this. By now, use it at your own risk.

Edit: Afterwards, I think a better approuch would be:

create a initializer like this:

#config/initializers/myapp_init.rb
require ‘my_module’

Include the module where needed, like this:

1) if you want to use it as "Class Methods" use "extend":

class Myclass < ActiveRecord::Base
   extend MyModule
   def self.method1
      Myclass.my_module_method
   end
end

2) if you want to use it as "Instance Methods" include it inside Class definition:

class Myclass < ActiveRecord::Base
include MyModule
   def method1
      self.my_module_method 
   end
end

3) remember that include MyModule refers to a file my_module.rb in your load path that must be required first

Baldrick
  • 23,882
  • 6
  • 74
  • 79
Fernando Fabreti
  • 4,277
  • 3
  • 32
  • 33
  • 3
    I have created my module on `lib` folder, therefore i have added `config.autoload_paths += %W(#{config.root}/lib)` on `config/application.rb` file. After that i followed your suggestion to adding `config/initializers/myapp_init.rb` file and it's contents. Everything is good. Thanks a lot :) – S.M.Mousavi Nov 12 '12 at 11:50
  • Even though `require` works for me and auto-loading does not (undefined module method), [this comment says you shouldn't use `require`](http://stackoverflow.com/questions/3356742/best-way-to-load-module-class-from-lib-folder-in-rails-3#comment3541227_3376401). – Dennis Oct 14 '14 at 15:03
3

To use the module lib/my_module.rb in your models and controllers:

In config/application.rb:

config.watchable_dirs['lib'] = [:rb]

In your model (similar idea for your controller):

require_dependency 'my_module'

class MyModel < ActiveRecord::Base
  include MyModule

  MyModule.some_method
end

This method is described in more detail at http://hakunin.com/rails3-load-paths

Dennis
  • 56,821
  • 26
  • 143
  • 139
0

It might be the case that you want to explicitly load file(s) under lib directory at time of application initialization.
In my config/application.rb, I have an entry as,
config.autoload_paths += %W(#{config.root}/lib)

Also this might be the case that module name/hierarchy is not same as it is in file or location/name of file is not same as that hierarchy, so auto-load of that file is also not possible. So when I added an entry at bottom of config/application.rb as,
require "./lib/file_name_without_extention
it worked fine.

Swapnil Chincholkar
  • 2,869
  • 3
  • 22
  • 22