Build systems style computational model for the web
This project is deprecated. Use easy-app instead.
Can you see what's wrong with this code?
var db = require'db'var user = require'user'var res = require'response'dbcollection'users'findid: $in: userfriendsif err return doneerrresjsondocs
Actually it is a very neat piece of code except that there is no way to require
response with node.js require system.
This project is about making the above snippet fully functional and it seems it
succeeded. It completely removes artificial boundary between static, boot-time,
request-time, whatever-time dependencies allowing you super easily define as
many layers as you want while completely freeing modules from dependency issues.
This project was inspired by The-Kiln
and is nothing but an implementation of
dependency based computational model :)
similar to what is used by build systems (like rake, make) and by node.js
var express = require'express'var App = require'the-box'var app =appset'connection-string' 'localhost/mydb,192.168.1.1'appdef'db'return require'monk'get'connection-string'appdef'user'var req = get'request'get'db'collection'users'findOneid: reqparam'user'doneappdef'list-friends'var db = get'db'var user = get'user'var res = get'response'dbcollection'users'findid: $in: userfriendsif err return doneerrresjsondocsappdef'init' 'db'expressuseappeval'init'Objectcreateappset'request' reqset'response' resonerrornexteval'list-friends'listen3000
app.def() method defines what is internally called a box, aka task (in
rake), aka module (in node).
Once the box was defined we can evaluate it.
appeval'db'// use db instance here
A value of evaluated box is cached, so subsequent evals do not result in repeated calls to the definition function.
Another way to define box is to use
We can get a value of evaluated box with
app.get(). It returns
box doesn't exist or not yet evaluated.
Boxes themselves also can get values:
and framework ensures that before evaluation of the box all its dependencies were evaluated. There are two ways to specify dependencies
// list them explicitlyappdef'foo' 'bar' 'baz'return get'bar' + get'baz'// or allow to infer them from the function sourceappdef'foo'return get'bar' + get'baz' // it's evident that we depend on bar and baz
Of course, boxes can be asynchronous
appdef'user'var req = get'request'get'db'collection'users'findOneid: reqparam'user'done
Perhaps the most strongest point of
the-box is how it manages dependency
levels. You can just create a new app instance with
this instance will inherit all box definitions and every evaluated box will
remain evaluated while subsequent manipulations (evals, defs, etc) with new
instance will not change the parent app.
appeval'init'Objectcreateapp // we have app.run() for this, but Object.create is what going onset'request' reqset'response' reseval'something'
All boxes are evaluated sequentially.
There is a concept of path
appat'a/b'def'./c' fnset'../d' val// is the same asappdef'a/b/c' fnset'a/d' val
this of the box is set to
appdef'a/b'if something thiseval'./c' // eval a/b/c if something
Errors from boxes (both sync and async) are catched and can be handled.
appdef'foo/bar/baz'throw 'foo error'apponerror'foo/bar/baz'errmessageshouldequal'foo error'
Errors are bubbling. So if the handler for
foo/bar/baz wouldn't be defined the
foo/bar would be checked and so on up to the root level handler
app.onerror(fn)) which by default just throws a given error. It is possible
to rethrow catched errors
appdef'foo/bar/baz'throw 'error'apponerror'foo/bar/baz'errshouldequal'error'throw 'baz'apponerror'foo/bar'errshouldequal'baz'raise'bar'// for convenience raise function can be used as a node style callback// raise(null, 'bar') will not throwapponerror'foo'errshouldequal'bar'apponerrorshouldfail"shouldn't be called since we are not rethrowing in foo handler"appeval'foo/bar/baz'
It is possible to define
after hooks for any box.
are ordinal boxes which are executed before "main" box and it's dependencies.
After hooks are also boxes (they can have dependencies, etc) but their signature
is slightly different.
appdef'foo'return 'foo'appafter'foo'valshouldequal'foo'// we can change the result value of the box by returning// something different from undefinedreturn 'bar'appeval'foo'valshouldequal'bar'
An async version of after hook has
(get, val, done) signature.
It is not important when to define hooks. They can be defined before or after corresponding boxes.
// Example: Automatically parse the request bodyvar bodyParser = expressbodyParserappafter'request'bodyParserreq reqres nextapprunset'request' reqdef'task'get'request'shouldhaveproperty'body'eval'task'
npm install the-box
To run tests first install dev dependencies and then run npm test command.
npm install -dnpm test
express-in-the-box project is an integration of awesome express request-response prototypes and router with the-box container.
(The MIT License)
Copyright (c) 2012 Eldar Gabdullin email@example.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.