On Tuesday, February 7, 2012 5:23:43 PM UTC-5, jfreeman wrote:
On 2/7/12 2:47 PM, neonstalwart wrote:
> i wanted to
> mention a format for writing modules called asynchronous module
> definition (https://github.com/amdjs/amdjs-api/wiki/AMD).I stumbled on this pattern when I first created the library. I
originally decided against it, because it would introduce another
dependency, and I didn't feel it was well justified. At the time, there
were no optional parts to the library, and we didn't have any users to
complain about library size. :) I felt that if the need arose, we could
easily switch.
these are good points - hotdrink can be kept very lean because it's basically "pure" javascript (ie it doesn't need much help in the way of normalizing differences between various javascript environments) so its good to scrutinize every dependency that may be added. its also true that you could probably easily switch at any time.
Also at that time, there was no clear standard for this pattern or a
clearly "best" library for implementing it. There were several options,
each with different trade-offs. That may have been resolved by now.
part of the goal of amd is interoperability and so being able to have multiple implementations of loaders is considered a strength. when building a library, you don't have to enforce a specific loader if you've written your modules in a format that works with multiple loaders. the end user can choose which loader/builder combination suits them best. the other thing that multiple loaders provides is a way for various implementations to innovate beyond the core of amd and help drive forward the core "specification" -
https://groups.google.com/forum/#!forum/amd-implement is where discussion takes place.
I'm not yet convinced HotDrink needs this. Is this pattern a clear
advantage for /every/ library, not just large, popular libraries like
jQuery or mootools? If so, in what ways? Out of curiosity, does this
pattern interfere with or completely bypass client-side caching?
the advantages for libraries of any size matter mostly to the end user. any library, regardless of size, is typically used as part of a multiplicity of libraries when it comes to the end user. in the past, it has been up to the end user to properly insert the appropriate script tags in the proper order and ensure that one library doesn't produce side effects that interfere with another library etc. this results in script tag soup and becomes more and more difficult to manage as the number of libraries increases. this complexity is compounded when you start concatenating and minifying all these resources for deployment.
there are also advantages for development. with amd, you declare your dependencies and the loader takes care of ensuring your dependencies are properly satisfied before your module is activated. the build tools also take care of building your code in such a way that dependencies are properly satisfied for built code. by simply adding/removing a dependency in your source code, you can have the loader and builder properly satisfy those dependencies. also, because amd can pass you references to those dependencies, you can build a whole library that depends only on the `define` global - your library can work without producing a single global. by avoiding globals, you greatly reduce the chance of one library interfering with another.
since amd uses dynamically injected script tags to load modules, it leverages all the benefits that the browser provides to any other script tag - caching, cross-domain loading, non-blocking loading, etc.
> along with AMD loaders, there are also builders to concatenate and
> minify built modules.
Right now we use cat for concatenation and YUICompressor for
minification. Are there alternatives you would recommend?
i think that without using amd, this is reasonable. its simple and effective.
> testing via the command line is a great fit.
This could be nice. I am pleased with the current state of our
(browser-based) tests, but I could just be ignorant of the advantages. I
have no experience with Node.js. It would be nice to have automated
testing, but I have no experience establishing that, nor do I believe
our team or library large enough to necessitate it yet. I would welcome
advice or assistance, though.
given that the architecture of hotdrink keeps it very cleanly decoupled from a specific UI, there's no reason you couldn't do testing by implementing mock view controllers. in fact, hotdrink seems to generally have a really clean decoupling between components/modules which lends itself to very granular tests that focus on specific interfaces. this means that a lot of your testing can be done without any user interaction and hence without a browser. it would be feasible to get the code to a point where you type `make test` (or some similar command) and it actually runs the tests and prints a report.
http://tddjs.com/ is a book that does a good job of outlining an approach for this and also gives some exposure to nodejs.
http://travis-ci.org/ is also a continuous integration server that has become quite popular recently. i can certainly help with the logistics of these things if you're interested.
the last piece i want to point out is how using amd relates to using nodejs. nodejs uses a module format that is basically the same as CommonJS modules (
http://wiki.commonjs.org/wiki/Modules/1.1) - there are minor changes made by nodejs but the basics are much the same. you write modules like so:
var foo = require('./foo');
var bar = require('./bar');
module.exports = function (input) {
return foo(bar(input));
};
with amd, you would write the same module like this:
define(function (require, exports, module) {
var foo = require('./foo');
var bar = require('./bar');
module.exports = function (input) {
return foo(bar(input));
};
});
you'll notice its exactly the same except for the wrapper that calls define. nodejs doesn't make a `define` available so we have to do something to overcome this. one option is to use some kind of shim that provides an amd loader within node.
https://github.com/jrburke/r.js is an example of such a loader.
an alternative that i prefer is to avoid yet another dependency and write a function wrapper that's a little bit smarter and only uses `define` when its available. i would write our example module like this:
(typeof define === 'undefined' ? function ($) { $(require, exports, module) } : define)(function (require, exports, module) {
var foo = require(./'foo');
var bar = require('./bar');
module.exports = function (input) {
return foo(bar(input));
};
});
this makes the same source code work with amd and nodejs. lets say that code was in fooBar.js then i can type `node fooBar` from that directory and it will run that code for me. i can also use an amd loader in a browser and have it work.
https://github.com/Gozala/test-commonjs/tree/master/tests/engines contains a folder that includes the code to test that library in a browser and in node. if you examine it, you'll see that the same library code is tested in both environments.
hopefully i've been able to show you how writing your modules in amd relates to being able to run tests from the command line. if you're interested in the idea and want some help implementing it or need more questions answered then let me know. amd has its opponents too but i've found that its the best option for my needs and i use it in all my code.
thanks,
ben...