52

I am trying to convert an existing app to the new 3.1 asset pipeline layout, and want to include a lot of vendor files that have to be in a specific order, (underscore.js and backbone being one pair). As such, I can't just use a = require_tree . to pull in my vendor files, (without renaming each file with a prefix. Yuck).

The following is within my app/assets/javascripts/application.js file:

//= require modernizr-1.7
//= require jquery-1.6.1
//= require underscore-1.1.5
//= require backbone-0.3.3
//= require_tree .

I have tried every combination of with/out extensions, with/out the require_tree and with/out the relative paths, and nothing works. All of my vendor files are in /vendor/assets/javascripts/.

I feel like I am being stupid because this seems like such an obvious use case, (including specific files by name in an order is common with JS, no?) that I must be doing something idiotic?

New Alexandria
  • 6,951
  • 4
  • 57
  • 77
beseku
  • 917
  • 1
  • 6
  • 12
  • Romain Tribes has the correct answer, just wanted to add that instead of 'require jquery-1.6.1' you can do 'require jquery' and it will pull it in from a gem instead, it is installed along with 3.1 by default – house9 May 27 '11 at 15:21

5 Answers5

48

You have two possible structure : the first one and the second one. With both following examples, you expose a package at /assets/externals.js. You can javascript_include_tag this package, but you can also require it in your application.js file.

The first one

vendor/
├── assets
│   ├── javascripts
│   │   ├── externals.js
│   │   ├── modernizr-1.7.js
│   │   └── underscore-1.1.6.js
│   └── stylesheets
└── plugins

The file externals.js contains :

//= require ./underscore-1.1.6.js
//= require ./modernizr-1.7.js

The second one

vendor/
├── assets
│   ├── javascripts
│   │   └── externals
│   │       ├── index.js
│   │       ├── modernizr-1.7.js
│   │       └── underscore-1.1.6.js
│   └── stylesheets
└── plugins

The file index.js contains :

//= require ./underscore-1.1.6.js
//= require ./modernizr-1.7.js
Romain Tribes
  • 1,818
  • 2
  • 17
  • 21
  • 2
    one thing to add, it was confusing for me; once the externals is setup you can reference it in application.js with a simple //= require externals or via javascript_include_tag('externals') in a view/layout – house9 May 27 '11 at 15:04
  • Erf, i was just adding this information in my post, but good point anyway. Better to see that twice than zero. :D – Romain Tribes May 27 '11 at 15:08
  • I tried this, but when I go to /assets/externals.js I get `No route matches [GET] "/assets/externals.js"` – Shlomo Zalman Heigh May 27 '11 at 17:52
  • 1
    So there is no way of doing this without having to maintain two or more manifest files? That seems to go against the Rails Way of convention over configuration in a massive way, no? – beseku May 28 '11 at 01:35
  • I don't think so. Anyway, here is a trick to just use `require_tree` : you can name files with a prefix : `a_underscore-1.1.6.js`, `b_modernizr-1.7.js`, etc. – Romain Tribes May 28 '11 at 10:08
  • Something which took me a few minutes to figure out: //= directives need to be at the top of the document. Every line above must begin with //. – sandstrom Jun 27 '11 at 14:19
  • Thanks for the answer, this worked for me too! This still feels a bit strange having two manifest files. Does anyone know if there have been any updates related to this subject since June? – hade Oct 06 '11 at 07:07
29

You can require each file in particular order and then add:

//= require_self

instead of:

//= require_tree .
jwfearn
  • 28,781
  • 28
  • 95
  • 122
martinciu
  • 384
  • 3
  • 4
  • 2
    I started RoR recently, but soon noticed that require_tree . is shooting yourself in the foot on a big project since you end up loading a lot of JS/CSS that's not used... Require each file manually and if needed group them into "library folder" with its own index file... – luigi7up Apr 24 '14 at 08:07
8

My answer applies to Rails 3.1rc4, I don't know whether it functions the same with other versions.

You can actually put all require statements in app/assets/javascripts/application.js whether or not the .js files are in app/assets/javascripts/ or vendor/assets/javascripts/

Like so:

// this is in app/assets/javascripts/application.js

//= require modernizr-2.0
//= require jquery
//= require jquery_ujs
//= require jqueryui-1.8.12
//= require jquery.easing-1.3
//= require jquery.noisy
//= require jquery.jslide-1.0
//= require respond
//= require smoke
//= require_tree

I included require_tree here because I have other javascript files for my individual controllers (pages.js.coffee, users.js.coffee) and a general one for site-wide stuff (site.js.coffee)

Meanwhile here's the file structure.

app/
├── assets
│   ├── javascripts
│   │   ├── application.js
│   │   ├── pages.js.coffee
│   │   ├── users.js.coffee
│   │   └── site.js.coffee
│   └── stylesheets
└── plugins

vendor/
├── assets
│   ├── javascripts
│   │   ├── jquery.easing-1.3.js
│   │   ├── jquery.jslide-1.0.js
│   │   ├── jquery.noisy.js
│   │   ├── jqueryui-1.8.12.js
│   │   ├── modernizr-2.0.js
│   │   ├── respond.js
│   │   └── smoke.js
│   └── stylesheets
└── plugins

This allows me to control the load order of vendor libraries (which matters a lot, usually) and not worry about my internal javascript, where order generally matters less.

More importantly, I control all require statements within one often used file, I find that both safer and cleaner.

Olivier Lacan
  • 2,636
  • 2
  • 24
  • 22
  • +1 for this way of handling assets dependencies. This is in the Rails' way as per the guides http://guides.rubyonrails.org/asset_pipeline.html#asset-organization. Also remember to restart your Rails server after moving files around! – brutuscat Oct 13 '11 at 10:28
  • But this loads too much JS at once. Much better to `//= require` your dependencies only where they're actually needed. In general I think the Rails 3.1 way of organizing JS is not fine-grained enough for well-designed applications. – Marnen Laibow-Koser Jun 27 '12 at 23:01
  • @MarnenLaibow-Koser The Rails idea is that loading a big bunch of js/css at a time is good, because then the browser can cache that and then for any subsequent requests no assets need to be fetched from the server. So the initial load is slower, but subsequent requests are fast when every page do not have it's own css styles and js files. This also promotes writing sane and reusable css. However, you can of course tune the asset pipeline to be as finely granulated as you wish. – Timo Jul 09 '13 at 09:04
  • @TimoLehto I know that's the Rails idea. I disagree with it rather strongly, since it encourages the use of big JS/CSS files that contain a lot of code that isn't needed for the current page scope (and small, granular files are arguably more cacheable). On my own applications, I basically disregard the Rails recommendation and use granular JS and CSS. – Marnen Laibow-Koser Jul 09 '13 at 15:41
6

I believe you can put a library.js in your in vendor/assets/javascripts and then simply

//= require library.js

from your application.js, no?

jwfearn
  • 28,781
  • 28
  • 95
  • 122
muxcmux
  • 237
  • 4
  • 9
  • 2
    Pardon me, should be `vendor/assets/javascripts` – muxcmux Jun 06 '11 at 12:12
  • Yeah, at the moment I use two files, (for bug tracking primarily), a vendor.js and an application.js. I never solved the problem of being able to include all directly to the application.js manifest without another manifest file though. – beseku Jun 06 '11 at 14:59
4

require_tree does exactly what you tell it. If you give it

//= require_tree .

it loads the files in the current directory where require_tree is called. If you give it

//=require_tree ../../../vendor/assets/javascripts

then you'll get the javascript under vendor.

I did not like the ../../.. notation, so I created a file called vendor/assets/javascripts/vendor_application.js which contains:

//= require_tree .

That loads the javascript under the vendor directory.

Note, require does search the 3 pipeline locations (app, lib, vendor) for the file to require. require_tree is literal, which is probably the way it should be.

The railscast on this is very helpful: http://railscasts.com/episodes/279-understanding-the-asset-pipeline

justingordon
  • 12,553
  • 12
  • 72
  • 116