UPDATE: If you came for a practical tutorial, check out this piece on how to use Bower with Rails.

When writing single-page apps with Rails and the framework of your choice, may it be Ember, Angular or Backbone, there’s one huge pain: Maintaining a large front-end codebase while delivering with respect to the request count and size.

As with any important programming aspect in JavaScript (like DOM access or building SPAs), packet managing perfectly adheres to “XKCD’s Law of Standards”:

How standards proliferate

Sprockets recently and silently introduced a basic integration of Bower, a new packet manager by Twitter that ranks among the Top 10 starred projects on GitHub in 2012. The integration is a huge opportunity towards better maintainability, component orientation and, by thus, more efficient asset organization for single-page Rails apps. We will see why in this article.

Asset Gems for external dependencies

Without other tools than the plain asset pipeline, managing a set of external dependencies means downloading some archive files from a homepage and throwing the respective files in vendor/assets. This folder soon becomes an unmanageable pile of files. Have you tried updating or removing some dependencies? This approach also makes your app subject to what older folks know by the term DLL hell: A single source for external libraries that isn’t version-agnostic neglects the complexity of resolving dependencies and building a dependency graph.

Asset gems are the first logical step for a Rails developer to think of for distributing front-end libraries in a feasible manner. These are gems like jquery-rails, which expose the library’s assets according to the asset pipeline’s path conventions and hook into your Rails app (Further described in the Asset Pipeline Guide). This is approach is neat because we can centrally store all dependencies in the Gemfile und re-use bundler. It is also more flexible in Rails-specific cases, such as in the case of jquery-rails.

However all of the library’s assets still need to be required from within the application - a duplication, since a packet should know on its own which files it exposes to the parent app. Also, since asset gems are an Rails Engine, they pull in major dependencies and can’t be used with Sprockets standalone. Then also, apart from the most popular ones, there aren’t many libraries available as asset gems and still require repackaging. If only there was a way to include versioned assets from both a central registry or individual Git repositories, in a way that is not specific to Rails or Sprockets, but still compatible to their conventions?

Introducing Bower

Enter Bower to the rescue. At first, it looks like yet another packet management solution for scripts, images and stylesheets. What differentiates Bower is it’s low-level approach:

Bower is a generic tool which will resolve dependencies and lock packages down to a version. It runs over Git, and is package-agnostic. A package may contain JavaScript, CSS, images, etc., and doesn’t rely on any particular transport (AMD, CommonJS, etc.).

It provides a central repository with a growing number of libraries available for use. However, you are free to include components directly from Git, GitHub, a web url, the file system or even your own component repository.

Components are declared using the component.json:

{
  "name": "myProject",
  "version": "1.0.0",
  "main": ["./path/to/app.css", "./path/to/app.js", "./path/to/sprite.img"],
  "dependencies": {
    "jquery": "~1.7.2"
    "ember": "latest"
  }
}

The format of this manifest is pretty straight-forward. Give it a try and fetch bower using Node’s packet manager, npm:

npm install --global bower

For diving further into bower, follow e.g. this tutorial on NetTutsPlus.

Using Bower in Rails

Sprockets recently received a pull request that layed the groundwork for integrating Bower, from version 2.6.0 onwards. Use bower on the command line and the component.json manifest to manage your dependencies. Within your application.js (or vendor.js or wherever you like), you can require dependant components by their name.

Some tweaks need to be done to get this method to work. We’ll use the beta of Rails 4 for this example since we need a newer version of Sprockets than Rails 3 currently allows us to.

# Gemfile

gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders'
gem 'sprockets-rails', github: 'rails/sprockets-rails', group: :assets

With the upcoming Rails 4, the Sprocket integration is extracted into the sprocket-rails gem, so the necessary patch is already available in edge Rails. Unfortunately, using the newer version of Sprockets in Rails 3 is a bit tricky.

Then, we need to tell Bower where to find the component manifest and to put component files. In order to run bower from the app root, like rake or rails, we need to put a .bowerrc to adjust bower to Rails’ path conventions:

{
  "directory" : "vendor/assets/components",
  "json"      : "vendor/assets/component.json"
}  

I suggest rooting components under vendor, but you could also use app or lib accordingly.

Next, we need to tell Sprockets to include the component folder by passing the name to the environment configuration.

environment = Sprockets::Environment.new
environment.append_path 'components'

In Rails, config.assets aliases to the sprocket environment:

# config/environment.rb

config.assets.append_path 'components'

Then, you still need to require the your dependencies. For JavaScripts, that’s

# e.g. app/assets/javascripts/application.js

//= require ember
//= require jquery

Clone this small example application from GitHub to try it out live. This changeset applies the aforementioned alterations.

Outlook

This small example demonstrates the potential behind a proper management for internal and external asset components.

However, a solid integration is yet to be developed, e.g. as a gem that handles the integration steps described above. It should features a proper communication layer on top of Bower’s API, eliminating the need of having Node installed and running bower separately on the command line. The integration part should eliminate the need to redundantly require all dependencies, e.g. by providing an additional directive (think //= require dependencies).

Lastly, we should honor the great pioneering work of Joshua Peek on Sprockets and laying the cornerstone for the Bower integration, as well as the team behind Bower.

Packages and modules

The ultimate goal should be a solution that goes even further by being agnostic to modules.

Single-Page Apps are by their nature bundled in one or a few packages. Bower components are a neat way to define these packages and their dependencies. However, we still need to require dependant components.

For a more intelligent solution, require should go away - or, put less baltant, decend into the background. Sprockets should be able to determine the relevant source files: By enforcing a one-module-per-file and module declaration convention, it should be able to graph module dependencies and resolve the inclusion order and include only necessary modules. This has the large number of benefits: Only necessary code gets shipped over the wire, reducing request size and parsing time, and better isolation for focussed and faster tests to name a few.

I’m planning to expand on this issue in a future post.



blog comments powered by Disqus