Thanks to visit codestin.com
Credit goes to github.com

Skip to content

A micro-web-framework for Node.js centered on imperative workflows

Notifications You must be signed in to change notification settings

lethain/workframe.js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

workframe.js

workframe.js is a nascient Node.js micro-framework, which really means its just a collection of simple utilities and ideas with the aim of making it easier to develop complicated applications on Node.js. In particular it aims to solve the problems of

  • workflow.js: managing deeply-nested callbacks
  • urlpattern.js: regular expression based url dispatch
  • forms.js: form validation

Although all of these utilities are intended to play well together, none of them share code or have any special awareness of each other; they are all completely decoupled.

workflow.js

workflow.js is the combination of a simple tool and a simple convention which together make it possible to write imperative workflows in Node.js, which makes it possible to describe, modify and maintain complicated workflows whose underlying implementation relies on callbacks.

Workflows are composed of one or more segments. Some example segments are sending an HTTP response to a client, rendering a Mustache template, or generating a new unique identifier in Redis.

Workflows are defined and started like this (usually the index function would be called by the urlpattern.js dispatcher, but there isn't any programmatic coupling whatsoever):

exports.index = function(req, res) {
    var ctx = {title:"Home"};
    workflow.run([[redis.ids, "user.projects"],
                  redis.mget,
                  [utils.render_template, "index"],
                  [utils.http_response, req, res]], 
                 ctx)};

When specifying segments you may either specify a function (as in the above example), or you may specify a list where the zeroth index contains a function and the remaining positions are parameters which will be passed as positional parameters to the specific component (this reduces reliance on injecting values into the workflow's context dictionary which is a very brittle mechanism for passing information, especially if a single segment is used multiple times in a workflow).

workflow.run([redis.mget, [utils.http_response, "index"]]);
workflow.run([[utils.http_redirect, "/"]]);

When you pass parameters this way, they always get injected as position parameters before the parameters passed by the preceeding function. (This is because usually you only specify parameters this way when the flow isn't really linear.)

Where each workflow segment is a function which looks like this:

exports.http_redirect = function(funs, ctx, location) {
    if (location === undefined) location = ctx.redirect;
    ctx.res.writeHead(303, {"Location":location});
    ctx.res.close();
    workflow.run(funs, ctx);
}

More examples at segments/. (Well, they will be there soon, anyway.)

Existing Workflow Segments

These will be coming over the next few days.

urlpattern.js

urlpattern.js is a utility for doing regular expression based url dispatching. It is modeled very closely off the Django file by the same name. A simple example (which assumes workframe.js is checked out at ../workframe.js relative to the my_project.js file).

urlpattern.js example

// my_project.js
var http = require("http"),
    views = require("./views"),
    urlpattern = require("../workframe.js/urlpattern");

urlpattern.patterns = [
    [/^\/project\/view\/([\w-_]+)\/$/, views.view_project],
    [/^\/$/, views.index]
];

http.createServer(function(req, res) {
    // if you aren't interested in capturing any
    // incoming data, then you can remove the data
    // variable and also the data listener and
    // use urlpattern.dispatch(req, res) instead
    var data = "";
    req.addListener('data', function(chunk) {
        data += chunk;
    }
    req.addListener('end', function() {
    	urlpattern.dispatch(req, res, data);
    });
}).listen(8000);

// views.js
// 'data' is optional parameter, well, all parameters in JS are
// optional, but usually you don't want it, especially if you
// remove the data listener in your HTTP server
exports.index = function(req, res, data) {
    res.writeHead(200, {'Content-Type': "text/html"});
    res.write("this is index.html");
    res.close();
}

// this could also be declared as
//     exports.view_project = function(req, res, project_name) {
exports.view_project = function(req, res, project_name, data) {
    res.writeHead(200, {'Content-Type': "text/html"});
    res.write("this is view_project.html for project with name "+project_name);
    res.close();
}

Fall-Through Response / Default Response

In addition to defining patterns for dispatch, it is also possible to override the default page (to throw a custom 404 page, etc) by updating the value of urlpattern.default_404.

A valid default_404 function will look like this:

urlpatterns.default_404 = function(req, res) {
    res.writeHead(404, {'Content-Type': 'text/html'});
    res.write('No matching pattern found');
    res.close();
}

forms.js

The forms stuff is really really young and experimental and will undoubtedly change as I keep working with it.

Relies on the forms.validate function which takes a dictionary of raw data (probably from querystring.parse, but could be from anywhere) and a form schema.

Form schemas are very simple, and look like this:

[{name:"title"}, {name:"age", type:"number", coerce:parseInt}]

coerce can be any function with an arity of one, and type can be any value returned by the typeof function.

forms.validate returns a dictionary with two values errors and data. data is the input data passed through any coerce functions and errors is a dictionary where the key is the name of the field with an error and the value is a specific error message.

About

A micro-web-framework for Node.js centered on imperative workflows

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published