understanding AMD module names for use in Rails asset pipeline

92 views
Skip to first unread message

Eric Dobbs

unread,
Mar 27, 2013, 1:28:59 AM3/27/13
to cuj...@googlegroups.com
Background:

I play with javascript-in-browsers a lot in my free time (when I can find it), nevertheless new to AMD modules.  I get paid to write rails.  We have some tangled javascript at work (stop me if you've heard this one ;-) and I'm hoping to gradually introduce AMD modules to get better engineering discipline around our front end.


Goal:

I am working on a proof-of-concept to use Rails' asset pipeline (aka sprockets) where you folks would use cram.js or r.js to package some AMD modules into integrated bundles (using vocabulary I learned here: https://github.com/cujojs/cram/blob/master/docs/concepts.md).

My slightly longer-term ambition is to use wire because ... dependency injection.


Question:

Here's what I think I understand about AMD (ignoring Rails asset pipeline for the moment).  Can you confirm or deny this stuff -- and maybe tell me what I might not know I need to know?

    define(['foo', 'bar'],/* my awsome stuff happens here */);

With this module definition, if I were in node.js, I could do this:

    namesMatter = require('my-awesomeness');

And with curl in the browser I could do something similar, provided I get the paths right in my configuration:

    namesMatter = curl('my-awesomeness');

The important part being that 'my-awesomeness' is a name that's comming from the file system in the case of node and from the basename of the url in the case of curl.js (caveat that curl.js offers lots-o-configuration options for naming and paths and such).



By contrast this definition:

    define('hardCodedName', ['foo', 'bar'], /* still awesome */);

fixes the name of the module to 'hardCodedName' instead of using the filename or url.  If so, then my other modules could use that name in their definitions like so:

    define('soCool', ['hardCodedName'], /* let the magic begin */);


If I do in fact understand that stuff correctly, then I think this second way of defining modules will play more nicely with Rails asset pipeline.  In effect, I'll have a bunch of AMD modules with their names declared inside the parens of their defines.  It'll be my job to control the order in which the files are concatinated so that dependencies are defined before modules which depend upon them.

But I think it also means I'll have some work to do in local forks of curl, wire, when and such to hard code their names.  I can't tell yet if that would be more or less work than getting requirejs-rails working in our app and then connecting those dots to cujo.


Have I got this right?  Are there any waterfalls downstream from here that I need to know about?

Many thanks.
-Eric

Eric Dobbs

unread,
Mar 27, 2013, 1:39:20 AM3/27/13
to cuj...@googlegroups.com
I did see this thread:  https://groups.google.com/forum/#!topic/cujojs/lhMlcpFnOwI

But didn't see a conclusion to combining cram.js with Rails asset pipeline.

-Eric

Scott Andrews

unread,
Mar 27, 2013, 6:06:13 AM3/27/13
to cuj...@googlegroups.com
Hi Eric, good questions.

We have a couple early stage efforts you may be interested in.  The first is an emerging collection of tutorials.  The module tutorials[0] should answer your questions, but if anything isn't clear, please post back to the list.  The second is that we're actively exploring support for the Rails asset pipeline; hopefully we'll have something preliminarily next week.

-Scott




--
You received this message because you are subscribed to the Google Groups "cujojs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cujojs+un...@googlegroups.com.
To post to this group, send email to cuj...@googlegroups.com.
Visit this group at http://groups.google.com/group/cujojs?hl=en.
To view this discussion on the web visit https://groups.google.com/d/msg/cujojs/-/RsKX7i0fXGkJ.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

unscriptable

unread,
Mar 27, 2013, 10:33:33 AM3/27/13
to cuj...@googlegroups.com
Hey Eric!


On Wednesday, March 27, 2013 1:28:59 AM UTC-4, Eric Dobbs wrote:
We have some tangled javascript at work (stop me if you've heard this one ;-) and I'm hoping to gradually introduce AMD modules to get better engineering discipline around our front end.

Never heard that one. ;)
 

Here's what I think I understand about AMD (ignoring Rails asset pipeline for the moment).  Can you confirm or deny this stuff -- and maybe tell me what I might not know I need to know?

    define(['foo', 'bar'],/* my awsome stuff happens here */);

With this module definition, if I were in node.js, I could do this:

    namesMatter = require('my-awesomeness');

And with curl in the browser I could do something similar, provided I get the paths right in my configuration:

    namesMatter = curl('my-awesomeness');

I think you're close. :)  One thing to mention: `curl` is meant for bootstrapping the app into an AMD environment.  The global `curl` isn't context-aware when it's used inside modules.  Once you're inside an AMD module, you should [almost] never touch it again.  (In CommonJS environments, your app already boots into a controlled environment.)  Inside modules, CommonJS's `require` and AMD's `require` are roughly equivalent.  (AMD's `require` takes two additional parameters to allow it to load resources async.)  

There are two ways to get the `require` in AMD modules:

define(/* deps array is omitted! */ function (require) {
   
var namesMatter = require('my-awesomeness;);
});


and 

define(/* explicit: */ ['require'], function (require) {
   
var namesMatter = require('my-awesomeness;);
});


 
The important part being that 'my-awesomeness' is a name that's comming from the file system in the case of node and from the basename of the url in the case of curl.js (caveat that curl.js offers lots-o-configuration options for naming and paths and such).

Not true, actually.  Both CommonJS and AMD (and the forthcoming ES6 modules) identify modules by *id*.  It's both familiar and unfortunate that module ids use slashes like urls and file paths.  It's also familiar and unfortunate that they all allow urls and file paths as fallbacks when the system doesn't know about an id.  :)

In AMD and CommonJS environments -- outside of trivial examples -- it's almost *never* true that module id === file/url path.

 
By contrast this definition:

    define('hardCodedName', ['foo', 'bar'], /* still awesome */);

fixes the name of the module to 'hardCodedName' instead of using the filename or url.  If so, then my other modules could use that name in their definitions like so:

    define('soCool', ['hardCodedName'], /* let the magic begin */);

s/could use that name/must use that name :)

Yes. "Named modules" (modules with ids) are the way to go when packaging several together.  You can't package a bunch of anonymous modules together and expects the loader to know which is which. :)  The downside to named modules: the concatenated bundle of modules must have exactly one named module (out of the many modules) whose module id === bundle url.  You can get around this limitation by specifying a path or package mapping, as you noted earlier.  This allows the system to `curl(idOfBundle)` or `require(idOfBundle)` and ensure it got the right bundle.  (If you `curl("foo")` and curl finds no module with the name "foo" inside (or an anonymous module), then it assumes there's a problem.)

 
If I do in fact understand that stuff correctly, then I think this second way of defining modules will play more nicely with Rails asset pipeline.  In effect, I'll have a bunch of AMD modules with their names declared inside the parens of their defines.  It'll be my job to control the order in which the files are concatinated so that dependencies are defined before modules which depend upon them.

But I think it also means I'll have some work to do in local forks of curl, wire, when and such to hard code their names.  I can't tell yet if that would be more or less work than getting requirejs-rails working in our app and then connecting those dots to cujo.

Typically, a pipeline or concatenation tool will need to inject the names of the modules.  Forking all of the AMD libs in the world is probably more work.  Forking all of the cujo.js libs is considerably less work, but still a lot of work.

Like Scott said, Jeremy on our team has been working on this.  I'll ping him to see if he'd like to collaborate or share ideas.

-- John

Eric Dobbs

unread,
Mar 27, 2013, 12:30:52 PM3/27/13
to cuj...@googlegroups.com
Scott, John, many thanks for your answers.

The key answer I was looking for was understanding "named modules".  And (feedback for your docs) I think that information would really help.

I'm pretty good at using the source, but I think all but one example I've looked at in the various examples I've found use unnamed modules.

Being new to this I kept asking myself "where do the names come from?"  I'll phrase that question again for completeness.

I totally understand these parts:

1: wrap it in: define()
2: declare dependencies:  define(['cat', 'fish', 'thing1', 'thing2'])
3: return a javascript object : define ([...], function (...) { return {something: 'wicked'} })

What I didn't understand was the names declared in step 2 ( Cat in the Hat characters in my example above).  I get that those names refer to dependencies, but I don't (or didn't) understand where those names were getting defined.  I understand why it's valuable to NOT hard code the names in named modules. 

What I think I now understand is that those names are being bootstrapped in the curl config and otherwise managed by wire, or more to the point the AMD context.

So if I understand correctly what r.js and cram.js do is some kind of recursive walk through the dependency graph and collect all of those names at build time and concatenate the source for delivery to the browser.  The advantage there is that my application configuration gets to be in control of the names instead of having the library authors in control of those names in my application.

Jeremy, if you're listening in, feel free to ping me on- or off-list about rails side of this game.

Thanks again for your speedy and clear answers.

-Eric

unscriptable

unread,
Mar 27, 2013, 12:45:23 PM3/27/13
to cuj...@googlegroups.com
Hey Eric,

You just made a light bulb turn on in my head!  I think I know what the next modules tutorial is going to be!  :)

-- J

Eric Dobbs

unread,
Mar 27, 2013, 3:46:45 PM3/27/13
to cuj...@googlegroups.com, cuj...@googlegroups.com
On Mar 27, 2013, at 10:45 AM, unscriptable <jo...@unscriptable.com> wrote:

> think I know what the next modules tutorial is going to be! :)

John,

I look forward to seeing that tutorial! What I've read from you while digging into this has been really coherent and clear. Brian, you too.

-Eric

Brian Cavalier

unread,
Mar 27, 2013, 4:52:33 PM3/27/13
to cuj...@googlegroups.com
Thanks, Eric!  Welcome to cujo :)
Reply all
Reply to author
Forward
0 new messages