Face it, you are a top developer, one of the best at your game, you eat lambda's for breakfast, refactor whole apps as an afternoon snack and bad code just trembles when you approach. You are at the top of your game and nothing stands in between you and the code you desire. Yet, the Rails asset pipeline still trips you up, not once, not twice, but just about every time you try and use it.
Here's a list of things that I have learnt that aren't necessarily obvious, that can help you tame this wild and savage beast.
1. Two options, precompile OR include, not both
No CSS or JS files will be available to your app through the asset pipeline unless they are included in other files OR listed in the
config.precompile directive (see #2 and #3 below).
Only application.css and application.js are available by default of all the CSS and JS files.
For example, you make a file in the assets folder called:
And you want to include this in your page. You have two options, either include it as one of the CSS files to be loaded up in
application.css with the following:
// = require_self // = require 'site'
And then it will be inside the compiled
public/assets/application.css which is included with:
OR you can add it to the precompile list like so:
config.assets.precompile += %w( site.css )
and then reference it with:
The reason this happens is that when you run
rake assets:precompile Rails
public/assets. It then creates
application.js by reading
application.css by reading
app\assets\stylesheets\application.css, loading up all the "require" files it finds in there, and does not look at any other file unless you explicitly tell it to.
2. File extensions matter in the precompile directive
Another common mistake is forgetting to put the file extension on the precompile directive, for example:
config.assets.precompile += %w( site.css )
Will do what you expect, make
public/assets/site.css available on compile, but
config.assets.precompile += %w( site )
Will fail during the compilation phase as the
site file doesn't exist.
3. The asset pipeline is not quite your assets folder
app/assets folder will be copied by Rails into the
public/assets folder when you compile your assets.
So if you want to add some web fonts, you could make an
app/assets/fonts/ folder and put your fonts in there, these will then be copied to
public/assets/fonts folder when you compile your assets. Note that your
app/assets/stylesheets/fonts.css.scss file that references those fonts will NOT be copied over unless you either added it to the
config.assets.precompile directive or required it from your
application.css file as per #1 above.
This is a common mistake I see, people put a stylesheet into the
This is because, as per #1 above, these files won't get copied over into the
public/assets folder unless you explicitly tell them to. Common gotcha. There is no hard and fast rule here, except having a staging environment and following #5 below.
5. Don't fall back in staging or production
First of all, you need to understand what the directive
public/assets directory and if it can't find it, will hunt through your
app/assets folder looking for the file. If it finds it in
app/assets it will go ahead and compile on the fly and then serve this asset up.
The problem with this is that you don't notice it happening in development, then you commit everything and push to production and BOOM, everything is broken with 500 errors because production has
config.assets.compile set to "false".
Why don't you just have this set to "true" in every environment? Well, because it is sloooooow. And you don't want slow in production.
One of the first things I do on an app is make sure my
config/environments/staging.rb have the following set:
# Don't fallback to assets pipeline if a precompiled asset is missed config.assets.compile = false
This prevents the app from "falling back" and trying to load the file directly instead of using the asset pipeline. This might mean that you get failures, but it is better to fail fast than to find our later that something has been broken for months.
It also means you know if your assets and site works in staging, then it is going to work in production, this is a good thing.
Note, Heroku for quite a while will override this setting to
true automatically if your asset compilation fails, I believe this creates more confusion than benefit and I believe they deprecated this as of the 20th of September 2013.
6. Deploying with precompile can suck
If you use the I18n gem or need to load up your environment with asset precompilation it means your deploy process will take longer. This can suck and lead to various problems, missing databases and all sorts.
One place where this can be a pain is on Heroku. During the app deploy process, the database has not been configured which means that loading the environment during assets precompilation just doesn't work.
Two possible solutions to this problem are to either precompile locally and commit the assets folder before you push, or use Heroku's labs feature which may or may not exist in the future.
To precompile locally, you need to do the following:
- Setup a dummy production database on your local machine
- Create a production entry in database.yml
- Add in any other dummy settings to load a production environment
RAILS_ENV=production rake assets:clean assets:precompile
- Wait some more
- Commit the results in the
- Deploy this to Heroku
To use the Heroku labs precompile feature, follow their guide page
Note: I don't mean to harp on about Heroku (I've mentioned it twice so far) but their platform means you need to change the way you operate a little bit, other platforms might also have problems as well :)
7. Semi colons matter
So just put them in there, they are good for you.
8. Clean up after yourself
If you are trying to debug an asset pipeline problem, try running
rake assets:clean assets:precompile first. This removes all old assets and rebuilds them from scratch, meaning you get a clean new set to play with.
9. It's all relative, mostly
require_directory to reference other files to include. The thing to remember is that all of these reference different things.
require will look for any file that has the same name that is in in your assets load path. Don't know what your assets load path is? Well, fire up Rails console in the environment of your choice and run:
require_tree will look for any relative directory path starting from current directory (usually
app/assets folder) and then load every file under it recursively, so:
require_tree './my/directory' in the
application.js file will attempt to load every file in the
require_tree '../../../something' will do crazy things, just don't do that :) If you find yourself putting lots of
require_tree declarations, consider instead adding those directories to the
assets.paths config settings like so:
config.assets.paths << File.join(Rails.root, '/my/special/path')
require_directory will look only for a directory name starting at the current directory. So
require_directory "." will load every file in the current directory in a non recursive manner.
require_directory 'special' will load every file in the
app/assets/special directory, but will not look at or load any files in the directories nested within the
10. Don't require self
A common thing I see in
application.css is the
application.css file, which is bad form. These files should function as an index of the assets below, and should be kept free of clutter. Just move that stuff into another file and require it using the require commands listed in #9 above.
11. Use the helpers!
Another gotcha is that you should no longer do things like:
<img src="assets/my-image.jpg" />
Because doing this will serve up the image but not append the special cache-busting timestamp that Rails appends as part of it's assets precompile. Instead use the
image_path helpers to make sure you get the right URLs.
The last tip is to go and read the excellent Rails Asset Pipeline Guide which does contain all of the above, in parts :)
If you find anything in the above that is incorrect or have a suggested tip to include, please email me and I'll get it fixed up :)