Skip to content
Browse files

real initial commit

  • Loading branch information...
1 parent 5183bd6 commit 4298c3dad5b170ed7190bcf359ba0536d7473726 @chrisdickinson committed
View
1 .gitignore
@@ -0,0 +1 @@
+node_modules/
View
2 .npmignore
@@ -0,0 +1,2 @@
+node_modules/
+.gitignore
View
56 README.md
@@ -0,0 +1,56 @@
+# porcelain
+
+A tiny helper library for using plate templates in Node point JS.
+
+## API
+
+### require('porcelain') -> porcelain
+
+### porcelain([template_dir, ...]) -> loadTemplate(name, ready)
+
+Set `plate` to attempt to load templates from each of the provided `template_dir`
+arguments. If the paths are relative (they do not begin with a `/`), they will be
+considered relative to the file of the caller.
+
+### loadTemplate(name, function(err, template)) -> undefined
+
+Attempts to load a template given a name.
+
+If successful, the callback will be called with `null` as the first argument
+and the `plate.Template` object as the second argument.
+
+### loadTemplate.createReadStream(name) -> stream
+
+Return a readable stream suitable for piping to, e.g., a `ServerResponse`
+object.
+
+### stream.pause()
+
+Pause the stream. Headers and status code on the stream will not
+be passed to any piped writable stream until `stream.resume()` is called.
+
+### stream.resume()
+
+Resume the stream. Calls `response.writeHead(stream.code, stream.headers)`
+on any `ServerResponse` the stream is piped to.
+
+### stream.context
+
+The context with which to render the loaded template with.
+
+Defaults to `{}`.
+
+### stream.code
+
+The HTTP status code to be set when piping to a `ServerResponse`
+object.
+
+Defaults to `200`.
+
+### stream.headers
+
+The headers (as a an object literal) to be set when piping to a
+`ServerResponse` object.
+
+Defaults to `{'content-type': 'text/html'}`.
+
View
124 index.js
@@ -0,0 +1,124 @@
+module.exports = templated
+
+var Stream = require('stream')
+ , Path = require('path')
+
+var plate = require('plate')
+ , Loader = require('plate/lib/plugins/loaders/filesystem').Loader
+
+var PARENT_DIR = module.parent ? Path.dirname(module.parent.filename) : ''
+
+function templated() {
+ var dirs = arguments.length ? [].slice.call(arguments) : ['templates']
+ , loader
+
+ for(var i = 0, len = dirs.length; i < len; ++i) {
+ dirs[i] = Path.resolve(
+ dirs[i].charAt(0) === '/' ? dirs[i] : Path.join(PARENT_DIR, dirs[i])
+ )
+ }
+
+ loader = new Loader(dirs).getPlugin()
+
+ plate.Template.Meta.registerPlugin('loader', loader)
+
+ get_template.createReadStream = createReadStream
+
+ return get_template
+
+ function get_template(template_name, ready) {
+ var result = loader(template_name)
+
+ if(result && result.constructor !== plate.Template) {
+ result.once('done', got_template)
+ return
+ }
+
+ return got_template(template)
+
+ function got_template(template) {
+ return template ? ready(null, template) : ready(make_error(template_name))
+ }
+ }
+
+ function make_error(template_name) {
+ return new Error(
+ 'Could not find '+template_name+'. Tried:\n\t'+
+ dirs.map(function(d) { return Path.join(d, template_name) }).join('\n\t')+'\n'
+ )
+ }
+
+ function createReadStream(template_name) {
+ var stream = new Stream
+ , wrote_head = false
+ , pipe = stream.pipe
+
+ stream.readable = true
+ stream.code = 200
+ stream.headers = {'content-type':'text/html'}
+ stream.context = {}
+
+ stream.resume = function() {
+ this.emit('drain')
+ this._paused = false
+ }
+
+ stream.pause = function() {
+ this._paused = true
+ }
+
+ stream.paused = function() {
+ return this._paused
+ }
+
+ stream.pipe = function(response) {
+ pipe.call(stream, response)
+
+ if(!response.writeHead)
+ return
+
+ if(stream.paused()) {
+ return stream.once('drain', write_head)
+ }
+
+ write_head()
+
+ function write_head() {
+ if(wrote_head)
+ return
+
+ wrote_head = true
+ response.writeHead(stream.code, stream.headers)
+ }
+ }
+
+
+ get_template(template_name, function(err, template) {
+ if(err) return stream.emit('error', err)
+
+ template.render(stream.context, function(err, html) {
+
+ if(err) return stream.emit('error', err)
+
+ if(stream.paused()) {
+ return stream.once('drain', close)
+ }
+
+ return close()
+
+ function close() {
+
+ // the readable stream polka:
+ stream.emit('data', html)
+ stream.readable = false
+
+ stream.emit('end')
+ stream.closed = true
+ stream.emit('close')
+ }
+ })
+ })
+
+ return stream
+ }
+}
View
26 package.json
@@ -0,0 +1,26 @@
+{
+ "name": "porcelain",
+ "version": "0.0.1",
+ "description": "node.js helpers for plate templating language",
+ "main": "index.js",
+ "directories": {
+ "test": "test"
+ },
+ "dependencies": {
+ "plate": "~1.0.2"
+ },
+ "devDependencies": {},
+ "scripts": {
+ "test": "node test/index.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/chrisdickinson/porcelain.git"
+ },
+ "keywords": [
+ "plate",
+ "porcelain"
+ ],
+ "author": "Chris Dickinson <chris@neversaw.us>",
+ "license": "MIT"
+}
View
1 test/custom_templates/both.html
@@ -0,0 +1 @@
+custom_templates
View
1 test/custom_templates/test_custom_dir.html
@@ -0,0 +1 @@
+hello busey
View
167 test/index.js
@@ -0,0 +1,167 @@
+var assert = require('assert')
+ , http = require('http')
+ , plate = require('plate')
+
+var porcelain = require('../index')
+ , out = function(x) { return process.stdout.write(x) }
+
+var tests = [
+ test_default_dir
+ , test_custom_dir
+ , test_multiple_dir
+ , test_stream
+ , test_stream_http
+]
+
+run()
+
+function run() {
+ if(!tests.length)
+ return process.stdout.write('\n')
+
+ var test = tests.shift()
+ , now = Date.now()
+
+ setup()
+
+ out(test.name+' - ')
+ test(function() {
+ out(''+(Date.now() - now)+'ms\n')
+ run()
+ })
+}
+
+function setup() {
+ // reset the plugin registry.
+ plate.Template.Meta._classes.plugin.registry = {}
+ plate.Template.Meta._cache.plugin = null
+}
+
+// integration tests.
+
+function test_default_dir(ready) {
+ var loader = porcelain()
+
+ loader('test_default_dir.html', function(err, data) {
+ assert.ok(!err)
+ assert.equal(data.raw, 'hello world\n')
+ ready()
+ })
+}
+
+function test_custom_dir(ready) {
+ var loader = porcelain('custom_templates')
+
+ loader('test_custom_dir.html', function(err, data) {
+ assert.ok(!err)
+ assert.equal(data.raw, 'hello busey\n')
+ ready()
+ })
+}
+
+function test_multiple_dir(ready) {
+ var loader = porcelain('custom_templates', 'templates')
+
+ loader('both.html', got_both)
+
+ function got_both(err, template) {
+ assert.ok(!err)
+ assert.equal(template.raw, 'custom_templates\n')
+
+ loader('test_default_dir.html', got_default)
+ }
+
+ function got_default(err, template) {
+ assert.ok(!err)
+ assert.equal(template.raw, 'hello world\n')
+
+ loader('test_custom_dir.html', got_custom)
+ }
+
+ function got_custom(err, template) {
+ assert.ok(!err)
+ assert.equal(template.raw, 'hello busey\n')
+
+ ready()
+ }
+}
+
+function test_stream(ready) {
+ var loader = porcelain()
+ , didData = false
+ , didEnd = false
+ , didClose = false
+
+ var stream = loader.createReadStream('test_stream.html')
+
+ stream.context.object = 'world'
+
+ stream
+ .on('data', function(data) {
+ assert.ok(!didEnd)
+ assert.ok(!didClose)
+ didData = true
+
+ assert.ok(stream.readable)
+ assert.ok(!stream.closed)
+
+ assert.equal(data, 'hello world\n')
+ })
+ .on('end', function() {
+ assert.ok(!didClose)
+ assert.ok(didData)
+ assert.ok(!stream.readable)
+ assert.ok(!stream.closed)
+ didEnd = true
+ })
+ .on('close', function() {
+ assert.ok(didData)
+ assert.ok(didEnd)
+ didClose = true
+ assert.ok(!stream.readable)
+ assert.ok(stream.closed)
+
+ didData && didEnd && didClose && ready()
+ })
+}
+
+function test_stream_http(ready) {
+ var server = http.createServer(on_request_response)
+ , loader = porcelain()
+ , request
+
+ server.listen(9995, server_online)
+
+ function on_request_response(req, resp) {
+ var stream = loader.createReadStream('test_stream.html')
+ , data = []
+
+ stream.pause()
+ stream.pipe(resp)
+
+ req.on('data', data.push.bind(data))
+ req.on('end', function() {
+ stream.context.object = data.join('')
+ stream.resume()
+ })
+ }
+
+ function server_online() {
+ request = http.request({port:9995, method:'POST'}, get_response)
+
+ var expect = 'things and stuff '+(Math.random() * 0xFF & 0xFF)
+ request.end(expect)
+
+ function get_response(res) {
+ var data = []
+
+ res.on('data', data.push.bind(data))
+ res.on('end', function() {
+ server.close()
+
+ assert.equal('hello '+expect+'\n', data.join(''))
+ ready()
+ })
+ }
+ }
+}
View
1 test/templates/both.html
@@ -0,0 +1 @@
+default_templates
View
1 test/templates/test_default_dir.html
@@ -0,0 +1 @@
+hello world
View
1 test/templates/test_stream.html
@@ -0,0 +1 @@
+hello {{ object }}

0 comments on commit 4298c3d

Please sign in to comment.
Something went wrong with that request. Please try again.