Last active
August 7, 2016 18:29
-
-
Save fresc81/fc6a58b3a43f1c5120f8 to your computer and use it in GitHub Desktop.
simple Todo example of how to use hook.io and orchetrate.io together; use ajax if javascript is available otherwise use html forms; https://hook.io/paul-bottinhook-gmail-com/todo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = function (hook) { | |
// setup: | |
// Route /(:action)(/:id) | |
// Gist URL https://gist.github.com/fresc81/fc6a58b3a43f1c5120f8 | |
// View https://gist.github.com/fresc81/fc6a58b3a43f1c5120f8/raw/theme.html | |
// Presenter https://gist.github.com/fresc81/fc6a58b3a43f1c5120f8/raw/presenter.js | |
// create a collection on orchestrate.io: | |
// create two env variables: | |
// orchestrate_auth_token | |
// orchestrate_collection | |
var async = require('async'); | |
var request = require('request'); | |
var orchestrate = require('orchestrate'); | |
var db = orchestrate(hook.env.orchestrate_api_key); | |
var params = hook.params; | |
var resultList = null; | |
var resultMessage = null; | |
// decode action | |
switch (params.action) { | |
case 'create': | |
async.series({ result: create, list: list }, done); // hook.io's nodejs preserves order | |
break; // of object properties, so this is okay | |
case 'update': | |
async.series({ result: update, list: list }, done); | |
break; | |
case 'destroy': | |
async.series({ result: destroy, list: list }, done); | |
break; | |
default: | |
params.action = 'list'; | |
async.series({ list: list }, done); | |
break; | |
} | |
// little helper class for integrating promises with async callbacks | |
function ThenFailAdapter(cb) { | |
var me = this; | |
this.callback = cb; | |
this.__defineGetter__('then', function () { | |
return function (response) { | |
me.callback(null, true); | |
}; | |
}); | |
this.__defineGetter__('thenList', function () { | |
return function (response) { | |
var rows = response.body.results; | |
resultList = []; | |
for (var i=0; i<rows.length; i++) { | |
var row = rows[i]; | |
resultList.push({ id: row.path.key, text: row.value.text, done: row.value.done }); | |
} | |
me.callback(null, resultList); | |
}; | |
}); | |
this.__defineGetter__('fail', function () { | |
return function (response) { | |
me.callback(new Error(response.statusMessage + ' (' + response.statusCode + ')')); | |
}; | |
}); | |
} | |
ThenFailAdapter.prototype = {}; | |
// create todo | |
function create (cb) { | |
if (!params.text) | |
return [resultMessage = 'create: no text specified', cb(null, false)]; | |
console.log('create'); | |
var adapter = new ThenFailAdapter(cb); | |
db | |
.post(hook.env.orchestrate_collection, { | |
text: params.text, | |
done: params.done ? JSON.parse(params.done) : false | |
}) | |
.then(adapter.then) | |
.fail(adapter.fail); | |
} | |
// update todo | |
function update (cb) { | |
if (!params.id) | |
return [resultMessage = 'update: no id specified', cb(null, false)]; | |
if (!params.text) | |
return [resultMessage = 'update: no text specified', cb(null, false)]; | |
console.log('update'); | |
var adapter = new ThenFailAdapter(cb); | |
db | |
.merge(hook.env.orchestrate_collection, params.id, { | |
text: params.text, | |
done: params.done ? JSON.parse(params.done) : false | |
}) | |
.then(adapter.then) | |
.fail(adapter.fail); | |
} | |
// destroy todo | |
function destroy (cb) { | |
if (!params.id) | |
return [resultMessage = 'destroy: no id specified', cb(null, false)]; | |
console.log('destroy'); | |
var adapter = new ThenFailAdapter(cb); | |
db | |
.remove(hook.env.orchestrate_collection, params.id, true) | |
.then(adapter.then) | |
.fail(adapter.fail); | |
} | |
// fetch list of todos | |
function list (cb) { | |
console.log('list'); | |
var adapter = new ThenFailAdapter(cb); | |
db | |
.list(hook.env.orchestrate_collection, { limit : 25 }) | |
.then(adapter.thenList) | |
.fail(adapter.fail); | |
} | |
// return result as JSON | |
function done (err, results) { | |
if (err) { | |
return hook.res.end(JSON.stringify({ | |
action: params.action, | |
error: true, | |
message: err.message, | |
list: resultList | |
})); | |
} | |
var resultValue = (typeof results.result === 'undefined' ? (params.action === 'list' ? true : null) : results.result); | |
hook.res.end(JSON.stringify({ | |
action: params.action, | |
error: false, | |
message: resultMessage || params.action + ': okay', | |
result: resultValue, | |
list: results.list | |
})); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = function (opts, callback) { | |
// due the bigcompany/view engine is *meh* use templates provided by lodash instead | |
var _ = require('lodash'); | |
var util = require('util'); | |
var output, template, viewer; | |
var hook = this; | |
try { | |
template = _.template(hook.template, { imports: { _: _, util: util, hook: hook, opts: opts }}); | |
output = JSON.parse(opts.output); | |
viewer = template(output); | |
} catch (err) { | |
return callback(null, err.message + '\n' + err.stack + '\n' + hook.template); | |
} | |
callback(null, viewer); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Todo</title> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/g/[email protected](css/bootstrap.min.css)"> | |
<link rel="stylesheet" href="https://bootswatch.com/darkly/bootstrap.min.css"> | |
<!--[if lt IE 9]> | |
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> | |
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> | |
<![endif]--> | |
<script type="application/javascript" src="https://cdn.jsdelivr.net/jquery/2.2.1/jquery.min.js"></script> | |
<script type="application/javascript" src="https://cdn.jsdelivr.net/bootstrap/3.3.6/js/bootstrap.min.js"></script> | |
</head> | |
<body> | |
<style> | |
div#forms { | |
display: none; | |
visibility: hidden; | |
} | |
table, tr, th, td { | |
border: 1px solid grey; | |
} | |
td { | |
padding: 3px; | |
} | |
td { | |
text-align: center; | |
} | |
thead#head > tr, | |
tfoot#foot > tr { | |
color: white; | |
background-color: black; | |
} | |
tbody#body > tr:nth-child(even) { | |
background-color: lightgrey; | |
} | |
div#message { | |
margin: 4px; | |
} | |
input[type="text"] { | |
width: 100%; | |
} | |
</style> | |
<% var hookUrl = '/' + opts.params.owner + '/' + opts.params.hook; %> | |
<h1>Todo</h1> | |
<div id="forms"> | |
<% if (list && list.length) { %> | |
<% _.forEach(list, function (item) { %> | |
<form id="<%= item.id %>" class="update" method="POST" action="<%= hookUrl %>/update/<%= item.id %>"></form> | |
<% }); %> | |
<% } %> | |
<form id="create" method="POST" action="<%= hookUrl %>/create"></form> | |
</div> | |
<table width="100%"> | |
<thead id="head"> | |
<tr> | |
<th>todo</th> | |
<th colspan="2">actions</th> | |
</tr> | |
</thead> | |
<tbody id="body"> | |
<% if (list && list.length) { %> | |
<% _.forEach(list, function (item) { %> | |
<tr> | |
<td> | |
<div class="input-group"> | |
<input form="<%= item.id %>" type="text" name="text" value="<%= item.text %>" class="form-control"/> | |
<div class="input-group-addon"> | |
<input form="<%= item.id %>" type="checkbox" name="done" <% if (item.done) { %> checked="checked" <% } %> value="true"/> | |
</div> | |
</div> | |
</td> | |
<td><input form="<%= item.id %>" type="submit" value="update" class="btn btn-primary"></td> | |
<td><a class="destroy btn btn-primary" href="<%= hookUrl %>/destroy/<%= item.id %>">destroy</a></td> | |
</tr> | |
<% }); %> | |
<% } else { %> | |
<tr> | |
<td colspan="4">no todos yet</td> | |
</tr> | |
<% } %> | |
</tbody> | |
<tfoot id="foot"> | |
<tr> | |
<td> | |
<div class="input-group"> | |
<input form="create" type="text" name="text" class="form-control"/> | |
<div class="input-group-addon"> | |
<input form="create" type="checkbox" name="done" value="true"/> | |
</div> | |
</div> | |
</td> | |
<td><input form="create" type="submit" value="create" class="btn btn-primary"/></td> | |
<td><a class="reload btn btn-primary" href="<%= hookUrl %>">reload</a></td> | |
</tr> | |
</tfoot> | |
</table> | |
<div id="message" class="alert alert-info"><%- message %></div> | |
<script type="application/javascript"> | |
$(function () { | |
// rebuild table from ajax response | |
function makeTable (data) { | |
var $forms = $('div#forms') | |
.empty(); | |
var $tbody = $('tbody#body') | |
.empty(); | |
if (data.list && data.list.length) { | |
for (var i = 0; i < data.list.length; i++) { | |
var item = data.list[i]; | |
$forms.append($('<form>') | |
.attr('id', item.id) | |
.attr('method', 'POST') | |
.attr('action', '<%= hookUrl %>/update/'+item.id) | |
.addClass('update') | |
); | |
$tbody.append($('<tr>') | |
.append($('<td>') | |
.append($('<div>') | |
.addClass('input-group') | |
.append($('<input>') | |
.attr('form', item.id) | |
.attr('type', 'text') | |
.attr('name', 'text') | |
.attr('value', item.text) | |
.addClass('form-control') | |
) | |
.append($('<div>') | |
.addClass('input-group-addon') | |
.append($('<input>') | |
.attr('form', item.id) | |
.attr('type', 'checkbox') | |
.attr('name', 'done') | |
.attr('value', 'true') | |
.prop('checked', item.done) | |
) | |
) | |
) | |
) | |
.append($('<td>') | |
.append($('<input>') | |
.attr('form', item.id) | |
.attr('type', 'submit') | |
.attr('value', 'update') | |
.addClass('btn btn-primary') | |
) | |
) | |
.append($('<td>') | |
.append($('<a>') | |
.attr('href', '<%= hookUrl %>/destroy/'+item.id) | |
.addClass('destroy btn btn-primary') | |
.html('destroy') | |
) | |
) | |
); | |
} | |
} else { | |
$tbody.append($('<tr>').append($('<td>').attr('colspan', '4').html('no todos yet'))); | |
} | |
$forms.append($('<form>') | |
.attr('id', 'create') | |
.attr('method', 'POST') | |
.attr('action', '<%= hookUrl %>/create') | |
); | |
} | |
// intercept event handlers and use ajax interface | |
function processTable () { | |
// intercept update form submit | |
$('form.update').submit(function (event) { | |
event.preventDefault(); | |
$('#message').html(''); | |
var id = $(this).attr('id'); | |
var url = $(this).attr('action'); | |
var text = $('input[form="'+id+'"][name="text"]').val(); | |
var done = $('input[form="'+id+'"][name="done"]').prop('checked'); | |
$.post(url+'?'+$.param({ | |
theme: 'none' | |
}), { | |
text: text, | |
done: done | |
}, function (data) { | |
$('#message').html(data.result?'update: okay':data.message); | |
makeTable(data); | |
processTable(); | |
}, 'json'); | |
}); | |
// intercept destroy link click | |
$('a.destroy').click(function (event) { | |
event.preventDefault(); | |
$('#message').html(''); | |
var url = $(this).attr('href'); | |
$.get(url, { | |
theme: 'none' | |
}, function (data) { | |
$('#message').html(data.result?'destroy: okay':data.message); | |
makeTable(data); | |
processTable(); | |
}, 'json'); | |
}); | |
// intercept create form submit | |
$('form#create').submit(function (event) { | |
event.preventDefault(); | |
$('#message').html(''); | |
var url = $(this).attr('action'); | |
var text = $('input[form="create"][name="text"]').val(); | |
var done = $('input[form="create"][name="done"]').prop('checked'); | |
$.post(url+'?'+$.param({ | |
theme: 'none' | |
}), { | |
text: text, | |
done: done | |
}, function (data) { | |
$('#message').html(data.result?'create: okay':data.message); | |
// reset create form | |
$('input[form="create"][name="text"]').val(''); | |
$('input[form="create"][name="done"]').prop('checked', false); | |
makeTable(data); | |
processTable(); | |
}, 'json'); | |
}); | |
} | |
// initial process table | |
processTable(); | |
// intercept reload link click | |
$('a.reload').click(function (event) { | |
event.preventDefault(); | |
$('#message').html(''); | |
var url = $(this).attr('href'); | |
$.get(url, { | |
theme: 'none' | |
}, function (data) { | |
$('#message').html(data.result?'list: okay':data.message); | |
makeTable(data); | |
processTable(); | |
}, 'json'); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment