Unholy Rails: External Scripts, jQuery Plugins, and Page-Specific JavaScript
railsapps.github.com
railsapps.github.com
You still have to load in all your js but at least you can execute exactly what you want per controller and action and have it documented all in one place.
Also I'm a bit reluctant to use page-specific CSS selectors. It seems to be accepted practice (Modernizr and other libraries) but still seems like a hack. After all, there is never more than one body tag so it seems inconsistent with the HTML spec to apply a class or id to it.
My personal (strong) preference is to use semantic IDs as JS hooks (or just for markup readability), and keep all class names content-agnostic. Obviously there will be cases where you need to use (CSS) classes in your JS as well, so a good practice is to prefix them with js- and not base any style definitions on them. Separation of concerns and all that. You should be able to refactor your CSS, markup, and JS independently and not have anything break.
Additionally, you should be able to copy paste a chunk of markup and have it look identical no matter where you put it, which definitely won't be the case if you are using long, content-referencing selector chains. Keeping your CSS flat, low level, and generic increases reusability and prevents CSS bloat, which can quickly get out of hand even in a medium sized app. Of course, all this is probably overkill if you are making small, static sites.
The killer feature for me is being able to organize my coffeescript code into various classes and execute it all on one page. Makes it much easier to get other developers up to speed on the javascript code.
I'm still looking for a way not to load specific javascript files from the asset pipeline and not litter my views with content_for :head. It's better all in one place.
I also think it's a bad idea to enable asset compilation in production. From the Rails asset guides (http://guides.rubyonrails.org/asset_pipeline.html#live-compi...):
"This mode uses more memory, performs more poorly than the default and is not recommended."
My advice would be to use application.js to concatenate scripts you're highly likely to use on every page, e.g., jquery, bootstrap, etc. Then organize the rest of your page specific scripts into app/assets/javascripts/<controller>/<action>.js<.extensions> and add a `javascript_include_tag "#{params[:controller]}/#{params[:action]}` in any views that rely on page specific scripts (within a content_for(:head) block). This way you'll likely have 2 local scripts loading for each page - the application.js file and any page specific script. The upsides are that you don't need to worry about undesired javascript running, the application.js file will be cached and reused across all pages, and your lightweight page specific js file will be served the first time a user loads the page then cached with every subsequent visit. The potential downside is that you either need to specify a precompile array by hand in your environment specific config file or automatically glob files to be precompiled.
Note that you can also use the manifest declarations inside of your regular javascript files, e.g., `//= require 'backbone'` at the top of one of your page specific javascript files.
Your strategy of using two JS scripts per view, one site-wide and one page-specific is interesting. I wonder how to test the performance to really determine the value. Maybe look at time-to-render?
One other optimization you may want to talk about in your next article is to use a gem like the asset_sync gem to upload your assets to S3 or CloudFront (or similar) at compile time.
On these occassions the hit to download JQuery (or something big like JQueryUI) is zero.
This is more a consideration for a landing page than a heavy-use application though.
Then I have an application level CSS&JS that has everything generic in it. This is for a reasonably big app, so the overhead of having a single CSS/JS isn't insignificant - particularly when supporting mobile devices.
The other useful thing is a I as a dasherized version of the controller name and action to the body tag - as an id and a class respectively. So the body tag is something like <body id='my-controller' class='show'>
I also expose these as JavaScript globals CONTROLLER and ACTION (I also have LOCALE and COMPONENT).
These constructs are to be used sparingly -- you don't want to mash everything together too much -- but they're very handy when used right.
- number of $(document).ready() callbacks that check whether the current file should run within the single application.js approach. This could grow to be an issue with much larger apps.
- how often the application.js file gets invalidated by a deploy (if it is every deploy this may be annoying for users if there's always a large performance penalty on the first download - especially for apps where the usage isn't regular).
- in the 2 scripts version it's very unlikely that the heavyweight application.js global file will get invalidated regularly, but it is likely that several of the smaller specific page js files will get invalidated with each deploy.
I guess the question is whether downloading several smaller files for each deploy beats downloading the whole application.js file for each deploy (network overhead being the likely bottleneck here).
I don't see where the article recommended that. The article (and the Rails Guides) seem to be pre-compiling for production. This is the standard & recommended practice.
Asset pipeline is great, but it is a pain to get going initially with disparate assets/gems etc. Which, I assume, almost every Rails project has.
Good overview though.