

Unholy Rails: External Scripts, jQuery Plugins, and Page-Specific JavaScript - DanielKehoe
http://railsapps.github.com/rails-javascript-include-external.html

======
ScotterC
I've been using this technique with great success.
[http://viget.com/inspire/extending-paul-irishs-
comprehensive...](http://viget.com/inspire/extending-paul-irishs-
comprehensive-dom-ready-execution)

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.

~~~
DanielKehoe
That's the so-called Garber-Irish technique that I briefly mention in the
article. I want to investigate it further. I'm not sure whether to use classes
or ids in the body tag or try the data-* attributes he suggests.

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.

~~~
pault
CSS selectors should describe visual design, not content. Selector chains like
.landing-page #buy-button are a CSS code smell. Unfortunately this seems to be
more common than not, in my experience.

~~~
eurleif
What? IDs and classes are part of the HTML, not the CSS, and as such, they
should specify semantics, not presentation. If a class or ID specifies
presentation, like .red-button, you have to change the HTML when the design
changes, not just the CSS.

~~~
pault
Sorry, just generally ranting, not in your direction. :)

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.

------
DanielKehoe
I wrote the article to explore what to do when your application is not wholly
Rails. I got a lot of help and advice but I don't think there's full agreement
on recommended practices yet. I've seen a range of opinion but a lot of advice
doesn't hold up to analysis. The article reflects the best advice I've gotten.

~~~
dchuk
Well thank you for compiling all of this together because about to deploy a
new version of my product that has a significant amount of fairly complex JS
and I found it very frustrating to not have any goto resource in regards to
structuring and optimizing the code. I'm going to try and incorporate some of
your tips and will report back if you'd like

~~~
DanielKehoe
You're welcome. Take a look at the link (in the article) to Ken Collins’s
"Secrets Of The Asset Pipeline" slide deck. It's got some great
recommendations about organizing your JS code. Let me know how it goes (you
can leave comments).

------
Finbarr
I dislike the approach described where one concatenates every single
javascript file into application.js and then tests for the presence of
particular dom elements or classes within each file. This taxes the creation
of new scripts somewhat as you must ensure you're not accidentally creating a
dom element or class which is going to enable some undesired scripts. Another
possible option with "require_tree ." is to use a framework like
<http://requirejs.org/> and then use in line script tags within pages to
require a particular module.

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...](http://guides.rubyonrails.org/asset_pipeline.html#live-
compilation)):

"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.

~~~
DanielKehoe
I like your suggestion of using RequireJS. I'll add that to the next revision
of the article.

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?

~~~
Finbarr
Yes I guess that would be the right metric. You'd need to look at average time
to render over a number of deploys with both strategies. I'm pleased to see
articles approaching this subject as there's definitely not enough solid
discussion around this.

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.

~~~
DanielKehoe
I'd like to look deeper into this option. I didn't mention it in the article,
but @jo_liss suggested hosting all assets on CloudFront in production. She
points out that you can set up CloudFront and get your own content-delivery
network in five minutes. Once you have your own CDN, there's no advantage to
using any 3rd-party JS host.

~~~
jwilliams
That's somewhat true. However, using Google does have an advantage over your
own CDN -- it's more likely to already be cached in someone's browser.

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.

------
purephase
Good article. Through trial and error, this is the approach that we've taken.
My only gripe is that it appears (to me) to be the only way that works, as all
my earlier faltering efforts were not successful.

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.

------
railsjedi
Awesome writeup! Rails asset management can get pretty complex for newbies,
but with a few good strategies like described in the article, it can be tamed
and managed without too much trouble.

------
christopherscot
I'm suprised no mention of RequireJS...the part labeled "JavaScript’s Missing
Directive" seems to be specifically gearing up for it.

Good overview though.

