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!
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.
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
I know, this question was asked at least milion times, but I still can't find the right solution. I mean clean and easy, like it is supposed to be in Rails applications.
Let's imagine I am developer of an application that needs some code in the lib folder. My app's name is Xy. I will create a file xy.rb in the lib folder with this content:
require 'xy/version'
module Xy
end
After that I will create next file in lib/xy/ named version.rb:
module Xy
module VERSION
def self.to_s
"1.0.0"
end
end
end
So far, so good. Then I will create some controller and I will edit routes:
class IndexController < ApplicationController
def index
render text: Xy::VERSION
end
end
root 'index#index'
I also need require my code, in application.rb I will add require 'xy'
Then I will run my server rails s and I will wisit the app in the browser http://localhost:3000 - seems good, I see 1.0.0.
Then I will update the version string in Xy::VERSION to 1.0.1 and I'll refresh the browser - here it comes, I will no see the change. To see it I will have to restart the server. And this is quite anoying.
So the question is: What is the right way to force RoR 4+ to reload changes in the lib folder in development environment?
Thank you in advance.
Add this to application.rb:
config.autoload_paths += %W(#{config.root}/lib)
I'm using this in my app now, you never need to even require' the lib files anywhere and don't have to restart the server. Just make sure you name your files correctly. E.g
/lib/foo/rb
class Foo
/lib/foo/bar.rb
class Foo::Bar
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
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')