Notes to self

Overriding Rails engines models and controllers

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.

Check out my book
Interested in Ruby on Rails default testing stack? Take Minitest and fixtures for a spin with my latest book.

Get Test Driving Rails and make your tests faster and easier to maintain.

by Josef Strzibny
RSS