Bundler, Meet Bower

Bundler is a package management tool for ruby gems. It takes in a ruby file with a list of gems and version numbers, like this:

source 'https://rubygems.org'

gem 'rails', '4.0.0'
gem 'sqlite3'
gem 'sass-rails', '~> 4.0.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.0.0'
gem 'jquery-rails'

and a single command:

bundle install

and magically downloads all the dependencies for your project, so you don’t have to hunt them down yourself or store them in your git repository.

That means that when you update a dependency, rather than see all the changes to that dependency in your git history, you see this:

-gem 'rails', '4.0.0'
+gem 'rails', '4.0.1' 

Beautiful! Elegant and succinct – everything that I want to know expressed in one line.

Bundler: The best way to package your application's dependencies.

But now wait, what about all those pesky assets? What do we do when we want to update our version of Backbone.js? Well, without a package manager, we’ll end up with an ugly commit like this.

What? Did I make all those changes? No, but it sure looks like I did.

And how do I know that Backbone.js depends on jQuery and Underscore.js? I just need to know. And if the dependencies change, I’ll have to go hunt down the documentation to see what the new ones are.

Yuck. Not elegant at all.

It’s amazing to me that most of the Rails community seems satisfied with this sorry state of affairs. I supect that this is one of many factors contributing to the slow uptake of Javascript frameworks like Backbone.js by Rails developers.

If assets are first-class citizens, then they deserve to be managed as such.

Enter Bower, a package management tool for javascript libraries. It takes in a JSON file with a list of javascript dependencies and version numbers, like this:

{
  "name": "bower dependencies",
  "dependencies": {
    "backbone": "1.0.0",
    "backbone-relational": "0.8.5",
    "jquery": "1.10.1",
    "jquery-timeago": "https://raw.github.com/rmm5t/jquery-timeago/b311508ee019650200f552015a48f69de18c5857/jquery.timeago.js",
    "underscore": "1.4.4",
    "jquery-masonry": "http://www.fishspotr.com/jquery.masonry.js",
    "jquery-autosize": "latest"
  },
  "devDependencies": {
    "sinon": "http://sinonjs.org/releases/sinon-1.4.2.js",
    "jasmine-sinon": "0.1.0"
  }
}

and a single command:

bower install

and magically downloads all the dependencies for your project, so you don’t have to hunt them down yourself or store them in your git repository.

That means that when you update a dependency, rather than see all the changes to that dependency in your git history, you see this:

-    "backbone": "1.0.0",
+    "backbone": "1.1.0",

Beautiful! Elegant and succinct – everything that I want to know expressed in one line.

Bower: A package manager for the web.

The amazing thing is that these two package managers work beautifully in tandem: bundler for backend packages (gems) and bower for front-end assets.

I recently added bower to our pipeline and zapped 8,935 lines in one commit. Those were all lines of code from assets that should never have been in our repository in the first place. All of that code is now expressed in a single bower.json file, used at build-time to download all packages and dependencies, just like you would with bundler.

Now that we’ve switched to bower, I can’t believe that anyone would manage assets without it.


For those interested in trying out bower with a Rails stack, here are a few steps to get you going. First, assuming you have node and npm installed (they’re packaged together nowadays, so installing once will get you both), you’ll can install bower with:

npm install -g bower

Next, create a directory in vendor/assets called bower_components. Then add a .bowerrc file to your root directory to tell bower to install assets in that directory:

{
  "directory": "vendor/assets/bower_components"
}

You’ll probably want to ignore that directory in your .gitignore file since you the whole point is not to commit these dependencies to the git repository.

Now you’ll need to describe your dependencies in a JSON file named bower.json. The key element of that file is the dependencies hash, which contains all your apps dependencies and their version numbers (see the example above).

Once you have that file, just run: bower install to pull them all in with whatever their dependencies.

There are a couple other caveats. You have to add bower_components to the assets.paths config to get the asset pipeline to pick it up. In config/application.rb, add the line:

config.assets.paths << Rails.root.join('vendor', 'assets', 'bower_components')

Also, since bower packages are each given their own directory, you will need to require them with jquery/jquery rather than just jquery as you normally would, e.g.:

#= require jquery/jquery

Incidentally, if you dig into Sprockets, the Rack-based asset packaging system used by the Rails asset pipeline, you’ll see that it has in fact recently added support for bower.json configuration files in assets and if it finds one, will let you load the package with its name rather than the full path. However, many bower packages continue to name their configuration file package.json instead of bower.json, which sprockets will not find, so this setup will not work universally. (I submitted a pull request to change this but it was rejected. Bah.)

Also, somewhat annoyingly, the bower assets will be given a low priority in the asset search paths, so if you have gems that include assets, those assets will take precedence. Better just to use the full path (i.e. jquery/jquery) and avoid any potentially confusion bugs down the line.

That being said, using bower with a Rails stack is easy peasy and brings the full ecosystem of asset packages to your fingertips. Take the plunge, you won’t regret it!

Further reading: How to manage front-end packages in Rails with Bower

Posted on November 21st, 2024. Filed under: bundler, bower, rails, javascript.
blog comments powered by Disqus