Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'kixxauth'

  • Loading branch information...
commit a2931f7c9fd271f061d0c6dfcbe96ccad3aee654 2 parents b40f61f + 685b97c
@kixxauth authored
Showing with 261 additions and 29 deletions.
  1. +48 −0 docs/getting-started.md
  2. +51 −26 index.js
  3. +162 −3 index.test.js
View
48 docs/getting-started.md
@@ -19,6 +19,8 @@ In order to start using Swig, you should initialize it. Swig can be configured u
This step is _optional_, however it is recommended to at least set the `root` key when running Swig from node.js.
+You can also create multiple instances of the Swig engine in the same runtime. See <a href="#multiple-instances">Creating Multiple Intances of Swig</a> below.
+
### Options
#### allowErrors _optional_
@@ -61,6 +63,50 @@ Add library extensions that will be available to compiled templates. For more in
Sets a default timezone offset, in minutes from GMT. Setting this will make the [date filter](filters.md#date) automatically convert dates parsed through the date filter to the appropriate timezone offset.
+Creating Multiple Intances of Swig <a name="multiple-instances" href="#multiple-instances">#</a>
+----------------------------------
+Sometimes you may find you need more than one instance of the Swig template engine in the same program runtime. For example; one for HTTP responses, one for HTML emails, and one for plain text emails. You can do this with the `.engine()` constructor.
+
+ var templateEngine = swig.engine({
+ root: '/templates/',
+ });
+
+ templateEngine.compileFile('index.html');
+
+ // ...
+ // Later in your code, maybe in another module, you could do this:
+
+ var emailEngine = swig.engine({
+ root: '/emails/',
+ });
+
+ emailEngine.compileFile('email.txt');
+
+Any of the options accepted by `.init()` will work in `.engine()`, and the defaults are exactly the same. In fact, you can call `.init()` to set a global configuration and then create multiple Swig engines which will inherit your global configs elsewhere in your program.
+
+ swig.init({
+ allowErrors: true,
+ encoding: 'utf8',
+ root: '/templates',
+ });
+
+ // ...
+ // Later in your code, maybe in another module, you could do this:
+
+ var childEngine = swig.engine({
+ allowErrors: false,
+ filters: {
+ foo: function (input) {
+ return 'bar';
+ }
+ }
+ });
+
+In the example above the configs option `allowErrors: false` and the custom `foo` filter will only apply to `childEngine`.
+
+### GOTCHA!
+Whenever the `tzOffset` option is set, either with a call to `.init()` or `.engine()` it will be set globally for all instances of the Swig engine. You shouldn't need to change this value from the same program runtime more than once, so it is probably a good idea to just set it in `.init()` and then leave it alone.
+
Parsing a Template <a name="parsing" href="#parsing">#</a>
------------------
@@ -69,6 +115,8 @@ You have 2 methods for creating a template object:
swig.compileFile("path/to/template/file.html");
swig.compile("Template string here", { filename: 'templateKey' });
+The `.compileFile()` and `compile()` methods are available on an instance of `.engine()` as well.
+
Rendering a Template <a name="rendering" href="#rendering">#</a>
--------------------
View
77 index.js
@@ -39,10 +39,12 @@ function TemplateError(error) {
}};
}
-function createTemplate(data, id) {
+function createTemplate(data, id, conf) {
var template = {
// Allows us to include templates from the compiled code
- compileFile: exports.compileFile,
+ compileFile: function (filepath) {
+ return exports.compileFile(filepath, conf);
+ },
// These are the blocks inside the template
blocks: {},
// Distinguish from other tokens
@@ -55,11 +57,11 @@ function createTemplate(data, id) {
render;
// The template token tree before compiled into javascript
- if (_config.allowErrors) {
- template.tokens = parser.parse.call(template, data, _config.tags, _config.autoescape);
+ if (conf.allowErrors) {
+ template.tokens = parser.parse.call(template, data, conf.tags, conf.autoescape);
} else {
try {
- template.tokens = parser.parse.call(template, data, _config.tags, _config.autoescape);
+ template.tokens = parser.parse.call(template, data, conf.tags, conf.autoescape);
} catch (e) {
return new TemplateError(e);
}
@@ -89,11 +91,11 @@ function createTemplate(data, id) {
].join(''));
template.render = function (context, parents) {
- if (_config.allowErrors) {
- return render.call(this, context, parents, _config.filters, _, _config.extensions);
+ if (conf.allowErrors) {
+ return render.call(this, context, parents, conf.filters, _, conf.extensions);
}
try {
- return render.call(this, context, parents, _config.filters, _, _config.extensions);
+ return render.call(this, context, parents, conf.filters, _, conf.extensions);
} catch (e) {
return new TemplateError(e);
}
@@ -102,28 +104,31 @@ function createTemplate(data, id) {
return template;
}
-function getTemplate(source, options) {
- var key = options.filename || source;
- if (_config.cache || options.cache) {
- if (!CACHE.hasOwnProperty(key)) {
- CACHE[key] = createTemplate(source, key);
+function getTemplate(source, options, conf) {
+ var key = options.filename || source,
+ cache = conf.CACHE || CACHE;
+ if (conf.cache || options.cache) {
+ if (!cache.hasOwnProperty(key)) {
+ cache[key] = createTemplate(source, key, conf);
}
- return CACHE[key];
+ return cache[key];
}
- return createTemplate(source, key);
+ return createTemplate(source, key, conf);
}
-exports.compileFile = function (filepath) {
- var tpl, get;
+exports.compileFile = function (filepath, conf) {
+ conf = conf || _config;
+ var tpl, get,
+ cache = conf.CACHE || CACHE;
if (filepath[0] === '/') {
filepath = filepath.substr(1);
}
- if (_config.cache && CACHE.hasOwnProperty(filepath)) {
- return CACHE[filepath];
+ if (conf.cache && cache.hasOwnProperty(filepath)) {
+ return cache[filepath];
}
if (typeof window !== 'undefined') {
@@ -131,12 +136,12 @@ exports.compileFile = function (filepath) {
}
get = function () {
- var file = ((/^\//).test(filepath) || (/^.:/).test(filepath)) ? filepath : _config.root + '/' + filepath,
- data = fs.readFileSync(file, config.encoding);
- tpl = getTemplate(data, { filename: filepath });
+ var file = ((/^\//).test(filepath) || (/^.:/).test(filepath)) ? filepath : conf.root + '/' + filepath,
+ data = fs.readFileSync(file, conf.encoding);
+ tpl = getTemplate(data, { filename: filepath }, conf);
};
- if (_config.allowErrors) {
+ if (conf.allowErrors) {
get();
} else {
try {
@@ -148,11 +153,31 @@ exports.compileFile = function (filepath) {
return tpl;
};
-exports.compile = function (source, options) {
+exports.compile = function (source, options, conf) {
+ conf = conf || _config;
options = options || {};
- var tmpl = getTemplate(source, options || {});
+ var tmpl = getTemplate(source, options, conf);
return function (source, options) {
- return tmpl.render(source, options);
+ var rv = tmpl.render(source, options);
+ return rv;
};
};
+
+exports.engine = function (localConfig) {
+ localConfig = _.extend({}, _config, localConfig);
+ localConfig.filters = _.extend({}, _config.filters, localConfig.filters);
+ localConfig.tags = _.extend({}, _config.tags, localConfig.tags);
+ localConfig.CACHE = {};
+
+ var engine = {
+ compileFile: function (filepath) {
+ return exports.compileFile(filepath, localConfig);
+ },
+ compile: function (source, options) {
+ return exports.compile(source, options, localConfig);
+ }
+ };
+
+ return engine;
+};
View
165 index.test.js
@@ -1,5 +1,6 @@
var swig = require('./index'),
- testCase = require('nodeunit').testCase;
+ testCase = require('nodeunit').testCase,
+ ENOENT_RX = /<pre>Error\: ENOENT, no such file or directory/i;
exports.init = testCase({
'custom filtersm': function (test) {
@@ -20,9 +21,9 @@ exports.init = testCase({
allowErrors: false
});
var tpl = swig.compileFile('foobar.html');
- test.ok((/<pre>Error\: ENOENT, no such file or directory/i).test(tpl.render()), 'pushes a render function with the error');
+ test.ok(ENOENT_RX.test(tpl.render()), 'pushes a render function with the error');
tpl = swig.compileFile('includes_notfound.html');
- test.ok((/<pre>Error\: ENOENT, no such file or directory/i).test(tpl.render()), 'renders the error when includes a file that is not found');
+ test.ok(ENOENT_RX.test(tpl.render()), 'renders the error when includes a file that is not found');
test.done();
},
@@ -153,3 +154,161 @@ exports['double-escape forward-slash'] = function (test) {
test.done();
};
+
+exports.engine = testCase({
+ setUp: (function () {
+ var engineA, engineB;
+
+ engineA = swig.engine({
+ filters: {
+ foo: function (input) {
+ return 'engineA';
+ }
+ },
+ root: __dirname + '/tests/templates',
+ allowErrors: false,
+ extensions: {
+ foobar: function () {
+ engineA.extensionTestValue = 1;
+ }
+ },
+ tags: {
+ foo: function () {
+ return '_ext.foobar();';
+ }
+ }
+ });
+ engineA.extensionTestValue = null;
+
+ engineB = swig.engine({
+ filters: {
+ foo: function (input) {
+ return 'engineB';
+ }
+ },
+ root: __dirname + '/tests/templates',
+ allowErrors: true,
+ extensions: {
+ foobar: function () {
+ engineB.extensionTestValue = 1;
+ }
+ },
+ tags: {
+ foo: function () {
+ return '_ext.foobar();';
+ }
+ }
+ });
+ engineB.extensionTestValue = null;
+
+ return function (callback) {
+ this.engineA = engineA;
+ this.engineB = engineB;
+ this.stashCompile = swig.compile;
+ this.stashCompileFile = swig.compileFile;
+ callback();
+ };
+ }()),
+
+ tearDown: function (callback) {
+ swig.compile = this.stashCompile;
+ swig.compileFile = this.stashCompileFile;
+ callback();
+ },
+
+ 'usage A custom filters': function (test) {
+ var tpl = this.engineA.compile('{{ asdf|foo }}');
+ test.strictEqual(tpl({ asdf: 'blah' }), 'engineA');
+ test.done();
+ },
+
+ 'usage B custom filters': function (test) {
+ var tpl = this.engineB.compile('{{ asdf|foo }}');
+ test.strictEqual(tpl({ asdf: 'blah' }), 'engineB');
+ test.done();
+ },
+
+ 'usage A allowErrors = false': function (test) {
+ var tpl = this.engineA.compileFile('foobar.html');
+ test.ok(ENOENT_RX.test(tpl.render()), 'pushes a render function with the error');
+ tpl = this.engineA.compileFile('includes_notfound.html');
+ test.ok(ENOENT_RX.test(tpl.render()), 'renders the error when includes a file that is not found');
+ test.done();
+ },
+
+ 'usage B allowErrors = true': function (test) {
+ test.throws(function () {
+ this.engineA.compileFile('barfoo.html');
+ }, 'throws when allowErrors is true');
+
+ test.done();
+ },
+
+ 'custom extensions': function (test) {
+ test.strictEqual(this.engineA.extensionTestValue, null, 'extension A test value init null');
+ test.strictEqual(this.engineB.extensionTestValue, null, 'extension B test value init null');
+
+ var tpl = this.engineA.compile('{% foo %}');
+ tpl();
+ test.strictEqual(this.engineA.extensionTestValue, 1, 'extension A test value');
+
+ tpl = this.engineB.compile('{% foo %}');
+ tpl();
+ test.strictEqual(this.engineB.extensionTestValue, 1, 'extension B test value');
+ test.done();
+ },
+
+ 'use global defaults': function (test) {
+ var engine = swig.engine();
+
+ swig.compile = function (source, options, conf) {
+ test.strictEqual(conf.allowErrors, false, 'allowErrors defaults to false');
+ test.strictEqual(conf.encoding, 'utf8', 'encoding defaults to utf8');
+ test.strictEqual(conf.root, '/', 'root defaults to /');
+ };
+
+ swig.compileFile = function (filepath, conf) {
+ test.strictEqual(conf.autoescape, true, 'autoescape defaults to true');
+ test.strictEqual(conf.cache, true, 'cache defaults to true');
+ test.strictEqual(conf.tzOffset, 0, 'tzOffset defaults to 0');
+ };
+
+ engine.compile();
+ engine.compileFile();
+
+ test.expect(6);
+ test.done();
+ },
+
+ 'configure global defaults': function (test) {
+ swig.init({
+ allowErrors: true,
+ root: '/foo/templates',
+ cache: false
+ });
+
+ var engine = swig.engine({
+ root: '/bar/templates',
+ encoding: 'bollocks'
+ });
+
+ swig.compile = function (source, options, conf) {
+ test.strictEqual(conf.allowErrors, true, 'allowErrors should be true');
+ test.strictEqual(conf.encoding, 'bollocks', 'encoding should be bollocks');
+ test.strictEqual(conf.root, '/bar/templates', 'root should be /bar/templates');
+ };
+
+ swig.compileFile = function (filepath, conf) {
+ test.strictEqual(conf.autoescape, true, 'autoescape defaults to true');
+ test.strictEqual(conf.cache, false, 'cache should be false');
+ test.strictEqual(conf.tzOffset, 0, 'tzOffset defaults to 0');
+ };
+
+
+ engine.compile();
+ engine.compileFile();
+
+ test.expect(6);
+ test.done();
+ }
+});
Please sign in to comment.
Something went wrong with that request. Please try again.