20

I'm writing a rails engine that has some dependencies. I've specified the dependencies in the gemspec and the engine is finding them when I run bundle install (i.e. Gemfile.lock looks correct). When I want to use the plugin in a Ruby file, I can do so but need to explicitly require dependency-name at the top of the file.

However, when I want to use the dependency's asset pipeline, sprockets can't find it.

The app I'm using (for now) is the dummy app that comes in a rails plugin's test folder. Sprockets can find the assets if I specify them in the engine's Gemfile (which is really the dummy app's Gemfile), but not if I specify them in the gemspec. I don't want to rely on the Gemfile because that means that any app that uses my plugin will need to manually add all my dependencies to their Gemfile. For the same reason I don't want a solution that involves updating the app's config file.

This works (in a ruby file) when dependency is included from gemspec:

require 'dependency-name'

but this (in a JS file) doesn't work when dependency is included from gemspec:

//= require 'dependency-name'

Neither require is needed when dependency is included from Gemfile. I think it's pretty clear but let me know if you need more specifics.

Rich
  • 5,603
  • 9
  • 39
  • 61
Isaac Betesh
  • 2,935
  • 28
  • 37

2 Answers2

27

I needed to include the dependency explicitly in my engine.rb in order for its assets to end up in my asset pipeline. Not sure why this is necessary, as Alastor's answer sounded correct to me. It's worth noting that the dependencies are gems that I created using bundler, though I don't see why that should make a difference.

module MyRailsPluginFull
  class Engine < ::Rails::Engine
    require 'dependency1'
    require 'dependency2'
  end
end

Added 11/23/12

Having spent some more time working with Engines, I think I understand this more fully now. Gemspecs are just of a list of dependencies that are required, but the gemspec doesn't instruct the application, on startup, to load the files from those dependencies. Gemfiles, on the other hand, do load all the files during startup.

Added 3/20/2015

My statement from over 2 years ago that "Gemfiles, on the other hand, do load all the files during startup" is not entirely true. It's mostly true in Rails, which by default runs Bundler.require to require all dependencies listed in the Gemfile, as shown in the generator file here -- note that while the default behavior of Rails changed from Rails3 to Rails 4 as discussed here, both use Bundler.require. However, there is a strong case to be made for using Bundler.setup and then an explicit require "dependency1" in whichever file actually depends on depedency1. See this discussion of Bundler.require versus Bundler.setup.

Also, as @nruth points out in the comments, this may lead to loading of unnecessary classes. However, if the dependency is well designed, its classes will mostly be autoloaded, creating minimal overhead for requiring the entire dependency. Alternatively, if it defines its engine in a file that can be required in in isolation, you can just include the engine file, which should add the necessary files to your asset path, allowing you to require its assets in your CSS & JS manifests. See this bootstrap-sass example, where the gem both adds all its assets to config.assets.paths and adds some of them to config.assets.precompile.

While this question is a few years old and I don't even remember what Rails Engine I was writing at the time, I suspect the right way to do it would have been closer to this:

module MyRailsPluginFull
  class Engine < ::Rails::Engine
    initializer 'bootstrap-sass.assets.precompile' do |app|
      require 'dependency1'

      # add dependency1's assets to the list of paths
      app.config.assets.paths << ...
    end
  end
end

But note that this should not be necessary--the dependency itself should have defined this initializer so that simply requiring it would be sufficient, as the bootstrap example above does.

Isaac Betesh
  • 2,935
  • 28
  • 37
  • this seems to work (great) but won't it mean loading unnecessary things in production if you're precompiling assets? – nruth Mar 14 '15 at 16:02
  • requiring dependencies appears to be a common practice: https://github.com/comfy/comfortable-mexican-sofa/blob/master/lib/comfortable_mexican_sofa/engine.rb, https://github.com/spree/spree/blob/master/core/lib/spree/core.rb – Kadu Diógenes Apr 13 '15 at 14:36
  • If you're going to be packaging your code as a gem, you don't know if your eventual client will want to use `Bundler.setup` or `Bundler.require` and you don't want to force them onto either one. Hence, you want to explicitly `require` whatever dependencies are needed. That's why the examples linked to in the comment from @CarlosDiógenes do it. At the same time, that means longer load time and potentially unnecessary loading of classes, unless the dependencies you are requiring use `autoload`. – Isaac Betesh Apr 13 '15 at 16:38
0

You designed your engine according to the http://edgeguides.rubyonrails.org/engines.html ? If your engine class inherits from Rails::Engine, it really should find all assets by itself.

Atastor
  • 741
  • 3
  • 11
  • Commenting Isaac's own answer: Do you notice a difference in behaviour when not specifying the dependency in the gemspec but in the engines own Gemfile? – Atastor Sep 05 '12 at 07:07
  • Yes, I've noticed that [rake tasks are only inherited when you specify in the Gemfile](http://stackoverflow.com/questions/12518232/how-to-install-database-schema-from-a-rails-3-plugin-in-another-plugin). You're probably right that this is another difference, though I'm a bit too busy to verify at the moment. – Isaac Betesh Sep 23 '12 at 00:33