In my Rails application I have some classes under the /lib folder:
/lib/ems
/lib/ems/client.rb
/lib/ems/credentials.rb
/lib/ems/v2/base.rb
/lib/ems/v2/policy.rb
And all classes are autoloaded in my application with:
config.autoload_paths += Dir["#{config.root}/lib/**/"]
If I call: Rails.application.instance_variable_get(:"#_all_autoload_paths")
I can clearly see all my classes are being loaded correctly and they work fine in development.
However when in non-development environments, I get the NameError undefined constant for some of the classes under my /lib folder (not all of them, just some of them).
What I noticed is that if turn off class caching like so:
config.cache_classes = false
Then those classes will work fine and can be used without any issue... so it seems that it's not a problem with the autoloading, but rather a caching problem...
If I turn the cache_classes back on again, it then fails straight away.
How can I make it so that Rails can see these classes without having to turn this off?
You need to add the directory to the eager_load_paths as well.
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.eager_load_paths += Dir["#{config.root}/lib/**/"]
See Don't forget about eager_load when extending autoload paths from Arkency for an excellent (if somewhat dated) rundown on the details.
Related
I have a problem because my code does not work on the production server. When it comes to development environment, everything is fine. It looks like he doesn't read the classes. My ruby version is 2.6.6 and rails is set as 5.2.4.4.
lib/crm/api.rb
module CRM
class API
# some code
end
end
app/services/crm/changes.rb
module CRM
class Changes
def initialize
#api = API.new
end
# some code
end
end
The main service is running as a cronjob.
config/schedule.rb
every 5.minutes do
runner('CRM::CheckChanges.new.call', output: "#{path}/log/crm_check_changes.log")
end
And run this code
app/services/crm/check_changes.rb
module CRM
class CheckChanges
def initialize
#changes = Changes.new
end
# some code
end
end
At first I got errors log like 'Uninitialized constant CRM::Changes::API' after reload background jobs I got 'Unitialized constant CRM::API'
config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += %W(#{config.root}/app)
Dir.glob(Rails.root.join('lib/**')).inject(config.autoload_paths){ |autoload_paths, path| autoload_paths << path }
As I mentioned before everything works fine on development environment. Does anyone have any idea what this could be about? Thanks in advance for answer.
You need to add /lib to the eager load paths as well:
config.autoload_paths << Rails.root.join('lib')
config.eager_load_paths << Rails.root.join('lib')
The default setting for production uses eager loading (all the classes are loaded at startup) instead of autoloading.
In production, however, you want consistency and thread-safety and can
live with a longer boot time. So eager_load is set to true, and then
during boot (before the app is ready to receive requests) Rails loads
all files in the eager_load_paths and then turns off auto loading (NB:
autoloading may be needed during eager loading). Not autoloading after
boot is a good thing, as autoloading can cause the app to have
thread-safety problems.
See Autoloading and Reloading Constants (Classic Mode). Also note that /app and all its subdirecties are already on the autoloading paths.
I've got a class in /lib that I'm mucking with and testing via the console. I'm making changes to the class (adding a debugger line for instance) and using reload! but the new or removed line is not reflected in the version of the code that the console is running.
Tried these:
config.cache_classes = false
reload!
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths += Dir["#{config.root}/lib/service_processors"]
config.autoload_paths << 'lib'
I'm running a saved script that creates an instance of the class. I tried just loading the class at the top of my script, but that seemed to cause some unintended consequences.
So, how do I completely unload a class and then reload it? I'm thinking the script will force an unload of the class, then load the class via the file name.
Not sure it that is the problem, but in your application.rb you need to add the following line:
config.autoload_paths += %W( #{config.root}/lib )
And secondly, to make sure that classes/modules are found correctly on reload, the naming has to follow Rails conventions. This means that snake-casing a module or class name should give the filename, and different namespaces (or nesting) should be in different folders.
Some examples to make this more clear :)
class SomeClass --> /lib/some_class.rb
class SomeHTTPStuff --> /lib/some_http_stuff.rb
class API::Stuff --> /lib/api/stuff.rb
HTH.
Invalid after Rails tag added
Well, to load a class:
load "lib/class.rb"
To reload:
load "lib/class.rb"
Those settings are irrelevant.
It looks like this issue will be solved in Rails 4:
http://blog.plataformatec.com.br/2012/08/eager-loading-for-greater-good/
but until then, I'm wondering how to eager-load modules/classes in my /lib.
In IRB it appears that they are loaded on-demand the first time I try to access:
Foo::Bar.constants
=> []
Foo::Bar::Service
=> Foo::Bar::Service
Foo::Bar.constants
=> [:ServiceBase, :Service]
I have several other classes in that module, and my code depends on being able to look them up using Foo::Bar.const_defined? at runtime - how do I ensure all Foo::Bar's classes get loaded at startup?
I'm already using config.autoload_paths += %W(#{config.root}/lib) in application.rb.
Putting this in root/config/initializers/eager.rb should load all .rb files in that folder:
Dir["#{Rails.root}/lib/*.rb"].each {|file| load file}
For me putting this in application.rb solved the problem
config.eager_load_paths += Dir["#{config.root}/lib/**/"]
Use eager_load_paths combined with ActiveSupport::Reloader's to_prepare hook inside development.rb:
config.eager_load_paths += Dir["app/models/stimodel/**/*.rb"]
ActiveSupport::Reloader.to_prepare do
Dir["app/models/stimodel/**/*.rb"].each { |f| require_dependency("#{Dir.pwd}/#{f}") }
end
Adding your paths to eager_load_paths make sure that Rails loads them when it starts up. To make sure that Rails reloads our models if we do any changes or add new files, we also need to hook into the Reloader's to_prepare hook and manually require the dependency there.
I have configuration data (the host/post for a Redis server) that is managed by a class (RedisConfig) in my RAILS_HOME/lib folder. We decided to use redis-store as our cache.
This is what we have in RAILS_HOME/config/environments/production.rb:
config.cache_store = RedisStore.new "#{RedisConfig.host}:#{RedisConfig.port}"
I, of course, get a TypeError as RedisConfig is loaded AFTER production.rb is loaded, so it never exists in this context. Whats the best strategy for loading and using this configuration manager in our environment.rb or environments/#{RAILS_ENV}.rb files?
Consider taking this approach. The trick here is they get loaded during the initialisation stage - it's the same as including it in as an initialiser itself.
See this SO post on loading up your RedisConfig module. While this is about engines, it's got some interesting details on initialisation.
Update
Here's a better solution - update your application.rb as follows
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
This will autoload all modules stored in /lib.
I place a file name g.rb in side Rails.root/lib folder
The file content is like this:
module Google
end
Then I add
config.autoload_paths += %W(#{config.root}/lib #{Rails.root}/app/delayed_jobs)
to my Rails.root/config/application.rb
However, when I try to invoke Google from rails console, an exception is thrown. The exception goes away only if I execute require 'google'.
Why? Shouldn't my file is autoloaded and shouldn't I access the module without any extra require statement?
Hmm, I discovered an interesting thing. In order for Rails to auto load my class, the class name should be compliant to the file name and the folder structure.
For example, if I want to have Google module autoloaded, I must placed it inside google.rb, directly under /lib (incase I specify autoload from /lib).
If I want to auto load Google::Docs, then I either place it inside google.rb or google/docs.rb
I had a similar problem with getting my module to run on Heroku. In addition to the autoload naming convention stated by Stephen C, I found out that the module code must be require'd due to a threadsafe assumption made by the Rails' production environment on Heroku (even though threadsafe was commented out in my production.rb configuration file.) As soon as I require'd the module file before calling include on the module, everything started to work.
require 'mymodule'
include Mymodule
Please take a look at this excellent article on the subject of getting Modules to load correctly in Heroku (production).
That's because the point of autoload is not to 'require' everything up front (startup penalty). Classes are loaded as they are needed/referenced. In order to do this, you need some way to know where to look for the class. Otherwise, you would have to load every file in the autoload directory in advance to see what classes are declared. It's a tradeoff, but requiring everything in advance (as marbaq suggests) is not autoloading.
You can use the autoload command as provided by Ruby, which takes two arguments, the module to load (symbolized, i.e. :Google in your case), and the second argument is the filename, which would be g.rb if lib is in your load path ($:). See the Ruby docs for autoload.
Change config.autoload_paths to config.eager_load_paths
(based on Rails issue #6850 and Force reload! from lib directory in rails 3.2 console)
I faced the same problem just now, and my "solution" (or rather workaround) was to manually require every needed file from Rails.root/lib in my application.rb.
require 'lib/message'
require 'lib/store'
require 'lib/vault/vault.rb'
require 'lib/custom_loggers'
module MyApplication
class Application < Rails::Application
My next step would be to categorize the files in module folders as you mention.
i found this solution recently
config/application.rb
module AppName
class Application < Rails::Application
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
config.autoload_paths += Dir[Rails.root.join('app', 'lib', 'extensions')]
end
end
the first config call induces rails to auto-load all sub-directories of the app/models directory
so now i can have /app/models/sub_directory/model.rb auto-loaded
(handy for organising an app with a large code base)
the second config call induces rails to autoload the lib/extensions directory
hope this helps
note: i believe this is rails 3 specific