4

I'm in the process of organizing my code, so far I have successfully managed to groupcontrollers/helpers/views in folders "admin", but I used to have originally a library with the same module name "admin" that I'm not being able to call anymore. (name conflict?)

The new structure:

Directory Structure
  -> app
    -> controllers 
      -> admin #new
        -> admin_main
        -> admin_permissions

    -> Helpers
      -> admin #new
        -> admin_main_helper
        -> admin_permissions_helper

  -> lib
    -> admin
      -> pagerduty.rb

I used to be able to call my library from my helpers like this:

module Admin::AdminMainHelper #admin:: is new
  require "./lib/admin/pagerduty.rb"

  def pager_duty
    pagerduty = Admin::PagerDuty.new() #throws error after the new structure
    @on_call = pagerduty.first_on_call()
    @counts = pagerduty.open_incidents()
  end

end

The error is "uninitialized constant Admin::PagerDuty"

Do I have to rename my library to something else? or is there a way around this?

EDIT: It works if I rename my library module to "AdminLib" instead of "Admin" for example. So the question is if there is a way around this.

  • Have you tried `::PagerDuty.new()` instead of `Admin::PagerDuty.new()`? What about `PagerDuty.new()` only? – MrYoshiji Aug 16 '13 at 15:51
  • I get "uninitialized constant PagerDuty" –  Aug 16 '13 at 15:52
  • Maybe I just have to rename it ... right? –  Aug 16 '13 at 15:52
  • no I don't think so, there must be a way to make it work. If not, it would be a bad thing that you can't have the same structure in the Helper & Lib & combine them... – MrYoshiji Aug 16 '13 at 15:54

2 Answers2

4

To require dependencies the right way in Ruby you should:

  • rename pagerduty.rb => pager_duty.rb
  • require it with: require 'admin/pager_duty'

This is possible because Rails already adds your lib folder on your LOAD_PATH. This will work great in production for finished code (as libraries usually are).

But if you wish to develop your lib files in development - without the need to restart server each time you make a modification, you can change your setup like that:

  • add this line to you config/application.rb:

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

  • remove any explicit require statements of lib files inside controllers or models (any app/* file)

This will work good as well. But it is a common antipattern. Because:

  • The lib code should be completely independent of your app so you can share it among applications. And if you are using the autoloading mechanism it implies it is a first-class citizen of your application. In this case it is much better to set up a new folder inside app (e.g. app/tools) and set up autoloading for it. Otherwise you can end up with a cluttered lib folder filled with app dependent code. More info here.

  • Autoloading will not work for classes that are already defined or are defined in multiple places (e.g. monkeypatches). More info here

Community
  • 1
  • 1
jurglic
  • 3,599
  • 1
  • 17
  • 13
  • Sure. No problem with class name. – jurglic Aug 16 '13 at 16:52
  • by the way, every time I change any code I need to restart the rails server now. unlike before where I only restart when I change the code in Lib –  Aug 16 '13 at 21:59
  • yeah correct I used to restart the server when I change the lib folder which was totally okay, but now it's whenever I change anything in any controller! very weird. –  Aug 17 '13 at 16:24
  • http://stackoverflow.com/questions/18291542/rails-nameerror-uninitialized-constant-error-after-modifying-controller-code –  Aug 17 '13 at 17:33
  • I think you might have some inconsistencies in the naming of files and models/controllers. Which can break autoloading of app classes. Do you really have the controller code in admin/admin_main.rb? It should be admin/admin_main_controller.rb. That's definately not OK. And the controller name is then Admin::AdminMainController, right? – jurglic Aug 17 '13 at 17:40
2

I think the problem lies with the load path. I think the require should be:

require "#{Rails.root}/lib/admin/pagerduty.rb"

Another solution, albeit a little heavy handed, is to load all of the lib subdirectories in the LOAD_PATH, eg:

In application.rb for config.autoload_path:

config.autoload_paths += Dir["#{config.root}/lib/**/"]
Adrian CB
  • 651
  • 3
  • 5
  • Yes it works! very confusing sorry, #{Rails.root} is case sensitive by the way. –  Aug 16 '13 at 16:01
  • If you are using Windows to run your app, you should use `Rails.root.join('lib', 'admin', 'pagerduty.rb')` (it will deal with the slash/backslash depending on the Operating System) @Newbie – MrYoshiji Aug 16 '13 at 16:03
  • we're not on windows machines. will test more. –  Aug 16 '13 at 16:03
  • by the way, every time I change any code I need to restart the rails server now. unlike before where I only restart when I change the code in Lib –  Aug 16 '13 at 21:53
  • Hopefully this link helps: http://www.williambharding.com/blog/rails/automatically-reload-modules-on-change-in-rails/ – Adrian CB Aug 17 '13 at 00:15