Rails app can not load the custom app config from lib - ruby-on-rails

I have defined AppConfig class in lib folder like this:
// lib/app_config.rb
module AppConfig
mattr_accessor :title
def self.configure
yield self
end
end
Set custom AppConfig:
// config/initializers/app_config.rb
AppConfig.configure do |config|
config.title = "WOW"
end
I tried puts AppConfig.title => It works well and write "WOW" in the terminal. But when i use AppConfig.title in my controller:
// app/controllers/application_controller.rb#index
render json: { title: AppConfig.title }
It returns { title: null }
I don't know why, somebody can help me? Thank you so much!

You cannot autoload code in initializers. This is explained in the guides:
While booting, applications can autoload from the autoload once paths, which are managed by the once autoloader. Please check the section config.autoload_once_paths above.
However, you cannot autoload from the autoload paths, which are managed by the main autoloader. This applies to code in config/initializers as well as application or engines initializers.
Why? Initializers only run once, when the application boots. If you reboot the server, they run again in a new process, but reloading does not reboot the server, and initializers don't run again. [...]
If you want to to use your AppConfig class in an initializer you need to to use require or use the events that Rails provides so that you code is properly reloaded.
But before you do you should really ask yourself if you actually need to spread your app configuration out in yet another place. YAGNI.

Related

How can I extend gem class in Rails 6/Zeitwerk without breaking code reloading?

How do I extend a class that is defined by a gem when I'm using rails 6 / zeitwerk?
I've tried doing it in an initializer using require to load up the class first.
I've tried doing it in an initializer and just referencing the class to let autoloading load it up first.
But both of those approaches break auto-reloading in development mode.
I've tried putting it in lib/ or app/, but that doesn't work because then the class never gets loaded from the gem, since my new file is higher up in the load order.
There is a similar question here, but that one specifically asks how to do this in an initializer. I don't care if it's done in an initializer or not, I just want to figure out how to do it some way.
What is the standard way of doing something like this?
I do have one nasty hack that seems to be working, but I don't like it (update: this doesn't work either. reloading is still broken):
the_gem_root = $LOAD_PATH.grep(/the_gem/).grep(/models/).first
require("#{the_gem_root}/the_gem/some_model")
class SomeModel
def my_extension
...
end
end
I know is late, but this was a real pain and someone could find it helpful, in this example I'll be using a modules folder located on app that will contain custom modules and monkey patches for various gems.
# config/application.rb
...
module MyApp
class Application < Rails::Application
config.load_defaults(6.0)
overrides = "#{Rails.root}/app/overrides"
Rails.autoloaders.main.ignore(overrides)
config.to_prepare do
Dir.glob("#{overrides}/**/*_override.rb").each do |override|
load override
end
end
end
end
Apparently this pattern is called the Override pattern, it will prevent the autoload of your overrides by zeitwerk and each file would be loaded manually at the end of the load.
This pattern is also documented in the Ruby on Rails guide: https://edgeguides.rubyonrails.org/engines.html#overriding-models-and-controllers

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.

Auto-loading lib files in Rails 4

I use the following line in an initializer to autoload code in my /lib directory during development:
config/initializers/custom.rb:
RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?
(from Rails 3 Quicktip: Auto reload lib folders in development mode)
It works great, but it's too inefficient to use in production- Instead of loading libs on each request, I just want to load them on start up. The same blog has another article describing how to do this:
config/application.rb:
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
However, when I switch to that, even in development, I get NoMethodErrors when trying to use the lib functions.
Example of one of my lib files:
lib/extensions.rb:
Time.class_eval do
def self.milli_stamp
Time.now.strftime('%Y%m%d%H%M%S%L').to_i
end
end
Calling Time.milli_stamp will throw NoMethodError
I realize others have answered similar questions on SO but they all seem to deal with naming conventions and other issues that I didn't to have to worry about before- My lib classes already worked for per-request loading, I just want to change it to per-startup loading. What's the right way to do this?
I think this may solve your problem:
in config/application.rb:
config.autoload_paths << Rails.root.join('lib')
and keep the right naming convention in lib.
in lib/foo.rb:
class Foo
end
in lib/foo/bar.rb:
class Foo::Bar
end
if you really wanna do some monkey patches in file like lib/extensions.rb, you may manually require it:
in config/initializers/require.rb:
require "#{Rails.root}/lib/extensions"
P.S.
Rails 3 Autoload Modules/Classes by Bill Harding.
And to understand what does Rails exactly do about auto-loading?
read Rails autoloading — how it works, and when it doesn't by Simon Coffey.
Though this does not directly answer the question, but I think it is a good alternative to avoid the question altogether.
To avoid all the autoload_paths or eager_load_paths hassle, create a "lib" or a "misc" directory under "app" directory. Place codes as you would normally do in there, and Rails will load files just like how it will load (and reload) model files.
This might help someone like me that finds this answer when searching for solutions to how Rails handles the class loading ... I found that I had to define a module whose name matched my filename appropriately, rather than just defining a class:
In file lib/development_mail_interceptor.rb (Yes, I'm using code from a Railscast :))
module DevelopmentMailInterceptor
class DevelopmentMailInterceptor
def self.delivering_email(message)
message.subject = "intercepted for: #{message.to} #{message.subject}"
message.to = "myemail#mydomain.org"
end
end
end
works, but it doesn't load if I hadn't put the class inside a module.
Use config.to_prepare to load you monkey patches/extensions for every request in development mode.
config.to_prepare do |action_dispatcher|
# More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
Rails.logger.info "\n--- Loading extensions for #{self.class} "
Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
Rails.logger.info "Loading extension(s): #{entry}"
require_dependency "#{entry}"
end
Rails.logger.info "--- Loaded extensions for #{self.class}\n"
end

Rails 3 engine and code reloading in development mode

I have a rails 3 engine. In initializer it requires a bunch of files from some folder.
In this file user of my engine defines code, business logic, configures engine, etc..
All this data is stored statically in my engine main module (in application attribute)
module MyEngine
class << self
def application
#application ||= MyEngine::Application.new
end
end
end
I want this files to be reloaded on each request in development mode.
(So that the user don't have to reload server to see changes he just made)
Of course I can do something like this instead of initializer
config.to_prepare do
MyEngine.application.clear!
load('some/file')
end
But this way i will have issues (because constants defined in this file won't really be reloaded).
The ideal solution would be to make my whole engine reloadable on each request, but a have not found the way to do it.
It's an old question but I think adding ActiveSupport::Dependencies.explicitly_unloadable_constants += %w[ GemName ] to your development.rb should do the trick.
Have you tried turning reload_plugins on?
# environments/development.rb
config.reload_plugins = true
Its a bit of hack but using require_dependency and just reopening the class might work?
# app/models/project.rb
require_dependency File.join(MyEngine::Engine.root, 'app', 'models', 'project')
class Project
end
For those who are working on Engine views or I18n translations only: Those parts are autoreloaded by default, no need to restart the server!

Where is the best place to extend the functionality of built in classes in Rails?

I have a few methods I am adding to Ruby's Array class for my Rails application. Where is the best place to put them?
Right now I have them in config/environment.rb.
config/environment.rb isn't really the best place, since you can run into serious load ordering-problems if try to extend classes that haven't been resolved at the time environment.rb is executed.
Better to put a file into config/initializers. Any script placed there will be executed after the rails runtime is loaded.
What you could do is to create a file lib/my_extensions.rb
module MyExtensions
end
then in lib/my_extensions/array.rb :
module MyExtensions::Array
def join_with_commas
join(", ")
end
end
and in config/initializers/load_my_extensions.rb
class Array
include MyExtensions::Array
end
This will cause MyExtensions::Array to be auto-reloaded each time you invoke a request in development mode. This is nicer than restarting your application everytime you make a change to your code.
It would probably be cleaner to add a lib/ directory with all your extensions. Then add a line in config/environment.rb that loads the file:
require File.join(RAILS_ROOT, 'lib', 'array.rb')

Resources