Share
Embed
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
Transcript
77 slides
-
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 -
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. -
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" -
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" -
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 -
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. -
Common Features Custom template evaluation scopes / bindings. Ability to
pass locals to template evaluation. -
Common Features Custom template evaluation scopes / bindings. Ability to
pass locals to template evaluation. Support for passing a block to template eval for "yield". -
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. -
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. -
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. -
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 -
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 -
Really Simple! require 'erb' require 'tilt' template = Tilt.new('templates/foo.erb') #<Tilt::ERBTemplate
@file="templates/foo.rb"...> template.render # => "Hello world!" -
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) -
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. -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
Railtie Configuration MyProject::Application.configure do config.sass.line_comments = false config.sass.style = :nested
end In config/initializers/sass.rb -
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 -
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 -
Compass Extensions group :assets do gem 'compass-rails' gem 'compass-susy-plugin' gem
'ceaser-easing' end Susy - An un-obtrusive grid for designers. -
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. -
Learn the ways of the Force $ mate "$(bundle show
compass)/frameworks/compass/stylesheets" -
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 -
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. -
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 []) -
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 -
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 -
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 -
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 -
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 -
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 -
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