Last active
August 29, 2015 14:02
-
-
Save soareschen/db4cce1705e4253b8f7b to your computer and use it in GitHub Desktop.
Quiver DSL Experimentation
This file contains 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
/* | |
* This is an experimentation example of | |
* the new component definition DSL for Quiver. | |
* Here we will define a simple hello handler | |
* with a few middleware to transform the | |
* input/output. | |
* | |
* - The base handler get the name as text from | |
* standard input stream and output the greeting | |
* with "hello" in front of the name. | |
* | |
* - The html escape filter escapes the name input | |
* before it reach the main handler. | |
* | |
* - The uppercase filter transform the entire | |
* greeting output to upppercase. | |
* | |
* - The compress filter apply gzip compression | |
* to the final text stream result. | |
*/ | |
// load a third party component module | |
var escapeLib = require('escape-component') | |
// call begin from quiver-define to start | |
// a new component definition context. | |
// We use a very short identifier, tentatively | |
// underscore _, because it will be used a lot | |
// in component definition. | |
var _ = require('quiver-define').begin({ | |
// all components defined will have | |
// their name normalized with given namespace | |
namespace: 'my-namespace', | |
// One can import component from other namespace | |
using: [ | |
// all component names from the escape | |
// module are imported so that | |
// unqualified names can be used | |
escapeLib, | |
// or individual components from | |
// other namespace can be imported | |
// without requiring the module | |
'compress::gzip compress out' | |
], | |
// all components defined will be exported | |
// to through module.exports or some other object | |
exportTo: module.exports | |
}) | |
// define a simple handler component | |
_('simple handler', { | |
name: 'greet hello', | |
// input/output type of the simple handler | |
type: 'text/text', | |
// return mode can be async, sync, promise, generator. | |
// default return mode is async | |
returnMode: 'sync', | |
handler: function(args, name) { | |
return 'hello, <b>' + name + '</b>' | |
}, | |
// multiple middlewares can be applied | |
// with last one as innermost middleware | |
middlewares: [ | |
// resolve to 'escape::html escape in' | |
'html escape in', | |
// inline filter of type transform filter | |
_('transform filter', { | |
// mode out means the filter pipe the result | |
// through another handler specified below. | |
// transform mode can be in, out, inout | |
mode: 'out', | |
// middleware used by transform filter | |
// can also be defined inline or by name | |
handler: _('simple handler', { | |
// no name equals anonymous handler | |
type: 'text/text', | |
handler: function(args, input, callback) { | |
callback(null, input.toUpperCase()) | |
} | |
}) | |
}), | |
// resolve to 'compress::gzip compress out' | |
// middleware loaded from config at building time | |
'gzip compress out' | |
] | |
}) | |
/* | |
* Alternatively a much cleaner way to define | |
* a component is to define each building blocks | |
* as separate components and reference them | |
* by name. However the formal approach can | |
* sometimes be more expressive and is | |
* quite handy in rapid prototyping. | |
*/ | |
_('simple handler', { | |
name: 'greet hello', | |
type: 'text/text', | |
handler: function(args, name, callback) { | |
callback(null, 'hello, <b>' + name + '</b>') | |
}, | |
middlewares: [ | |
'html escape in', | |
// resolve to 'my-namespace::to uppercase' | |
// defined elsewhere | |
'uppercase out', | |
'gzip compress out' | |
] | |
}) | |
// No need to manually export the components | |
// at the end, as they are exported automatically. |
This file contains 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
/* | |
* The first example was an attempt to | |
* get the cleanest DSL syntax possible. | |
* Along the deprecation of the `with` keyword | |
* in JavaScript, it is hard to activate | |
* more than one identifier to be used as | |
* DSL keyword. As a result all DSL keywords | |
* have to be become prefixed with a common | |
* identifier and the syntax becomes uglier. | |
* | |
* The previous example do some straight forward | |
* "syntactic sugar" to make DSL keywords look | |
* slightly better. The original style: | |
* | |
* _('keyword action', { ... }) | |
* | |
* is semantically equivalent to: | |
* | |
* _['keyword action']({ ... }) | |
* | |
* or using camel case convention: | |
* | |
* _.keywordAction({ ... }) | |
* | |
* This example present the alternative syntax | |
* to allow easier comparison of which DSL | |
* approach is better. | |
*/ | |
var escapeLib = require('escape-component') | |
var _ = require('quiver-define').begin({ | |
namespace: 'my-namespace', | |
using: [ | |
escapeLib, | |
'compress::gzip compress out' | |
], | |
exportTo: module.exports | |
}) | |
_.simpleHandler({ | |
name: 'greet hello', | |
type: 'text/text', | |
returnMode: 'sync', | |
handler: function(args, name) { | |
return 'hello, <b>' + name + '</b>' | |
}, | |
middlewares: [ | |
'html escape in', | |
_.transformFilter({ | |
mode: 'out', | |
handler: _.simpleHandler({ | |
type: 'text/text', | |
handler: function(args, input, callback) { | |
callback(null, input.toUpperCase()) | |
} | |
}) | |
}), | |
'gzip compress out' | |
] | |
}) |
This file contains 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
/* | |
* This is another DSL attempt that use | |
* the more conventional chained method style | |
* to define DSL keywords. However in my | |
* opinion the approach is less stylish and | |
* fragile. | |
* | |
* Using the chained method as constructor, | |
* it is hard to know where the construction | |
* arguments are finished and therefore the | |
* workaround is to add an end() method to | |
* finish the construction. | |
* | |
* The chained method is also hard to use | |
* for nested construction, as demonstrated | |
* in this example with anonymous inline | |
* handler. It result in additional close | |
* bracket hell that look similar to Lisp. | |
*/ | |
var escapeLib = require('escape-component') | |
var _ = require('quiver-define') | |
.begin() | |
.namespace('my-namespace') | |
.using(escapeLib) | |
.using('compress::gzip compress out') | |
.exportTo(module.exports) | |
.end() | |
_.simpleHandler('greet hello') | |
.type('text/text') | |
.returnMode('sync') | |
.handler(function(args, name) { | |
return 'hello, <b>' + name + '</b>' | |
}) | |
.middleware('html escape in') | |
.middleware(_.transformFilter() | |
.mode('out') | |
.handler(_.simpleHandler() | |
.type('text/text') | |
.handler(function(args, input, callback) { | |
callback(null, input.toUpperCase()) | |
}))) | |
.middleware('gzip compress out') | |
.end() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment