Skip to content
Browse files

Adds basic functionality for reading templates and layouts

  • Loading branch information...
1 parent 7ef33d0 commit fca250db82a4532f2f3babafe23dde703af42f12 @jxson committed Sep 15, 2012
View
1 .gitignore
@@ -0,0 +1 @@
+node_modules
View
131 index.js
@@ -0,0 +1,131 @@
+var path = require('path')
+ , fs = require('fs')
+ , glob = require('glob')
+ , hogan = require('hogan')
+ , sigmund = require('sigmund')
+ , LRU = require('lru-cache')
+ , cache = LRU({ max: 500 })
+ , templates = {} // move or zap
+ , methods
+ , attributes
+
+methods = {
+ add: function(name, string){
+ var beardo = this
+ , template = hogan.compile(string)
+
+ templates[name] = template
+
+ return Object.create({
+ render: function(context){
+ var context = context || {}
+ , template = this
+ , layout = templates[path.join('layouts', context.layout)]
+ , out = template.hulkamania.render(context, templates)
+
+ context.yield = function(){
+ return layout ? out : null
+ }
+
+
+ if (layout) return layout.render(context, templates)
+ else return out
+ }
+ }, { hulkamania: { value: template } })
+ },
+ scan: function scan(string){
+ var scan = hogan.scan(string)
+
+ return scan.filter(function(node){
+ return node.tag === '>'
+ }).map(function(tag){
+ return tag.n
+ })
+ },
+ // rename this
+ layouts: function layouts(callback){
+ var beardo = this
+ , gloptions = { cwd: beardo.directory }
+ , collection = {}
+ , done
+
+ done = function done(layout){
+ collection
+ callback(null)
+ }
+
+ glob('layouts/**/*.mustache', gloptions, function(err, layouts){
+ if (err) return callback(err)
+
+ if (layouts.length) {
+ layouts.forEach(function(layout){
+ // console.log('layout:', layout)
+
+ beardo.read(layout, function(err, tpl){
+ if (err) return callback(err)
+
+ collection[layout.replace('.mustache', '')] = tpl
+
+ if (Object.keys(collection).length === layouts.length) {
+ callback(null, collection)
+ }
+ })
+ })
+ } else {
+ return callback(null, collection)
+ }
+ })
+ },
+ read: function read(name, callback){
+ var beardo = this
+ , name = name.replace('.mustache', '')
+ , file = path.join(beardo.directory, name + '.mustache')
+
+ // this could be pulled into a separate method
+ fs.stat(file, function(err, stats){
+ if (err) return callback(err)
+
+ var key = sigmund([ stats.ino
+ , stats.mtime
+ , stats.size
+ ])
+ , cached = cache.get(key)
+
+ if (cached) {
+ // console.log('cached!')
+ return callback(null, cached)
+ }
+
+ // console.log('not cached', name)
+
+ fs.readFile(file, 'utf8', function(err, data){
+ if (err) return callback(err)
+
+ var template = beardo.add(name, data)
+ , partials = beardo.scan(data)
+ , counter = 0
+
+ // console.log('partials', partials)
+
+ if (! partials.length) return callback(null, template)
+
+ partials.forEach(function(partial){
+ beardo.read(partial, function(err){
+ counter++
+
+ if (counter === partials.length) callback(null, template)
+ })
+ })
+ })
+ })
+ }
+}
+
+attributes = {
+ directory: { enumerable: true
+ , writable: true
+ , value: path.resolve('templates')
+ }
+}
+
+module.exports = Object.create(methods, attributes)
View
12 package.json
@@ -0,0 +1,12 @@
+{
+ "name": "beardo",
+ "version": "0.0.0",
+ "description": "The best mustaches were beards first.",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": "",
+ "author": "",
+ "license": "BSD"
+}
View
106 test/beardo.js
@@ -0,0 +1,106 @@
+
+var beardo = require('../')
+ , path = require('path')
+ , assert = require('assert')
+
+describe('beardo', function(){
+ it('require returns a beardo instance', function(){
+ assert.ok(beardo, 'beardo is undefined. WTF?')
+ assert.ok(beardo.directory, 'Missing `directory` property')
+ assert.equal(typeof beardo.read, 'function', 'Missing `read` method')
+ })
+})
+
+describe('beardo.directory', function(){
+ it('defaults to `process.cwd()`', function(){
+ assert.equal(beardo.directory, path.resolve('templates'))
+ })
+
+ it('is writeable', function(){
+ beardo.directory = path.join(__dirname, './templates')
+
+ assert.equal(beardo.directory, path.join(__dirname, './templates'))
+ })
+})
+
+describe('beardo.read', function(){
+ before(function(){
+ beardo.directory = path.join(__dirname, './templates')
+ })
+
+ it('reads templates', function(done){
+ beardo.read('basic', function(err, template){
+ if (err) return done(err)
+
+ assert.ok(template, 'Missing `template` argument')
+ assert.equal(template.render({ text: 'foo' }), 'basic tom fooery')
+
+ done()
+ })
+ })
+
+ it('reads templates with partials', function(done){
+ beardo.read('has-partial', function(err, template){
+ if (err) return done(err)
+
+ assert.ok(template, 'Missing `template` argument')
+ assert.equal(template.render(), 'partial! w007')
+
+ done()
+ })
+ })
+
+ it('reads templates with nested partials', function(done){
+ beardo.read('has-nested-partials', function(err, tpl){
+ if (err) return done(err)
+
+ assert.ok(tpl, 'Missing `template` argument')
+ assert.equal(tpl.render(), 'A dream, within a dream, within a dream')
+
+ done()
+ })
+ })
+
+ it('errs on non-existing templates', function(done){
+ beardo.read('bogus', function(err, template){
+ assert.ok(err, 'Missing `error`')
+ assert.equal(err.code, 'ENOENT', 'Bad `error.code`')
+ assert.equal(template, undefined, '`template` should be `undefined`')
+
+ done()
+ })
+ })
+})
+
+describe('beardo.layouts', function(){
+ before(function(){
+ beardo.directory = path.join(__dirname, './templates')
+ })
+
+ it('reads the layouts dir', function(done){
+ beardo.layouts(function(err, layouts){
+ if (err) return done(err)
+
+ assert.ok(layouts)
+ assert.ok(layouts['layouts/default'])
+ assert.equal(layouts['layouts/default'].render(), '=== ===')
+
+ done()
+ })
+ })
+
+ it('allows templates with layouts', function(){
+ beardo.layouts(function(err, layouts){
+ if (err) return done(err)
+
+ beardo.read('needs-layout', function(err, tpl){
+ if (err) return done(err)
+
+ assert.ok(tpl, 'Missing `template` argument')
+ assert.equal(tpl.render({ layout: 'default' }), '=== gimme danger ===')
+
+ done()
+ })
+ })
+ })
+})
View
1 test/templates/basic.mustache
@@ -0,0 +1 @@
+basic tom {{ text }}ery
View
1 test/templates/has-nested-partials.mustache
@@ -0,0 +1 @@
+A dream, {{>partials/level-one}}
View
1 test/templates/has-partial.mustache
@@ -0,0 +1 @@
+partial! {{>partials/w007}}
View
1 test/templates/layouts/default.mustache
@@ -0,0 +1 @@
+=== {{{ yield }}} ===
View
1 test/templates/needs-layout.mustache
@@ -0,0 +1 @@
+gimme danger
View
1 test/templates/partials/level-one.mustache
@@ -0,0 +1 @@
+within a dream, {{>partials/level-two}}
View
1 test/templates/partials/level-two.mustache
@@ -0,0 +1 @@
+within a dream
View
1 test/templates/partials/w007.mustache
@@ -0,0 +1 @@
+w007

0 comments on commit fca250d

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