Skip to content

Instantly share code, notes, and snippets.

@dportalesr
Created April 26, 2018 00:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dportalesr/4be6d1b0a99f1993af41a6f600a20aed to your computer and use it in GitHub Desktop.
Save dportalesr/4be6d1b0a99f1993af41a6f600a20aed to your computer and use it in GitHub Desktop.
assetpipeline.md

Asset Pipeline

The asset pipeline is implemented by the sprockets-rails gem, and is enabled by default.

You can disable it while creating a new application by passing the --skip-sprockets option. This will generate a config/application.rb with a require to sprockets commented out.

Main features

  1. Concatenate assets into master (.js, .css) files and name them with a SHA256 hash to keep tracking of content changes, improving browser caching management.
  2. Minification or compression.
  3. Preprocessing: SASS->CSS, CoffeScript->Javascript, ERB for both.

How to

Assets placed in app/assets will be served thru sprockets, except in production, where assets are precompiled to public/assets to be served as static files.

Assets placed directly in public will be served by the app as static files (not processed) when config.public_file_server.enabled = true. In production this setting is set to false because it's the web server (not the app) that should serve static files.

Controller-Scoped Assets

Disable it with:

config.generators do |g|
  g.assets false
end

Organization

  1. app/assets stores assets owned by your application.

  2. lib/assets stores your own libraries' code that doesn't really fit into the scope of the application or those libraries which are shared across applications.

  3. vendor/assets is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks. Keep in mind that third party code with references to other files also processed by the asset Pipeline (images, stylesheets, etc.), will need to be rewritten to use helpers like asset_path.

Search paths

When a file is referenced from a manifest or a helper, Sprockets searches any direct subdirectory of all 3 assets directories (default directories are images, javascripts and stylesheets).

//= require home       # => app/assets/javascripts/home.js
//= require moovinator # => lib/assets/javascripts/moovinator.js
//= require slider     # => vendor/assets/javascripts/slider.js
//= require phonebox   # => vendor/assets/somepackage/phonebox.js

Subdirectories can be found with:

//= require sub/hello   # => vendor/assets/javascripts/sub/hello.js

You can view the search path by inspecting Rails.application.config.assets.paths in the Rails console.

Paths can be added (fully qualified) to this search path with something like:

config.assets.paths << Rails.root.join('lib', 'videoplayer', 'flash')

The paths are searched in order, meaning that by default app/assets will override lib/assets and vendor/assets counterparts.

Files referenced outside a manifest must be added to the precompile array or they will not be available in the production environment.

Index Files

Sprockets handles index files as manifest files. Example: lib/assets/javascripts/library_name/index.js serves as the manifest for all files of library_name lib (folder). This file could include a list of all the required files in order, or a simple require_tree directive. Then the library can be referenced as:

//= require library_name

Linking

Tags

favicon_link_tag 'myicon.ico' # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/x-icon" />

image_tag("icon.png") # => <img src="/assets/icon.png" alt="Icon" />
image_tag("icons/icon.png") # => <img src="/assets/icons/icon.png" alt="Icon" />
image_tag("/custom_root/icon.png", alt: "Edit entry") # => <img src="/custom_root/icon.png" alt="Edit entry" />

javascript_include_tag "lib" # => <script src="/assets/lib.js"></script>
javascript_include_tag "lib", "/elsewhere/hello"
# => <script src="/assets/lib.js"></script>
#    <script src="/elsewhere/hello.js"></script>
javascript_include_tag "http://abc.com/no_ext" # => <script src="http://abc.com/no_ext"></script>
javascript_include_tag "http://abc.com/ext.js" # => <script src="http://abc.com/ext.js"></script>

stylesheet_link_tag "style" # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
stylesheet_link_tag "http://abc.com/style.css" # => <link href="http://abc.com/style.css" ... />
stylesheet_link_tag "style", "/css/stylish"
# => <link href="/assets/style.css" ... />
#    <link href="/css/stylish.css" ... />

Paths

asset_path("app.js") # => "/assets/app.js"
asset_path("foo/bg.png") # => "/assets/foo/bg.png"
asset_path("/foo.css") # => "/foo.css"
asset_path("/foo") # => "/foo"
asset_path("http://abc.com/js/xmlhr.js") # => "http://abc.com/js/xmlhr.js"

asset_url 'app.js'                         # => http://example.com/assets/app.js
asset_url 'app.js', host: "http://cdn.com" # => http://cdn.com/assets/app.js

CSS + ERB

If you add an erb extension to a CSS asset file, then helpers like asset_path are available:

# style.css.erb
.class { background-image: url(<%= asset_path 'image.png' %>) }

CSS + Sass

sass-rails provides -urland -path helpers (hyphenated):

# style.scss
asset-url("rails.png") # => url(/assets/rails.png)
asset-path("rails.png") # => "/assets/rails.png"

JS + ERB

# application.js.erb
$('#logo').attr({ src: "<%= asset_path('logo.png') %>" });

Manifest files

These are files containing directives - instructions that begin with = that tell Sprockets which files to require in order to build (load, process, concatenate and compress) a single CSS or JavaScript file.

Examples:

# app/assets/javascripts/application.js
// ...
//= require jquery
//= require jquery_ujs
//= require_tree .

# app/assets/stylesheets/application.css
/* ...
*= require_self
*= require_tree .
*/

Extensions are optional and assumed from the manifest file extension. Paths are relative to manifest file. Files are loaded once even if targeted in multiple directives.

//= require foo requires a single file //= require_directory bar requires all files in a single directory (no recursive) //= require_tree . requires all files in current directory and subdirectories (recursive) //= require_self requires any content within the file (if any) at the precise location of the require_self call.

For Sass is recommended to use @import rule instead of sprockets directives, since the latter will make mixins and variables not available outside the files they were defined in.

@import "mixins/*" is equivalent to require_directory mixins @import "mixins/**/*" is equivalent to require_tree mixins

Any valid ruby glob may be used. The imports are sorted alphabetically.

Development

Configuration

The following settings can be configured in config/environments/development.rb

config.assets.debug

When enabled (default), Sprockets assets are served as separate files in the order they are specified in the manifest file. Example:

# app/assets/javascripts/application.js:
//= require core
//= require projects
//= require tickets

would generate this HTML:

<script src="/assets/core.js?body=1"></script>
<script src="/assets/projects.js?body=1"></script>
<script src="/assets/tickets.js?body=1"></script>

The body param is required by Sprockets.

Disabling asset debugging will replace detailed asset exception pages with a much terser error or a runtime Javascript exception, but it'll greatly improve local requests speed by using local asset caching that will still pick up new changes. So, it is recommended to enable it only when needed.

config.assets.raise_runtime_errors

When enabled (default), the asset pipeline will check if all the assets loaded in your application are included in the config.assets.precompile list.

config.assets.unknown_asset_fallback

When enabled (default), the path will be output instead and no error is raised. When disabled, an error will be raised when an asset cannot be found.

config.assets.digest

When enabled (default), digests will be generated for asset URLs.

Local Precompilation

Precompiling assets in development provides the fastest asset responses in local since it processes assets once and servers static files in subsequent requests, but at the cost of having to precompile each time a change is done in them, so it's recommended when not working with them. Just make sure to run rake assets:clobber before committing to avoid adding those precompiled assets to your repository.

Production

By default Rails assumes assets have been precompiled and will be served as static assets by your web server.

Precompiling Assets

Rails has the following task to compile the asset manifests and other files in the pipeline:

RAILS_ENV=production bin/rails assets:precompile

Files meeting any of the following conditions will be precompiled automatically:

  • File is named application.css or application.js
  • File is under app/assets directory (the one in your app and those from gems) with extension different to .js or .css (images, fonts, etc).

These conditions are applied to final compiled file names. This means anything that compiles to JS/CSS is excluded, as well as raw JS/CSS files; for example, .coffee and .scss files are not automatically included as they compile to JS/CSS.

Manifests (other than the main application ones) or individual stylesheets and JavaScript files can be added to the precompile array in config/initializers/assets.rb:

Rails.application.config.assets.precompile += %w( admin.js admin.css )

Always specify an expected compiled filename that ends with .js or .css, even if you want to add Sass or CoffeeScript files to the precompile array.

Local Precompilation

Local compilation allows you to commit the compiled files into source control, and deploy as normal in cases like:

  • You may not have write access to your production file system.
  • You may be deploying to more than one server, and want to avoid duplication of work.
  • You may be doing frequent deploys that do not include asset changes.

Live Compilation

config.assets.compile = true

In this mode all requests for assets in the pipeline are handled by Sprockets directly, compiled and cached on the first request, and the manifest names used in the helpers are altered to include the MD5 hash.

This mode uses more memory, performs more poorly than the default and is not recommended.

CDN

To use a CDN, you need to set config.action_controller.asset_host in config/environments/production.rb. Using an environment variable is recommended.

config.action_controller.asset_host = ENV['CDN_HOST']

You can also override the host for a specific asset by using the host option in a helper:

<%= asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %>

Customization

CSS Compression

This can be configured by using the config.assets.css_compressor. Possible values are:

  • :yui (yui-compressor gem)
  • :sass (sass-rails gem)

Javascript Compression

This configuration setting is config.assets.js_compressor. Possible values are:

  • :uglifier (uglifier gem) [default].
  • :clousre (closure-compiler gem)
  • :yui (yui-compressor gem)

Assets Cache Store

By default, Sprockets caches assets in tmp/cache/assets in development and production environments. To disable it:

config.assets.configure do |env|
  env.cache = ActiveSupport::Cache.lookup_store(:null_store)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment