2
votes

Broadly: What are the best practices for organizing JS in the Rails pipeline?

Specifically: I have rapidly growing JS files that I'm fine with including in the overarching application.js manifest and letting Sprockets uglify. However, each individual JS file is starting to become unwieldy, and I'd love to organize them so that I don't have to dig through hundreds of lines of utility functions to get to the meat of the code. I recognize that the goal is some kind of namespacing/modularity, but I don't know what the best practices are for combining this with the asset pipeline, especially Sprockets manifests.

Things I've already considered, and read thoroughly:

  • Yes, I've read the entire Rails guide on its asset pipeline. I know how Sprockets directives like require and require_tree work; the issue is that I want to use those same directives like the equivalent of an ES6 import command so I can do something like

     // in, say, controller.js
     //= require 'utilities'
     ...
     more code
     ...
    
     // and in application.js, more confidently
     //= require 'controller'
    

    but I get the feeling that's not how manifests should be used, or at least that it'll needlessly recompile layers of assets every time I change a single line in utilities. I've also considered requiring every file individually from application.js but that doesn't really give the modularity that seems appropriate.

  • Gems like Paloma, CommonJS, or RequireJS. These seem like overkill, and seem meant to replace the pipeline rather than supplement it.

  • "Modern" JS like ES6, Babel, or Browserify. (I admit I don't really understand the overlap in these projects yet, but I think I get the gist of their purposes.) Maybe eventually, since JS seems to be moving in that direction, but also seems like overkill.
  • Gulp. In the same vein as previous, overkill and an unnecessary rewriting of the asset pipeline.
  • Rails 5 and Sprockets 4. We're on Rails 4 right now, and I'm aware that Sprockets 4 has some ES6 built in, but I don't plan on upgrading until at least a while after Rails 5 is publicly released.

So what should I do? I think I need to bite the bullet and go with one of these, but I just can't figure out which makes the most sense. The project isn't especially reliant on JS, but there's enough of it that I want to organize it now, rather than later.

1
I answered this awhile ago about Google Maps, it details how to break out the page-specific js in Rails apps--might be helpful: stackoverflow.com/questions/36811056/…Elvn
I more or less knew how to do that already (actually, I'm doing something like it.) The issue is when the analog of my_map_handler.js is something I wrote, and huge, and I need submodularity for that file. Basically, I get how to organize my JS in views so they're not redundantly used (which I'd be okay with anyway)--it's more about code hygiene in the JS files I'm writing, combined with working in the pipeline's framework.Raynor Kuang
Cool. I just thought I'd throw put that out there in regards to page specificness, not so much the rest of your question.Elvn

1 Answers

2
votes

I find when I have heavy amounts of page-specific js that it is best to leave the assets pipeline for only the js that is repeated more than once. Obviously all outside libraries should continue to go in your vendor/js folder. The most rails-esque way to add page-specific js to your app (thus avoiding loading js in every page when a library or script is only needed in one page) is to add to the very bottom of your layouts/application.html.erb file right before the closing body tag:

<%= yield :javascript %>

Then on the view that you're looking to run some snippets of javascript you should put this at the very bottom of it after all html tags have been closed. Make sure it's not within some divs or anything in that view but at the very bottom of it.

<%= content_for :javascript do %>
  <script type="text/javascript">
   $(document).on('page:load', function() {
       all your page specific code here..
     });
  </script>
<% end %>

This code will now be yielded to the bottom of your layout view only loading on the specific views you need instead of being concatenated along with all other assets in your assets/js folder. I do this with all custom js that isn't application wide and it's very easy to maintain/debug.