Saturday, August 24, 2013

Using classical inheritance to contain express controllers and socket events

Coffeescript has +1'ed itself for me lately.  I will tell you why and try to explain.  As far as I know, there is no native support in coffeescript for having a class extend multiple parents classes.  In plain javascript, scope and prototypal inheritance can be confusing, as you'll see later on.

Lets imagine we are taking our express web application and isolating out the app routes and the socket events.  The reason for this is simple:  when a Web class extends the controller and event classes, they can share important pieces of information related to the web server such as a user session or login information.  This will help us later on when implementing socket events for authorized users.

Here is the entire bit of code to get you going.  It is simple, elegant, and for the most part completely readable as long as you have a bit of understanding in regards to the way prototypes in javascript work.

moduleKeywords = ['included', 'extended']
class SuperClass
@include: (obj) ->
throw('include(obj) requires obj') unless obj
for key, value of obj.prototype when key not in moduleKeywords
@::[key] = value
included = obj.included
included.apply(this) if included
@
class Controller
constructor: ()->
@hasController = true
class Events
constructor: ()->
@hasEvents = true
class Web extends SuperClass
@include Controller
@include Events
constructor: ()->
Controller.call @
Events.call @
module.exports = Web



Here is the compiled, fully javascript compliant version.  As you can see, its a bit messy and definitely not as readable as the coffeescript version.

(function() {
var Controller, Events, SuperClass, Web, moduleKeywords,
__indexOf = [].indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item)
return i;
}
return -1;
},
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key))
child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
moduleKeywords = ['included', 'extended'];
SuperClass = (function() {
function SuperClass() {}
SuperClass.include = function(obj) {
var included, key, value, _ref;
if (!obj) {
throw 'include(obj) requires obj';
}
_ref = obj.prototype;
for (key in _ref) {
value = _ref[key];
if (__indexOf.call(moduleKeywords, key) < 0) {
this.prototype[key] = value;
}
}
included = obj.included;
if (included) {
included.apply(this);
}
return this;
};
return SuperClass;
})();
Controller = (function() {
function Controller() {
this.hasController = true;
}
return Controller;
})();
Events = (function() {
function Events() {
this.hasEvents = true;
}
Events.prototype.getEvents = function() {
return [];
};
return Events;
})();
Web = (function(_super) {
__extends(Web, _super);
Web.include(Controller);
Web.include(Events);
function Web() {
Controller.call(this);
Events.call(this);
}
return Web;
})(SuperClass);
module.exports = Web;
}).call(this);


Happy coding.

Monday, August 19, 2013

Custom Nagios Notifications, now with Jade

Recently, I posted about using coffee-script and node-mailer to send custom Nagios notification emails.  While it is a great way of extending the capabilities of Nagios Alerts by allowing any arbitrary hook into other coffee / javascript apis (such as node-mailer), it is also capable of creating pretty HTML e-mails with the help of the Jade template engine.

Since I've already created the Nagios Command and bound it to my contact information, all I need to do is install jade into the project folder's node_modules:

$ npm install jade

We then need to create our templates.  Since I wanted to use a layout, I created a jade sub-folder in my projects folder.

Here is the layout (./jade/layout.jade):
doctype 5
html
head
link(rel='stylesheet', href='http://domain.com/components/bootstrap/docs/assets/css/bootstrap.css')
link(rel='stylesheet', href='http://domain.com/components/bootstrap/docs/assets/css/bootstrap-responsive.css')
link(rel='stylesheet', href='http://domain.com/components/font-awesome/css/font-awesome.min.css')
script(type="text/javascript", src="http://domain.com/components/jquery/jquery.min.js")
script(type="text/javascript", src="http://domain.com/components/bootstrap/docs/assets/js/bootstrap.js")
body
block content
view raw layout.jade hosted with ❤ by GitHub

Here is a neat header bar that I'm using(./jade/nav-bar-email.jade):
.container-fluid
.navbar
.navbar-inner
a.brand(href="domain.com/") Company
ul.nav
li
a(href="domain.com/") Home
li
a(href="domain.com/projects") Projects
li
a(href="domain.com/about") About
ul.nav.pull-right
li
a(href="domain.com/docs") Docs

Here is the alert e-mail(./jade/nagiosAlert.jade):
extends layout.jade
block content
include nav-bar-email
.container-fluid
.row-fluid
.span12
.well
i.icon-warning-sign.icon-4x.pull-right
b
p.lead Nagios Alert for #{hostname}
hr
.container-fluid
.row-fluid
.span3
label.label Hostname:
.span9
span #{hostname}
if service
.row-fluid
.span3
label.label service:
.span9
span #{service}
.row-fluid
.span3
label.label ip:
.span9
span #{ip}
.row-fluid
.span3
label.label notify:
.span9
span #{notify_type}
.row-fluid
.span3
label.label status:
.span9
span #{status}
.row-fluid
.span3
label.label date:
.span9
span #{date}
.row-fluid
.span3
label.label output:
.span9
span #{output}

Here is what my notify-service.coffee script looks like(/opt/bin/notify-service.coffee):
#!/usr/bin/env coffee
#
# incoming argv is order sensitive
#
# [0] - coffee
# [1] - /opt/bin/notify-service.coffee
# [2] - hostname
# [3] - service
# [4] - IP address
# [5] - notification type [ PROBLEM, RECOVERY, OK ]
# [6] - status [ OK, WARNING, CRITICAL ]
# [7] - long date time
# [8] - service output
require 'coffee-script'
nodemailer = require 'nodemailer'
jade = require 'jade'
argvObject =
hostname: process.argv[2]
service: process.argv[3]
ip: process.argv[4]
notify_type: process.argv[5]
status: process.argv[6]
date: process.argv[7]
output: process.argv[8]
smtpTransport = nodemailer.createTransport "SMTP"
text = "Nagios requires your attention.\n
\n
Hostname:\t\t" + argvObject.hostname + "\n
service:\t\t" + argvObject.service + "\n
ip:\t\t" + argvObject.ip + "\n
notify:\t\t" + argvObject.notify_type + "\n
status:\t\t" + argvObject.status + "\n
date:\t\t" + argvObject.date + "\n
output:\t\t" + argvObject.output + "\n
"
jade.renderFile '/opt/bin/jade/nagiosAlert.jade', argvObject, (err, html)->
if err
console.log err
mailOptions =
from: "Nagios <admin@domain.com>"
to: "admin@domain.com"
subject: argvObject.hostname + "'s " + argvObject.service +
" IS " + argvObject.status
text: text
html: html
smtpTransport.sendMail mailOptions, (err, res) ->
if err
console.log err
process.exit()


The final result is a pretty looking E-Mail. I'm viewing this with Mail.app in OS X.  Ive blanked out a few bits.  Just imagine there is Company Text to the left of home and a link between home and projects in the nav-bar


























Sunday, August 18, 2013

Simple Callbacks - Executing parallel and dependent tasks using async without the sphagetti

There has been some bloggers complaining lately about the callback pattern and its spaghetti code structure.  Some even compare it to GOTO statements, although that post is less about coffee/javascript.  The beauty of javascript, especially written in coffee-script, is that you can conceptualize parallel and dependent tasks based on code indentation.  Commands executed in the same depth are executed at the same time, while code resting deeper (in the particular case of callback patterns) are deferred until dependent execution is complete.

The following image on the left depicts what I am explaining above.  Blue lines are executed at the same time.  Red lines are dependent on their blue parent's completion before they execute, and so on.

I then take the idea a step further and mix batches of parallel tasks with tasks dependent on the batch to complete.

This is a sample gist of personal work I am doing on a Project Management webApp. The goal of this gist is to show how non dependent tasks can be parallelized and dependent tasks can be run after those parallel dependencies are run. eachTask iterator takes a task object and a callback. It uses the Function prototype method call to pass scope to each parallel task.



_helper = require '_helper'
###
completeIssue is a great example of parallel scripting.
req
- project ObjectId
- milestone ObjectId
- issue ObjectId
Since we know all three ObjectIds related to an Issue from the req
object, we can parallelize a few of the tasks: findIssueAndClose, socket.emit response,
getMilestoneTallyCompletedIssues, getProject info, getIssueAttachments
each pushed task has a cb which runs along side the cb of async.each iterator. each task
cb adds a particular result to the scope object. When all iterators are complete, the parallel
tasks end and execute the last few callback statements: findAccount of the issue owner,
and sendIssueCompletedMail
###
completeIssue = (req)->
async = require 'async'
# Add other necessary references to this object
# Scope dependencies are hidden behind _helper classes
# to simplify this example
scope =
req : req
log = console.log
parallelTasks = []
parallelTasks.push
fn : _helper.findIssueAndClose
opts : { _id: req.issue }
cb : (err, issue)->
if err
log err
scope.issue = issue
parallelTasks.push
fn : _helper.getMilestoneTallyCompletedIssues
opts : {_id: req.milestone}
cb : (err, milestone)->
if err
log err
scope.milestone = milestone
parallelTasks.push
fn : _helper.getProject
opts : { _id: req.project }
cb : (err, project)->
if err
log err
scope.project = project
parallelTasks.push
fn : _helper.getIssueAttachments
opts : { issue: req.issue }
cb : (err, mailAttachments)->
if err
log err
scope.mailAttachments = mailAttachments
parallelTasks.push
fn : _helper.getComments
opts : { issue: req.issue }
cb : (err, comments)->
if err
log err
scope.comments = comments
eachTask = (task, cb)->
task.fn.call scope, task.opts, (err, res)->
task.cb(err, res)
cb()
async.each parallelTasks, eachTask, ()->
_helper.findAccount.call scope, { _id: scope.issue.owner }, (err, account)->
if err
log err
scope.account = account
if scope.project && scope.milestone && scope.issue
_helper.sendIssueCompletedMail.call scope, (err)->
if err
log err
return @
module.exports = completeIssue

Tuesday, August 6, 2013

Async HTML5 File Uploads with Node / Express Framework

File uploads with HTML5 these days is easy as pie, especially when you have a framework that handles everything for you, internally.  Combining Node, the Express framework and a little HTML5 magic with FormData and XMLHttpRequest, we can create a very simple file uploader that can support large files.

The Express framework supports multi-part uploads through its bodyParser middleware which utilizes the node-formidable module internally.



Start off with the client side.
<div class="progress" id="progress" style="width:100%">
<div class="bar bar-success" style="width:100%"><p id=progressDisplay>0%</p></div>
</div>
<form enctype="multipart/form-data" action="/mUpload" method="post" id="fileForm">
<input type="file" id="fileField">
<a class="btn btn-small btn-primary" id="addFile">Upload File</a>
</form>
view raw form.html hosted with ❤ by GitHub


Add the button click event with a bootstrap progress indicator
handler = ()->
document.getElementById("fileField").files
# Make sure a file is selected first
if files.length <= 0
alert('choose a file, first')
return
file = files[0]
fd = new FormData()
fd.append("fileForm", file)
xhr = new XMLHttpRequest()
# define our finish fn
loaded = ()->
alert('finished uploading')
$("#addFile").one "click", handler
xhr.addEventListener 'load', loaded, false
# add progress indicator
xhr.upload.onprogress = (e)->
percent = Math.round((e.loaded * 100) / e.total)
$("#progress").width "#{percent}%"
$("#progressDisplay").html "#{percent}%"
xhr.open "post", "/mUpload"
xhr.send fd
$(document).onReady ()->
$("#addFile").one "click", handler
view raw handler.coffee hosted with ❤ by GitHub




Add the server route.
mUpload = (req, res)->
file = req.files["fileForm"]
tmpPath = file.path
fileName = file.name
dest = __dirname + "/#{fileName}"
fs.readFile tmpPath, (err, data)->
fs.writeFile dest, data, (err)->
if err
console.log err
res.end({success: true})
module.exports = mUpload
view raw mUpload.coffee hosted with ❤ by GitHub