Alternative Asset Pipelines

At today’s dev discussion in Boston, we discussed alternatives to the Rails asset pipeline for JavaScript apps. My notes follow - please feel free to add to the disucssion!

Rails Asset Pipeline

Pros:

  • It “just works”
  • CoffeeScript and Sass out of the box
  • Can chain on ERB to inject things like environment variables at compile time.
  • Fingerprinting for free - so long as you use the right helpers for images, css, etc.
  • version your JavaScript dependencies with asset gems

** Cons**:

  • Asset gems are terrible. Often lag official release, often have unnecessary dependencies. Consider railsassets.org which fronts bower.
  • Smashes all your JavaScript together in the global namespace.

JavaScript Apps

If you’re writing an app that doesn’t use Rails for the front end, you should consider using something other than the Rails Asset pipeline for compilation. This is certainly the case if your backend doesn’t use rails at all. Unfortunately, the JavaScript community doesn’t really have a canonical, do-it-all solution. There are several decisions you need to make.

Package Managers

There are two, slightly incompatible package managers that are typically used for this.

  • NPM - mostly stuff you can use in Node but also has many (most? all?) typical front end dependencies available as well
  • Bower - front end focused. It’s not clear to us why this needs to exist in addition to NPM.

You will probably want a command line tool to actually run the compilation, so you’re probably going to need Node anyway. You also need to install bower via npm. Both package managers lack support for something as thorough as Gemfile.lock, so you should probably check these dependencies in, vendored.

Task Runners/Compilers

Lots of different options here.

  • Grunt - oldest. Configuration based, reliant on each step outputting files. Tends to get unwieldy and is slower than newer runners.
  • Gulp - Seems to be winning favor in the JS community. Things are done in code rather than described with JSON. It works with streams, so there are no intermediate files. Fast.
  • Webpack - New(ish). Not a general purpose task runner. No one had much experience with this. Seemed to be used with React?
  • Broccoli - Not a general purpose task runner.Exists exclusively to be an asset pipeline. Used by ember_cli.

Module Formats

Using something that isn’t sprockets for your asset pipeline means you can take advantage of various JS module systems.

  • CommonJS - like what node uses: require('./path/to/file'). Use the exports object to expose things.
  • AMD - requireJS is the canonical implementation here
  • ECMAScript 6 Modules - Coming soon to a browser near you. A middle ground hoping to please both AMD and CJS users. Seems most similar to CJS.
  • UMD - something @christoomey said and was maybe making up. You never can tell.
  • Angular - angular has its own module system.

Browserify allows you to use common-js style require’s in your browser code. It also lets you use any (probably a lie) node module there. If you’re using browserify, you should probably stick to NPM. You can use packages from bower if you need to with a shim. That shim takes your browser code, npm-ifies it so it works in NPM, then browserifies it so you can require it in the browser. Right…

Sass

When compiling Sass with JS, you’re probably using libsass. The advantage here is speed. It’s significantly faster than sass compilation in ruby. Unfortunately, it’s not 100% compatible with Ruby Sass, which is still the canonical implementation.

What about…

  • Individual files in development/concatenated in production
  • Adding digests to the file names

There are various packages that do this. Usemin, for instance. Sounded like we needed more experience to make decisions here…

Recommendations

  • If you’re writing an ember app with a rails back end (or otherwise) consider splitting it into two repositories. Use ember_cli and broccoli for your asset pipeline needs.
  • If you’re using angular, there’s less of a reason to do this as you still need to use its module system.

There was some feeling that separating the front end as its own repository promoted it to a first class citizen rather than being rooted down in app/assets. It also lets you more readily use whatever conventions arise around your framework of choice.

Worth noting that sprockets and the rails asset pipeline work with bower out of the box (mostly). In my experience, the only pro to the rails asset pipeline has been ease of full integration testing (hitting the real API, real database, ability to clean the database between tests, access to factories). Borg will hopefully solve that part of the problem soon™

1 Like

@seangriffin To me the killer asset pipeline feature (other than “just working… mostly. Now that it’s been around a while”) is that the helpers you use in views and sass are aware of the digesting that happens. If you’re on a rails project, it seems crazy to give that up. Can other tools hook into that somehow?

Since this discussion is from a while ago, I’m curious to know if thoughtbot have settled on an approach for handling front-end assets in Rails apps? I notice Suspenders doesn’t seem have anything yet for this yet.

Not a Thoughtboter, but I’ve had good success using Rails-Assets. You load the libraries as if they are gems, but they are versioned according to their versions on Bower. Rails-Assets compiles gems for any version of the library published on Bower. You then just require the CSS/JS in your manifests as if it’s sitting in your app/assets or vendor/assets folder.

Pros:

  • As @derekprior wrote, Ruby gem versions of libraries suck. They often don’t keep up with the “real” (Bower) versions. With Rails-Assets you have the gem as soon as it’s available on Bower.
  • Rails-Assets uses Bower package dependencies to resolve the JS dependency graph, so you get compatible versions of gems (as far as I can tell)
  • Gemfile becomes the single source of dependency version truth for both Ruby and JS, which means you can use Bundler commands to manage all your dependencies, and your Gemfile.lock will lock both Ruby and JS versions on development and production environments.

Cons:

  • Some JS library authors don’t properly tag their releases with Git tags for versions. I’ve had to file PRs and/or cajole a couple library authors to tag releases just so I can use them with Rails-Assets. (In general, I would regard this as a dependency smell, and a reason to look for alternatives if an author isn’t following good practices with versioning)
2 Likes

For rails projects we use the asset pipeline with asset gems, sometimes via rails-assets.org. I think a few people have tried bower, but it hasn’t worked out spectacularly well.

If you’re using rails as an API and doing something like ember, then using bower + ember-cli makes the most sense. We seem to be taking on more of these projects of late – either ember specifically or non-rails. Ember seems to have a good story around this, but other frameworks - like Angular - are still a bit like the wild west.