Gliding is a web framework on nodejs, minimal (only 400 or more lines of code) and modular, extremely easy to be extended.
This is the first javascript program I write, I just start to learn javescript and nodejs, and want to try it on the server side programming. But soon I found the callback hell problem is really annoying, making your code more and more unreadable as you code along.
However, since you use nodejs, you could not escape that, that's the beautiful way that node deal with async IO, only with callback and event we need not to worry about those complex lock and sync, while in the same time making cpu and io working more parallel and gain more efficiency.
So after thinking quite a long time, I come up with a simple idea to try to make lives a little bit easier.
Also I have some tastes on angularjs, so I want it to look like it.
# Usage
-
Now provide easy "api" for
$form
and$cookie
, also fix bug on$scope.JSON
in this version. -
Also fix bugs in route.js, you don't necessarily have attribute
callback
on the factory and provider that register to use those non-blocking functions. This way will do fine, in the handler:
function($scope,$render ,$dbFactory){
$dbFactory.query($scope.username, function(data){
$scope.HTTP.Response.write($render(filename,json))
$scope.HTTP.Response.end();
};
}
But because by default the gliding would help you to write the HTTP.Response
based on the $scope.JSON
and $scope.HTML, $scope.TMPL
after the execution of handler chain. So if you use callback function on your own, then $scope.JSON
must be undefined
when the handler function return. The following code in route.js explains why:
if ($scope.HTML !== undefined) {
response.writeHead($scope.HTTP.status, $scope.HTTP.Head);
response.write(service['$template'].render($scope.HTML, $scope.JSON));
response.end();
} else if ($scope.TMPL !== undefined) {
response.writeHead($scope.HTTP.status, $scope.HTTP.Head);
response.write(service['$render'].render($scope.TMPL,
$scope.JSON
));
response.end();
} else if ($scope.JSON !== undefined) {
response.writeHead($scope.HTTP.status, $scope.HTTP.Head);
response.write(JSON.stringify($scope.JSON));
response.end();
}
Also, non-blocking functions inside the handler chain would mess up the order of execution, so the non-blocking functions must occur in the tail of the handler chain. The last thing added, if the $final
provider is defined when you have non-blocking in the handler chain, which will exectue at end of each handler execution chain, its behavior would be restricted because of the execution order.
Once you have node and npm installed properly, just use
npm install gliding
to install. _current version: 0.3.2
- register components as provider or factory
- register handler that use components as function arguments (deal with callback internally), and it looks like angularjs
function($scope, $form, $db, $utility){ // function that registered as handlers
//use $form.fields, $db.user, $utility(x,y,z)
}
- Run
var gliding = require('gliding');
var myServer = new gliding.Server(options);
myServer.Run();
var gliding = require('gliding');
var md = new gliding.Module();
md.provider.register('$fun', foo);
md.factory.register('dbFactory',obj);
md.handler.register('GET','/',[ function($scope,$fun,$form,$cookie,dbFactory){
}]);
exports.myModule = md;
run node index.js
in the shell, then it's running in the localhost:8080
.
How to use in details, please see Documentation.
Example could be found in Example
It works based a very simple idea. The following is the pseudo-code Process of this framework (just show the process, not strictly correct):
var allModules = collectModules(options.MODULES),
service = getService(allModules) //provider and factory are treated the same internally
handler = getHandler(allModules);
route.get(path, procedure(path ,service, handler))
function procedure(path, service, handler){
var certainFunChain = handler[path],
argHash = getArgument(certainFunChain).slice(1), //argHash = {fun: ['$form','$factory']} , slice(1) since every function has $scope as the first argument
arglist = [$scope];,
funQueue = copy(funChain),
argQueue = [],
options = getOptions(path); // get the option of this path
return function (request,response){ // the actually function passed in route.get
var $scope = {};
var $scope.HTTP = {Request:request,Response:response}; //pass data across handlers and services
var fall = function(service){
if (service != undefine)
arglist.push(service);
if (isEmpty(funQueue))
end();
if (isEmpty(argQueue)){ // get all the services
currentFun.apply(this,arglist);
arglist = [$scope]
argQueue = copy(argHash[dequeue(funQueue)]);
fall();
} else{
var arg = dequeue(argQueue),
nowService = service[arg]; //get the service that this string correspond
if (hasCallback(sev)){
nowService.callback($scope, options, fall);
}else{
argList.push(nowService);
fall();
}
}
}
//begins
fall();
}
}
## Explain
-
collect modules, and get all handlers and services that registered
-
for each path,
procedure
return a route function -
the key part in the route function is the
fall
, ideas are:- call service while passing
fall
itself as argument, so control could be obtained back to thefall
. - when completing IO, service call
fall
in the callback, while pass what the result intofall
,fall
save it into arglist - use closure a lot lot. (remember you could not change service, or options in the service's callback that you write)
- call service while passing
#### The following is about how we fall: Once it start to `fall`, it would `fall` into a service that you register which has `callback`, those don't have callback wouldn't be fall into but just:
argList.push(nowService);
but it would pass fall as argument to the callback, so in the callback, it would be call again, just like the previous example
form.parse(data.HTTP.Request, function(err, fields, files) {
fun({'fields': fields, 'files': files });
});
and then, we back to fall
, save this value to our list
if (sv !== undefined)
argList.push(sv);
once we fall
all the services that the arguments of this function correspond to, it would call this function with
currentFun.apply(this, argList);
and then refill the argQueue
, dequeue the funQueue
, and fall
again
argList = [$scope];
argQueue = copy(argHash[dequeue(funQueue)]);
fall();
util the end;
if (isEmpty(funQueue))
end();
# Current State
No, I think, it haven't been tested a lot yet, also it lacks a lot of other features maybe, so still a long way to go.
I am trying to apply to graduate school these days, and I have serveral projects and courses at the same time, so I'm very very busy. So gliding wou't be developed so actively like I did at the beginning three days. But I would still work on it, and will have more time on it when my other burning priorities pass.