Search code examples
ruby-on-railsactivesupport

How can I automatically reload gem code on each request in development mode in Rails?


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?


Solution

  • 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.