Torba: escape the next JS trap

TL;DR Last week I discovered a gem called Torba that solved all our problems with frontend dependencies. I was so blown away that I had to write about it!

How did we get here?

Back in 2012 you basically had two possibilities to include a frontend dependency:

  • Copy/Past it in your vendor/assets directory, quite obvious this is not really the way to go
  • Find a brave soul that will maintain a gem that adds the assets in the sprockets search path

Of course a half year later you see that your favorite jQuery plugin has a fancy new feature and you want to use it. You lookup the gem and see that the original maintainer chose a new path (became JS frontend dev for example) and abandoned the gem. Not their fault, it happens, but you are suddenly hunting for another brave soul...

A good example is the countless gems that were created for bootstrap 2: twitter-bootstrap-rails, less-rails-bootstrap, anjlab-bootstrap-rails, compass_twitter_bootstrap, bootstrapped-rails. We have switched between these gems a couple of times in our main project, just to have the latest version of the framework.

It's 2013 and JS frontend frameworks start to emerge like mushrooms and suddenly everyone is using one. Fast forward six months and they ditch it for another framework. The term javascript framework trap is coined and honestly I can't keep up. More JS history here.


In these crazy times some good things happen like bower, suddenly you could easily define your JS dependencies. For the Rails world rails-assets was born, using bower it was a drop-in replacement for native asset gems. Sprockets caught up and officially started supporting bower.json syntax in version v2.10.0 (May 24 2013), meanwhile bower-rails is created, which wraps bower and places your JS dependencies in vendor/assets. Happy times!

But as time goes by things aren't that great anymore, bower failed to create a specification from the start and many packages didn't include the correct bower.json sections (ex: main or ignore). For a bower + sprockets setup this means that sprocket wouldn't pick up the assets. This results in a lot of stuff being precompiled (everything that's not .js or .css). Another big issue is that JS world doesn't seem to understand semantic versioning at all. Minor version changes API syntax so upgrading a JS dependency is becoming a very painful job. Also bower doesn't have a Gemfile.lock concept, we didn't pin down versions and had the pleasure of a JS plugin that completely broke our app, semantic versioning WUT?.

We are in frontend land so things change a lot. In 2014/2015 other people took over, suddenly we have 10 alternatives to bower and we Rails people are like WUT?!? Frontend package maintainers don't know anymore and start adding/removing bower.json, package.json, jam.json,...

Current state anno 2016

Currently you have the following options for JS dependencies:

  • Copy/Past it in your vendor/assets directory: still not the way to go
  • Use bower + sprockets (current setup): Bower is dead you know?
  • Use asset-rails: Asset Rails is dead you know? and it depents on bower which is heu... dead
  • Use a sprockets replacement: Like webpack (follow up of grunt/gulp/browserify), but this falls for me in category of the javascript trap. I bet that in the next year webpack will be replaced by at least 4 other alternatives.

We noticed that package maintainers slowly remove bower.json files and only use npm. We also didn't like the way bower would download and include the complete repo, even when we only need one JS file. A good example is lodash, their main repo contains a vendor folder with a bunch of images. During precompile they all get processed by sprockets, fun times!

We had it with all these JS traps, yea let's use package manager x and see how far we get (read: how long does it take till it's replaced by a new shiny thing).

Torba to the rescue!

Torba is a gem that manages your frontend dependencies. It has support for npm packages (which is the standard for JS currently), github releases (incase packages don't release on npm anymore sigh) and general tar/zip support. For all options and the full monty head over to the readme!

You define a Torbafile with your dependencies listed as followed:

npm package: "coffee-script", version: "1.9.2", import: %w(dist/jquery.js)  
gh_release source: "janpaepke/ScrollMagic", tag: "v.2.0.0", import: %w(dist/scrollmagic.js)  

Torba will place itself before assets:precompile. It will download the packages (cache them) and then only include the files you defined in the import section. All these folders are then added to the sprockets load path. You can then reference them in your application.js / application.css.

I start converting all our JS dependencies from bower.json to Torba and after getting the hang of it (and cleared up some issues with scss file with the maintainer) I quickly became use to it. It's easy and guards you from the next JS trap!

Another thing we didn't like about bower is that if somebody changed an assets all developers had to remember to run bower install. With torba you can add a verify step to see if all packages are installed, if not an error will be raised. I extended this behaviour to not raise an error but to actually package everything when in development (read: auto install of new JS dependencies).

# config/initializers/torba_autopack.rb
# Auto pack when in development, normally assets:precompile takes care of this
class TorbaAutoPack  
  def self.start!
  rescue Torba::Errors::MissingPackages
    Rails.logger.error "Assets not packed yet, packing..."
    Rails.logger.error 'Done asset packing, booting...'
  rescue Errors::NothingToImport => e
    Rails.logger.error "Couldn't import an asset(-s) '#{e.path}' from import list in '#{e.package}'."
    Rails.logger.error "Check for typos."
    Rails.logger.error "Make sure that the path has trailing '/' if its a directory."
  rescue Errors::AssetNotFound => e
    Rails.logger.error "Couldn't find an asset with path '#{e.message}'."
    Rails.logger.error "Make sure that you've imported all image/font assets mentioned in a stylesheet(-s)."

TorbaAutoPack.start! if GetinEnv.development?  


Protect yourself from the next JS packager / builder / framework and just use github. We can be pretty sure that github will be around for a while. Torba is simple and does its job. I'm again a happy camper! The downside is that you cannot play with yet another new shiney toy.

Comments powered by Disqus