Chapter 14. MooTools on Deck

Before CommonJS, there was ServerJS, a movement to advance the state of JavaScript on the server. Server-side JavaScript isn't something new: there have been several attempts to bring JavaScript to the backend of applications, but none of them was really successful. ServerJS (eventually renamed CommonJS) changed that.

The idea of JavaScript on the server might seem weird to some people. After all, there are already dozens of established languages that can be used to write the backend of web applications. There's a big allure, however, to being able to write both parts of an application—server and client—in the same language, and that alone is enough to warrant exploration into server-side JavaScript.

In this chapter, we'll take a quick look at the server-side aspect of applications and how CommonJS expands JavaScript to work on the backend. We'll also take a look at Deck, a cross-engine server-side JavaScript application layer written in MooTools.

Revisiting Request and Response

In Chapter 11, we learned about HTTP requests and responses in the context of the browser. We learned that when a browser needs to load a resource, it sends a request to a server, which the server answers with a response. The browser then parses this response to display the data or update the interface.

This side of the process should be familiar to you by now. However, we still haven't considered the other piece of the puzzle: the server itself. We know how to send requests and process the received response, but we don't know anything about how the server actually creates those responses.

Web servers come in different forms, from simple ones written in languages like Lua or Io, to much more complex systems like the Apache HTTP Server and Nginx. All of them, however, follow a very simple formula when it comes to the request and response cycle.

It begins, of course, with the web server software itself. As I said, web servers take a multitude of forms, and there's really no general engineering format that can describe them all. There is, however, a similarity in their architecture: all web servers will have some form of listening loop that lets them listen for incoming connections. Like the long-running programs we discussed in Chapter 10, web servers will sit and wait for events to happen. In this case, the events are client connections and requests.

When a client, such as a browser, connects to a server and sends a request, the server performs an inverse operation from the one we saw in Chapter 11. Say we have the following GET request:

GET /index.html HTTP/1.1
User-Agent: BrowserX/1.1
Accept: text/html
Host: example.org

The server, like the browser, understands the HTTP protocol, and therefore knows how to parse this message. In this case, the server knows that the client is requesting to retrieve (GET) a resource named /index.html and wants to receive files of the text/html type.

Most web servers have the capability of serving two kinds of resources: static and computed. A static resource is simply a resource that's stored as a file in some location on the server. For example, images and style sheets are almost always served as static resources; they are stored directly on the server and sent as is. For static resources, the web server only needs to locate the particular file on the server, process its contents to get its mime-type and length, and then send the whole resource back as a response.

If our /index.html is a static resource, for example, the web server simply looks for a file named index.html in the site's root directory, reads this file to get its contents and length, and then sends a response back to the client with the contents of the file included. The process is quick and straightforward, and there's not much processing involved.

On the other hand, computed resources, as their name implies, are resources that are the result of some computational process that's generally done using scripts and programs. In this category are the resources generated by PHP scripts, Rails or Django applications, or other dynamically created resources.

If static resources are stored directly as files on the server, does that mean that computed resources aren't stored as files? The answer is it depends. What makes a resource computed isn't actually whether it's stored in a file, but how the server retrieves the data associated with a resource.

Let's take PHP scripts, for instance. A PHP script is simply a plaintext script containing code that can be interpreted by a PHP interpreter. Say we have a PHP script file called program.php in the root of the server. If we request GET /program.php and the server responds with the actual content of the PHP file, then we are served a static resource. But if the server runs the PHP script file first by passing it to the PHP interpreter and then takes the result of that execution and sends it back to us, the resource we received is computed—the result of running a PHP script.

The decision as to whether to serve a resource as static or computed is therefore up to the server. Of course, this decision is aided by the configuration that the server administrator sets: a user can specify that a particular file type always be sent as static, while another file type should always be interpreted first to yield a computed resource.

Aside from this static versus computed resource distinction, web servers also distinguish between direct and routed resources. Remember that when we request a resource, we send a resource URI to the server that identifies the location of the resource we want to access. The location of the resource, therefore, determines whether the resource is direct or routed, which adds another dimension to the web server process.

In our examples, both index.html and index.php are files that are stored in the root directory. Therefore, the resource URIs /index.html and /index.php both correspond to the actual location of the files on the server. Thus, these resources are said to be direct because their resource URIs directly correspond to their locations in the file system. The resource index.html is therefore a direct static resource, while index.php is a direct computed resource.

However, web servers also allow us to define "virtual" locations for our resources. For example, we can set up our web server so that the resource URI /home would access the resource /index.php. The resource URI is therefore not directly connected to the actual location of the resource, and it is up to the web server to match the resource URI to the real resource location. Thus, this resource is said to be routed, because the web server needs to determine the route from the resource URI to the actual location.

Of course, all these ideas are transparent to the clients requesting resources from a server. To them, the web server is simply a black box: send a request, get a response. However, these ideas are important in our current discussion because we're moving away from the client-side of the equation and moving into the realm of the server.

JavaScript on the Server

Most simple web sites are served using direct static resources. Write a bunch of HTML pages, add in some images and style sheets, upload to a server, and you're done. Sometimes the resources are mapped so that they become routed resources, which make their external URLs prettier and more accessible, yet they still depend on static files.

Web applications, meanwhile, are built to be computed resources. Some parts of the web apps, such as the client-facing site, are sometimes built using static resources, but the server end of a web application is always computed. The computed parts of a web application are always written as programs, usually using one of the popular web programming languages such as PHP, Ruby, or Python, or more "arcane" yet interesting ones like Smalltalk or Lua.

In the early days of JavaScript, writing the computed, server-side part of a web application in JavaScript was an almost impossible idea. The technology was there, but there wasn't enough interest yet to capture the attention of developers. This gradually changed through the years, and eventually led to CommonJS. In fact, this was exactly the goal of ServerJS, the precursor to CommonJS: to provide a viable way to create web applications with a JavaScript backend.

And that's no longer just a pipe dream. Nowadays, lots of server-side JavaScript applications are being launched, and more companies are starting to test the usefulness of the language for building their next web application.

This confuses quite a lot of people, of course: why would we want to write a web application in JavaScript when there are already a lot of other languages to choose from? The most common answer is reuse. By using JavaScript, you can use the same language to develop both the client-side and the server-side parts of the application. JavaScript is the de jure language of the browser, and it now has the blessed status of being natively supported by both the client-side and the server-side—something that no other language can claim.

JSGI

One thing that's needed, though, to make JavaScript work on the server side is a set of language APIs that deal with application servers. In order for it to be used on the server side, JavaScript must learn the language of requests and responses from a server's point of view.

The CommonJS specification that deals with web applications is called the JavaScript Gateway Interface specification, or simply JSGI. This specification defines a model for creating JavaScript server-side applications using a very simple flow via function decoration.

In the JSGI model, an application is a simple JavaScript function that receives an object argument called the environment. It should then return a response object that contains the details of the response. Here's a very simple JSGI application:

var app = function(env){
    return {
        status: 200,
        headers: {
            'content-type': 'text/plain',
            'content-length': 12
        },
        body: ['Hello World!']
    };
};

When this application is called by the JSGI server, it's invoked as a function and passed a single env argument. This is the environment object that contains details about the request and the current environment. It has the following main properties:

  • method is the HTTP method of the request in an uppercase string form, such as 'GET' or 'POST'.

  • pathInfo is the request URI in string form. Like in our examples, this value is always prefixed with /.

  • queryString is the query string portion of the request URI, if available.

  • host and port give information about the host and port used to connect to the server.

  • scheme is the string representation of the URL scheme used to access the server, such as 'http' or 'https'.

  • headers is an object containing the request headers. Each header is defined as a key-value pair, with the key being the lowercase equivalent of the name of the header. For example, the HTTP header Content-Type will be available from this object through headers['content-type'].

  • input is a special stream object that can be used to read the body of the request.

  • jsgi is an object that contains special JSGI model values that are used to determine the nature of the engine's JSGI implementation.

  • env is an object to which you can add new application- or host-defined properties, since the top-level environment object should not be augmented with new properties.

In JSGI applications, the environment object is used to determine the nature of the request, and its properties are inspected so we can properly serve a response. Since we're not doing any complex procedure in our application example, we simply ignore the environment object.

To properly respond to a request, the JSGI application needs to return a response object. This is a basic JavaScript object with the following properties:

  • status is a number property that defines the HTTP status of the response.

  • headers is an object containing the response headers. Like the response property of the environment object, each header is defined as a key-value pair with keys being lowercase header names.

  • body is a collection object that contains the response body. For simple applications, a basic array is used as the body property, while more complex applications can use special byte-array objects provided by the CommonJS engine. When the response is sent to the client, the body property is either concatenated into a single string or sent in chunks using streams depending on the CommonJS engine.

In our example, the application sends back a simple response that will look like this to the client:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 12
Hello World!

Because JSGI applications are simply functions, they can be extended and changed using simple function decoration, which we discussed in Chapter 2. For example, let's say we want to add the string 'Hi Universe' to the end of the response body. We can do this using a simple decorator:

// decorator
var decorate = function(app){
    return function(env){
        var response = app(env);
        response.body.push('/nHi Universe!'),
        response.headers['content-length'] = response.body.join('').length;
        return response;
    };
};

// jsgi application
var app = function(env){
    return {
        status: 200,
        headers: {
            'content-type': 'text/plain',
            'content-length': 12
        },
        body: ['Hello World!']
    };
};

// decorate app
app = decorate(app);

Here we defined a decorator function that takes a JSGI app and returns a new decorated app. What this decorated app does is call the original application and store its response object. It then pushes a new value to the body of the response object before returning it. This will yield the following response:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 26

Hello World!
Hi Universe!

Without the decoration, the original application is invoked directly by the JSGI server and passed the environment object as an argument. When decorated, however, the application is no longer called directly. Instead, the new decorated function is the one called by the server, and it is this new decorated function that invokes the original application. The decorated function is therefore said to be in the middle between the server and the application, which gives functions like this their JSGI designation of middleware.

In JSGI, middleware is simply JSGI applications that call other JSGI applications. These applications are stacked much as in our example, which makes it possible to create complex transformations that let us create powerful applications. There is no limit to the amount of middleware that can be added to an application, and middleware functions can be used to produce simple transformations, as we did above, or more complex ones like changing the environment object itself.

JSGI and CommonJS Engines

The JSGI model is very simple to understand and, when used properly, lets us create really powerful applications. It is the de facto model for application development in CommonJS.

However, the JSGI model isn't supported by all CommonJS engines, often because of the different nature of the engine implementation itself. This creates a problem when it comes to developing applications that can run on several engines, and is therefore an interesting issue to look at.

Meso, the MooTools runner and toolkit for CommonJS that we'll be using here, currently supports four popular CommonJS implementations: Flusspferd, RingoJS, NodeJS, and v8cgi. In the following sections, we'll look at each one of these and take a peek at how they implement a web application model.

Flusspferd

Flusspferd (http://flusspferd.org) is a CommonJS implementation first released in April 2009. It is written in C++ and uses the Mozilla SpiderMonkey JavaScript engine, which is written in C.

Flusspferd itself doesn't have a web application module, but one is available in the form of Zest, an HTTP server implementation for Flusspferd written by Ash Berlin. Zest natively supports JSGI, and is therefore compatible with the CommonJS spec.

Here's an example of a Zest-based JSGI application:

// the application
var app = function(env){
    return {
        status: 200,
        headers: {
            'content-type': 'text/plain',
            'content-length': 12
        },
        body: ['Hello World!']
    };
};

// zest module
var Zest = require('zest').Zest;

// create a server
var server = new Zest({
    handler: app,
    port: 8081
});

server.start();

We used our original application example and then included the Zest module using the require function. The Zest module exports a main constructor function called Zest, which is used to create an HTTP server. In the last part of our snippet, we created a new Zest HTTP server and we passed two options: handler is a reference to our JSGI application and port is the local port where the server will listen for connections. Finally, we called the start method of the server object to start the HTTP server.

RingoJS

RingoJS (http://ringojs.org) is the successor to the Helma project, which came into being in 1998. As such, it's one of the older server-side JavaScript projects around, and is representative of just how far server-side JavaScript has come.

RingoJS is written in Java and is based on the Mozilla Rhino engine, which is also written in Java. This CommonJS implementation is meant to be a full stack implementation, and it has one of the more extensive core libraries available. Like Flusspferd, RingoJS's web application module is fully JSGI-compliant.

Ringo uses Jetty—a popular Java HTTP server implementation—for its web application stack. The Jetty parts of RingoJS, though, are abstracted into the HTTP server module of the framework. Rewriting our previous example, we get this:

// the application
var app = function(env){
    return {
        status: 200,
        headers: {
            'content-type': 'text/plain',
            'content-length': 12
        },
        body: ['Hello World!']
    };
};

// http server module
var Server = require('ringo/httpserver').Server;

// create a server
var server = new Server({
    app: app,
    port: 8081
});

server.start();

The first part of this snippet is similar to our previous one. The main changes are with the module import statement, which now imports the Ringo HTTPServer module. This module exports a Server constructor that can be used to create a new HTTP server object. We used this to create a new server instance, passing in two options similar to those in Zest, with the exception that the key for the JSGI application is app instead of handler. We then start the server using the start method, just like with Zest.

NodeJS

NodeJS (http://nodejs.org)—or simply Node—was introduced to the world by Ryan Dahl in early 2009 and has since gained the distinction of being the most popular CommonJS engine today. It is written largely in C and C++, and uses the Google v8 JavaScript engine, also written in C++.

Node's main difference from other CommonJS engines is its event-based nature. Like a regular browser environment, Node employs an event loop that enables it to have asynchronous features that depend on event loops and JavaScript timers.

Because of this different paradigm, Node doesn't support JSGI by default, but instead uses a different model that can support asynchronous event-based code. Like in JSGI, a basic Node application is a function. However, it does not receive a single environment argument but instead receives two special objects called the request and the response objects.

The request object is similar to the JSGI environment object, and it has properties and methods that can be used to determine the nature of the client request. The response object is a unique object that corresponds to the response object from JSGI—but isn't exactly like it. The main difference is that you can't recreate a response object like you do in JSGI; instead, you have to reuse the same object passed by the node server. You also don't need to return this response object in your application, since Node automatically tracks and reads this object to retrieve the response.

Here's an example Node application:

var app = function(request, response){
    response.writeHead(200, {
        'content-type': 'text/plain',
        'content-length': 12
    });
    response.write('Hello World!'),
    response.end();
};

Unlike the JSGI version of this same application, this Node example uses methods to output the response details. The writeHead method is used to prepare the status code and the headers of the response, while the write method is used to send chunks of data to the client. Finally, the end method is used to signal that the response is finished, and that the requesting client should be informed that no more data will be sent.

Like Flusspferd and RingoJS, Node uses its own HTTP server implementation for its web application stack. Node's HTTP module is used to create servers, and here's an example of a basic one:

var app = function(request, response){
    response.writeHead(200, {
        'content-type': 'text/plain',
        'content-length': 12
    });
    response.write('Hello World!'),
    response.end();
};

var http = require('http'),

var server = http.createServer(app);
server.listen(8081);

Here we used the createServer function to create a new HTTP Server object, passing in the application function. We then used the listen method of the server to start it, passing in the number of the port where the server will listen.

v8cgi

Of all the CommonJS engines we've seen so far, v8cgi (http://code.google.com/p/v8cgi/) is probably the most unique. First released by Ondrej Zara in the middle of 2009, v8cgi was originally meant to be a way to run JavaScript applications via a module for the Apache HTTP Server. Like Node, it is written in C++ and also uses the Google v8 JavaScript engine.

Because of its close ties with the Apache HTTP Server, v8cgi's paradigm is much more like PHP than the other CommonJS implementations. Scripts that run on the v8cgi stack are simply uploaded to the server and are handled directly by Apache, just like PHP. Thus, v8cgi does not require setting up an HTTP server in the application itself, since the HTTP part is automatically handled by Apache.

Applications written in v8cgi do not need to be functions, and are often written in a very linear fashion. Inside these scripts, the application handles requests and responses through the global request and response objects. These two objects are similar to the Node objects of the same name, but are automatically created and are globally available throughout the application.

Here's an example of our Hello World! application for v8cgi:

response.status(200);
response.header({
    'content-type': 'text/plain',
    'content-length': 12
});
response.write('Hello World!'),

You'll notice that this script is the simplest application example we've seen so far. This snippet goes in a simple file that is then uploaded to a v8cgi-enabled server. The resource will then have to be accessed as a computed resource, and will yield the same response as all our other examples.

A Common Deck

One thing you've probably noticed by now is that there's no single cross-compatible solution for CommonJS application development. As we've seen, not all CommonJS implementations agree on how to do things, and each one provides its own APIs for handling the work of application development.

This is not a big problem if we're going to target only a single platform. Unlike with browser-based development, we have the freedom to choose the implementation we want to support, without having to worry about our application not being able to run on other platforms. We're no longer bound to external constraints like the browser preference of our target users or the different bugs in the implementations of these browsers.

There is, however, the question of risk. The CommonJS effort is very young and the various engines available today are mostly still under development. Targeting a particular engine is inherently risky, since you're locking yourself into a very particular set of APIs. This is very disconcerting once you take into account the "bus factor" of some CommonJS projects: some of these projects need only one developer to get hit by a bus to send the whole project into disarray (a bus factor of one).

Also worth considering are projects that are meant to be cross-platform. If you're building open-source discussion software or a blog engine, for example, you'll probably want to be able to deploy the end product to as many engines as possible. You therefore have to take into account the different APIs involved, and write your application so it can use all those APIs.

In fact, there are a whole lot of reasons why a common application development model would be useful, aside from those we already discussed. While the choices are no longer bound by our end users, they are still bound by external factors.

Enter Deck

In early 2009, Jabis Sevon and I decided to create a new View-Controller application framework for the v8cgi engine called Raccoon. After our initial development period, we successfully deployed a set of Raccoon applications on our server, which prompted the first documented use of the MooTools framework for server-side development.

During this time, however, the newer CommonJS engines like Node and Flusspferd started gaining more traction, and we decided that it would be great to be able to deploy Raccoon applications on different engines aside from v8cgi. To do this, though, we had to rewrite the internals of the Raccoon framework to be able to take into account the various application models used in these implementations.

This was bound to complicate the Raccoon stack, so we decided to do something different: instead of rewriting Raccoon to be able to understand all these APIs, why not simply write a lower level framework that Raccoon—or any other framework—can use. Thus, DeckJS was born.

DeckJS—or simply Deck—is a cross-platform server abstraction layer for CommonJS. It was created to provide a simple way to allow application and framework developers to build cross-implementation server-side JavaScript applications without having to worry about implementation-specific details. Deck is written using the MooTools framework, and exposes the MooTools framework to the application as well.

Getting Decked

You can download Deck from the official website at http://deckjs.com/, and use the instructions on the site to install Deck on your system. There are a few ways to use Deck, but we recommend using it together with Meso, which you saw in the previous chapter. For simplicity, we'll assume you're running the following example using Meso.

var app = function(request, response){
    response.setStatus(200);
    response.setHeaders({
        'content-type': 'text/plain',
        'content-length': 12
    });
    response.write('Hello World!'),
    response.finish();
};

var Deck = require('deck').Deck;

var deck = new Deck({
    app: app
});

deck.serve({port: 8000});

When we run this program and load http://localhost:8000, we're greeted with the 'Hello World!' message as with the other implementations.

You'll notice that the code has elements from all the APIs we've seen so far. Like the previous APIs, the application is simply a regular JavaScript function. We create a new instance using the Deck constructor, passing in an options object with the app property to define our application. We then start the application using the serve method, passing in an options object that defines the port to listen to. Like in Node, application functions in Deck receive two arguments: request and response. These objects represent the actual HTTP request and response messages, and they're used to determine the nature of the request and set the response appropriately.

It might not seem like a big deal right now, since we can pretty much do the same thing in other engines—and for the most part, we've proven that already. However, this Deck example is unique because it runs on all the engines we've seen so far: the same code can be used whether we're using Flusspferd, RingoJS, NodeJS, or v8cgi—and those are only the engines that have adaptors so far! This means that with proper adaptors, we can run this same example anywhere.

It's also interesting because underneath this single API, Deck uses the same per-engine APIs we've seen above. This means that Deck is using Zest when it's running on Flusspferd, or http.createServer when running on Node. However, we don't notice this because everything is done transparently.

Routing

Of course, Deck isn't limited to just abstracting low-level APIs. Aside from providing an API that works on multiple engines, Deck also has additional features that make writing applications and frameworks easier.

One of these features is routing. In creating applications, it's rare that we use a single request URL. Each point of the application is usually accessed using a different URL, so applications must be able to handle each URL differently.

Imagine we're creating an application that changes its behavior depending on the requested path. If our users access the /hello path, the application will respond with Hello World!, while if they request the /hi path, it'll respond with Hi Universe!. To achieve this, we can rewrite our previous app function to look like this:

var app = function(request, response){
    response.setStatus(200);
    response.setHeaders({
        'content-type': 'text/plain',
        'content-length': 12
    });

    if (request.pathInfo == '/hello'){
        response.write('Hello World!'),
    } else if (request.pathInfo == '/hi'){
        response.write('Hi Universe!'),
    }

    response.finish();
};

var Deck = require('deck').Deck;

var deck = new Deck({
    app: app
});

deck.serve({port: 8000});

Here we add an if-else statement to our app function to check the pathInfo property of the current request. We then serve the appropriate response body according to the requested path: 'Hello World!' for /hello and 'Hi Universe!' for /hi, which you can check for yourself by running this example and going to http://localhost:8000/hello or http://localhost:8000/hi.

While this style works for simple examples like the one above, it becomes hard to use once we start working with more complex applications. As our applications become bigger, the internal logic for each part of our application increases: our /hi path might require some database access, while /hello might have to write some files. If we're going to use a single application function for all our paths, our code will very quickly become unmanageable.

To make our code easier to work with, we need to split up our monolithic application function. Deck makes this easy by allowing you to have multiple application functions using routes. A route is a special rule that's checked against the request object. If the request satisfies the rule, the application associated with that route is served. If it doesn't, Deck will continue comparing the request object against other routes until it finds one that matches.

We use the addRoute method of a Deck instance to add new routes. It takes two main arguments: matcher, which is used to match the pathInfo property of the request, and app, which is the application function to serve.

var appHello = function(request, response){
    response.setStatus(200);
    response.setHeaders({
        'content-type': 'text/plain',
        'content-length': 12
    });
    response.write('Hello World!'),
    response.finish();
};

var appHi = function(request, response){
    response.setStatus(200);
    response.setHeaders({
        'content-type': 'text/plain',
        'content-length': 12
    });
    response.write('Hi Universe!'),
    response.finish();
};

var Deck = require('deck').Deck;

var deck = new Deck();

deck.addRoute('/hello', appHello);
deck.addRoute('/hi', appHi);

deck.serve({port: 8000});

Here we split up the original app function into two separate functions: appHello and appHi. We also removed the app option object from the Deck instantiation code, which tells the Deck class that we want to use multiple routes. Finally, we added two routes using the addRoute method, one for the appHello function and another for appHi.

Running this example, we'll get the same behavior as with the previous one: 'Hello World!' for http://localhost:8000/hello and 'Hi Universe!' for http://localhost:8000/hi. What happens is that when we request these URLs, Deck automatically compares the path of the requested URL to the defined routes. If the path is /hello, it serves the appHello function, and if it's /hi, it serves appHi.

Middleware using Modules

I mentioned earlier that in the JSGI model, application functions can be chained together to form more complex applications, and these decorator functions are called middleware because they sit in between the application and the server implementation. Deck also allows for middleware, which in Deck terms are called modules.

Like in the JSGI model, Deck modules lie between the application and the server implementation. However, modules and application functions aren't chained using function decoration. Instead, Deck implements a stack-based model for using modules.

In this model, the modules and the application are placed in an array, with the application function always sitting in the middle. Deck will call each of the functions one after the other, passing in the request and response objects to each one. The chaining therefore occurs inside the Deck instance, and not through explicit decoration on the part of the user.

Also unlike the JSGI model, Deck modules are actually objects and not functions. The functions that are invoked from each module come from handlers, which are methods of the module objects.

var module = {

    preHandler: function(request, response){
        request.next();
    },

    postHandler: function(request, response){
        request.next();
    }

};

Here we have a simple Deck module object, with the two handler methods. The preHandler method of the module object is added to the front of the application, while the postHandler method is added at the back. This means that Deck modules can be invoked before, after, or before and after the application function, depending on the handlers they define.

Adding a module to a Deck instance is simply a matter of calling the addModule method:

var app = function(request, response){
    response.setStatus(200);
    response.setHeaders({
        'content-type': 'text/plain',
        'content-length': 12
    });
    response.write('Hello World!'),
    response.next();
};

var module = {

    preHandler: function(request, response){
        request.next();
    },

    postHandler: function(request, response){
        request.next();
    }

};

var Deck = require('deck').Deck;

var deck = new Deck({
    app: app
});

deck.addModule(module);

deck.serve({port: 8000});

Deck Internals

Deck is, in essence, an abstraction layer that sits on top of the implementation-specific HTTP server APIs we've seen. Deck provides a layer of abstraction that enables us to use the same API for any application model the engine uses. Because the underlying implementation-specific APIs are abstracted by Deck, we are able to create projects that will run on any supported CommonJS implementation without changes, removing any platform-specific lockdowns.

At the center of Deck is a single class called Base, which is exported as Deck. This class is a unifier class that implements all other minor classes using mixins. There are about half a dozen classes that make up Deck itself, each of them controlling a different aspect of the system, and we'll look at the most important ones in turn.

Request and Response

The bulk of Deck's internals focus on the creation and manipulation of Request and Response objects. For every request received by the underlying HTTP server, Deck creates an object representation of the request and a corresponding response object. These objects are passed to the application, which then processes and manipulates them before passing them back to the Deck instance to be sent to the originator of the request.

The Request object contains several properties that describe the request, including the HTTP method, the requested resource, the user-agent, and other headers. It also has several utility methods that can be used to modify its properties, add additional headers, or change the requested resource. The Response object represents the response to be sent back and has methods to set the HTTP status of the response, add headers, and modify the response body.

The Request and Response objects are always created as pairs: both are created when a request is received and both are passed to the application for access and modification. Unlike the existing CommonJS JSGI standard that expects the application to create and return its own Response object, it is the work of the Deck instance to create the Response object that the application modifies.

The Filtered Model

Deck implements a filtered model for server requests and responses. In a simple model, a request is made to the server-side application by the browser and the application then sends a corresponding response. In Deck's filtered model, several "filters" sit before and after the application and they modify the request or response in order to change behavior.

The filtered model can be viewed like a deck of cards (from which Deck gets its name): the application sits in the middle of the deck and several modules are placed on top and on the bottom of the application. The Request and Response objects are then passed to each of these items in turn, starting from the topmost module and ending at the bottom module.

With this model, complex applications can be simplified by separating common tasks into several modules. An example might be a Rewrite Module, which could modify the Request object's pathInfo property before it reaches the application, thereby changing the request. The application would then be able to serve the appropriate response without having to do further checks on the Request object. Another example might be a Logger Module that logs every Request and its corresponding Response. Instead of having to do this within the application, you could simply add the module to the Deck instance.

Deck uses a double-ended queue (or a deque, pronounced "deck") to keep track of the application and the modules. In the middle of the deque is the application and the modules are added either before or after so that the application always remains in the middle. The addition of modules also follows the deque's two ends: the preHandler method of a module is always added in the front of the deque and the postHandler is always added at the end of the deque.

Dispatching

After creating the Request and Response objects, the Deck instance "dispatches" the modules and the application by invoking the first function in the deque, passing the Request and Response objects as arguments. The Deck instance waits until it is signaled to move on to the next item in the deque, and then repeats the process until all items have been dispatched before sending the response back.

The signal to the Deck instance to move to the next item is done by the modules and the application themselves. The Request object has a special method called next that's called by the modules and the application within their function bodies to tell the Deck instance to move to the next item. All Modules and Applications are required to call this method; otherwise, the Deck instance won't be able to move to the next item in the deque and the response will timeout.

Additionally, the Response object has a special method called finish that tells the Deck instance that the response has been finalized. Calling this method will stop the dispatching of the next items on the deque and return the response immediately.

Because the dispatching of items is done using an explicit signal, Deck is able to support asynchronous applications natively.

The Router

Deck includes a built-in router that dynamically changes the internal module and application deque before dispatching. Multiple routes can be set for a Deck instance, describing the rules and the corresponding application function to dispatch for a specific request.

The base of the routing rule is a string or a regular expression describing the request's requested path and the HTTP request method. After the Deck instance creates the Request and Response objects, it checks the Request object's pathInfo property and HTTP request method to see whether it matches any of the routing rules set for the Deck instance. If it does, it builds a new deque with the application function described in the route rule before dispatching the deque.

Additionally, a constraint object can be passed when setting a new route that will be used to check other rules before dispatch. The constraint object is a simple JavaScript object with keys corresponding to the Request objects properties and values that can be strings, regular expressions, or functions that will be used to check the info against.

The Wrap-Up

In this chapter we learned about JavaScript on the server side, and how different CommonJS engines implement their application server APIs. We also learned about Deck, a server-side framework for application development that's written on top of MooTools.

Deck itself is a very complex framework, and it's unfortunate that it will take at least another book to discuss it in full. It is a young project, but it does offer a glimpse of how complicated JavaScript applications and frameworks can be written on top of MooTools.

If you're hungry for more information about Deck, I suggest you visit the official site at http://deckjs.com. You'll find a more extensive coverage of both the API and the framework itself on the website, and I personally invite you to try it out and contribute to the next iterations of the framework.

The Wrap-Up to Conquer All Wrap-Ups

Whew! It's been quite a ride, hasn't it? We've covered a lot, rested at a few pitstops and learned a trick or two from roadblocks. We've explored the nooks and crannies of JavaScript and the internals of MooTools, and we've seen things that will gladly go to our development arsenal.

My hope is that you've learned a thing or two from this book. We can't cover everything in a single volume, but at least now you have enough material to start your own experimentation. Remember that learning a language doesn't end with reading a single book. It takes experimentation, more reading and lots of application in order to really become a great coder.

So fire up your text editor, get your fingers moving and start exploring!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset