A.K.A. The Rails asset pipeline.
The Last-Modified header
GET /ajax/libs/jquery/1.11.1/jquery.min.js HTTP/1.1
Host: ajax.googleapis.com
Accept: text/javascript
HTTP/1.1 200 OK
Content-Type: text/javascript; charset=UTF-8
Last-Modified: Tue, 13 May 2014 02:26:31 GMT
Date: Thu, 10 Jul 2014 08:51:40 GMT
<94 KB of JS>
GET /ajax/libs/jquery/1.11.1/jquery.min.js HTTP/1.1
Host: ajax.googleapis.com
Accept: text/javascript
If-Modified-Since: Tue, 13 May 2014 02:26:31 GMT
HTTP/1.1 304 Not Modified
<JS is not sent again>
But can do better.
The Expires
/ Cache-Control
headers
GET /ajax/libs/jquery/1.11.1/jquery.min.js HTTP/1.1
Host: ajax.googleapis.com
Accept: text/javascript
HTTP/1.1 200 OK
Content-Type: text/javascript; charset=UTF-8
Cache-Control: max-age=31536000, public
Date: Thu, 10 Jul 2014 08:51:40 GMT
<94 KB of JS>
No other request will be issued for a year
But how can I update my assets then?
Append the file mtime
as query string
<script src="/javascripts/application.js?1405197924" ...
Include the file MD5 in the file name
<script src="/assets/application-d3b07384d113edec49eaa6238ad5ff00.js"
//= require jquery
//= require_tree myapp
//= require_self
//= depend_on config
//= stub prototype
depend_on
do not include the file, but use it to compute the MD5 sumstub
can be useful to prevent one of your dependency from requiring somethinginclude
(deprecated) like require
without the unicity# config.js.coffee.erb
@Config =
truth: <%= 21 * 2 %>
class Csv2Json < Tilt::Template
def evaluate(scope, locals, &block)
JSON.dump(CSV.parse(data))
end
end
Sprockets.register_engine '.csv', Csv2Json
# foo.json.csv
Id,Name
1,George Abitbol
// foo-6d04991772b0ab12f0a96971ad290d68.json
[["Id","Name"],["1","George Abitbol"]]
Processors are Tilt templates like engines, but they do not convert the asset to another type
Sprockets.register_preprocessor 'text/javascript', RemoveBreakPoints
Sprockets.register_postprocessor 'text/javascript', PrependSafetySemiColon
Sprockets.register_bundle_processor 'text/javascript', MinifyJS
Note that processors are registered by MIME type while engines are registered by extensions.
Shorthand methods
environment.js_compressor = :uglify
environment.css_compressor = :scss
The sprockets context provide helpers to engines, which can eventually expose them to the transormed assets
/* main.css.erb */
.header { background-image: url(<%= image_path('header-background.png') %>);}
They are mostly available in .erb
assets.
// main.css.scss
.header { background-image: image-url(header-background.png); }
But some engines like SCSS provide integrated support
Most usefull ones are:
image_path
, font_path
, etc. Gives you the digested path of another asset.asset_data_uri
gives you the Base64 encoded asset content (for embeding images).And you can of course add custom ones
Even if he is often nicknamed "The Rails asset pipeline", Sprockets is actually framework agnostic
It can even easilly be used in non Ruby projects (PHP, Node, Python, etc)
# lib/sprockets_environment.rb
require 'sprockets'
module MyEnvironment < Sprockets::Environment
def initialize(production=true)
root = File.expand_path(File.join(__dir__, '..'))
super(root + '/public')
append_path(root + '/app/assets/javascripts')
append_path(root + '/app/assets/stylesheets')
append_path(root + '/app/assets/images')
if production
self.css_compressor = :scss
self.css_compressor = :uglifier
end
end
end
All the configuration would happen in a subclass of Sprockets::Environment
# config.ru
require 'sprockets_environment'
map '/assets' do
run MyEnvironment.new
end
Sprockets::Environment
acts as a rack middleware.# Rakefile
require 'rake/sprocketstask'
require 'sprockets_environment'
Rake::SprocketsTask.new do |t|
t.environment = run MyEnvironment.new
t.output = "./public/assets"
t.assets = %w( application.js application.css )
end
rake assets:precompile
will statically compile every listed bundles.manifest.json
file will be generated, containing all the mapping between the source files and the compiled files.# in your app helpers
ASSETS_ROOT = 'https://my.cdn.com/assets/'
def sprockets
@sprockets ||= begin
env = MyEnvironment.new
ENV['RACK_ENV'] == 'production' ? env.index : env
end
end
def script_url(name)
ASSETS_ROOT + sprockets.find_asset(name + '.js').digested_path
end
script_url('application') # => 'https://my.cdn.com/assets/application-d3b07384d113edec49eaa6238ad5ff00.js'
assets
directory between releases.public/
directory or fix the few URLs.Jean Boussier
Shopify
https://github.com/byroot