Breaking Up with the Asset Pipeline

Thanks Pete, Brett, Maureen

~ $ WhoAmI

Alan Peabody - Developer @  Agilion

@alanpeabody

https://slides.com/alanpeabody/breaking-up-with-the-asset-pipeline

This Talk

  • Asset Pipeline Pain
  • Experimenting with JS build tools
  • Implementing SPA along side Rails
  • Benefits of Decoupling

Agilion's First Single Page Apps

The DHH Rails Way

  • Turbolinks
  • Caching
  • The Asset Pipeline

The UX Still Sucked

SIngle Page Apps

(on the asset pipeline)

Gem install Javascript

  • Usually not maintained by JS lib Author
  • Frequently out of date
  • Multiple gems wrapping same JS lib.

Manifest Disaster

  • Literally just concatenating files where required
  • Must organize code to ensure everything is in proper place

//= jquery
//= underscore
//= backbone
//= require my_app_setup
//= require_tree models/
//= require_tree collections/
//= require_tree templates/
//= require_tree views/
//= require_tree routers/
//= require my_app_start

Namespacing

  • Error Prone
  • Tedious
  • Antiquated
//my_app_setup.js
window.App = {};
App.Models = {};
App.Collections = {};
App.Controllers = {};
App.Routes = {};
App.Views = {};

Authentication

AuthenTication


Development is SLOW

  • 128 JavaScript files
  • 3 SCSS files
  • Each file is a separate request
  • 5-7 second reload times
The request response cycle is terrible for maintaining application state,
and the asset pipeline is terrible for building JavaScript Single Page Apps.

Small EXPERIMENT

Seperate Repositories

Treating the UI as its own application

AppAPI/
  .git
  Gemfile
  Rakefile
  app/
AppUI/
  .git
  package.json
  bower.json
  gulpfile.js
  app/

JS Tech Stack

  • Backbone
  • NPM
  • Bower
  • Browserify
  • Gulp.js

NPM JS Package Management

Development/build tools dependencies

Like development group Gemfile dependencies

//package.json
{
  "name": "MyAppUI",
  "description": "UI for MyApp",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "gulp": "~3.8.6",
    "gulp-concat": "~2.3.4",
    "browserify": "~5.9.1",
    "less": "~1.7.4",
    "bower": "~1.3.8",
    "browser-sync": "~1.3.2"
  }
}
npm install

Bower JS Package Management

  • Front end package dependencies
  • Often published by JS library authors
  • Like Gemfile with require: false

// bower.json
{
  "name": "MyAppUI",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "jquery": "~2.0.3",
    "bootstrap": "~3.0.3",
    "backbone": "~1.1.2"
  }
}
bower install
Gem based bower wrapper: Rails Assets

Browserify Modules

  • Transpiles CJS Modules to browser compatible JavaScript
  • Based on Common JS and Node Modules
  • Replaces Manifest Files
  • One JS request, even in Development

// app/collections/users.js
var Backbone = require('backbone'),
    User     = require('./app/models/user');

module.export = Backbone.Collection.extend({
  model: User
});

http://browserify.org/

Alternatives: require.js, ES6 Module Transpiler

Source Maps

  • Concatenation is transparent
  • You see your original source files, by file name, in:
    • Dev tools debugger
    • Error stack traces 
  • Maps compiled JS/CSS to the source on disk


Gulp config

var gulp       = require('gulp'),
    browserify = require('browserify'),
    gconcat    = require('gulp-concat');

gulp.task('javascript', function() {
  gulp.src('application.js')
    .pipe(browserify({debug: !gulp.env.production}))
    .pipe(gconcat('build.js'))
    .pipe(gulp.dest('./dist/'));
});

gulp.task('default', function() {
  gulp.watch('js/**', ['js']);
});
gulp javascript
gulp

http://gulpjs.com/

Alternatives: Grunt, Broccoli

BrowserSync

var gulp        = require('gulp'),
    browserify  = require('browserify'),
    gconcat     = require('gulp-concat'),
    browserSync = require('browser-sync');

gulp.task('javascript', function() {
  gulp.src('application.js')
    .pipe(browserify({debug: !gulp.env.production}))
    .pipe(gconcat('build.js'))
    .pipe(gulp.dest('./dist/'))
    .pipe(browserSync.reload({stream: true, once: true});
});

gulp.task('browser-sync', function() {
  browserSync({server: { baseDir: "./", port: 4200 }});
});

gulp.task('default', ['browser-sync'], function() {
  gulp.watch('js/**', ['js']);
});

http://www.browsersync.io/

Alternatives: Live Reload

What about deployment?

Heroku & CORS

2 Heroku Apps w/ Cross Origin Resource Sharing (CORS)

Your front end application deserves to be treated as an application in its own right,
not relegated to the asset directory.

Single Page Apps on Rails Redux

JS Tech Stack

  • Ember
  • NPM
  • Bower
  • Grunt & Broccoli (alternatives to Gulp)
  • ES6 Modules (alternative to browserify)
 

Rails::APi

  • A gem you install in existing rails apps
  • Provides rails-api generator
  • ActionController::API
  • Cuts out some middle ware
  • No sessions
  • Faster responses

AUthentication

Finally a real RESTful API!

OAuth2 Resource Owner Password Credentials Grant
// POST /api/token
{
  grant_type: "password",
  username: "alan@agilion.com",
  password: "correct horse battery staple"
}
// response 200
{
  access_token: 'sdlkyuskdfyiwesdfkljsdf',
  token_type: 'Bearer',
  expires_in: 30.days,
  user_id: 1
}
GET /api/protected
Headers: 'Authorization': "Bearer #{access_token}"

Rails - Active Model Serializers

class UsersController < ApplicationController
  def show
    render json: User.find(params[:id]), serializer: UserSerializer
  end
end
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :email, :is_admin
  
  def is_admin
    object.admin?
  end
end
{
  "user": {
    { 
      "id"      : 1,
      "name"    : "alan",
      "email"   : "alan@agilion.com",
      "is_admin": true
    }
  }
}
On Github

Better Deployments

Heroku & Proxy

2 Heroku apps with Nginx Proxy

Custom Deploys

Both apps on same domain, same server(s)

Development Is Fast

  • The Largest App to Date:
    • 315 JavaScript files
    • 57 Less files
    • < 4 second build time
    • < 1 second browser load time
Oh yeah I would never go back by choice.
- Pete Brown

Feels Good

Development is Faster*

*Or at least it feels faster

Rails is Beautiful Again

Decoupled Deploys

API First development


Purple Farts Slide

We have the opportunity to build a synergistic, mobile enabled, web platform at an accelerated pace with a dynamic user experience years ahead of its time.

Thanks!

Twitter: @alanpeabody

http://slides.com/alanpeabody/breaking-up-with-the-asset-pipeline-2/ 

Made with Slides.com