Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 932
nbrustein Avatar asked Nov 14 '19 21:11

nbrustein


1 Answers

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

like image 111
Alexis Avatar answered Sep 30 '22 09:09

Alexis