Pennyworth is the compliment to Jobsworth, handling tasks for Dave the JS Bin bot, and generally running around like a headless chicken turning gobbledegook in to sensible code!
This is the server (and sample client) to handle processors. Though most of JS Bin's processors are handled both on the client side and server side, some processors need to be server side only (like Sass), but also they need to be (effectively) "thread safe".
This server will respond to zeromq messages with appropriate source types, and respond with the translated output.
All processors live in the targets
directory, and are structured as so:
- Directory name for the target processor (such as
markdown
) index.js
will be loaded by the processor servermodule.exports
is a function that receivesresolve
,reject
anddata
- The processor must handle both the resolve and the reject.
data
is an object structured as:
{
language: "<string>", // maps to target processor
source: "<string>", // source text to be processed
file: "<string>", // optional filename to create tmp files from, should be unique
}
The directory structure:
.
└── targets
└── coffeescript
└── index.js
The package.json
for this project includes the coffee-script
npm module.
index.js
contains:
'use strict';
var coffeescript = require('coffee-script');
module.exports = function (resolve, reject, data) {
try {
var res = coffeescript.compile(data.source);
resolve({
errors: null,
result: res
});
} catch (e) {
// index starts at 0
var errors = {
line: parseInt(e.location.first_line, 10) || 0,
ch: parseInt(e.location.first_column, 10) || 0,
msg: e.message
};
resolve({
errors: [errors],
result: null
});
}
};
Now the processor server can handle requests for markdown conversion.
Note that the actually processor won't need to reject
, if the processor has errors, then these are considered runtime errors and they are sent back to the requester.
Pennyworth will return a single object with output
(from the processor) and error
(if there's any system level errors, like timeouts):
{
output: {
result: "<string>",
errors: null, // or: [{ line: x, ch: y, msg: string }, ... ]
},
error: null, // or Error object
}
The output
property contains data if the processor successfully returned a result (be it intended result or otherwise). The output
object contains result
(a string representing processed code) and errors
an array of compilation errors.
The compilation errors
array contains object structured as:
{
line: x, // integer with index starting at 0
ch: y, // integer with index starting at 0, or null
msg: 'string' // error message
}
Ideally node is used to run each processor, but some processors (like Sass and SCSS) run using Ruby.
If the processor needs a ruby gem to run, add it to ./Gemfile
to be automatically installed by npm run-script gems
# Pennyworth Gemfile
source "https://rubygems.org"
gem "compass", ">= 1.0.0.alpha.19"
gem "bourbon"
gem "<your-gem-file>"
More information about Gemfile and Bundler
All processor specific tests live in test/targets/<processor>/*.test.js
and can be run with npm test
. They use Mocha and should.
The following outline is our current process for processor tests (using markdown as the example, obviously change names and extensions as appropriate):
- Directory name for the target processor in
test/targets/markdown
markdown.test.js
will contain the tests.broken.md
contains code that will return errors.sample.md
contains working code that will succesfully parse.- Tests should check for at least one positive and negative outcome.
We welcome more tests and ideas on how to improve this process.