DailyJS

DailyJS

The JavaScript blog.


Tagnarwhal
Featured

tutorials frameworks lmaf narwhal jake

Let's Make a Framework: Aliasing and Packaging

Posted on .

Welcome to part 12 of Let's Make a Framework, the ongoing series about
building a JavaScript framework. This part discusses aliasing and
packaging.

If you haven't been following along, these articles are tagged with
lmaf. The project we're creating is called Turing and is available on GitHub:
turing.js.

A User-Friendly API

Turing uses namespaced modules and methods, so calls look like this:

turing.dom.get('#selector');

The reason Turing is designed this way is it allows it to be modular --
you don't need to load every module, which is great for server-side
work.

Lots of frameworks use a dollar sign, so we could shorten Turing to
\$t:

$t.dom.get('#selector');

That's better, but get is one of those operations that will
be commonly used for browser-based work. Let's take a leaf out of
jQuery's book and accept a parameter. If the parameter is a string, it
will be considered a selector:

$t('#selector');

This can be implemented using arguments:

$t = (function() {
  var alias = function() {
    return turing.dom.get(arguments[0]);
  }

  return alias;
})();

Then other parts of the framework can be referenced by the
alias object:

if (turing.dom) {
  alias.dom = turing.dom;
}

if (turing.events) {
  alias.events = turing.events;
}

Enumerable Shortcuts

The turing.enumerable module contains a big set of commonly
used functions. Rather than retaining the namespace, let's copy those
straight into the alias object:

if (turing.enumerable) {
  turing.enumerable.each(turing.enumerable, function(fn, method) {
    alias[method] = fn;
  });
}

Now using each is much easier: \$t.each().

Configuration

BBC's Glow framework is interesting because it can work alongside different versions of itself:

Also, as many BBC pages are built by separate development teams, we needed a library that could work alongside different versions of itself, without encountering namespace issues or CSS clashes. None of the major libraries were capable of doing this.

I like this idea, and I wish more frameworks used careful namespacing
and supported concurrent use. One way to support this is to use a
loading system that can load the framework and its modules. We're not
quite there yet, so I decided to allow Turing's global alias to be
changed as an experiment.

At the moment this works using an eval to fetch the
framework's alias from the configuration, but this could be changed in
the future:

eval(turing.alias + ' = turing.aliasFramework()');  

In turing.core.js I set some high-level configuration
options. This is where I've stored a reference to '\$t'.

Packaging and Minification

I've carefully handcrafted Turing to work minified. Concatenating
scripts and running a minifier is boring though, so let's script it.

To be fully JavaScript and awesome I'm using
Jake. It's a JavaScript build tool that's easy to work with.

The first thing you need to do is get Narwhal.
Installation is pretty easy:

git clone git://github.com/280north/narwhal.git
export PATH=$PATH:~/narwhal/bin

# Check it works
narwhal narwhal/examples/hello

# If that didn't work, check that you don't have any Rhino jars kicking around

# Next, install jake and shrinksafe
tusk install jake
tusk install shrinksafe

With those tools installed we can build Turing into a single file. Jake
build scripts are standard JavaScript, usually named
Jakefile:

var jake = require('jake'),
    shrinksafe = require('minify/shrinksafe'),
    FILE = require('file'),
    SYSTEM = require('system')

jake.task('concat', function(t) {
  var output = '',
      files = ('turing.core.js turing.oo.js turing.enumerable.js '
              + 'turing.functional.js turing.dom.js turing.events.js turing.alias.js').split(' ')

  files.map(function(file) {
    output += FILE.read(file)
  })

  if (!FILE.exists('build')) {
    FILE.mkdir('build')
  }

  FILE.write(FILE.join('build', 'turing.js'), output)
})

jake.task('minify', function(t) {
  var shrunk = shrinksafe.compress(FILE.read(FILE.join('build', 'turing.js')))
  FILE.write(FILE.join('build', 'turing.min.js'), shrunk)
})

jake.task('build', ['concat', 'minify'])

The task at the end defines the build task which we'll run with
jake build. I've used the convention of capitalising
libraries like FILE so they don't clash with common
variable names. I've hardcoded the Turing source files rather than
globbing them because I want to specify the order they appear.

Running jake build will generate
build/turing.js and build/turing.min.js.

Conclusion

Designing good APIs isn't just about avoiding globals, it's also about
using concise yet clear naming conventions. Frameworks that use
\$() really went a long way to making JavaScript
development more accessible.

Most mature projects end up with some kind of build chain. Don't rush to
add one to your project, but if you notice you're spending a lot of time
packaging things, try writing your own Jakefile. You can technically
script anything with tools like Jake, just keep an eye out for repeated
actions during development.

Featured

server nodejs narwhal

JavaScript Library Dependencies

Posted on .

I've been thinking about strategies for hosting JavaScript apps. One
core part of this is dependency management -- it makes deployment as
smooth as possible, and takes away a lot of maintenance pain. But what
library dependency management options are there right now?

Require and Packages

The building block of dependency management is require.
This is specified in CommonJS
modules
, and they have unit
tests for implementors to use.

That's not enough though. Production server-side applications need to be
specific about library versions to ensure that what's deployed is tested
and safe. Yet nobody wants to waste time downloading specific versions
of scripts. This is where a package manager comes in handy.

Much like there are multiple implementations of JavaScript there can be
many package managers. Some programming languages opt to bundle one as
standard, and others allow the community to provide and support them.

Narwhal

Narwhal has tusk, which manages virtual environments and is also a package manager. Packages use a json file to express metadata like dependencies, and can include jars,
executables, C and Java source, and
engines.

The package.json files look like this:

{
    "author": "Author Name",
    "contributors": [
        "Contributor Name"
    ],
    "dependencies": [
        "narwhal"
    ],
    "jars": [
        "jars/simple-4.1.10.jar"
    ],
    "version": "0.2.2"
}

Tusk usage looks like this:

alex@mog ~/narwhal[git:master]$ tusk install jack
Updating catalog.
Downloading http://github.com/280north/narwhal/raw/master/catalog-2.json
# ... etc

Tusk installs into your own path, rather than system-wide. You could use
it to manage your own apps to ease deployment to production servers.

Kiwi

Kiwi is a package manager for node.js. You can install it like this:

git clone git://github.com/visionmedia/kiwi.git
cd kiwi
# sudo if required or available
sudo make install
# This installs into ~/.kiwi
kiwi -v install oo

A node app that uses kiwi looks like this:

var kiwi = require('kiwi'),
    sys = require('sys')

sys.p(kiwi.require('oo'))

Kiwi uses YAML seed files: