Make the Handlebars environment into an object

The idea is that the environment wraps up the mutable stuff in
Handlebars (like the helpers) and that you could theoretically create a
new one at any time and pass it in to Handlebars.template.

Every test makes a new environment and uses it in template compilation.
This commit is contained in:
Yehuda Katz
2013-07-26 16:50:37 +00:00
parent f5c8484ea0
commit 5f664dd78b
8 changed files with 70 additions and 61 deletions
+9 -2
View File
@@ -1,4 +1,4 @@
import { base as handlebars } from "./handlebars/base";
import { HandlebarsEnvironment } from "./handlebars/base";
// Each of these augment the Handlebars object. No need to setup here.
// (This is done to easily share code between commonjs and browse envs)
@@ -8,7 +8,14 @@ import { template } from "./handlebars/runtime";
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
var create = function() {
var hb = handlebars();
var hb = {},
env = new HandlebarsEnvironment();
// support new environments in global namespace mode
hb.HandlebarsEnvironment = HandlebarsEnvironment;
hb.registerHelper = env.registerHelper.bind(env);
hb.registerPartial = env.registerPartial.bind(env);
hb.SafeString = SafeString;
hb.Exception = Exception;
+33 -34
View File
@@ -1,6 +1,6 @@
/*jshint eqnull: true */
import { Exception, extend } from "./utils";
import { Exception, extend, isEmpty } from "./utils";
var K = function() { return this; };
@@ -14,40 +14,41 @@ export var REVISION_CHANGES = {
4: '>= 1.0.0'
};
// TODO: Make this a class
export function base(helpers, partials) {
var exports = {};
var helpers = helpers || {};
var partials = partials || {};
exports.helpers = helpers;
exports.partials = partials;
var toString = Object.prototype.toString,
var toString = Object.prototype.toString,
functionType = '[object Function]',
objectType = '[object Object]';
exports.registerHelper = function(name, fn, inverse) {
export function HandlebarsEnvironment(helpers, partials) {
this.helpers = helpers || {};
this.partials = partials || {};
registerDefaultHelpers(this);
}
HandlebarsEnvironment.prototype = {
constructor: HandlebarsEnvironment,
registerHelper: function(name, fn, inverse) {
if (toString.call(name) === objectType) {
if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); }
extend(helpers, name);
extend(this.helpers, name);
} else {
if (inverse) { fn.not = inverse; }
helpers[name] = fn;
this.helpers[name] = fn;
}
};
},
exports.registerPartial = function(name, str) {
registerPartial: function(name, str) {
if (toString.call(name) === objectType) {
extend(partials, name);
extend(this.partials, name);
} else {
partials[name] = str;
this.partials[name] = str;
}
};
}
};
exports.registerHelper('helperMissing', function(arg) {
function registerDefaultHelpers(instance) {
instance.registerHelper('helperMissing', function(arg) {
if(arguments.length === 2) {
return undefined;
} else {
@@ -55,7 +56,7 @@ export function base(helpers, partials) {
}
});
exports.registerHelper('blockHelperMissing', function(context, options) {
instance.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse || function() {}, fn = options.fn;
var type = toString.call(context);
@@ -68,7 +69,7 @@ export function base(helpers, partials) {
return inverse(this);
} else if(type === "[object Array]") {
if(context.length > 0) {
return Handlebars.helpers.each(context, options);
return instance.helpers.each(context, options);
} else {
return inverse(this);
}
@@ -77,7 +78,7 @@ export function base(helpers, partials) {
}
});
exports.registerHelper('each', function(context, options) {
instance.registerHelper('each', function(context, options) {
var fn = options.fn, inverse = options.inverse;
var i = 0, ret = "", data;
@@ -112,34 +113,32 @@ export function base(helpers, partials) {
return ret;
});
exports.registerHelper('if', function(conditional, options) {
instance.registerHelper('if', function(conditional, options) {
var type = toString.call(conditional);
if(type === functionType) { conditional = conditional.call(this); }
if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
if(!conditional || isEmpty(conditional)) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
exports.registerHelper('unless', function(conditional, options) {
return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
instance.registerHelper('unless', function(conditional, options) {
return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
});
exports.registerHelper('with', function(context, options) {
instance.registerHelper('with', function(context, options) {
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
if (!isEmpty(context)) return options.fn(context);
});
exports.registerHelper('log', function(context, options) {
instance.registerHelper('log', function(context, options) {
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
Handlebars.log(level, context);
});
return exports;
}
var levels = {
+3 -3
View File
@@ -443,17 +443,17 @@ export function compile(input, options) {
var compiled;
function compile() {
function compileInput() {
var ast = parse(input);
var environment = new Compiler().compile(ast, options);
var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true);
return template(templateSpec);
return template(templateSpec, options.env || Handlebars, compile);
}
// Template is only compiled on first use and cached after that point.
return function(context, options) {
if (!compiled) {
compiled = compile();
compiled = compileInput();
}
return compiled.call(this, context, options);
};
+7 -9
View File
@@ -3,17 +3,14 @@ import { COMPILER_REVISION, REVISION_CHANGES } from "./base";
// TODO: Remove this line and break up compilePartial
export function template(templateSpec, Hbars) {
// TODO: Make this less global
Hbars = Hbars || Handlebars;
if (Hbars.compile) {
export function template(templateSpec, Hbars, compile) {
if (compile) {
var invokePartialWrapper = function(partial, name, context, helpers, partials, data) {
var result = invokePartial.apply(this, arguments);
if (result) { return result; }
var options = { helpers: helpers, partials: partials, data: data };
partials[name] = Hbars.compile(partial, { data: data !== undefined });
partials[name] = compile(partial, { data: data !== undefined });
return partials[name](context, options);
};
} else {
@@ -24,6 +21,10 @@ export function template(templateSpec, Hbars) {
};
}
if (!Hbars) {
throw new Error("YUNO HANDLEBARS");
}
// Just add water
var container = {
escapeExpression: escapeExpression,
@@ -56,9 +57,6 @@ export function template(templateSpec, Hbars) {
return function(context, options) {
options = options || {};
Hbars = Hbars || require("handlebars");
// TODO: Why does templateSpec require a reference to the global Handlebars?
var result = templateSpec.call(container, Hbars, context, options.helpers, options.partials, options.data);
var compilerInfo = container.compilerInfo || [],
+6
View File
@@ -1,3 +1,9 @@
global.handlebarsEnv = null;
beforeEach(function() {
global.handlebarsEnv = new Handlebars.HandlebarsEnvironment();
});
describe("basic context", function() {
it("most basic", function() {
shouldCompileTo("{{foo}}", { foo: "foo" }, "foo");
+5 -3
View File
@@ -3,12 +3,14 @@ require('./common');
global.Handlebars = require('../../zomg/lib/handlebars');
global.CompilerContext = {
compile: function(template, options) {
compile: function(template, options, env) {
env = env || handlebarsEnv;
var templateSpec = Handlebars.precompile(template, options);
console.log(templateSpec);
return Handlebars.template(eval('(' + templateSpec + ')'));
return Handlebars.template(eval('(' + templateSpec + ')'), env);
},
compileWithPartial: function(template, options) {
options = options || {};
options.env = handlebarsEnv;
return Handlebars.compile(template, options);
}
};
+3 -3
View File
@@ -5,11 +5,11 @@ global.Handlebars = require('../../dist/handlebars.runtime');
var compiler = require('../../lib/handlebars');
global.CompilerContext = {
compile: function(template, options) {
compile: function(template, options, env) {
var templateSpec = compiler.precompile(template, options);
return Handlebars.template(eval('(' + templateSpec + ')'));
return Handlebars.template(eval('(' + templateSpec + ')'), env);
},
compileWithPartial: function(template, options) {
compileWithPartial: function(template, options, env) {
return compiler.compile(template, options);
}
};
+5 -8
View File
@@ -176,15 +176,12 @@ describe('helpers', function() {
var helpers = Handlebars.helpers;
try {
Handlebars.helpers = {};
Handlebars.registerHelper('if', helpers['if']);
Handlebars.registerHelper('world', function() { return "world!"; });
Handlebars.registerHelper('test_helper', function() { return 'found it!'; });
//Handlebars.registerHelper({
//'if': helpers['if'],
//world: function() { return "world!"; },
//test_helper: function() { return 'found it!'; }
//});
Handlebars.registerHelper({
'if': helpers['if'],
world: function() { return "world!"; },
test_helper: function() { return 'found it!'; }
});
shouldCompileTo(
"{{test_helper}} {{#if cruel}}Goodbye {{cruel}} {{world}}!{{/if}}",