Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. 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
  2. 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.
  3. 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"
  4. 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"
  5. 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
  6. 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.
  7. Common Features Custom template evaluation scopes / bindings. Ability to

    pass locals to template evaluation. Support for passing a block to template eval for "yield".
  8. 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.
  9. 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.
  10. 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.
  11. 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
  12. 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
  13. 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)
  14. 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.
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  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 Directory Namespaces Manifest Only Top Files Generators Garbage Require Tree Bad Try To Match With Layout Names Sub Files @import’ed
  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 Manifest Only Top Files Generators Garbage Require Tree Bad Try To Match With Layout Names Sub Files @import’ed Base Files Are Your Compass
  29. 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
  30. 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
  31. 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
  32. Compass Extensions group :assets do gem 'compass-rails' gem 'compass-susy-plugin' gem

    'ceaser-easing' end Susy - An un-obtrusive grid for designers.
  33. 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.
  34. Learn the ways of the Force $ mate "$(bundle show

    compass)/frameworks/compass/stylesheets"
  35. 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
  36. 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.
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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