rails: autoload files inside engine's lib directory - ruby-on-rails

I am working on this rails application with an engine which is sort of sub application adding some more routes to my existing application.
The concept is so powerful, thanks to rails.
But I am facing this weird challenge to autoload file changes inside my engines lib directory in development mode. Every time I make a change inside app directory of engine be it model or controller , it works flawlessly, but no changes to any files under lib directory get's picked up. Is there a way I can do this ? Thanks for your help.

According to Rails::Engine docs you can autoload paths like-
class MyEngine < Rails::Engine
# Add a load path for this specific Engine
config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
initializer "my_engine.add_middleware" do |app|
app.middleware.use MyEngine::Middleware
end
end
If you don't want to autoload, you can directly require the file in your class with the require statement-
require 'my_engine/my_object'
class MyModel < AR::Base
...
end
This will work because your Engine is already loaded in your app, so you can access libs inside of it.

Put the following code in your config/application.rb
config.eager_load_paths += ["#{Rails.root}/lib"]
If you want this only in development mode use the following
config.eager_load_paths += ["#{Rails.root}/lib"] if Rails.env.development?

Related

How can I add a services directory to the load path in Rails?

In my Rails project, I want to add services directory in app folder and include some service objects.
So let's say I want to add app/services/foo/test.rb which looks like:
module Services
module Foo
class Test
end
end
end
In my config/application.rb I added:
config.paths.add File.join('app', 'services'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'services', '*')]
However when I try to load the files in console it doesn't work:
⇒ rails c
Loading development environment (Rails 4.1.4)
[1] pry(main)> Services::Foo::Test
NameError: uninitialized constant Services
Any help how can I solve this issue?
After add new dir, reload spring
spring stop
First of all, the code under app folder will be loaded without any config.
I think the problem was the folder structure doesn't match with your class definition.
So this config will work:
app/services/foo/test.rb
module Foo
class Test
end
end
My clue is, for example we have app/controllers/api/v1/users_controllers.rb and the class constant will be Api::V1::UsersController, not Controllers::Api::V1::UsersController
Update
Conventionally, we usually use FooServices instead of Foo, it is clearer, for example:
app/services/foo_services/bar_parser.rb
module FooServices
class BarParser
# Do stuff
end
end
So we understand that every class inside foo_services folder is a service which related to Foo
My problem was because of Rails naming conventions, I suppose. I just renamed class to not use module Services and it worked.

rails 4 - require class in initializer or module that uses it - best practice

I'm a fairly new RoR dev and ran into a question about best practices for require ing files from lib in Rails 4.
Background:
As it stands, files in my app's lib don't get autoloaded. I want to require a helper class called rate_limiter that lives in a subdirectory of lib. I've also created a throttle module that handles routes related to rate limiting, and uses this rate_limiter class.
The throttle module is already required at the top of my application controller.
Question: to make sure rate_limiter gets loaded at start of app, what's better:
1) include an initializer rate_limiter.rb that simply says require rate_limiter to load class.
2) add require rate_limiter to the top of a different module throttle, which uses rate_limiter and which already gets required at the top of application controller.
Wondering about best practices for clarity and maintainability going forward.
Thanks for any suggestions!
I believe this would be a more Rails way to include desired files and folders.
# config/application.rb
module YourAppName
class Application < Rails::Application
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
config.autoload_paths += Dir[Rails.root.join('lib')]
end
end
You probably could try the following scheme:
# config/application.rb:
# To make sure that your module namespace will be initialized without name collisions with files in your app directories
# require only root file and autoload other relative files in root file using features of ActiveSupport::Autoload
require_relative '../lib/my_module/my_module'
module AppName
class Application < Rails::Application
# Do not include all files in your lib. Require explicitly
# .
# ..
# - lib/my_module/my_module.rb - root file
# - lib/my_module/my_module - directory
config.autoload_paths += Dir[Rails.root.join('lib', 'my_module')]
end
end
Then in you module you can specify explicitly which files required and when
# lib/my_module/my_module.rb
module MyModule
extend ActiveSupport::Autoload
autoload :Configuration
autoload :SomeClass # at lib/my_module/my_module/some_class.rb
autoload :AnotherClass
eager_autoload do
autoload :Errors # at lib/my_module/my_module/errors.rb
autoload :BillError, 'my_module/errors'
end
end
It is worth to read an official guide to understand generic process of Rails constant lookup
To me, since this is going into your application controller which every request gets funneled through, I'd suggest adding this to your autoload paths.
In config/application.rb:
config.autoload_paths += %W(#{config.root}/lib/mymodule)

How can I add autoload_paths in my Rails4 Engine?

Usually I add the following in config/application.rb to add autload_paths:
config.autoload_paths += Dir[Rails.root.join('app', 'poros', '{**}')]
How can I achieve the same in an engine?
It seems to work when I just use the same code in application.rb in the host app, however I think it's ugly that the code is not in the engine and needs to be added to the host app to make things work.
The only solution I found to add the load path through the engine is by adding this to lib/engine/engine.rb:
config.to_prepare do
Dir.glob(Rails.root + "../../app/poros/**/*.rb").each do |c|
require_dependency(c)
end
end
However there seems to be something fundamentally wrong with this as this leads to problems when I'm doing console reloads (e.g. it tells me that constants are already defined or that concerns can't execute the include block twice)
What is the right way to do this in the engine itself? (can't believe this is so hard/uncommon, I have really googled a lot but I can't find a solution)
According to the Rails::Engine documentation, you can add autoload paths in your Railtie like this:
class MyEngine < Rails::Engine
# Add a load path for this specific Engine
config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
initializer "my_engine.add_middleware" do |app|
app.middleware.use MyEngine::Middleware
end
end
If poros is a subdirectory of app, you do not need add it again.
All subdirectories of app in the application and engines present at boot time. For example, app/controllers. They do not need to be the default ones, any custom directories like app/workers belong automatically to autoload_paths.

Adding lib to 'config.autoload_paths' in Rails 3 does not autoload my module

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

Rails lib includes

I have a puzzling issue regarding modules defined in the lib dir
I have two files
#lib/authentication.rb
module Authentication
end
#lib/test_module.rb
module TestModule
end
In my application controller I have
class ApplicationController < ActionController::Base
include Authentication
include TestModule
end
The Authentication Module loads properly but the TestModule does not
I get "uninitialized constant ApplicationController::TestModule"
I am stumped... anyone?
EDIT: Does anyone know where I could look to debug this?
As of Rails 3, make sure to add the lib directory to config.autoload_paths in config/application.rb, so that the file containing your module is read and the module is loaded.
config.autoload_paths += %W(#{config.root}/lib)
Look here for more info on this and loading subdirectories.
Also, supposedly "you shouldn't use require within a rails app, because it prevents ActiveSupport::Dependencies from [un]loading that code properly".
Adding require 'lib/test_module' at the top of your ApplicationController file might help

Resources