We have been developing gems a lot as part of giving more structure to Rails applications: the idea and some techniques. Doing this often sees a Rails application and one or more gems being developed at the same time. This unfortunately breaks Rails autoloading. This article will show you how to get it back…
Gems usually require their files from their root file. This makes Ruby load all necessary classes which makes them unavailable to Rails autoloading. Thus, whenever the gem changes the server needs to be restarted. Making the gem an engine, will allow for autoloading, but can add a lot of extra dependencies. The technique shown here will make the gem “progressively enhanceable” such that it can be autoloaded when used within a Rails application without adding any additional dependencies to the gem itself.
Progressively enhance your gem to be an engine
Let this be our gem
#/lib/our_gem.rb
module OurGem
require 'our_gem/difficult_stuff'
end
Autoloading in a Rails application will “just work” if this code is changed to the following. Be sure to add the engine class and to only require it when Rails is available.
#/lib/our_gem.rb
module OurGem
if defined?(Rails)
require 'our_gem/engine'
else
require 'our_gem/difficult_stuff'
end
end
#/lib/our_gem/engine.rb
module OurGem
class Engine < ::Rails::Engine
config.autoload_paths += Dir["#{config.root}/lib/**/"]
end
end
I have a sample component-based Rails application in which you can see this technique at work in this commit.
Other possibilities
I previously thought that other approaches to this problem should work as well: Rails’ `autoload` method, setting autoload_paths and eager_load_paths properly in the main app, or require_dependency. So far I have not had success with these approaches. I would love to hear about how to make these work!