The initial choice to abandon the Rails asset pipeline, was due to the difficulty of achieving the following goals within the rails ecosystem:
Browserify compatibility.
Browserify is one of the best things to happen to front-end JS development in the last few years. Here are a few of the benefits:
-
Modular node-style code (CommonJS) in the browser. This improves organization, maintainablity, and dependency management considerably.
-
Access to NPM modules make grabbing, managing, and using modules super convenient. This is where JS developers publish (not rubygems).
# download to node_modules and save as dependency in package.json npm install someLibrary --save
# Profit. $ = require 'someLibrary'
Using Gulp to process files is noticeably faster than any alternatives thanks to node streams.
By harnessing the power of node's streams you get fast builds that don't write intermediary files to disk. - gulpjs.com
During build out, we're re processing files and reloading the browser every time we save. Thats thousands of times. Faster compiles means less time wasted waiting.
BrowserSync w/stylesheet injection updates (vs full page refreshes).
BrowserSync live reloads as many browsers as you have pointed to your local site simultaneously, across devices. Automatic hard refreshes work with Rails, but live stylesheet updates without a full page refresh don't, due to the way the asset pipeline works. BrowserSync relies on watching a css file change - but in development, Rails doesn't regenerate files until a server asks for it.
The Asset Pipeline is dated. Clearly, we've had trouble integrating it with modern front end toolsets. Rails itself, along with many gems are out of sync with current versions of the tools they're trying to port, and other tools are just missing or incompatible. By having assets independently managed, we'd had hoped to break free from any one framework's opinions, allowing us to use the same tools and process across projects.
While we were able to create near feature parity with the Rails asset pipeline, using Gulp tasks, many third party gems still assume the default pipeline is being used. This means we can't totally abandon it. Rails still needs to process these dependencies
Additionally, nearly all of the benefits of a home-rolled asset pipeline are only seen by the front-end team. For Rails devs and dev ops, it adds complexity, without much benefit for them. With that in mind, and the fact that most of the majority of front-end work will be complete as we hand this off, here are the re-priorized goals:
- Simplicity and Maintainability
- Browserify compatibility
- Fast on the fly compiling
Below are some options to address these goals. Each one has trade offs in different areas. We're open to whatever solution you think will serve you best moving forward, and will make any necessary adjustments as soon as possible.
The main global.coffee file would still be compiled with Browserify either with Gulp, or the command line. The standard asset pipeline would handle everything, else, and the compiled browserify bundle would simply be pulled into an application.js file using sprockets, along with any other javascript you want to require (third party or otherwise).
The workflow would look like this:
- Compile
global.coffee
with browserify (with a gulp task or the command line) toapp/assets/javascripts/bundles/global.js
- In
app/assets/javascripts/application.js.coffee
, pull in the bundle like you would any other javascript asset:
#= require 'ahoy'
#= require 'updates/someNewThing.coffee'
#= require 'bundles/global.js'
Pipeline does its thing, and you include applicaiton.js
in your layout like normal.
- Lets rails do as much as possible, and only uses browserify (with gulp or the command line) to compile one file.
- All the browserify benefits are available, but optional moving forward.
- Rails handles assets like normal, and just pulls in your compiled browserify bundle with a regular sprockets
//=require
in application.js - The browserify workflow is optional. New JS could be pulled in however you want.
- Compiled code is checked in, so no need to compile or npm install on deploy
- Can still keep gulp around for browserify compiling, or non-asset tools like watching, live reloading or compressing images
- No compiled code checked in
- Still have to run gulp locally when working with bundled javascript
- Checked in compiled code causes needless rebase conflicts. This can be avoided with some git configuration. Alternatively, you could .gitignore bundles and compile them on deploy, similar to what we're doing now.
Remove most if not all Gulp tasks, and get Browserify functionality through the browserify-rails gem. This still uses a package.json for configuration and js dependencies.
- Use Browserify and Sprockets interchangeably
- Asset pipeline otherwise works like normal
- Reduces complexity: gulp is out of the picture (except maybe for some standalone tasks like image compression)
- CommonJS requires can be used
- Still get NPM for managing front end dependencies
- Slower compiling (maybe ~10x) because there's no way to use it with watchify (cacheing, watching, verison of browserify)
- Still some extra overhead.
Remove Gulp AND Browserify, let Rails do what it's always done. This would require refactoring javascript out of modules and into a namespace on the window, and using sprockets requires to concatenate.
- Least complexity, no additional tech to learn, configure or maintain
- Dated (arguably less maintainable) JavaScript style
- Limited toolset
- Slower compiling
Ever thought about using webpack? I'm in a similar spot and it's the only promising solution I've seen: https://medium.com/brigade-engineering/setting-up-webpack-with-rails-c62aea149679