If you’ve ever worked with a Rails engine, you might have come to a moment you needed to quickly override something. But do you know how to override the engine application files from within your Rails application?
Adjusting Rails engines
Rails engines are little Rails applications you can mount inside your main app. They can extend your application beyond what a usual Ruby library does since they can come with models, controllers, and views.
This is extremely useful, but sometimes also confusing. What if you need to tweak an engine model or controller a little?
The usual rescue? Forking.
Forking works well, but if you want to do a simple change and prefer to maintain it inside the application, you can also use Rails overrides.
Rails overrides
Rails overrides let’s you reopen classes you cannot easily monkey-patch. They are also well documented, but a lot of people might not know about them.
To load your own overrides, you need to remove the files from autoloading and then load them yourself within to_prepare
block. This way you’ll load the file after the original is already in memory.
Let’s say we’ll put the files to app/overrides
directory with _override.rb
suffix.
We first ignore this path in the config/application.rb
:
module Template
class Application < Rails::Application
config.load_defaults 7.0
...
overrides = "#{Rails.root}/app/overrides"
Rails.autoloaders.main.ignore(overrides)
Rails should happily ignore the overrides
directory and you shouldn’t be getting any errors at the moment.
Then we’ll load them ourselves again:
module Template
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
...
overrides = "#{Rails.root}/app/overrides"
Rails.autoloaders.main.ignore(overrides)
config.to_prepare do
Dir.glob("#{overrides}/**/*_override.rb").sort.each do |override|
load override
end
end
Now if we want to monkey-patch a Foo::Bar
class, we can create a following foo_bar_override.rb
file:
# app/overrides/foo_bar_override.rb
Foo::Bar.class_eval do
def method_i_need_to_replace
puts "A very nice replacement indeed."
end
end
Conclusion
And that’s pretty much it. Overrides are pretty straighforward way for replacing engine models and controllers. There is also a way how to replace code using ActiveSupport::Concern
if you need a bit more and it’s also documented in the same guide.
Get Test Driving Rails and make your tests faster and easier to maintain.