When you work with a Rails 3.1+ application, you will be working with the
asset pipeline. The asset pipeline is awesome until you deploy. Then it will be less so if you haven't done everything as the pipeline expected it from you.
The problem
When using the asset pipeline your assets (images, javascripts, stylesheets, fonts) live in folders inside app
:
COPY
app/assets/fonts
app/assets/images
app/assets/javascripts
app/assets/stylesheets
With the asset pipeline, you can use the full power of Ruby to generate assets. E.g. you can have ERB tags in your Javascript. Or you can have an ERB template which generates Haml which generates HTML. You can chain as many preprocessors as you want.
When you deploy, Rails runs assets:precompile
which precompiles all assets into static files that live in public/assets
. This way you have all the performance of static files with all the expressiveness of Ruby.
Unfortunately, the path of every single file changes during precompilation. This means that you have to be a bit careful how you reference assets.
COPY
.tile
background-image: url('../images/foo.png')
won't work any longer, because there is no public/assets/images
. foo.png
now lives directly in public/assets
.
Example
COPY
app/assets/fonts
app/assets/fonts/fonts_root.css
app/assets/fonts/fonts_root.js
app/assets/fonts/fonts_root.png
app/assets/fonts/fonts_root.ttf
app/assets/fonts/subfolder
app/assets/fonts/subfolder/fonts_subfolder.css
app/assets/fonts/subfolder/fonts_subfolder.js
app/assets/fonts/subfolder/fonts_subfolder.png
app/assets/fonts/subfolder/fonts_subfolder.ttf
app/assets/images
app/assets/images/images_root.css
app/assets/images/images_root.js
app/assets/images/images_root.png
app/assets/images/images_root.ttf
app/assets/images/subfolder
app/assets/images/subfolder/images_subfolder.css
app/assets/images/subfolder/images_subfolder.js
app/assets/images/subfolder/images_subfolder.png
app/assets/images/subfolder/images_subfolder.ttf
app/assets/javascripts
app/assets/javascripts/application.js
app/assets/javascripts/javascripts_root.css
app/assets/javascripts/javascripts_root.js
app/assets/javascripts/javascripts_root.png
app/assets/javascripts/javascripts_root.ttf
app/assets/javascripts/subfolder
app/assets/javascripts/subfolder/javascripts_subfolder.css
app/assets/javascripts/subfolder/javascripts_subfolder.js
app/assets/javascripts/subfolder/javascripts_subfolder.png
app/assets/javascripts/subfolder/javascripts_subfolder.ttf
app/assets/stylesheets
app/assets/stylesheets/application.css
app/assets/stylesheets/stylesheets_root.css
app/assets/stylesheets/stylesheets_root.js
app/assets/stylesheets/stylesheets_root.png
app/assets/stylesheets/stylesheets_root.ttf
app/assets/stylesheets/subfolder
app/assets/stylesheets/subfolder/stylesheets_root.png
app/assets/stylesheets/subfolder/stylesheets_subfolder.css
app/assets/stylesheets/subfolder/stylesheets_subfolder.js
app/assets/stylesheets/subfolder/stylesheets_subfolder.ttf
This precompiles into this (fingerprinted and compressed files removed for clarity):
COPY
public/assets/application.css
public/assets/application.js
public/assets/fonts_root.png
public/assets/fonts_root.ttf
public/assets/images_root.png
public/assets/images_root.ttf
public/assets/javascripts_root.png
public/assets/javascripts_root.ttf
public/assets/stylesheets_root.png
public/assets/stylesheets_root.ttf
public/assets/subfolder
public/assets/subfolder/fonts_subfolder.png
public/assets/subfolder/fonts_subfolder.ttf
public/assets/subfolder/images_subfolder.png
public/assets/subfolder/images_subfolder.ttf
public/assets/subfolder/javascripts_subfolder.png
public/assets/subfolder/javascripts_subfolder.ttf
public/assets/subfolder/stylesheets_root.png
public/assets/subfolder/stylesheets_subfolder.ttf
What has happened?
All asset folders (app/assets/images
, app/assets/stylesheets
, app/assets/fonts
, etc.) are merged into one folder:public/assets
.
Subfolders are also merged into public/assets
. That means both app/assets/javascripts/subfolder/*
andapp/assets/stylesheets/subfolder/*
now live in public/assets/subfolder/*
All CSS files are concatenated into one file (public/assets/application.css
). With the default manifest inapp/assets/stylesheets/application.css
, stylesheets in app/assets/stylesheets/**/*.css
are concatenated. Sopublic/application.css
has the following content:
COPY
All Javascript files are concatenated into one file in root (public/assets/appliction.js
). With the default manifest inapp/assets/javascripts/application.js
, Javascripts in app/assets/javascripts/**/*.js
are concatenated. Sopublic/application.js
has the following content:
COPY
What will break after precompilation?
- Stylesheets, that used
url(../images/foo.png)
-style tags
- @font-face definitions that used
url('fonts/bar.ttf')
-style tags
How can I fix my stylesheets?
Do not use url(../images/foo.png)
.
This is plainly wrong, since from your browser's point of view, your stylesheet lives at /assets/application.css
and your image at /assets/foo.png
. I have no idea why it works in development in the first place. Simply use the SASS helper:
COPY
.tile
background-image: image-url('foo.png')
How can I fix my font definitions?
With the Asset-Pipeline fonts are stored in app/assets/fonts
.
Depending on your rails version, you may need to add
COPY
config.assets.paths << Rails.root.join("app", "assets", "fonts")
to your config/application.rb
- Do not use
src: url(...)
in your @font-face definitions.
Again instead use a sass-helper: src: font-url(...)
- Maybe the referenced paths need adjustment. If before it was
src: url(fonts/...)
now the leading fonts/
directory needs to be removed, because the fonts are compiled to the assets folder by the asset-pipeline.
https://makandracards.com/makandra/8951-rails-asset-pipeline-why-things-break-in-production-and-what-precompilation-does-to-your-assets