$30 off During Our Annual Pro Sale. View Details »

Secrets Of The Asset Pipeline

Secrets Of The Asset Pipeline

Following up from my recent deep code dive into the less-rails and less-rails-bootstrap gems come some of the best hidden features of the rails asset pipeline. My talk will include a behind the scenes look of what makes the asset pipeline possible, best practices, advanced usage followed by a review of some of the top level CSS frameworks being used.

Ken Collins

April 11, 2012
Tweet

More Decks by Ken Collins

Other Decks in Technology

Transcript

  1. Secrets Of The Asset Pipeline Ken Collins metaskills.net

  2. Congratulations! The simple fact that you are sitting here listening

    to me, means you've made a glorious contribution to Science! Tragic, but informative. Cave Johnson
  3. The Foundation What makes the asset pipeline possible.

  4. Hike

  5. Hike Hike is a Ruby library for finding files in

    a set of paths. Use it to implement search paths, load paths, and the like.
  6. Find Ruby Files In Your Project trail = Hike::Trail.new "/Users/sam/Projects/hike"

    trail.append_extension ".rb" trail.append_paths "lib", "test" trail.find "hike/trail" # => "/Users/sam/Projects/hike/lib/hike/trail.rb" trail.find "test_trail" # => "/Users/sam/Projects/hike/test/test_trail.rb"
  7. Explore Your Shell Path trail = Hike::Trail.new "/" trail.append_paths *ENV["PATH"].split(":")

    trail.find "ls" # => "/bin/ls" trail.find "gem" # => "/Users/sam/.rvm/rubies/ree/bin/gem"
  8. Digging Deeper # Fallback logical paths. Equivalent. trail.find "hike", "hike/index"

    trail.find("hike") || trail.find("hike/index") # Block yields multiple matches. trail.find("application") do |path| return path if mime_type_for(path) == "text/css" end # Like Dir#entries. Filters "." and "~" swap files. trail.entries('/usr/local/bin') # Like File.stat. trail.stat('/usr/local/bin') # Cached trail. Avoids excess system calls. trail.index
  9. Tilt

  10. Tilt Tilt is a thin interface over a bunch of

    different Ruby template engines in an attempt to make their usage as generic possible. This is useful for web frameworks, static site generators, and other systems that support multiple template engines but don't want to code for each of them individually.
  11. Common Features

  12. Common Features Custom template evaluation scopes / bindings.

  13. Common Features Custom template evaluation scopes / bindings. Ability to

    pass locals to template evaluation.
  14. Common Features Custom template evaluation scopes / bindings. Ability to

    pass locals to template evaluation. Support for passing a block to template eval for "yield".
  15. Common Features Custom template evaluation scopes / bindings. Ability to

    pass locals to template evaluation. Support for passing a block to template eval for "yield". Backtraces with correct filenames and line numbers.
  16. Common Features Custom template evaluation scopes / bindings. Ability to

    pass locals to template evaluation. Support for passing a block to template eval for "yield". Backtraces with correct filenames and line numbers. Template file caching and reloading.
  17. Common Features Custom template evaluation scopes / bindings. Ability to

    pass locals to template evaluation. Support for passing a block to template eval for "yield". Backtraces with correct filenames and line numbers. Template file caching and reloading. Fast, method-based template source compilation.
  18. Engine File Extension Required Lib. ERB .erb, .rhtml none (stdlib)

    Interpolated String .str none (core) Erubis .erb, .rhtml, .erubis erubis Haml .haml haml Sass .sass sass (>= 3.1) Scss .scss sass (>= 3.1) Less CSS .less less Builder .builder builder Liquid .liquid liquid RDiscount .markdown, .mkd, .md rdiscount Redcarpet .markdown, .mkd, .md redcarpet BlueCloth .markdown, .mkd, .md bluecloth Kramdown .markdown, .mkd, .md kramdown Maruku .markdown, .mkd, .md maruku RedCloth .textile redcloth
  19. Builder .builder builder Liquid .liquid liquid RDiscount .markdown, .mkd, .md

    rdiscount Redcarpet .markdown, .mkd, .md redcarpet BlueCloth .markdown, .mkd, .md bluecloth Kramdown .markdown, .mkd, .md kramdown Maruku .markdown, .mkd, .md maruku RedCloth .textile redcloth RDoc .rdoc rdoc Radius .radius radius Markaby .mab markaby Nokogiri .nokogiri nokogiri CoffeeScript .coffee coffee-script (+javascript) Creole (Wiki markup) .wiki, .creole creole WikiCloth (Wiki markup) .wiki, .mediawiki, .mw wikicloth Yajl .yajl yajl-ruby
  20. Really Simple! require 'erb' require 'tilt' template = Tilt.new('templates/foo.erb') #<Tilt::ERBTemplate

    @file="templates/foo.rb"...> template.render # => "Hello world!"
  21. Evaluation Scope template = Tilt::ERBTemplate.new('templates/foo.erb') joe = Person.find('joe') output =

    template.render(joe, :x => 35, :y => 42) jane = Person.find('jane') output = template.render(jane, :x => 22, :y => nil)
  22. Sprockets

  23. Sprockets Rack-based asset packaging for compiling and serving web assets.

    It features declarative dependency management for JavaScript and CSS assets, as well as a powerful preprocessor pipeline that allows you to write assets in languages like CoffeeScript, Sass, SCSS and LESS.
  24. Rack Application # In config.ru require 'sprockets' map '/assets' do

    environment = Sprockets::Environment.new environment.append_path 'app/assets/javascripts' environment.append_path 'app/assets/stylesheets' run environment end map '/' do run YourRackApp end
  25. Renders Templates /* Multi-line comment blocks (CSS, SCSS, JavaScript) *=

    require foo */ // Single-line comment blocks (SCSS, JavaScript) //= require foo # Single-line comment blocks (CoffeeScript) #= require foo modal.css.scss.erb
  26. Best Practices How to wire it up right!

  27. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css
  28. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces
  29. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces Manifest Only Top Files
  30. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces Manifest Only Top Files Generators Garbage
  31. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces Manifest Only Top Files Generators Garbage Require Tree Bad
  32. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces Manifest Only Top Files Generators Garbage Require Tree Bad Try To Match With Layout Names
  33. site.js //= require jquery-1.6.2 //= require jquery-ui-1.8.12.ui-darkness //= require jquery_ujs

    //= require shared/base //= require shared/utils //= require shared/modal //= require shared/spinner //= require site/quickie //= require site/flash //= require site/site
  34. site.css /* *= require application/application *= require shared/modal *= require

    shared/utility */
  35. Precompile Additional Assets! # Precompile additional assets # (application.js, application.css,

    # and all non-JS/CSS are already added) config.assets.precompile += ['site.js', 'site.css'] config/production.rb
  36. Do Not Use Minified Files # Compress both stylesheets and

    JavaScripts config.assets.js_compressor = :yui config.assets.css_compressor = :yui Gemfile group :assets do # ... gem 'yui-compressor', :require => 'yui/compressor' end config/production.rb
  37. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces Manifest Only Top Files Generators Garbage Require Tree Bad Try To Match With Layout Names
  38. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces Manifest Only Top Files Generators Garbage Require Tree Bad Try To Match With Layout Names Sub Files @import’ed
  39. Dir. Structure $ tree app/assets | !"" images # !""

    app # # !"" ... # $"" site # $"" ... !"" javascripts # !"" application.js # | $"" ... # !"" shared # # !"" base.js.coffee # # !"" modal.js.coffee.erb # # $"" ... # !"" site | | !"" base.js.coffee # | !"" flash.js.coffee # | $"" ... | $"" site.js $"" stylesheets !"" application # $"" application.css.scss !"" application.css !"" shared # !"" base.scss # !"" modal.css.scss # $"" ... !"" site # !"" 960.css # $"" site.css.scss $"" site.css Directory Namespaces Manifest Only Top Files Generators Garbage Require Tree Bad Try To Match With Layout Names Sub Files @import’ed Base Files Are Your Compass
  40. Base Files Are Your Compass $legacy-support-for-ie: false; $experimental-support-for-opera: false; $experimental-support-for-khtml:

    false; @import "compass"; @import "compass/layout"; app/assets/stylesheets/shared/base.scss /* *= depend_on shared/base.scss */ @import "shared/base"; app/assets/stylesheets/shared/modal.scss
  41. Advanced Usage Secrets of the asset pipeline.

  42. LESS, Rails & Bootstrap less-rails.gem less-rails-bootstrap.gem

  43. Too LESS? http://metaskills.net/2012/02/27/too-less-should-you-be-using-sass/

  44. Too LESS?

  45. Too LESS?

  46. Too LESS?

  47. sass-rails.gem https://github.com/rails/sass-rails

  48. Railtie Configuration MyProject::Application.configure do config.sass.line_comments = false config.sass.style = :nested

    end In config/initializers/sass.rb
  49. Asset Helpers!!! image-url("rails.png") /* Becomes: url(/assets/rails.png) */ font-url("rails.ttf") /* Becomes:

    url(/assets/rails.ttf) */ video-url("rails.mp4") /* Becomes: url(/videos/rails.mp4) */ audio-url("rails.mp3") /* Becomes: url(/audios/rails.mp3) */ javascript-url("rails.js") /* Becomes: url(/assets/rails.js) */ stylesheet-url("rails.css") /* Becomes: url(/assets/rails.css) */ asset-data-url("rails.png") /* Becomes: url(data:image/png;base64,iV...) */ In config/initializers/sass.rb
  50. Keep /public Clean!

  51. Very Extendable module Sass::Script::Functions def experimental_properties(value) assert_type value, :String prefixes

    = "-webkit-#{value}, -moz-#{value}, -ms-#{value}, #{value}" Sass::Script::String.new prefixes end declare :experimental_properties, [:value] end class Sass::Script::Color < Sass::Script::Literal def hsv_h rgb_to_hsv! @attrs[:hsv_h] end def hsv_s rgb_to_hsv! @attrs[:hsv_s] end def hsv_v rgb_to_hsv! @attrs[:hsv_v] end private def rgb_to_hsv! # ... end end
  52. Compass CSS Framework http://compass-style.org/

  53. compass-rails.gem https://github.com/Compass/compass-rails

  54. Compass Extensions group :assets do gem 'compass-rails' gem 'compass-susy-plugin' gem

    'ceaser-easing' end
  55. Compass Extensions group :assets do gem 'compass-rails' gem 'compass-susy-plugin' gem

    'ceaser-easing' end Susy - An un-obtrusive grid for designers.
  56. Compass Extensions group :assets do gem 'compass-rails' gem 'compass-susy-plugin' gem

    'ceaser-easing' end Susy - An un-obtrusive grid for designers. Ceaser Easing - Functions for CSS3 animations.
  57. Learn the ways of the Force $ mate "$(bundle show

    compass)/frameworks/compass/stylesheets"
  58. Twitter Bootstrap With Sass https://github.com/thomas-mcdonald/bootstrap-sass

  59. Bootstrap Icon Fonts http://fortawesome.github.com/Font-Awesome/ https://github.com/littlebtc/font-awesome-sass-rails

  60. Discreet jQuery UI Assets https://github.com/joliss/jquery-ui-rails

  61. Discreet jQuery UI Assets /* *= require jquery.ui.core *= require

    jquery.ui.theme */ //= require jquery.ui.core //= require jquery.ui.widget //= require jquery.ui.mouse //= require jquery.ui.slider //= require jquery.ui.progressbar //= require jquery.effects.pulsate //= require jquery.effects.shake //= require jquery.effects.slide
  62. Technical Tricks

  63. Hooking Into Sprockets Rails.application.assets # => #<Sprockets::Environment:0x3fda9a195f8c root="/Repos/ homemarks_app", paths=["/Repos/homemarks_app/app/assets/images",

    "/Repos/homemarks_app/app/assets/javascripts", "/Repos/ homemarks_app/app/assets/stylesheets", "/Repos/homemarks_app/ vendor/assets/javascripts", "/Repos/homemarks_app/vendor/assets/ stylesheets", "/Users/ken/.rbenv/versions/1.9.3/lib/ruby/gems/ 1.9.1/gems/jquery-rails-1.0.13/vendor/assets/javascripts"], digest="46dde6621c301f4928e3b34efee9e3b5"> Or use the `asset_environment` helper.
  64. Finding Assets Rails.application.assets['shared/modal'] # => #<Sprockets::BundledAsset:0x3fcd7e90c6cc pathname="/ Repos/homemarks_app/app/assets/javascripts/shared/ modal.js.coffee.erb", mtime=2011-11-25

    17:41:11 -0500, digest="775822db3e101bf38d7978627380e62b"> #find_asset method (aliased as [])
  65. Uses Of Assets _buildStyle: -> return unless @options.injectStyle style =

    $('<style>') style.attr 'type', 'text/css' style.html <%= Rails.application.assets['shared/modal'].to_s %> $('head').append style Programmatically Use Assets
  66. Uses Of Assets _buildStyle: -> return unless @options.injectStyle style =

    $('<style>') style.attr 'type', 'text/css' style.html <%= Rails.application.assets['shared/modal'].to_s %> $('head').append style Programmatically Use Assets #to_s - Render Asset
  67. Uses Of Assets _buildStyle: -> return unless @options.injectStyle style =

    $('<style>') style.attr 'type', 'text/css' style.html <%= Rails.application.assets['shared/modal'].to_s %> $('head').append style Programmatically Use Assets #to_s - Render Asset #length - Bytes
  68. Uses Of Assets _buildStyle: -> return unless @options.injectStyle style =

    $('<style>') style.attr 'type', 'text/css' style.html <%= Rails.application.assets['shared/modal'].to_s %> $('head').append style Programmatically Use Assets #to_s - Render Asset #length - Bytes #mtime - Last Modified
  69. Uses Of Assets _buildStyle: -> return unless @options.injectStyle style =

    $('<style>') style.attr 'type', 'text/css' style.html <%= Rails.application.assets['shared/modal'].to_s %> $('head').append style Programmatically Use Assets #to_s - Render Asset #length - Bytes #mtime - Last Modified #pathname - Full Path
  70. Other Asset Envs & Lazy Eval if defined?(Bundler) # If

    you precompile assets before deploying to production, use this line Bundler.require(*Rails.groups(:assets => %w(development test))) # If you want your assets lazily compiled in production, use this line # Bundler.require(:default, :assets, Rails.env) end In config/application.rb
  71. Precompiling & Digests $ rake assets:precompile $ tree public/assets ...

    !"" application-3d94ca17a2aa6425c775e38a658f75b7.js !"" application-3d94ca17a2aa6425c775e38a658f75b7.js.gz !"" application-5a38464d5ec7e849e138184f810d4fc1.css !"" application-5a38464d5ec7e849e138184f810d4fc1.css.gz !"" application.css !"" application.css.gz !"" application.js !"" application.js.gz
  72. The Future?

  73. JavaScript Source Maps http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/ https://github.com/sstephenson/sprockets/pull/311

  74. CommonJS Proposal https://github.com/sstephenson/sprockets/issues/298

  75. Other Resrouces

  76. Sass/Compass/LESS Tips & Tricks http://speakerdeck.com/u/beausmith/p/less-tips-tricks-best-practices

  77. Thanks! Ken Collins metaskills.net