From c4b345f14c33d66a7e35606315a32fbf46721ea3 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Thu, 7 Nov 2013 08:42:57 -0800 Subject: [PATCH] Smaller API surface area Removed cache* functions altogether. mustache.parse is all that's really needed. --- CHANGES | 6 +- mustache.js | 145 ++++++++++++++------------------------------ test/render-test.js | 2 +- test/writer-test.js | 36 ----------- 4 files changed, 49 insertions(+), 140 deletions(-) delete mode 100644 test/writer-test.js diff --git a/CHANGES b/CHANGES index 6b7702c..4c9fca1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,12 +1,8 @@ = HEAD - * Remove compile* writer functions and replace them with cache* equivalents. This - reflects more clearly what the functions are actually doing, and also provides - a better basis for understanding what writer.clearCache() does. - * Do not cache dynamically loaded partials. + * Remove compile* writer functions, use mustache.parse instead. Smaller API. * Throw an error when rendering a template that contains higher-order sections and the original template is not provided. - * Remove partials argument from low-level writer.render. * Remove low-level Context.make function. * Better code readability and inline documentation. diff --git a/mustache.js b/mustache.js index ce39f8f..5784314 100644 --- a/mustache.js +++ b/mustache.js @@ -272,7 +272,7 @@ } /** - * A simple string scanner that is used by the mustache.parse to find + * A simple string scanner that is used by the template parser to find * tokens in template strings. */ function Scanner(string) { @@ -388,62 +388,27 @@ /** * A Writer knows how to take a stream of tokens and render them to a - * string, given a context. It also maintains a cache of templates and - * partials to avoid the need to parse the same template twice. - * - * The `partialLoader` argument may be a function that is used to load - * partials on the fly as they are needed if they are not found in cache. - * Partials loaded in this manner are not cached. + * string, given a context. It also maintains a cache of templates to + * avoid the need to parse the same template twice. */ - function Writer(partialLoader) { - this.clearCache(); - - if (isFunction(partialLoader)) { - this._loadPartial = partialLoader; - } + function Writer() { + this._cache = {}; } /** - * Clears all cached templates and partials in this writer. + * Clears all cached templates in this writer. */ Writer.prototype.clearCache = function () { this._cache = {}; - this._partialCache = {}; - }; - - /** - * Gets an array of tokens for the partial with the given `name`, either - * from cache or from this writer's partial loading function. - */ - Writer.prototype.getPartial = function (name) { - if (!(name in this._partialCache) && this._loadPartial) { - var template = this._loadPartial(name); - if (template) { - // Intentionally do not cache dynamically-loaded templates. - return mustache.parse(template); - } - } - - return this._partialCache[name]; - }; - - /** - * Parses and caches the given `template` with the given `name` as a - * partial. This writer may then render other templates that reference - * that partial using the ">" tag and that partial's name. Returns the - * array of tokens that is generated from the parse. - */ - Writer.prototype.cachePartial = function (name, template, tags) { - return (this._partialCache[name] = this.cache(template, tags)); }; /** * Parses and caches the given `template` and returns the array of tokens * that is generated from the parse. */ - Writer.prototype.cache = function (template, tags) { + Writer.prototype.parse = function (template, tags) { if (!(template in this._cache)) { - this._cache[template] = mustache.parse(template, tags); + this._cache[template] = parseTemplate(template, tags); } return this._cache[template]; @@ -452,29 +417,35 @@ /** * High-level method that is used to render the given `template` with * the given `view`. + * + * The optional `partials` argument may be an object that contains the + * names and templates of partials that are used in the template. It may + * also be a function that is used to load partial templates on the fly + * that takes a single argument: the name of the partial. */ - Writer.prototype.render = function (template, view) { - var tokens = this.cache(template); + Writer.prototype.render = function (template, view, partials) { + var tokens = this.parse(template); var context = (view instanceof Context) ? view : new Context(view); - return this.renderTokens(tokens, context, template); + return this.renderTokens(tokens, context, partials, template); }; /** * Low-level method that renders the given array of `tokens` using - * the given `context`. + * the given `context` and `partials`. * - * Note: The `template` string is only needed for templates that use - * higher-order sections to extract the portion of the original template - * that was contained in that section. + * Note: The `originalTemplate` is only ever used to extract the portion + * of the original template that was contained in a higher-order section. + * If the template doesn't use higher-order sections, this argument may + * be omitted. */ - Writer.prototype.renderTokens = function (tokens, context, template) { + Writer.prototype.renderTokens = function (tokens, context, partials, originalTemplate) { var buffer = ''; // This function is used to render an arbitrary template - // in the current context by higher-order functions. + // in the current context by higher-order sections. var self = this; function subRender(template) { - return self.render(template, context); + return self.render(template, context, partials); } var token, value; @@ -488,21 +459,21 @@ if (isArray(value)) { for (var j = 0, jlen = value.length; j < jlen; ++j) { - buffer += this.renderTokens(token[4], context.push(value[j]), template); + buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate); } } else if (typeof value === 'object' || typeof value === 'string') { - buffer += this.renderTokens(token[4], context.push(value), template); + buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate); } else if (isFunction(value)) { - if (typeof template !== 'string') { + if (typeof originalTemplate !== 'string') { throw new Error('Cannot use higher-order sections without the original template'); } // Extract the portion of the original template that the section contains. - value = value.call(context.view, template.slice(token[3], token[5]), subRender); + value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender); if (value != null) buffer += value; } else { - buffer += this.renderTokens(token[4], context, template); + buffer += this.renderTokens(token[4], context, partials, originalTemplate); } break; @@ -512,13 +483,14 @@ // Use JavaScript's definition of falsy. Include empty arrays. // See https://github.com/janl/mustache.js/issues/186 if (!value || (isArray(value) && value.length === 0)) { - buffer += this.renderTokens(token[4], context, template); + buffer += this.renderTokens(token[4], context, partials, originalTemplate); } break; case '>': - value = this.getPartial(token[1]); - if (value != null) buffer += this.renderTokens(value, context, template); + if (!partials) continue; + value = this.parse(isFunction(partials) ? partials(token[1]) : partials[token[1]]); + if (value != null) buffer += this.renderTokens(value, context, partials, originalTemplate); break; case '&': value = context.lookup(token[1]); @@ -541,60 +513,33 @@ mustache.version = "0.7.3"; mustache.tags = [ "{{", "}}" ]; - // Export the parsing function. - mustache.parse = parseTemplate; - - // Export the escaping function so that the user may override it. - // See https://github.com/janl/mustache.js/issues/244 - mustache.escape = escapeHtml; - // All high-level mustache.* functions use this writer. var defaultWriter = new Writer(); /** - * Clears all cached templates and partials in the default writer. + * Clears all cached templates in the default writer. */ mustache.clearCache = function () { return defaultWriter.clearCache(); }; /** - * Caches the given partial in the default writer and returns the - * array of tokens it contains. - */ - mustache.cachePartial = function (name, template, tags) { - return defaultWriter.cachePartial(name, template, tags); - }; - - /** - * Caches the given template in the default writer and returns the + * Parses and caches the given template in the default writer and returns the * array of tokens it contains. + * + * Pre-caching templates ahead of time avoids the need to parse templates + * on the fly as they are rendered. */ - mustache.cache = function (template, tags) { - return defaultWriter.cache(template, tags); + mustache.parse = function (template, tags) { + return defaultWriter.parse(template, tags); }; /** - * Renders the `template` with the given `view` using the default writer. - * - * The optionals `partials` argument may either be a function that is used to load - * partials on the fly or an object containing names and templates of partials. If - * it is an object, the partials will be cached in the default writer. + * Renders the `template` with the given `view` and `partials` using the + * default writer. */ mustache.render = function (template, view, partials) { - if (partials) { - if (isFunction(partials)) { - defaultWriter._loadPartial = partials; - } else { - for (var name in partials) { - if (partials.hasOwnProperty(name)) { - defaultWriter.cachePartial(name, partials[name]); - } - } - } - } - - return defaultWriter.render(template, view); + return defaultWriter.render(template, view, partials); }; // This is here for backwards compatibility with 0.4.x. @@ -608,6 +553,10 @@ } }; + // Export the escaping function so that the user may override it. + // See https://github.com/janl/mustache.js/issues/244 + mustache.escape = escapeHtml; + // Export these mainly for testing, but also for advanced usage. mustache.Scanner = Scanner; mustache.Context = Context; diff --git a/test/render-test.js b/test/render-test.js index acec47f..a011678 100644 --- a/test/render-test.js +++ b/test/render-test.js @@ -17,7 +17,7 @@ function getView(testName) { function getPartial(testName) { try { return getContents(testName, 'partial'); - } catch (e) { + } catch (error) { // No big deal. Not all tests need to test partial support. } } diff --git a/test/writer-test.js b/test/writer-test.js deleted file mode 100644 index fc552db..0000000 --- a/test/writer-test.js +++ /dev/null @@ -1,36 +0,0 @@ -require('./helper'); -var Writer = Mustache.Writer; - -describe('A new Mustache.Writer', function () { - var writer; - - describe('with a cached partial', function () { - beforeEach(function () { - writer = new Writer; - }); - - it('caches partials by content, not name', function () { - writer.cachePartial('partial', 'partial one'); - assert.equal(writer.render('{{>partial}}'), 'partial one'); - - writer.cachePartial('partial', 'partial two'); - assert.equal(writer.render('{{>partial}}'), 'partial two'); - }); - }); - - describe('with a partial loader', function () { - var partial; - beforeEach(function () { - partial = 'The content of the partial.'; - writer = new Writer(function (name) { - assert.equal(name, 'partial'); - return partial; - }); - }); - - it('loads partials correctly', function () { - assert.equal(writer.render('{{>partial}}'), partial); - }); - }); - -});