Replacing the Rails asset pipeline with Gulp

As a developer who spends his days working across different stacks, any parts of the development process I can share between environments is definitely an advantage.

The Asset pipeline is a good way to work with CSS and JS in Rails, but it's very different to how things work elsewhere.

In this post I'll show how to use Gulp to compile and bundle your SASS and Javascript files, producing the same result as the Asset pipeline would.

Why Gulp?

Gulp has a few features I tend to make use of regularly, namely SASS, LESS and JS compiling and bundling (sourcemaps work well too) via plugins. It's well documented and has tons of tutorials online, which is handy for when you just need your assets to work in your solution so you can get some work done!

Over time I've built up a number of Gulp tasks that can tackle everything from combining JS files together into one bundle, to combining forces with Browerify and Watchify to create a full React.js pipeline.

Making way for the new kid on the block

The first thing we need to do is turn off the Rails asset pipeline and remove any references in the project.

In Rails 4 onwards, we can disable the asset pipeline using config/application.rb:

class Application < Rails::Application
  config.assets.enabled = false
end

Remove the stylesheet and javascript tags from the application.html.erb layout file:

<%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>

Gems like turbolinks and sass-rails etc can also be removed from your Gemfile, as Gulp will be taking care of this going forward.

Using Gulp, but in a Rails kinda way

I try to keep to the 'Rails way' as much as possible when it comes to project structure.

Fortunately, there are already reasonably sensible places to put vendor and application-specific assets when using the Gulp pipeline:

  • External libraries: I tend to put libraries like jQuery etc in the vendor\assets directory.
  • Application-specific code: I put front-end code I've written for for the rails app in app\assets\javascripts and app\assets\stylesheets (clearing out what was in there from the Rails boilerplate).
  • gulpfile.js and package.json (etc): I usually put these in the project root, so they are easy to locate and run.

Everyone has a different way of laying out projects of course, but my advice is to try and keep things as consistent as possible between projects - both for you and your team's sanity!

Writing your Gulpfile

Gulp's gulpfile.js is where all of the tasks that can be run are defined. This is typically Javascript minification/concatenation, and compiling SASS/LESS into goold old CSS.

When setting outputs for Gulp tasks, I tend to compile JS and CSS assets out to the public directory. Here's a sample gulpfile (modified from the default Foundation{:target="_blank"} gulpfile:

var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var concat = require('gulp-concat');

var sassPaths = [
    'vendor/assets/bower_components/normalize.scss/sass',
    'vendor/assets/bower_components/foundation-sites/scss',
    'vendor/assets/bower_components/motion-ui/src'
];

gulp.task('sass', function() {
    return gulp.src('app/assets/stylesheets/app.scss')
        .pipe($.sass({
                includePaths: sassPaths,
                outputStyle: 'compressed' // if css compressed **file size**
            })
            .on('error', $.sass.logError))
        .pipe($.autoprefixer({
            browsers: ['last 2 versions', 'ie >= 9']
        }))
        .pipe(gulp.dest('public/css'));
});

gulp.task('js', function() {
    return gulp.src([
            'vendor/assets/bower_components/jquery/dist/jquery.js',
            'vendor/assets/bower_components/what-input/dist/what-input.js',
            'vendor/assets/bower_components/foundation-sites/dist/js/foundation.js',
            'app/assets/javascripts/**/*.js'
        ])
        .pipe(concat('all.js'))
        .pipe(gulp.dest('public/js'));
});

gulp.task('default', ['sass', 'js'], function() {
    gulp.watch(['app/assets/stylesheets/**/*.scss'], ['sass']);
});

Wrapping up

Well, that's pretty much it! We've switched off the Rails Asset Pipeline and replaced it with our own Gulp process.

There are some additional things you'll probably want to do that aren't covered in this article - like hooking Gulp into your build process and make sure everything is properly optimised for production builds, but I hope this is a good starter.

As always, feel free to Tweet, email or leave a comment below with any thoughts!

Show Comments