How can I automatically reload gem code on each re

2019-07-21 15:02发布

问题:

I am developing a Rails app where most of the code not specific to the app has been written inside of various gems, including some Rails engines and some 3rd party gems for which I am enhancing or fixing bugs.

gem 'mygem', path: File.expath_path('../../mygem', __FILE__)

Since a lot of the code in these gems is really part of the app, it's still changing frequently. I'd like to be able to utilize the Rails feature where code is reloaded on each request when in development (i.e. when config.cache_classes is false), but this is only done within the normal application structure by default.

How can I configure Rails to reload gem code on each request, just like with the app code?

回答1:

I have found through trial and error that several steps are required, with the help of ActiveSupport.

  • Add activesupport as a dependency in the .gemspec files

    spec.add_dependency 'activesupport'
    
  • Include ActiveSupport::Dependencies in the top-level module of your gem (this was the most elusive requirement)

    require 'bundler'; Bundler.setup
    require 'active_support/dependencies'
    
    module MyGem
      unloadable
      include ActiveSupport::Dependencies
    end
    
    require 'my_gem/version.rb'
    # etc...
    
  • Set up your gem to use autoloading. You an either manually use ruby autoload declarations to map symbols into filenames, or use the Rails-style folder-structure-to-module-hierarchy rules (see ActiveSupport #constantize)

  • In each module and class in your gem, add unloadable.

    module MyModule
      unloadable
    end
    
  • In each file that depends on a module or class from the gem, including in the gem itself, declare them at the top of each file using require_dependency. Look up the path of the gem as necessary to properly resolve the paths.

    require_dependency "#{Gem.loaded_specs['my_gem'].full_gem_path}/lib/my_gem/myclass"
    

If you get exceptions after modifying a file and making a request, check that you haven't missed a dependency.

For some interesting details see this comprehensive post on Rails (and ruby) autoloading.