Skip to content

Instantly share code, notes, and snippets.

@cawel
Forked from bennadel/code-1.js
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cawel/f9107ff54a8985b624ff to your computer and use it in GitHub Desktop.
Save cawel/f9107ff54a8985b624ff to your computer and use it in GitHub Desktop.
(function(){
// Define your class.
return( Singleton || Constructor );
})();
// Define our module.
(function(){
// Counter variable.
var counter = 0;
// Return public API.
return({
countUp: function(){
return( ++counter );
}
});
})();
// Define our module.
// Counter variable.
var counter = 0;
// Export our public API.
exports.countUp = function(){
return( ++counter );
};
// Include the necessary modules.
var sys = require( "sys" );
var http = require( "http" );
// Include the Controller layer.
var controller = require( "./controller.js" );
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
// Create an instance of the HTTP server.
var server = http.createServer(
function( request, response ){
// Our controller is an instance of the Event Emitter. We
// will be using this to listen to controller events. We
// could have handled this with Success and Fail callbacks;
// but, I wanted an excuse to experiment with the Event
// stuff as it appears to be quite central to Node.js.
// Bind to the "Data" event. This is the event that gets
// emitted when the controller has data to return.
controller.on(
"data",
function( data ){
// Set the 200-OK header.
response.writeHead(
200,
{ "Content-Type": "text/plain" }
);
// Return the response from the API request.
response.write( JSON.stringify( data ) );
// Close the request.
response.end();
}
);
// Bind to the "Error" event. This is the event that gets
// triggered when a success response cannot be returned. This
// will either be because an error occurred; or, because the
// record request by the API cannot be found.
//
// NOTE: For our simple purposes, we are going to assume that
// the errorType is simply an HTTP Status Code. This will
// keep our logic very simple for this exploration.
controller.on(
"error",
function( errorType ){
// Set the error header (is really just an HTTP
// status for our purposes).
response.writeHead(
errorType,
{ "Content-Type": "text/plain" }
);
// Close the request.
response.end();
}
);
// Pass the request off to the controller.
controller.handle( request, response );
}
);
// Point the server to listen to the given port for incoming
// requests.
server.listen( 8080 );
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
// Write debugging information to the console to indicate that
// the server has been configured and is up and running.
sys.puts( "Server is running on 8080" );
// Include the event emitter class - the controller is a specialized
// instance of the event emitter and will emit the events:
//
// - data / response
// - error / errorType (HTTP Status code)
var EventEmitter = require( "events" ).EventEmitter;
// Include the Girl Service layer.
var girlService = require( "./girl-service.js" );
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
// Create an instance of our event emitter.
var controller = new EventEmitter();
// Add a handle method to the event emitter instance (controller)
// so that the HTTP server will be able to pass the request off for
// proper routing.
controller.handle = function( request, response ){
// We are going to be looking at urls for RESTful commands.
// These will be in the form of:
//
// NOTE: I am using the term RESTful in the loosest sense.
// Really, this is just easy for illustration purposes.
//
// girls/get
// girls/{id}/get
// girls/{id}/delete
// girls/add/{name}
// Define our patterns.
var patterns = {
getAll: new RegExp( "girls/get", "i" ),
getGirl: new RegExp( "girls/(\\d+)/get", "i" ),
deleteGirl: new RegExp( "girls/(\\d+)/delete", "i" ),
addGirl: new RegExp( "girls/add/([^/]+)", "i" )
};
// Strip off the leading and trailing slashes.
var restUri = request.url.replace(
new RegExp( "^/|/$", "g" ),
""
);
// Loop over the patterns to see if any match.
for (var patternKey in patterns){
// Try to match the pattern against the URL.
if ( match = restUri.match( patterns[ patternKey ] ) ){
// Pass the request off to the service layer. Since
// the service layer is performing asynchronous I/O
// (theoretically), we need to pass it a callback so
// that the service layer can alert us to data events
// when they are available.
// Build the arguments. Our last argument will always
// be a callback for our asynchronous API. In this case,
// the callback will be expecting an API response for a
// successful call; OR, a null response for a record that
// could not be found.
var apiArguments = [function( apiResponse ){
// Check to see if we have a valid API response.
if (apiResponse){
// The API request was successful - announce
// the data event.
controller.emit( "data", apiResponse );
} else {
// The API request was not successful - announce
// the error event.
controller.emit( "error", "404" );
}
}];
// If there is a captured group in the regex pattern
// that we used above, add it as the first argument to
// our collection of service-layer invocation arguments.
if (match.length > 1){
// Prepend the captured group (an ID) to the list
// of arguments used to invoke the service layer.
apiArguments.unshift( match[ 1 ] );
}
// Invoke the service layer (remember, the last argument
// of our invocation array is always the callback for
// asynchronous I/O).
girlService[ patternKey ].apply(
girlService,
apiArguments
);
// The RESTful URL can only match one pattern.
// Since we found a match, return out of the request
// handler as there is nothing more we can do here
// until the data-callback is triggered.
return;
}
}
// If we have made it this far, then the incoming request did
// not match up with any known API signature. As such, we will
// announce (emit) a server error.
controller.emit( "error", "500" );
};
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
// Expose the controller / event emitter. Since we are exposing
// the whole object, rather than just an API interface, we are
// redefining the entire exports value.
module.exports = controller;
// Create our collection of girls. This collection is keyed by the
// ID of the girl.
var girls = {};
// Keep a running auto-incrementer.
var primaryKey = 0;
// ---------------------------------------------------------- //
// ---------------------------------------------------------- //
// Now, we have to define the external, public API of the module.
// Each of the methods in this API gets "exported" as part of the
// exposed object.
// All of these methods deal with data READS and WRITES. In Node,
// this kind of I/O is supposed to be asynchronous; that is, a
// method that reads or writes to storage can't return a value
// directly (as it is non-blocking... for the most part). Therefore,
// we have to pass callbacks to all of our IO methods so that our
// calling context can be *alerted* to data events.
// I am creating a noop function so I can simplify the logic
// surrounding my callbacks. With a noop() (read as No-Op), I can
// always have a reference to *a* callback.
var noop = function(){};
// I add a girl to the collection.
exports.addGirl = function( name, callback ){
// Make sure a callback is defined.
callback = (callback || noop);
// Create the new girl instance.
var girl = {
id: ++primaryKey,
name: name
};
// Add it to the collection.
girls[ girl.id ] = girl;
// Pass the girl to the callback (the calling context).
callback( girl );
// Return this object reference to allow for method chaining.
return( this );
};
// I delete the girl with the given ID.
exports.deleteGirl = function( id, callback ){
// Make sure a callback is defined.
callback = (callback || noop);
// Get the girl.
var girl = (girls[ id ] || null);
// If the girl exists, delete her.
if (girl){
delete girls[ girl.id ];
}
// Pass the girl to the callback (the calling context).
callback( girl );
// Return this object reference to allow for method chaining.
return( this );
};
// I return the girl with the given id.
exports.getGirl = function( id, callback ){
// Make sure a callback is defined.
callback = (callback || noop);
// Pass the girl to the callback (the calling context).
callback( girls[ id ] || null );
// Return this object reference to allow for method chaining.
return( this );
};
// I get all the girls.
exports.getAll = function( callback ){
// Make sure a callback is defined.
callback = (callback || noop);
// Create a holder for our ordered collection.
var orderedGirls = [];
// Loop over the primary keys to build up the collection
// of ordered girls.
for ( var i = 1 ; i <= primaryKey ; i++ ){
// Check to see if a girl exists at this key.
if (girls[ i ]){
// Add this girl to the result in order.
orderedGirls.push( girls[ i ] );
}
}
// Pass the collection to the callback (the calling context).
callback( orderedGirls );
// Return this object reference to allow for method chaining.
return( this );
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment