#279 Understanding the Asset Pipeline
The asset pipeline is probably the biggest feature in Rails 3.1, but it can seem like magic at first. Here I dive into exactly how the asset pipeline works.
- Download:
- source codeProject Files in Zip (438 KB)
- mp4Full Size H.264 Video (24.8 MB)
- m4vSmaller H.264 Video (13.5 MB)
- webmFull Size VP8 Video (17.5 MB)
- ogvFull Size Theora Video (28.5 MB)
Nice job as always. Can't wait to see your screencast explaining Tilt in more detail.
Great cast! Could you elaborate on where would you put images and why, in app/assets or public/ directory?
I put images specific for my app in the app/assets/images directory. Depending on the complexity of the application you may want to store some images in the lib/assets or vendor/assets directories. It actually doesn't matter but convention says:
app/assets - assets specific to your application (custom images, custom javascript, etc.)
lib/assets - assets that you wrote but are may be shared across applications (shared icons, javascript, base stylesheet)
vendor/assets - assets not specific to you app or written by you (Jquery plugins, CSS frameworks, etc)
Nice episode! Would be interesting to see more about Tilt.
This is very useful. Can you please get into more details of the asset pipeline? You have a knack of getting to the point and getting it across.
Thanks.
Bharat
Hi Ryan,
as always a terrific episode of railscasts.
I'm currenty working on a little project, where I use Coffeescript to generate the necessary Javascript.
Do you - or does anyone reading this - know, if coffescript is automatically compiled when application.js is requested? Or do I need to compile it myself and put the resulting .js-file in the assets-folder?
What's best practice for that scenario?
Thank you for you insights.
Greetings, keyJ
If the coffeescript gem is in your gemfile, it should compile automatically yes.
You also need a js compiler in your gemfile, rubyracer seems to be a popular choice. At least that was an issue with rc4
Is there a technique for using the assets pipeline on Heroku? Since we can't write to the public directory, I'm wondering if there is a configuration option to use tmp?
Heroku docs for Rails 3.1 explains it:
http://devcenter.heroku.com/articles/rails31_heroku_cedar
I'll add to that by saying you have never been able to write to the public folder on heroku anyway. For up-loaders like carrierwave you need to re-define in your model where the tmp upload goes before going off to S3
Such as:
def cache_dir
"#{Rails.root}/tmp/uploads"
end
Heroku precompiles assets for you during push, in the cedar stack. However, it is broken for 3.1 rc6, They are working on fixing it.
About the read only file system:
"Cedar offers an ephemeral writeable filesystem. You can write out to disk anywhere you would like. Your changes will be lost on dyno restart and spin-up."
From: http://devcenter.heroku.com/articles/read-only-filesystem#cedar
As always, great railscast. But what'd I'd really like to get peoples' opinion on is how they have/will use this asset pipeline to better merge the work of designers/html coders and web developers (as I'm assuming most of you guys are).
I used to set up a "mockups" folder in "public" so the mockups and the real app pulled from the same javascript, images, and stylesheets. This really isn't possible anymore, so I've changed to teaching the designer some basic rails so he can write things like <%= asset_path 'background.png' %> in application.css.erb. Anyone have good suggestions on a better setup?
Thanks very much!
scss has a asset-url method you can call.
Rails.application.config.assets.path returns nil for me...
Loading development environment (Rails 3.1.0.rc5)
ruby-1.9.2-p0 > Rails.application.config.assets.path
=> nil
Just use
Rails.application.config.assets.paths
instead ofRails.application.config.assets.path
oops, thx, that works! ;-)
However vendor/assets and lib/assets are not included...
I tried putting something in vendor assets, but it didn't work.
Oh works now, had to restart the server after putting something in vendor. It's probably the same with the lib folder.
Fantastic! This came just as I was scratching my head about a few things. I do have a couple of questions:
1) What happens in the case of duplicate files? For example, I have someFile.js in my app/assets/javascripts folder, and another one in app/assets/javascripts/subfolder -- are they both included, or just the first one on the path order?
2) What happens if I list a file in the manifest that is also included in the require_tree . list? For example, I have a JS file that I need to ensure it loads before anything else. I list it manually, right above the require_tree line; will that file then be included twice?
1) You have to reference those files appropriately as
someFil.js
andsubfolder/someFile.js
. There is no collision.2) Every file is only included once - the first time it is required implicitly or explicitly.
But, which file is included first? Which file is implicitly referenceable by "someFile.js" vs explicitly? Alphabetically by directory?
Any tricks for speeding up asset serving in development?
When I have been playing with a 3.1 branch of our main rails app at work I experience terribly slow performance. I gather this is because Rails is serving every request for every asset (as seen in the log) instead of Unicorn doing it.
I ask since the performance I see is so bad I can't imagine any developer coping with it... hence I must be missing some crucial bit of information.
I guess the reason assets don't work like Compass (which generates static files in public) is that I can make use of a full request object with session and all in my assets or something?
For me in the development it is not that slow.
Although during JS unit testing it is. But it is solved by guard-rails-assets.
Use the rails-dev-tweaks gem and you have your speed back. ;-)
This gem seems pretty dead now.
I've got the same problem (unicorn taking long time to compile assets in dev) and am using the rails-dev-tweaks gem, which still doesn't speed the compilation up...
Any other ideas?
Thanks for the great intro! I particularly appreciated the part about how to exclude "admin" javascript from your "public" application. However, it seemed like you only answered half the question.
You showed how to always prevent the inclusion of files in /app/assets/javascripts/admin from the compiled application.js file for all controllers/views.
However, you didn't show how you would go about including the /admin js files when viewing an admin view. How would you do that?
+1 I just jumped into the comments to mention this issue also :) Glad someone else has already noticed.
Assets Pipeline has nothing to do with including the scripts.
you can use
config.assets.precompile
to configure which assets will be generated. But that's about it.If you want to include the script into
/admin
pages just use normaljavascript_include_tag('admin/your-file.js')
.Alternatively, you could include the application javascripts in the admin package and only serve that on admin pages.
Don't know where in the rails stack this logic is executed, but if there is access to instance variables per request then you could:
Add .erb onto the end of your application.js file => application.js.erb
Do something like:
I guess what you mean is: "how do I include /admin.js alongside my general CSS stylesheet in the admin view in a single file ?"
Your JS files aren't supposed to be regenerated every request, and won't in production - so I would discourage using Donnie's solution.
The only solution is to include your 'screen' stylesheet in your admin stylesheet and include only the 'admin' stylesheet in your layout like Dmytrii suggested.
Ryan shows how to avoid a CSS collision in Episode #264 @ about 10:17 when discussing how the ActiveAdmin CSS can cause a conflict with the normal CSS.
Removing from the application.css file:
*= require_tree .
and manually loading each of them in works much better.
WOOT! I am very very glad to see my Railscast request was featured! +1 So very useful thanks Ryan!
Also I may have missed it but how does one handle naming conflicts? I'll re-watch...
When there are naming conflicts, the first path that appears in the
config.assets.paths
array is the file that is chosen. This can be avoided by using theasset_path()
helper and specifying the directory.Hi!
Great screencast! You should probably link to the current asset pipeline docs on github. There have been a lot of changes to the docs since Ryan posted that page!
Yes, awesome rails cast as usual!
I did a few blog posts on asset related rails awhile back, might be of interest?
http://house9.blogspot.com/2011/05/rails-31-asset-pipeline.html
http://house9.blogspot.com/2011/05/rails-31-javascript-execution.html
http://house9.blogspot.com/2011/06/rails-31-asset-gem.html
Asset pipeline is awesome, but how to deal with scripts like lightbox or wysiwyg, that distributed in one folder with local paths for included js, css and images? How to deal with this via asset pipeline?
Nice tuts..
By the by, how can I do this?
I have my assets folder structure like this
I want the project.js and projectValidate.js to be added in my application.js as a part of asset pipe-lining only when actions in product controller is called and store.js when actions in store controller is called.
Thanks for the bundler open tip. I have always wondered a quick way to open up my gems.
I created a file in vendor/assets/javascripts and put the manifest command in there:
vendor.js:
//= require_tree .
Then back in app/assets/javascripts/application.js:
//= require vendor
Hope this helps others.
I find I am having the same issue -- files in vendor/javascripts are not being included unless I explicitly include them using your method. Seems strange. This is not the documented behavior?
It've been confused as well, that the sprocket directive
//= require_tree .
in app/assets/javascripts/application.js does not include javascripts in /vendor/assets/javascripts. But your solution works well. Same goes for stylesheets, put this line in your app/assets/stylesheets/application.css:and create a file /vendor/assets/stylesheets/application.css containing:
This is not an error from the doc. Indeed, "require_tree ." means that the manifest requires the file dans the subfiles (the "tree") in the current directory (the "." means current directory).
And the lib and vendor directories aren't subdirectories of the current directory.
So your way to do it is good ;)
if you want controller specific scripts or stylesheets, you can do something like this:
javascript_include_tag params[:controller]
stylesheet_link_tag params[:controller]
Just a quick note for anyone using mongomapper or mongoid, or anything that requires removing active record but removing the line
require 'rails/all'
make sure you add:
require 'sprockets/railtie'
this is not currently documented
note that if you precompile your assets in dev, you'll need to mv or rm /public/assets if you want any future changes to a .css.sass file to show up. At least that is the case here :)
why can't i access http://127.0.0.1:3000/assets/applications.js
its show me
Routing Error
No route matches [GET] "/assets/applications.js"
I'm fairly new to rails. Would someone explain why jquery plugins work normally on localhost but fail to do so when deployed to heroku
I`m clone repo
git@github.com:railscasts/381-jquery-file-upload.git
bundle
rake db:migrate
rails s
but js and css file does`t load.
If I look at the source code of a site:
link href="/assets/application-7270767b2a9e9fff880aa5de378ca791.css" media="screen" rel="stylesheet" type="text/css" /
script src="/assets/application-673db32530fb40e931676ebc22eaebc6.js" type="text/javascript"/script
Why don`t load other js/css file?
In 1 mim resolved all of my problem tks..
came back please srsrsr
Can we put code in products.js.coffee / home.js.coffee to application.js? so there will be less file in the folder and those files won't be necessary?
Threre's a typo for a link in the ASCIIcast. In http://railscasts.com/episodes/279-understanding-the-asset-pipeline?view=asciicast in the first paragraph, "http://ryanbigg.com/guides/asset_pipeline.html" should be "http://guides.rubyonrails.org/asset_pipeline.html"
But that doesn't diminish the goodness of the episode -- thanks!
This cast is awesome, even years later as it truly helps out!
As an add-on question to developing with this, I'd like to know the following as I'm struggling with it. If you're ADDING to an existing library (ie., extending a bootstrap CSS file), then it look like the sprockets library is ALWAYS needed at the top of your SCSS files. Attempted to add in the sprockets by default in the application.css file, but it looks like the pre-parsers error - as it cannot locate the $gray-medium-light: variable in bootstrap when running. Only when the IMPORT lines are pre-pended in the file does it actually run. Is this really needed in each/every one of my CSS files which overload/extend bootstrap?
Example (in application.css) -- which I thought would globally load the bootstrap.css
*= require_bootstrap
Example of my file after having to add the @import to my css file
<--- MYFILE.CSS.SCSS --->
@import "bootstrap-sprockets";
@import "bootstrap";
/* universal */
$gray-medium-light: #eaeaea;
<------ >
Any ideas on how to get bootstrap to load globally so I can extend it in my CSS files?