Skip to content

Commit

Permalink
Broke up templating code into separate libs
Browse files Browse the repository at this point in the history
  • Loading branch information
mde committed Apr 30, 2013
1 parent 003cf6a commit 0ab7983
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 177 deletions.
180 changes: 3 additions & 177 deletions lib/template/index.js
Expand Up @@ -15,14 +15,10 @@
* limitations under the License.
*
*/
var path = require('path')
, fs = require('fs')
, utils = require('utilities')
, Adapter = require('./adapters').Adapter
var Adapter = require('./adapters').Adapter
, Templater
, Partial
, Layout
, cache = {};
, Partial = require('./partial').Partial
, Layout = require('./layout').Layout;

Templater = function () {};

Expand All @@ -38,173 +34,3 @@ Templater.prototype = new (function () {
})();
exports.Templater = Templater;

Partial = function (templatePath, data, parent) {
var self = this;
this.id = utils.string.uuid();
this.templatePath = templatePath
this.data = data;
this.parent = parent;
this.children = [];
this.content = '';

// Hang a `partial` method on the execution-context for the
// template rendering (e.g., will be the EJS global `partial`
// function to add sub-templates
this.data.partial = function (templatePath, data) {
var child = new Partial(templatePath, data, self);
self.addChild(child);
return '###partial###' + child.id
};
};

Partial.prototype = new (function () {

this.render = function (cb) {
var self = this
, templateData = this.getTemplateData()
, file = templateData.file
, ext = templateData.ext
, handleData = function (data) {
self.renderSelf(data, ext);
self.renderChildren(cb);
};

// Use cached template content if possible
if (cache[file]) {
handleData(cache[file]);
}
// Otherwise fetch off disk
else {
// Get the template from the FS then cache it for subsequent requests
fs.readFile(file, 'utf8', function (err, data) {
if (err) {
throw err;
}
if (geddy.config.environment != 'development') {
cache[file] = data;
}
handleData(data);
});
}
};

this.renderSelf = function (templateContent, ext) {
var adapter = new Adapter();
// Render with appropriate engine
adapter.set({engine: ext, template: templateContent});
this.content = adapter.render(this.data);
};

this.renderChildren = function (cb) {
var self = this
, children = this.children
, incr = children.length;

if (children.length) {
children.forEach(function (child) {
child.render(function (content) {
self.content = self.content.replace(
'###partial###' + child.id, content);
incr--;
if (!incr) {
cb(self.content);
}
});
});
}
else {
cb(this.content);
}

};

this.getTemplateData = function () {
var dirs = []
, dir
, key
, templatePath = this.templatePath
, templateData;

// Look for an exact match
templateData = geddy.templateRegistry[templatePath];

// No exact match, try with some directory prefixes
if (!templateData) {
// Loop through dirs until a registered template path is found
// Note: Template paths are gathered at init so we don't have to
// touch the FS when looking for templates

// If this is a child partial, then look in the parent's directory
if (this.parent) {
dirs.unshift(path.dirname(this.parent.templatePath));
}

// Any local template directory
dirs.unshift(path.dirname(templatePath));

// Last resort; look in the base views directory
dirs.unshift(path.normalize('app/views'));


for (var i = 0, ii = dirs.length; i < ii; i++) {
dir = dirs[i];
// Not full path (No extension(s))
key = path.normalize(path.join(dir, templatePath));

templateData = geddy.templateRegistry[key];
if (templateData) {
break;
}
}
}

// Still no joy
if (!templateData) {
// Is this a Layout?
if (this instanceof Layout) {
// Try to use the default application layout
key = path.normalize('app/views/layouts/application');
templateData = geddy.templateRegistry[key];
// If they've removed the default layout for some reason
if (!templateData) {
throw new geddy.errors.InternalServerError('Layout template "' +
templatePath + '" not found in ' +
geddy.utils.array.humanize(dirs));
}
}
// If it's a normal Partial then it doesn't exist, boom
else {
throw new geddy.errors.InternalServerError('Partial template "' +
templatePath + '" not found in ' +
geddy.utils.array.humanize(dirs));
}
}

return templateData || null;
};

this.addChild = function (child) {
this.children.push(child);
};

})();

// Subclass of Partial
Layout = function (layoutPath, templatePath, data) {
var self = this;
// Call the ctor on 'this' -- the the layoutPath will be our
// templatePath
Partial.call(this, layoutPath, data, null);

// `yield` is just a special case of `partial` using the template-path
// that the layout wraps -- just hard-code the path and pass along
// the same data
this.data.yield = function () {
return self.data.partial(templatePath, data);
};
};

Layout.prototype = Object.create(Partial.prototype);
Layout.prototype.constructor = Layout;

exports.Templater = Templater;
22 changes: 22 additions & 0 deletions lib/template/layout.js
@@ -0,0 +1,22 @@
var Layout
, Partial = require('./partial').Partial;

// Subclass of Partial
Layout = function (layoutPath, templatePath, data) {
var self = this;
// Call the ctor on 'this' -- the the layoutPath will be our
// templatePath
Partial.call(this, layoutPath, data, null);

// `yield` is just a special case of `partial` using the template-path
// that the layout wraps -- just hard-code the path and pass along
// the same data
this.data.yield = function () {
return self.data.partial(templatePath, data);
};
};

Layout.prototype = Object.create(Partial.prototype);
Layout.prototype.constructor = Layout;

exports.Layout = Layout;
165 changes: 165 additions & 0 deletions lib/template/partial.js
@@ -0,0 +1,165 @@
var Partial
, Layout
, Adapter = require('./adapters').Adapter
, path = require('path')
, fs = require('fs')
, utils = require('utilities')
, cache = {};

Partial = function (templatePath, data, parent) {
var self = this;
this.id = utils.string.uuid();
this.templatePath = templatePath
this.data = data;
this.parent = parent;
this.children = [];
this.content = '';

// Hang a `partial` method on the execution-context for the
// template rendering (e.g., will be the EJS global `partial`
// function to add sub-templates
this.data.partial = function (templatePath, data) {
var child = new Partial(templatePath, data, self);
self.addChild(child);
return '###partial###' + child.id
};
};

Partial.prototype = new (function () {

this.getTemplateData = function () {
var dirs = []
, dir
, key
, templatePath = this.templatePath
, templateData;

// Look for an exact match
templateData = geddy.templateRegistry[templatePath];

// No exact match, try with some directory prefixes
if (!templateData) {
// Loop through dirs until a registered template path is found
// Note: Template paths are gathered at init so we don't have to
// touch the FS when looking for templates

// If this is a child partial, then look in the parent's directory
if (this.parent) {
dirs.unshift(path.dirname(this.parent.templatePath));
}

// Any local template directory
dirs.unshift(path.dirname(templatePath));

// Last resort; look in the base views directory
dirs.unshift(path.normalize('app/views'));


for (var i = 0, ii = dirs.length; i < ii; i++) {
dir = dirs[i];
// Not full path (No extension(s))
key = path.normalize(path.join(dir, templatePath));

templateData = geddy.templateRegistry[key];
if (templateData) {
break;
}
}
}

// Still no joy
if (!templateData) {
// Is this a Layout?
if (this instanceof Layout) {
// Try to use the default application layout
key = path.normalize('app/views/layouts/application');
templateData = geddy.templateRegistry[key];
// If they've removed the default layout for some reason
if (!templateData) {
throw new geddy.errors.InternalServerError('Layout template "' +
templatePath + '" not found in ' +
geddy.utils.array.humanize(dirs));
}
}
// If it's a normal Partial then it doesn't exist, boom
else {
throw new geddy.errors.InternalServerError('Partial template "' +
templatePath + '" not found in ' +
geddy.utils.array.humanize(dirs));
}
}

return templateData || null;
};

this.render = function (cb) {
var self = this
, templateData = this.getTemplateData()
, file = templateData.file
, ext = templateData.ext
, handleData = function (data) {
self.renderSelf(data, ext);
self.renderChildren(cb);
};

// Use cached template content if possible
if (cache[file]) {
handleData(cache[file]);
}
// Otherwise fetch off disk
else {
// Get the template from the FS then cache it for subsequent requests
fs.readFile(file, 'utf8', function (err, data) {
if (err) {
throw err;
}
if (geddy.config.environment != 'development') {
cache[file] = data;
}
handleData(data);
});
}
};

this.renderSelf = function (templateContent, ext) {
var adapter = new Adapter();
// Render with appropriate engine
adapter.set({engine: ext, template: templateContent});
this.content = adapter.render(this.data);
};

this.renderChildren = function (cb) {
var self = this
, children = this.children
, incr = children.length;

if (children.length) {
children.forEach(function (child) {
child.render(function (content) {
self.content = self.content.replace(
'###partial###' + child.id, content);
incr--;
if (!incr) {
cb(self.content);
}
});
});
}
else {
cb(this.content);
}

};

this.addChild = function (child) {
this.children.push(child);
};

})();

exports.Partial = Partial;

// Layout is a subclass of Partial, depends on it
// being defined
Layout = require('./layout').Layout;

0 comments on commit 0ab7983

Please sign in to comment.