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 December 21st, 2024.