Slide 1

Slide 1 text

Rails: beyond the asset pipeline RubyC, Київ, Україна | 31 May 2015

Slide 2

Slide 2 text

Frontend has changed

Slide 3

Slide 3 text

asana

Slide 4

Slide 4 text

typecast

Slide 5

Slide 5 text

blocs

Slide 6

Slide 6 text

So how can Rails keep up?

Slide 7

Slide 7 text

psst… ditch the asset pipeline

Slide 8

Slide 8 text

Привіт RubyC! Привет RubyC!

Slide 9

Slide 9 text

Привіт Київ! Привет Киев!

Slide 10

Slide 10 text

About me @myabc github.com/myabc alexbcoles.com

Slide 11

Slide 11 text

Де я живу Где я живу

Slide 12

Slide 12 text

Де я працюю Где я работаю

Slide 13

Slide 13 text

Те, що я працюю на То, что я работаю на

Slide 14

Slide 14 text

31 August 2011 Hello Rails 3.1

Slide 15

Slide 15 text

gem wrappers

Slide 16

Slide 16 text

$ gem list --remote jquery | wc -l 300

Slide 17

Slide 17 text

Bower

Slide 18

Slide 18 text

Three different ways to use Bower 1. gem Using an integration like bower-rails or bower gem gem install bower-rails source 'https://rubygems.org' gem 'bower-rails'

Slide 19

Slide 19 text

Three different ways to use Bower 2. Load path config.assets.paths << File.join(Rails.root, 'bower_components') requires Rails 4 (Sprockets 2+)

Slide 20

Slide 20 text

Three different ways to use Bower 3. rails-assets.org source 'https://rubygems.org' gem 'rails' source 'https://rails-assets.org' do gem 'rails-assets-bootstrap' gem 'rails-assets-angular' gem 'rails-assets-leaflet' end requires Bundler >= 1.8.4

Slide 21

Slide 21 text

Torba torba-rb/Torba

Slide 22

Slide 22 text

Bower only helps solve dependency management

Slide 23

Slide 23 text

Things you might need Dependency management Pre and post-processing Code loading Code bundling

Slide 24

Slide 24 text

Sprockets plugins alexspeller/non-stupid-digest-assets

Slide 25

Slide 25 text

Sprockets plugins Post-processing JS/CSS ai/autoprefixer-rails uses PostCSS project autoprefixer TannerRogalsky/sprockets-es6 uses Babel (formerly 6to5), requires Sprockets 3

Slide 26

Slide 26 text

Problem with Sprockets No standard plugin configuration style Cannot control pre/post-processing order Asset dependencies

Slide 27

Slide 27 text

Things you might need Dependency management Pre and post-processing Code loading Code bundling

Slide 28

Slide 28 text

Webpack

Slide 29

Slide 29 text

Webpack configuration + entry point // webpack.config.js module.exports = { context: __dirname + '/app', entry: 'rubyc-app.js', output: { filename: '[name].js', path: path.join(__dirname, '..', 'app', 'assets', 'javascripts', 'bundles'), publicPath: '/assets/bundles' } }

Slide 30

Slide 30 text

Requiring JS single files and dependencies require('./another-file'); //= require ./another-file (Sprockets) var angular = require('angular'); var jQuery = require('jquery'); require('jquery-ui');

Slide 31

Slide 31 text

Requiring JS a tree var requireTemplate = require.context('./app/controllers', true, /\.js$/); requireTemplate.keys().forEach(requireTemplate); //= require_tree ./app/controllers (Sprockets)

Slide 32

Slide 32 text

Requiring assets require('jquery-ui/ui/jquery-ui'); // .js (default) require('jquery-ui/themes/base/jquery.ui.core.css'); require('jquery-ui/themes/base/jquery.ui.datepicker.css'); require('select2/select2'); // .js (default) require('select2/select2.css');

Slide 33

Slide 33 text

Requiring assets quick start require('jquery-ui/ui/jquery-ui'); // .js (default) require('!style-loader!css-loader!jquery-ui/themes/base/jquery.ui.core.css'); require('!style-loader!css-loader!jquery-ui/themes/base/jquery.ui.datepicker.css');

Slide 34

Slide 34 text

Requiring assets with a bit of config // webpack.config.js module.exports = { context: __dirname + '/app', entry: 'rubyc-app.js', module: { loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.png$/, loader: 'url-loader?limit=100000&mimetype=image/png' }, { test: /\.gif$/, loader: 'file-loader' }, { test: /\.jpg$/, loader: 'file-loader' } ]} } require('jquery-ui/ui/jquery-ui'); // .js (default) require('jquery-ui/themes/base/jquery.ui.core.css'); require('jquery-ui/themes/base/jquery.ui.datepicker.css');

Slide 35

Slide 35 text

Requiring assets body { background: url(file:///Users/alexbcoles/git-repos/talk-rails-beyond-asset-pipeline//assets/bundles/backgroun } /* border-image: url(file:///Users/alexbcoles/git-repos/talk-rails-beyond-asset-pipeline/border-image.png); */ .box { border-image: url(file:///Users/alexbcoles/git-repos/talk-rails-beyond-asset-pipeline/data:image/png;base64,i }

Slide 36

Slide 36 text

Loaders and plugins Webpack is built on the concept of loaders and plugins

Slide 37

Slide 37 text

Loaders

Slide 38

Slide 38 text

Loaders Loaders are transformations that are applied on files. They preprocess files. I. e. they can transform CoffeeScript to JavaScript.

Slide 39

Slide 39 text

Chaining Loaders eslint ← coffee json ← yaml style ← postcss ← css ← sass ngtemplate-loader ← markdown

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Requiring Rails-style translation files $ npm install --save-dev json-loader yaml-loader I18n.translations = I18n.translations || {}; I18n.translations.en = require('!json!yaml!config/locales/en_US.yml').en; I18n.translations.de = require('!json!yaml!config/locales/en_DE.yml').de;

Slide 42

Slide 42 text

Transpiling (ES6 → ES5) $ npm install --save-dev babel-loader module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'} ] }

Slide 43

Slide 43 text

Transpiling (ES6 → ES5) // app-defaults.js export default { favouriteConf: 'Ruby C' }; // app.js import appDefaults from './app-defaults.js'; class ExampleApp { constructor() { console.log(appDefaults.favouriteConf); } } export default ExampleApp;

Slide 44

Slide 44 text

Legacy code support $ npm install --save-dev exports-loader module: { loaders: [ { test: /[\/]angular\.js$/, loader: 'exports?angular' } ] }

Slide 45

Slide 45 text

How to integrate with Rails rake webpack – or – rake assets:precompile

Slide 46

Slide 46 text

Rails integration /// manifest-84b43dda218a2c29ce11f4f7b9ca4e5f.json { "assets": { "1downarrow.png": "1downarrow-d2055955ce2927de07f2e33abdbfdc1b.png", "1uparrow.png": "1uparrow-a4eef1942dd999e6a16e84c1c8122b8a.png", "2downarrow.png": "2downarrow-e8bc5b59fa922f68637dc22b4a467f5c.png" } }

Slide 47

Slide 47 text

Rails integration # app/helpers/application_helper.rb def webpack_bundle_tag(bundle) src = if Rails.configuration.webpack[:use_manifest] manifest = Rails.configuration.webpack[:asset_manifest] filename = manifest[bundle] "#{compute_asset_host}/assets/#{filename}" else "#{compute_asset_host}/assets/#{bundle}-bundle" end javascript_include_tag(src) end http://clarkdave.net/2015/01/how-to-use-webpack-with-rails/

Slide 48

Slide 48 text

Rails integration Running everything together foreman start # Procfile web: bundle exec rails server -e ${RAILS_ENV:="development"} assets: $(npm bin)/webpack --colors --watch --progress

Slide 49

Slide 49 text

How does CSS fit into the picture?

Slide 50

Slide 50 text

Core application CSS 1. Stick with the Rails asset pipeline for CSS 2. Using gulp rather than Webpack

Slide 51

Slide 51 text

Gulp for Sass/CSS $ npm install -g gulp $ gulp sass gulp.task('sass', function() { return gulp.src('app/assets/css/default.css.sass') .pipe(sass({ loadPath: [ './bower_components/bourbon/app/assets/stylesheets' ] })) .pipe(autoprefixer({ cascade: false })) .on('error', function(err) { console.log(err.message); }) .pipe(gulp.dest('public/assets/css')); });

Slide 52

Slide 52 text

One more thing…

Slide 53

Slide 53 text

http://xkcd.com/303/

Slide 54

Slide 54 text

Hot reloading

Slide 55

Slide 55 text

Hot reloading colektivo/song-song-song gaearon/react-hot-loader

Slide 56

Slide 56 text

Conclusion

Slide 57

Slide 57 text

Alternatives (why not Ruby?)

Slide 58

Slide 58 text

Ruby alternatives lotus/assets livingsocial/rake-pipeline (including wycats/rake-pipeline-web-filters)

Slide 59

Slide 59 text

Спасибі Спасибо

Slide 60

Slide 60 text

Questions?