Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency included in gemspec not added to asset pipeline in rails engine

Tags:

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.

like image 961
Isaac Betesh Avatar asked Sep 04 '12 03:09

Isaac Betesh


1 Answers

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.

like image 104
Isaac Betesh Avatar answered Oct 08 '22 01:10

Isaac Betesh