From 6159198227caa1ddd2b63ab65e076bea0681fc86 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Sat, 1 Jun 2013 01:56:05 -0700 Subject: [PATCH 001/107] Refactor to not use Handlebars as global; getTemplate & getLayout methods --- index.js | 36 ++++++++-- package.json | 6 +- server/layoutFinder.js | 21 +++--- shared/globals.js | 3 - shared/helpers.js | 138 ++++++++++++++++++++++++++------------- shared/templateFinder.js | 30 ++++----- 6 files changed, 148 insertions(+), 86 deletions(-) delete mode 100644 shared/globals.js diff --git a/index.js b/index.js index 53a248e..ca06225 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,32 @@ -require('./shared/globals'); +var Handlebars = require('handlebars'); -exports.templateFinder = require('./shared/templateFinder'); -if(!this.window) { - exports.layoutFinder = require('./server/layoutFinder'); -} \ No newline at end of file +/** + * Export the `Handlebars` object, so other modules can add helpers, partials, etc. + */ +exports.Handlebars = Handlebars; + +/** + * `getTemplate` is available on both client and server. + */ +exports.getTemplate = require('./shared/templateFinder')(Handlebars).getTemplate; + +/** + * `getLayout` should only be used on the server. + */ +if (typeof window === 'undefined') { + exports.getLayout = require('./server/layoutFinder')(Handlebars).getLayout; +} else { + exports.getLayout = function() { + throw new Error('getLayout is only available on the server.'); + }; +} + +/** + * Register helpers, available on both client and server. + */ +var handlebarsHelpers = require('./shared/helpers')(Handlebars, exports.getTemplate); + +for (var key in handlebarsHelpers) { + if (!handlebarsHelpers.hasOwnProperty(key)) continue; + Handlebars.registerHelper(key, handlebarsHelpers[key]); +} diff --git a/package.json b/package.json index 45b75b7..96f1d40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.0", + "version": "0.0.1", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -8,10 +8,10 @@ }, "repository": { "type": "git", - "url": "http://github.com/hurrymaplelad/rendr-handlebars.git" + "url": "http://github.com/airbnb/rendr-handlebars.git" }, "peerDependencies": { - "rendr": "0.2.x" + "rendr": "0.4.6-rc.1" }, "dependencies": { "underscore": "~1.4.4", diff --git a/server/layoutFinder.js b/server/layoutFinder.js index 7ff2a10..9968fdd 100644 --- a/server/layoutFinder.js +++ b/server/layoutFinder.js @@ -1,14 +1,15 @@ +/*global rendr*/ var fs = require('fs'); -module.exports = { - getTemplate: function (name, callback) { - var layoutPath = rendr.entryPath + '/app/templates/' + name + '.hbs'; - fs.readFile(layoutPath, 'utf8', function (err, str) { - if (err) { - return callback(err); - } - var template = Handlebars.compile(str); - callback(null, template); - }); +module.exports = function(Handlebars) { + return { + getLayout: function(name, callback) { + var layoutPath = rendr.entryPath + '/app/templates/' + name + '.hbs'; + fs.readFile(layoutPath, 'utf8', function (err, str) { + if (err) return callback(err); + var template = Handlebars.compile(str); + callback(null, template); + }); + } } }; diff --git a/shared/globals.js b/shared/globals.js deleted file mode 100644 index 7396515..0000000 --- a/shared/globals.js +++ /dev/null @@ -1,3 +0,0 @@ -if (!this.window) { - global.Handlebars = require('handlebars'); -} \ No newline at end of file diff --git a/shared/helpers.js b/shared/helpers.js index 1d444c3..69e1172 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,51 +1,95 @@ -var BaseView, Handlebars, modelUtils, templateFinder, _; - -templateFinder = require('./templateFinder'); -Handlebars = require('handlebars'); -_ = require('underscore'); +var _ = require('underscore'); // Lazy-required. -BaseView = null; -modelUtils = null; - -module.exports = { - view: function(viewName, block) { - var ViewClass, app, html, options, view; - - BaseView = BaseView || require('rendr/shared/base/view'); - modelUtils = modelUtils || require('rendr/shared/modelUtils'); - viewName = modelUtils.underscorize(viewName); - options = block.hash || {}; - app = this._app; - if (app != null) { - options.app = app; - } +var BaseView = null, + modelUtils = null; + +module.exports = function(Handlebars, getTemplate) { + var oldEach = Handlebars.helpers.each; + + return { + view: function(viewName, block) { + var ViewClass, html, options, view, data; + + BaseView = BaseView || require('rendr/shared/base/view'); + modelUtils = modelUtils || require('rendr/shared/modelUtils'); + viewName = modelUtils.underscorize(viewName); + options = block.hash || {}; + data = block.data || {}; + + // Pass through a reference to the app. + var app = this._app || data._app; + if (app) { + options.app = app; + } + + // Pass through a reference to the parent view. + parentView = this._view || data._view + if (parentView) { + options.parentView = parentView; + } + + // get the Backbone.View based on viewName + ViewClass = BaseView.getView(viewName); + view = new ViewClass(options); + + // create the outerHTML using className, tagName + html = view.getHtml(); + return new Handlebars.SafeString(html); + }, + + partial: function(templateName, block) { + var data, html, options, template; - // Pass through a reference to the parent view. - options.parentView = this._view; - - // get the Backbone.View based on viewName - ViewClass = BaseView.getView(viewName); - view = new ViewClass(options); - - // create the outerHTML using className, tagName - html = view.getHtml(); - return new Handlebars.SafeString(html); - }, - - partial: function(templateName, block) { - var data, html, options, template; - - template = templateFinder.getTemplate(templateName); - options = block.hash || {}; - data = _.isEmpty(options) ? this : options.context ? options.context : options; - data = _.clone(data); - data._app = data._app || this._app; - html = template(data); - return new Handlebars.SafeString(html); - }, - - json: function(object) { - return new Handlebars.SafeString(JSON.stringify(object) || 'null'); - } + template = getTemplate(templateName); + options = block.hash || {}; + data = _.isEmpty(options) ? this : options.context ? options.context : options; + data = _.clone(data); + data._app = data._app || this._app; + html = template(data); + return new Handlebars.SafeString(html); + }, + + json: function(object) { + return new Handlebars.SafeString(JSON.stringify(object) || 'null'); + }, + + /** + * Extend `each` to pass through important context. + */ + each: function(context, options) { + options.data = Handlebars.createFrame(options.data || {}); + + // Make sure `this._app`, `this._view`, etc are available. + _.extend(options.data, getOptionsFromContext(this)); + + // Call the original helper with new context. + return oldEach.call(this, context, options); + } + }; }; + +/** + * Grab important underscored properties from the current context. + * These properties come from BaseView::decorateTemplateData(). + */ +function getOptionsFromContext(obj) { + var options, keys, value; + + keys = [ + '_app', + '_view', + '_model', + '_collection' + ]; + + options = keys.reduce(function(memo, key) { + value = obj[key]; + if (value) { + memo[key] = value; + } + return memo; + }, {}); + + return options; +} diff --git a/shared/templateFinder.js b/shared/templateFinder.js index bebc84e..ae64c1c 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -1,20 +1,14 @@ /*global rendr*/ - -var Handlebars, handlebarsHelpers, templates; - -Handlebars = require('handlebars'); -handlebarsHelpers = require('./helpers'); - -for (var key in handlebarsHelpers) { - if (!handlebarsHelpers.hasOwnProperty(key)) continue; - Handlebars.registerHelper(key, handlebarsHelpers[key]); -} - -templates = null; - -exports.getTemplate = function(templateName) { - /* Allow compiledTemplates to be created asynchronously. - */ - templates = templates || require(rendr.entryPath + '/app/templates/compiledTemplates')(Handlebars); - return templates[templateName]; +var templates = null; + +module.exports = function(Handlebars) { + return { + getTemplate: function(templateName) { + /** + * Allow compiledTemplates to be created asynchronously. + */ + templates = templates || require(rendr.entryPath + '/app/templates/compiledTemplates')(Handlebars); + return templates[templateName]; + } + } }; From acdcf9bca7d87e25b45bac00c5504ca9045903e3 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 28 Jun 2013 18:45:55 -0700 Subject: [PATCH 002/107] Create and expose a 'registerHelpers' method, for use by app --- index.js | 20 +++++++++++++++----- package.json | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index ca06225..5065dac 100644 --- a/index.js +++ b/index.js @@ -23,10 +23,20 @@ if (typeof window === 'undefined') { /** * Register helpers, available on both client and server. + * + * Export it so other modules can register helpers as well. */ -var handlebarsHelpers = require('./shared/helpers')(Handlebars, exports.getTemplate); +exports.registerHelpers = function registerHelpers(helpersModule) { + var helpers = helpersModule(Handlebars, exports.getTemplate); -for (var key in handlebarsHelpers) { - if (!handlebarsHelpers.hasOwnProperty(key)) continue; - Handlebars.registerHelper(key, handlebarsHelpers[key]); -} + for (var key in helpers) { + if (!helpers.hasOwnProperty(key)) continue; + Handlebars.registerHelper(key, helpers[key]); + } +}; + +/** + * Register the pre-bundled Rendr helpers. + */ +var rendrHelpers = require('./shared/helpers'); +exports.registerHelpers(rendrHelpers); diff --git a/package.json b/package.json index 96f1d40..03dce35 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "url": "http://github.com/airbnb/rendr-handlebars.git" }, "peerDependencies": { - "rendr": "0.4.6-rc.1" + "rendr": "~0.4.7" }, "dependencies": { "underscore": "~1.4.4", From a4f62739876767ffac59f67a06d31ba2807023b4 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 28 Jun 2013 18:49:54 -0700 Subject: [PATCH 003/107] Release 0.0.2. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03dce35..adb9b2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.1", + "version": "0.0.2", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From 4ca5632df50d3a11f7c3420d4ae03bd6b62156eb Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 28 Jun 2013 19:11:52 -0700 Subject: [PATCH 004/107] Update the peerDependency for rendr --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index adb9b2e..50d9072 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "url": "http://github.com/airbnb/rendr-handlebars.git" }, "peerDependencies": { - "rendr": "~0.4.7" + "rendr": "~0.4.8-alpha.01" }, "dependencies": { "underscore": "~1.4.4", From a63fcc5db6fd7163892a4c2a6ad8974bb09144e9 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Sat, 29 Jun 2013 09:34:31 -0700 Subject: [PATCH 005/107] Move rendr to dependencies from peerDependencies Because when it's in peerDependencies, it can't require the module for use by the 'view' Handlebars helper. --- package.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 50d9072..7d4ed10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.2", + "version": "0.0.3", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -10,12 +10,10 @@ "type": "git", "url": "http://github.com/airbnb/rendr-handlebars.git" }, - "peerDependencies": { - "rendr": "~0.4.8-alpha.01" - }, "dependencies": { "underscore": "~1.4.4", - "handlebars": "git://github.com/spikebrehm/handlebars.js.git#0687c7016c62122ab160a8683817a931b03354ad" + "handlebars": "git://github.com/spikebrehm/handlebars.js.git#0687c7016c62122ab160a8683817a931b03354ad", + "rendr": "~0.4.8" }, "keywords": [ "rendr", From 4880255028c02977773e5c0c666509d7d04690e9 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 12 Jul 2013 15:47:00 -0700 Subject: [PATCH 006/107] Update Handlebars to one commit past 1.0.12, to support CommonJS. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d4ed10..ba5fc4a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "underscore": "~1.4.4", - "handlebars": "git://github.com/spikebrehm/handlebars.js.git#0687c7016c62122ab160a8683817a931b03354ad", + "handlebars": "git://github.com/spikebrehm/handlebars.js.git#efca3c8ef5665fe3cf2802f866db0c73755824e0", "rendr": "~0.4.8" }, "keywords": [ From 03c3c7d03ff1aef7be13aeab8d25c27b7a506cee Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 12 Jul 2013 15:47:30 -0700 Subject: [PATCH 007/107] Release 0.0.4. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ba5fc4a..f576b6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.3", + "version": "0.0.4", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From ace4328c70bc838d50d1830f6986183d09154818 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Sun, 14 Jul 2013 12:32:28 -0700 Subject: [PATCH 008/107] 0.0.5: Use ref of handlebars that includes parser.js --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f576b6b..1ce2112 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.4", + "version": "0.0.5", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -12,7 +12,7 @@ }, "dependencies": { "underscore": "~1.4.4", - "handlebars": "git://github.com/spikebrehm/handlebars.js.git#efca3c8ef5665fe3cf2802f866db0c73755824e0", + "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e", "rendr": "~0.4.8" }, "keywords": [ From 9ffe9befa793a68a39d20bbb36a5cc648b151e42 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Wed, 31 Jul 2013 10:18:24 -0700 Subject: [PATCH 009/107] 0.0.6: Add support for multiple template source files. Previously, this module was hardcoded to look for pre-compiled Handlebars templates in `rendr.entryPath + '/app/templates/compiledTemplates'`. Now, that's the default, but apps that use this module can manipulate the `exports.templatePatterns` array to specify the template path patterns that match a given source file. This is useful for when a Rendr app supports different sets of templates for different pages; for example, an app could have a set of desktop templates and a set of mobile templates. --- index.js | 10 +++- package.json | 12 ++-- shared/templateFinder.js | 59 ++++++++++++++++--- .../app/templates/compiledTemplates.js | 6 ++ test/fixtures/app/templates/otherTemplates.js | 6 ++ test/shared/templateFinder.test.js | 31 ++++++++++ 6 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 test/fixtures/app/templates/compiledTemplates.js create mode 100644 test/fixtures/app/templates/otherTemplates.js create mode 100644 test/shared/templateFinder.test.js diff --git a/index.js b/index.js index 5065dac..4bd6c30 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ -var Handlebars = require('handlebars'); +var Handlebars = require('handlebars') + , templateFinder = require('./shared/templateFinder')(Handlebars); /** * Export the `Handlebars` object, so other modules can add helpers, partials, etc. @@ -8,7 +9,12 @@ exports.Handlebars = Handlebars; /** * `getTemplate` is available on both client and server. */ -exports.getTemplate = require('./shared/templateFinder')(Handlebars).getTemplate; +exports.getTemplate = templateFinder.getTemplate; + +/** + * Expose `templatePatterns` for manipulating how `getTemplate` finds templates. + */ +exports.templatePatterns = templateFinder.templatePatterns; /** * `getLayout` should only be used on the server. diff --git a/package.json b/package.json index 1ce2112..73a0d23 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "rendr-handlebars", - "version": "0.0.5", + "version": "0.0.6", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "mocha test/**/*.test.js" }, "repository": { "type": "git", @@ -13,16 +13,20 @@ "dependencies": { "underscore": "~1.4.4", "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e", - "rendr": "~0.4.8" + "rendr": "~0.4.8", + "minimatch": "~0.2.12" }, "keywords": [ "rendr", "handlebars", "templates" ], - "author": "", + "author": "Spike Brehm ", "license": "MIT", "engines": { "npm": ">=1.2" + }, + "devDependencies": { + "mocha": "~1.12.0" } } diff --git a/shared/templateFinder.js b/shared/templateFinder.js index ae64c1c..14bae6f 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -1,14 +1,57 @@ /*global rendr*/ -var templates = null; +var minimatch = require('minimatch') + , format = require('util').format + , cachedTemplates = {}; module.exports = function(Handlebars) { - return { - getTemplate: function(templateName) { - /** - * Allow compiledTemplates to be created asynchronously. - */ - templates = templates || require(rendr.entryPath + '/app/templates/compiledTemplates')(Handlebars); - return templates[templateName]; + + /** + * Provide a way for apps to specify that different template name patterns + * should use different compiled template files. + * + * The default pattern '**' is very greedy; it matches anything, including nested paths. + * To add rules that should match before this default rule, `unshift` them from this array. + */ + var templatePatterns = [{ + pattern: '**', + src: rendr.entryPath + '/app/templates/compiledTemplates' + }]; + + /** + * Given a template name, return the compiled Handlebars template. + */ + function getTemplate(templateName) { + /** + * Find the correct source file for this template. + */ + var src = getSrcForTemplate(templateName); + + /** + * Allow compiledTemplates to be created asynchronously. + */ + cachedTemplates[src] = cachedTemplates[src] || require(src)(Handlebars); + return cachedTemplates[src][templateName]; + } + + /** + * For a given template name, find the correct compiled templates source file + * based on pattern matching on the template name. + */ + function getSrcForTemplate(templateName) { + var currentPattern = templatePatterns.filter(function(obj) { + return minimatch(templateName, obj.pattern); + })[0]; + + if (currentPattern == null) { + throw new Error(format('No pattern found to match template "%s".', templateName)) } + + return currentPattern.src; + } + + return { + getTemplate: getTemplate, + getSrcForTemplate: getSrcForTemplate, + templatePatterns: templatePatterns } }; diff --git a/test/fixtures/app/templates/compiledTemplates.js b/test/fixtures/app/templates/compiledTemplates.js new file mode 100644 index 0000000..a7734ac --- /dev/null +++ b/test/fixtures/app/templates/compiledTemplates.js @@ -0,0 +1,6 @@ +module.exports = function(Handlebars) { + return { + 'my_test_template': function() {}, + 'subdir/other_template': function() {} + } +}; diff --git a/test/fixtures/app/templates/otherTemplates.js b/test/fixtures/app/templates/otherTemplates.js new file mode 100644 index 0000000..3628c61 --- /dev/null +++ b/test/fixtures/app/templates/otherTemplates.js @@ -0,0 +1,6 @@ +module.exports = function(Handlebars) { + return { + 'other_template_pattern/home_view': function() {}, + 'other_template_pattern/subdir/nav_view': function() {} + } +}; diff --git a/test/shared/templateFinder.test.js b/test/shared/templateFinder.test.js new file mode 100644 index 0000000..95c5b63 --- /dev/null +++ b/test/shared/templateFinder.test.js @@ -0,0 +1,31 @@ +/** + * We have to define the global `rendr`, which has an `entryPath` property. + * This is a reminder that globals are *bad*. + */ +global.rendr = { + entryPath: process.cwd() + '/test/fixtures' +}; + +var assert = require('assert') + , templateAdapter = require('../../index'); + +describe('templateFinder', function() { + describe('getTemplate', function() { + it('should support the default pattern if none given', function() { + assert.equal('function', typeof templateAdapter.getTemplate('my_test_template')); + assert.equal('function', typeof templateAdapter.getTemplate('subdir/other_template')); + }); + it('should support prepending to the templatePatterns to find templates in other files', function() { + /** + * Add the pattern for templates that should come from a different source file. + */ + templateAdapter.templatePatterns.unshift({ + pattern: 'other_template_pattern/**/*', + src: rendr.entryPath + '/app/templates/otherTemplates' + }); + + assert.equal('function', typeof templateAdapter.getTemplate('other_template_pattern/home_view')); + assert.equal('function', typeof templateAdapter.getTemplate('other_template_pattern/subdir/nav_view')); + }); + }); +}); From c0ecf299fe7c0582b18e5b7ed094536d90683583 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Wed, 31 Jul 2013 11:34:21 -0700 Subject: [PATCH 010/107] Using regexp instead of 'minimatch' for better browser compat --- package.json | 3 +-- shared/templateFinder.js | 9 ++++----- test/shared/templateFinder.test.js | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 73a0d23..a6d4af6 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,7 @@ "dependencies": { "underscore": "~1.4.4", "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e", - "rendr": "~0.4.8", - "minimatch": "~0.2.12" + "rendr": "~0.4.8" }, "keywords": [ "rendr", diff --git a/shared/templateFinder.js b/shared/templateFinder.js index 14bae6f..aff0534 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -1,6 +1,5 @@ /*global rendr*/ -var minimatch = require('minimatch') - , format = require('util').format +var format = require('util').format , cachedTemplates = {}; module.exports = function(Handlebars) { @@ -9,11 +8,11 @@ module.exports = function(Handlebars) { * Provide a way for apps to specify that different template name patterns * should use different compiled template files. * - * The default pattern '**' is very greedy; it matches anything, including nested paths. + * The default pattern `/.+/` is very greedy; it matches anything, including nested paths. * To add rules that should match before this default rule, `unshift` them from this array. */ var templatePatterns = [{ - pattern: '**', + pattern: /.+/, src: rendr.entryPath + '/app/templates/compiledTemplates' }]; @@ -39,7 +38,7 @@ module.exports = function(Handlebars) { */ function getSrcForTemplate(templateName) { var currentPattern = templatePatterns.filter(function(obj) { - return minimatch(templateName, obj.pattern); + return obj.pattern.test(templateName); })[0]; if (currentPattern == null) { diff --git a/test/shared/templateFinder.test.js b/test/shared/templateFinder.test.js index 95c5b63..1755862 100644 --- a/test/shared/templateFinder.test.js +++ b/test/shared/templateFinder.test.js @@ -20,7 +20,7 @@ describe('templateFinder', function() { * Add the pattern for templates that should come from a different source file. */ templateAdapter.templatePatterns.unshift({ - pattern: 'other_template_pattern/**/*', + pattern: /^other_template_pattern\//, src: rendr.entryPath + '/app/templates/otherTemplates' }); From 243396ecc3b48a5034be200ecf29ee32385e1234 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 2 Aug 2013 16:56:02 -0700 Subject: [PATCH 011/107] Can't use 'util' module in shared environment --- shared/templateFinder.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shared/templateFinder.js b/shared/templateFinder.js index aff0534..693d6c5 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -1,6 +1,5 @@ /*global rendr*/ -var format = require('util').format - , cachedTemplates = {}; +var cachedTemplates = {}; module.exports = function(Handlebars) { @@ -42,7 +41,7 @@ module.exports = function(Handlebars) { })[0]; if (currentPattern == null) { - throw new Error(format('No pattern found to match template "%s".', templateName)) + throw new Error('No pattern found to match template "' + templateName + '".'); } return currentPattern.src; From f4e5c2f82c8cbbb9ceca47696412802dbc9d4dd7 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 2 Aug 2013 17:07:10 -0700 Subject: [PATCH 012/107] Use rendr@0.4.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6d4af6..4caffea 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dependencies": { "underscore": "~1.4.4", "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e", - "rendr": "~0.4.8" + "rendr": "~0.4.10" }, "keywords": [ "rendr", From 261acba00f111d73c2fbdf476e14269c388a180a Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Thu, 24 Oct 2013 16:23:57 -0700 Subject: [PATCH 013/107] Fix use of {{partial}} inside block helper like {{each}} Before, `{{partial}}` was incorrectly looking for the `_app` property; it could find it in the helper context (`this._app`), but not in the helper options (`options.data._app`), which are utilized by block helpers like `{{#each}}` for passing through the app context. This adds a `getProperty(key, context, options)` method which DRYs up that logic. Also cleans up the context logic in `{{partial}}`. --- shared/helpers.js | 60 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 69e1172..ce34b26 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -8,45 +8,63 @@ module.exports = function(Handlebars, getTemplate) { var oldEach = Handlebars.helpers.each; return { - view: function(viewName, block) { - var ViewClass, html, options, view, data; + view: function(viewName, options) { + var ViewClass, html, viewOptions, view; BaseView = BaseView || require('rendr/shared/base/view'); modelUtils = modelUtils || require('rendr/shared/modelUtils'); viewName = modelUtils.underscorize(viewName); - options = block.hash || {}; - data = block.data || {}; + viewOptions = options.hash || {}; // Pass through a reference to the app. - var app = this._app || data._app; + var app = getProperty('_app', this, options); if (app) { - options.app = app; + viewOptions.app = app; } // Pass through a reference to the parent view. - parentView = this._view || data._view + var parentView = getProperty('_view', this, options); if (parentView) { - options.parentView = parentView; + viewOptions.parentView = parentView; } // get the Backbone.View based on viewName ViewClass = BaseView.getView(viewName); - view = new ViewClass(options); + view = new ViewClass(viewOptions); // create the outerHTML using className, tagName html = view.getHtml(); return new Handlebars.SafeString(html); }, - partial: function(templateName, block) { - var data, html, options, template; + partial: function(templateName, options) { + var data, html, context, template; template = getTemplate(templateName); - options = block.hash || {}; - data = _.isEmpty(options) ? this : options.context ? options.context : options; - data = _.clone(data); - data._app = data._app || this._app; - html = template(data); + + context = options.hash || {}; + + // First try to use Handlebars' hash arguments as the context for the + // partial, if present. + // + // ex: `{{partial "users/photo" user=user}}` + if (_.isEmpty(context)) { + // If there are no hash arguments given, then inherit the parent context. + // + // ex: `{{partial "users/photo"}}` + context = this; + } else { + // If a hash argument is given with key `context`, then use that as the context. + // + // ex: `{{partial "users/photo" context=user}}` + if (context.hasOwnProperty('context')) { + context = context.context; + } + } + context = _.clone(context); + + context._app = getProperty('_app', this, options); + html = template(context); return new Handlebars.SafeString(html); }, @@ -93,3 +111,13 @@ function getOptionsFromContext(obj) { return options; } + +/** + * Get a property that is being passed down through helpers, such as `_app` + * or `_view`. It can either live on the context, i.e. `this._app`, or in the + * `options.data` object passed to the helper, i.e. `options.data._app`, in the + * case of a block helper like `each`. + */ +function getProperty(key, context, options) { + return context[key] || (options.data || {})[key]; +} From c97c9cf69c19e8ef4d549d90354ca874b1743290 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Thu, 24 Oct 2013 16:35:40 -0700 Subject: [PATCH 014/107] More sane Rendr version ref: >=0.4.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4caffea..92380c0 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dependencies": { "underscore": "~1.4.4", "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e", - "rendr": "~0.4.10" + "rendr": ">=0.4.9" }, "keywords": [ "rendr", From 5e207bcd6a1d60f806d2a03b07d16ede16159fb0 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Thu, 24 Oct 2013 16:49:57 -0700 Subject: [PATCH 015/107] Release 0.0.7. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 92380c0..8833248 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.6", + "version": "0.0.7", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From 2489bfd303f8334fab31516a5ddd431e8ea5b70a Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Thu, 24 Oct 2013 17:05:50 -0700 Subject: [PATCH 016/107] Release 0.0.8. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8833248..b8a1a4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.7", + "version": "0.0.8", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From 9c110c837b45f5255e65f172709fe308ab2763e1 Mon Sep 17 00:00:00 2001 From: Christoph Neuroth Date: Sun, 3 Nov 2013 10:27:50 +0100 Subject: [PATCH 017/107] move rendr dependency to peerDependencies --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b8a1a4f..394e46b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ }, "dependencies": { "underscore": "~1.4.4", - "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e", + "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e" + }, + "peerDependencies": { "rendr": ">=0.4.9" }, "keywords": [ From d4a04778b78f4b49d66400838ef21931130f472e Mon Sep 17 00:00:00 2001 From: Christoph Neuroth Date: Sun, 3 Nov 2013 22:42:03 +0100 Subject: [PATCH 018/107] adjust engines.npm to ensure support for peerDeps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 394e46b..94a644a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "author": "Spike Brehm ", "license": "MIT", "engines": { - "npm": ">=1.2" + "npm": ">=1.2.10" }, "devDependencies": { "mocha": "~1.12.0" From 40e4c9ab7b17fdcb0fa5217ba54bc4c825609c5a Mon Sep 17 00:00:00 2001 From: Alex Indigo Date: Tue, 5 Nov 2013 18:58:46 -0800 Subject: [PATCH 019/107] Made it play nice with RequireJs powered setup. --- index.js | 3 ++- server/layoutFinder.js | 2 +- shared/helpers.js | 7 +++++-- shared/templateFinder.js | 2 +- test/shared/templateFinder.test.js | 4 ++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 4bd6c30..c52d573 100644 --- a/index.js +++ b/index.js @@ -20,7 +20,8 @@ exports.templatePatterns = templateFinder.templatePatterns; * `getLayout` should only be used on the server. */ if (typeof window === 'undefined') { - exports.getLayout = require('./server/layoutFinder')(Handlebars).getLayout; + var serverOnlyPath_layoutFinder = './server/layoutFinder'; + exports.getLayout = require(serverOnlyPath_layoutFinder)(Handlebars).getLayout; } else { exports.getLayout = function() { throw new Error('getLayout is only available on the server.'); diff --git a/server/layoutFinder.js b/server/layoutFinder.js index 9968fdd..8e7cea6 100644 --- a/server/layoutFinder.js +++ b/server/layoutFinder.js @@ -4,7 +4,7 @@ var fs = require('fs'); module.exports = function(Handlebars) { return { getLayout: function(name, callback) { - var layoutPath = rendr.entryPath + '/app/templates/' + name + '.hbs'; + var layoutPath = rendr.entryPath + 'app/templates/' + name + '.hbs'; fs.readFile(layoutPath, 'utf8', function (err, str) { if (err) return callback(err); var template = Handlebars.compile(str); diff --git a/shared/helpers.js b/shared/helpers.js index ce34b26..e17f399 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -11,8 +11,11 @@ module.exports = function(Handlebars, getTemplate) { view: function(viewName, options) { var ViewClass, html, viewOptions, view; - BaseView = BaseView || require('rendr/shared/base/view'); - modelUtils = modelUtils || require('rendr/shared/modelUtils'); + // Another lazy-loading attempt + var lazyRequire_baseView = 'rendr/shared/base/view'; + var lazyRequire_modelUtils = 'rendr/shared/modelUtils'; + BaseView = BaseView || require(lazyRequire_baseView); + modelUtils = modelUtils || require(lazyRequire_modelUtils); viewName = modelUtils.underscorize(viewName); viewOptions = options.hash || {}; diff --git a/shared/templateFinder.js b/shared/templateFinder.js index 693d6c5..d4b9bac 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -12,7 +12,7 @@ module.exports = function(Handlebars) { */ var templatePatterns = [{ pattern: /.+/, - src: rendr.entryPath + '/app/templates/compiledTemplates' + src: rendr.entryPath + 'app/templates/compiledTemplates' }]; /** diff --git a/test/shared/templateFinder.test.js b/test/shared/templateFinder.test.js index 1755862..09b4edb 100644 --- a/test/shared/templateFinder.test.js +++ b/test/shared/templateFinder.test.js @@ -3,7 +3,7 @@ * This is a reminder that globals are *bad*. */ global.rendr = { - entryPath: process.cwd() + '/test/fixtures' + entryPath: process.cwd() + '/test/fixtures/' }; var assert = require('assert') @@ -21,7 +21,7 @@ describe('templateFinder', function() { */ templateAdapter.templatePatterns.unshift({ pattern: /^other_template_pattern\//, - src: rendr.entryPath + '/app/templates/otherTemplates' + src: rendr.entryPath + 'app/templates/otherTemplates' }); assert.equal('function', typeof templateAdapter.getTemplate('other_template_pattern/home_view')); From b87d17f77794a103f47d7cea258fdd008bc967be Mon Sep 17 00:00:00 2001 From: Alex Indigo Date: Tue, 12 Nov 2013 21:59:41 -0800 Subject: [PATCH 020/107] Adjust compiled templates loader to suport AMD. --- shared/templateFinder.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/shared/templateFinder.js b/shared/templateFinder.js index d4b9bac..a8103c9 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -27,7 +27,22 @@ module.exports = function(Handlebars) { /** * Allow compiledTemplates to be created asynchronously. */ - cachedTemplates[src] = cachedTemplates[src] || require(src)(Handlebars); + // Make it play nice with AMD + // since amd options of grunt-contrib-handlebars + // produces different stucture as commonjs options + // accomodate both options here + if (!cachedTemplates[src]) + { + // amd returns object + cachedTemplates[src] = require(src); + + // commonjs returns function + if (typeof cachedTemplates[src] == 'function') + { + cachedTemplates[src] = cachedTemplates[src](Handlebars); + } + } + return cachedTemplates[src][templateName]; } From 4b3c2bcf7c640b145115d15e70aa6318333cffa4 Mon Sep 17 00:00:00 2001 From: Alex Indigo Date: Fri, 22 Nov 2013 12:05:08 -0800 Subject: [PATCH 021/107] Made globals dependency more vocal. --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index c52d573..ab41a8f 100644 --- a/index.js +++ b/index.js @@ -23,6 +23,10 @@ if (typeof window === 'undefined') { var serverOnlyPath_layoutFinder = './server/layoutFinder'; exports.getLayout = require(serverOnlyPath_layoutFinder)(Handlebars).getLayout; } else { + // add globals depedency for async loading + // should have on effect on the server + // and should be preloaded on the client + require('rendr/shared/globals'); exports.getLayout = function() { throw new Error('getLayout is only available on the server.'); }; From 736681413f1b4d63292e86ba04acb475fb4ec83a Mon Sep 17 00:00:00 2001 From: Thomas Shafer Date: Thu, 21 Nov 2013 10:51:22 -0800 Subject: [PATCH 022/107] consume the entryPath and return a new version of the templateAdapter removed entryPath from layoutfinder and getTemplate ensure app is always passed in view Added tests for multi-template-safe adapter requiring --- index.js | 103 ++++++++++++++++------------- package.json | 2 +- server/layoutFinder.js | 4 +- shared/helpers.js | 10 +-- shared/templateFinder.js | 7 +- test/index.test.js | 23 +++++++ test/shared/templateFinder.test.js | 8 +-- 7 files changed, 93 insertions(+), 64 deletions(-) create mode 100644 test/index.test.js diff --git a/index.js b/index.js index 4bd6c30..4056acb 100644 --- a/index.js +++ b/index.js @@ -1,48 +1,61 @@ -var Handlebars = require('handlebars') - , templateFinder = require('./shared/templateFinder')(Handlebars); - -/** - * Export the `Handlebars` object, so other modules can add helpers, partials, etc. - */ -exports.Handlebars = Handlebars; - -/** - * `getTemplate` is available on both client and server. - */ -exports.getTemplate = templateFinder.getTemplate; - -/** - * Expose `templatePatterns` for manipulating how `getTemplate` finds templates. - */ -exports.templatePatterns = templateFinder.templatePatterns; - -/** - * `getLayout` should only be used on the server. - */ -if (typeof window === 'undefined') { - exports.getLayout = require('./server/layoutFinder')(Handlebars).getLayout; -} else { - exports.getLayout = function() { - throw new Error('getLayout is only available on the server.'); - }; -} +var Handlebars = require('handlebars'); + +module.exports = function(options){ + var localExports, templateFinder; + + localExports = {}; + templateFinder = require('./shared/templateFinder')(Handlebars); + + /** + * Export the `Handlebars` object, so other modules can add helpers, partials, etc. + */ + localExports.Handlebars = Handlebars; + + /** + * `getTemplate` is available on both client and server. + */ + localExports.getTemplate = templateFinder.getTemplate; -/** - * Register helpers, available on both client and server. - * - * Export it so other modules can register helpers as well. - */ -exports.registerHelpers = function registerHelpers(helpersModule) { - var helpers = helpersModule(Handlebars, exports.getTemplate); - - for (var key in helpers) { - if (!helpers.hasOwnProperty(key)) continue; - Handlebars.registerHelper(key, helpers[key]); + /** + * Expose `templatePatterns` for manipulating how `getTemplate` finds templates. + */ + localExports.templatePatterns = templateFinder.templatePatterns; + /** + * The default pattern `/.+/` is very greedy; it matches anything, including nested paths. + * To add rules that should match before this default rule, `unshift` them from this array. + */ + localExports.templatePatterns.push({pattern: /.+/, src: options.entryPath + 'app/templates/compiledTemplates'}) + + /** + * `getLayout` should only be used on the server. + */ + if (typeof window === 'undefined') { + localExports.getLayout = require('./server/layoutFinder')(Handlebars).getLayout; + } else { + localExports.getLayout = function() { + throw new Error('getLayout is only available on the server.'); + }; } -}; -/** - * Register the pre-bundled Rendr helpers. - */ -var rendrHelpers = require('./shared/helpers'); -exports.registerHelpers(rendrHelpers); + /** + * Register helpers, available on both client and server. + * + * Export it so other modules can register helpers as well. + */ + localExports.registerHelpers = function registerHelpers(helpersModule) { + var helpers = helpersModule(Handlebars, localExports.getTemplate); + + for (var key in helpers) { + if (!helpers.hasOwnProperty(key)) continue; + Handlebars.registerHelper(key, helpers[key]); + } + }; + + /** + * Register the pre-bundled Rendr helpers. + */ + var rendrHelpers = require('./shared/helpers'); + localExports.registerHelpers(rendrHelpers); + + return localExports; +} diff --git a/package.json b/package.json index 94a644a..2df8e9b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { - "test": "mocha test/**/*.test.js" + "test": "mocha test/*.test.js test/**/*.test.js" }, "repository": { "type": "git", diff --git a/server/layoutFinder.js b/server/layoutFinder.js index 9968fdd..5d27009 100644 --- a/server/layoutFinder.js +++ b/server/layoutFinder.js @@ -3,8 +3,8 @@ var fs = require('fs'); module.exports = function(Handlebars) { return { - getLayout: function(name, callback) { - var layoutPath = rendr.entryPath + '/app/templates/' + name + '.hbs'; + getLayout: function(name, entryPath, callback) { + var layoutPath = entryPath + 'app/templates/' + name + '.hbs'; fs.readFile(layoutPath, 'utf8', function (err, str) { if (err) return callback(err); var template = Handlebars.compile(str); diff --git a/shared/helpers.js b/shared/helpers.js index ce34b26..880fc75 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,8 +1,7 @@ var _ = require('underscore'); // Lazy-required. -var BaseView = null, - modelUtils = null; +var BaseView = null; module.exports = function(Handlebars, getTemplate) { var oldEach = Handlebars.helpers.each; @@ -12,14 +11,15 @@ module.exports = function(Handlebars, getTemplate) { var ViewClass, html, viewOptions, view; BaseView = BaseView || require('rendr/shared/base/view'); - modelUtils = modelUtils || require('rendr/shared/modelUtils'); - viewName = modelUtils.underscorize(viewName); viewOptions = options.hash || {}; // Pass through a reference to the app. var app = getProperty('_app', this, options); if (app) { viewOptions.app = app; + viewName = app.modelUtils.underscorize(viewName); + } else{ + throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") } // Pass through a reference to the parent view. @@ -29,7 +29,7 @@ module.exports = function(Handlebars, getTemplate) { } // get the Backbone.View based on viewName - ViewClass = BaseView.getView(viewName); + ViewClass = BaseView.getView(viewName, app.options.entryPath); view = new ViewClass(viewOptions); // create the outerHTML using className, tagName diff --git a/shared/templateFinder.js b/shared/templateFinder.js index 693d6c5..717928c 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -7,13 +7,8 @@ module.exports = function(Handlebars) { * Provide a way for apps to specify that different template name patterns * should use different compiled template files. * - * The default pattern `/.+/` is very greedy; it matches anything, including nested paths. - * To add rules that should match before this default rule, `unshift` them from this array. */ - var templatePatterns = [{ - pattern: /.+/, - src: rendr.entryPath + '/app/templates/compiledTemplates' - }]; + var templatePatterns = []; /** * Given a template name, return the compiled Handlebars template. diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..b3b1c3a --- /dev/null +++ b/test/index.test.js @@ -0,0 +1,23 @@ +var assert = require('assert'); + +describe("require('rendr-handlebars')", function() { + it('returns a new templateAdapter', function() { + var templateAdapter, firstPatternSrc; + templateAdapter = require('../index')({entryPath: '/some/place/'}) + assert.equal(templateAdapter.templatePatterns.length, 1); + firstPatternSrc = templateAdapter.templatePatterns[0].src; + assert.equal(firstPatternSrc, '/some/place/app/templates/compiledTemplates'); + }); + + it('does not squash an old templateAdapter', function() { + var templateAdapter1, templateAdapter2, firstPatternSrc, secondPatternSrc; + templateAdapter1 = require('../index')({entryPath: '/some/place/'}) + templateAdapter2 = require('../index')({entryPath: '/some/other/place/'}) + assert.equal(templateAdapter1.templatePatterns.length, 1); + assert.equal(templateAdapter2.templatePatterns.length, 1); + firstPatternSrc = templateAdapter1.templatePatterns[0].src; + secondPatternSrc = templateAdapter2.templatePatterns[0].src; + assert.equal(firstPatternSrc, '/some/place/app/templates/compiledTemplates'); + assert.equal(secondPatternSrc, '/some/other/place/app/templates/compiledTemplates'); + }); +}); diff --git a/test/shared/templateFinder.test.js b/test/shared/templateFinder.test.js index 1755862..f7a2726 100644 --- a/test/shared/templateFinder.test.js +++ b/test/shared/templateFinder.test.js @@ -2,12 +2,10 @@ * We have to define the global `rendr`, which has an `entryPath` property. * This is a reminder that globals are *bad*. */ -global.rendr = { - entryPath: process.cwd() + '/test/fixtures' -}; +entryPath = process.cwd() + '/test/fixtures/' var assert = require('assert') - , templateAdapter = require('../../index'); + , templateAdapter = require('../../index')({entryPath: entryPath}); describe('templateFinder', function() { describe('getTemplate', function() { @@ -21,7 +19,7 @@ describe('templateFinder', function() { */ templateAdapter.templatePatterns.unshift({ pattern: /^other_template_pattern\//, - src: rendr.entryPath + '/app/templates/otherTemplates' + src: entryPath + 'app/templates/otherTemplates' }); assert.equal('function', typeof templateAdapter.getTemplate('other_template_pattern/home_view')); From 27dc5ff95d28b5bbafdcdff663f43bb93769c0df Mon Sep 17 00:00:00 2001 From: Thomas Shafer Date: Thu, 21 Nov 2013 12:32:48 -0800 Subject: [PATCH 023/107] Version bump the change to local entryPath requires the newer version of rendr --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2df8e9b..2b2dfbb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.8", + "version": "0.0.9", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -15,7 +15,7 @@ "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e" }, "peerDependencies": { - "rendr": ">=0.4.9" + "rendr": ">=0.5.0-alpha09" }, "keywords": [ "rendr", From 67195e9e2dbc705d9bffeee2528d6afc192d1536 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 22 Nov 2013 16:46:08 -0800 Subject: [PATCH 024/107] Release 0.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b2dfbb..7112d4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.0.9", + "version": "0.1.0", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From b204579aeea86bef27bb6c0a513bdb335c303558 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Tue, 10 Dec 2013 18:15:13 -0800 Subject: [PATCH 025/107] Remove references to global 'rendr' --- server/layoutFinder.js | 1 - shared/templateFinder.js | 1 - test/shared/templateFinder.test.js | 7 +------ 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/server/layoutFinder.js b/server/layoutFinder.js index 5d27009..6d014a9 100644 --- a/server/layoutFinder.js +++ b/server/layoutFinder.js @@ -1,4 +1,3 @@ -/*global rendr*/ var fs = require('fs'); module.exports = function(Handlebars) { diff --git a/shared/templateFinder.js b/shared/templateFinder.js index f0c4108..e8704e4 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -1,4 +1,3 @@ -/*global rendr*/ var cachedTemplates = {}; module.exports = function(Handlebars) { diff --git a/test/shared/templateFinder.test.js b/test/shared/templateFinder.test.js index f7a2726..977294d 100644 --- a/test/shared/templateFinder.test.js +++ b/test/shared/templateFinder.test.js @@ -1,10 +1,5 @@ -/** - * We have to define the global `rendr`, which has an `entryPath` property. - * This is a reminder that globals are *bad*. - */ -entryPath = process.cwd() + '/test/fixtures/' - var assert = require('assert') + , entryPath = process.cwd() + '/test/fixtures/' , templateAdapter = require('../../index')({entryPath: entryPath}); describe('templateFinder', function() { From 56ac927e04e3343da82fa51adbac88d629bfb590 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 11 Dec 2013 15:55:03 -0800 Subject: [PATCH 026/107] Update helpers.js Optional spacing for outputting the JSON --- shared/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 85c0449..8cc2095 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -71,8 +71,8 @@ module.exports = function(Handlebars, getTemplate) { return new Handlebars.SafeString(html); }, - json: function(object) { - return new Handlebars.SafeString(JSON.stringify(object) || 'null'); + json: function(object, spacing) { + return new Handlebars.SafeString(JSON.stringify(object, null, spacing) || 'null'); }, /** From 1092c7fbd5492ddca68b2fee8e6a9b4bea786ff7 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 20 Dec 2013 08:39:30 -0800 Subject: [PATCH 027/107] Small style tweaks * Comment formatting. * Comma-last. * If-block formatting. --- index.js | 8 +++----- shared/templateFinder.js | 23 +++++++++++------------ test/shared/templateFinder.test.js | 6 +++--- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index 6218c15..6b3723e 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,8 @@ var Handlebars = require('handlebars'); module.exports = function(options){ - var localExports, templateFinder; - - localExports = {}; - - templateFinder = require('./shared/templateFinder')(Handlebars); + var localExports = {}, + templateFinder = require('./shared/templateFinder')(Handlebars); /** * Export the `Handlebars` object, so other modules can add helpers, partials, etc. @@ -21,6 +18,7 @@ module.exports = function(options){ * Expose `templatePatterns` for manipulating how `getTemplate` finds templates. */ localExports.templatePatterns = templateFinder.templatePatterns; + /** * The default pattern `/.+/` is very greedy; it matches anything, including nested paths. * To add rules that should match before this default rule, `unshift` them from this array. diff --git a/shared/templateFinder.js b/shared/templateFinder.js index e8704e4..14e8e76 100644 --- a/shared/templateFinder.js +++ b/shared/templateFinder.js @@ -19,20 +19,19 @@ module.exports = function(Handlebars) { var src = getSrcForTemplate(templateName); /** - * Allow compiledTemplates to be created asynchronously. - */ - // Make it play nice with AMD - // since amd options of grunt-contrib-handlebars - // produces different stucture as commonjs options - // accomodate both options here - if (!cachedTemplates[src]) - { - // amd returns object + * Allow compiledTemplates to be created asynchronously by lazy-requiring it. + */ + if (!cachedTemplates[src]) { cachedTemplates[src] = require(src); - // commonjs returns function - if (typeof cachedTemplates[src] == 'function') - { + /** + * Make it play nicely with both AMD and CommonJS. + * The `grunt-contrib-handlebars` module produces different stucture + * of compiled templates with `amd` vs `commonjs` options. Accommodate + * both options here. the `amd` option results in templates as an Object, + * whereas the `commonjs` option results in templates as a Function. + */ + if (typeof cachedTemplates[src] == 'function') { cachedTemplates[src] = cachedTemplates[src](Handlebars); } } diff --git a/test/shared/templateFinder.test.js b/test/shared/templateFinder.test.js index 977294d..80afca8 100644 --- a/test/shared/templateFinder.test.js +++ b/test/shared/templateFinder.test.js @@ -1,6 +1,6 @@ -var assert = require('assert') - , entryPath = process.cwd() + '/test/fixtures/' - , templateAdapter = require('../../index')({entryPath: entryPath}); +var assert = require('assert'), + entryPath = process.cwd() + '/test/fixtures/', + templateAdapter = require('../../index')({entryPath: entryPath}); describe('templateFinder', function() { describe('getTemplate', function() { From 5e2e506bf3dd8e6590a66ddc7fa79016743b8384 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 20 Dec 2013 08:43:38 -0800 Subject: [PATCH 028/107] Release 0.2.0-rc1. --- HISTORY.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 HISTORY.md diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..c8dda23 --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,3 @@ +# 0.2.0-rc1 +## 2013-12-13 +* Add support for AMD modules. diff --git a/package.json b/package.json index 7112d4a..a1066ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.1.0", + "version": "0.2.0-rc1", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From 4427cb70c7f88abf1cf7d0afcde2c154b3d31c42 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 20 Dec 2013 12:17:19 -0800 Subject: [PATCH 029/107] Add .travis.yml; closes #10 --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e0b37e6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - "0.10" + - "0.8" +notifications: + email: false From b114215fedb418c7bd68262451e645353a8a5dea Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 20 Dec 2013 12:22:28 -0800 Subject: [PATCH 030/107] Add Travis CI, David DM badges to README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 16d6e20..7651903 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ +[![travis-ci status](https://secure.travis-ci.org/airbnb/rendr-handlebars.png)](http://travis-ci.org/#!/airbnb/rendr/builds) +[![Dependency Status](https://david-dm.org/airbnb/rendr-handlebars.png)](https://david-dm.org/airbnb/rendr) + rendr-handlebars ================ -[Handlebars](http://handlebarsjs.com/) template adapter for [Rendr](https://github.com/airbnb/rendr) apps. \ No newline at end of file +[Handlebars](http://handlebarsjs.com/) template adapter for [Rendr](https://github.com/airbnb/rendr) apps. From 48a58d024037a5fd32cdc973967a731ab02897eb Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 20 Dec 2013 12:22:58 -0800 Subject: [PATCH 031/107] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7651903..5496c0f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![travis-ci status](https://secure.travis-ci.org/airbnb/rendr-handlebars.png)](http://travis-ci.org/#!/airbnb/rendr/builds) -[![Dependency Status](https://david-dm.org/airbnb/rendr-handlebars.png)](https://david-dm.org/airbnb/rendr) +[![travis-ci status](https://secure.travis-ci.org/airbnb/rendr-handlebars.png)](http://travis-ci.org/#!/airbnb/rendr-handlebars/builds) +[![Dependency Status](https://david-dm.org/airbnb/rendr-handlebars.png)](https://david-dm.org/airbnb/rendr-handlebars) rendr-handlebars ================ From 7bcc2fc290d0f0577d918c4008f6b9be680b8165 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 20 Dec 2013 12:26:40 -0800 Subject: [PATCH 032/107] Update to underscore@1.5.2 to match rendr --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1066ad..1713a05 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "url": "http://github.com/airbnb/rendr-handlebars.git" }, "dependencies": { - "underscore": "~1.4.4", + "underscore": "1.5.2", "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e" }, "peerDependencies": { From 6e5363074aa8fdfda0cbb2d9f70ed272be8040ea Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 20 Dec 2013 12:27:20 -0800 Subject: [PATCH 033/107] Update peerDependency to rendr@0.5.0-rc1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1713a05..9e36065 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e" }, "peerDependencies": { - "rendr": ">=0.5.0-alpha09" + "rendr": ">=0.5.0-rc1" }, "keywords": [ "rendr", From 2e12160c0dc3e9b9ae54e1a7b2738b8ddd3e4f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20H=C3=BCsgen?= Date: Thu, 13 Feb 2014 17:15:53 +0100 Subject: [PATCH 034/107] save the original Handlebars each function module wide --- shared/helpers.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 8cc2095..6c12428 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,10 +1,11 @@ var _ = require('underscore'); // Lazy-required. -var BaseView = null; +var BaseView = null, + oldEach; module.exports = function(Handlebars, getTemplate) { - var oldEach = Handlebars.helpers.each; + oldEach = oldEach || Handlebars.helpers.each; return { view: function(viewName, options) { From 40c941612cd1888e9106da391b7c7fd952914633 Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Thu, 13 Feb 2014 17:18:22 +0100 Subject: [PATCH 035/107] =?UTF-8?q?don=E2=80=99t=20hide=20the=20require=20?= =?UTF-8?q?path=20from=20browserify?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- shared/helpers.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 8cc2095..373495b 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,4 +1,6 @@ -var _ = require('underscore'); +var _ = require('underscore'), + isServer = typeof window === 'undefined', + isAMDEnvironment = !isServer && (typeof define !== 'undefined'); // Lazy-required. var BaseView = null; @@ -10,10 +12,16 @@ module.exports = function(Handlebars, getTemplate) { view: function(viewName, options) { var ViewClass, html, viewOptions, view; - // it's lazy loaded, not a compile time dependency - // hiding it from r.js compiler - var lazyRequire_baseView = 'rendr/shared/base/view'; - BaseView = BaseView || require(lazyRequire_baseView); + if (!BaseView) { + if (isAMDEnvironment) { + // it's lazy loaded, not a compile time dependency + // hiding it from r.js compiler + var lazyRequire_baseView = 'rendr/shared/base/view'; + BaseView = require(lazyRequire_baseView); + } else { + BaseView = require('rendr/shared/base/view'); + } + } viewOptions = options.hash || {}; // Pass through a reference to the app. From a0e7ee78ce22055b6bf872c28c03382888c8dbf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20H=C3=BCsgen?= Date: Fri, 14 Feb 2014 15:39:56 +0100 Subject: [PATCH 036/107] added tests for rendr-bundled Handlebars each helper --- package.json | 5 ++++- test/shared/helpers.test.js | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 test/shared/helpers.test.js diff --git a/package.json b/package.json index 9e36065..db1c74f 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,9 @@ "npm": ">=1.2.10" }, "devDependencies": { - "mocha": "~1.12.0" + "mocha": "~1.12.0", + "sinon-chai": "~2.5.0", + "chai": "~1.9.0", + "sinon": "~1.8.2" } } diff --git a/test/shared/helpers.test.js b/test/shared/helpers.test.js new file mode 100644 index 0000000..dc2b70d --- /dev/null +++ b/test/shared/helpers.test.js @@ -0,0 +1,38 @@ +'use strict'; + +var chai = require('chai'), + sinon = require('sinon'), + sinonChai = require('sinon-chai'), + should = chai.should(), + helpers = require('../../shared/helpers'), + Handlebars = require('handlebars').create(); + +chai.use(sinonChai); + +describe('helpers', function () { + + function createRendrHandlebarsHelpers() { + var newRendrHelpers = helpers(Handlebars); + sinon.spy(newRendrHelpers, 'each'); + return newRendrHelpers; + } + + describe('each', function () { + it('should only call the original Handlebars each function on subsequent helper creation', function () { + var helperInstance0, + helperInstance1, + handlebarsOptions = { inverse: function () {} }, + handlebarsSpy = sinon.spy(); + + helperInstance0 = createRendrHandlebarsHelpers(); + helperInstance1 = createRendrHandlebarsHelpers(); + + helperInstance1.each(handlebarsSpy, handlebarsOptions); + + helperInstance0.each.should.have.not.been.called; + handlebarsSpy.should.have.been.called; + }); + }); +}); + + From bf35ef99f4da0ca92f915bd27987a6b66610bb4b Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 14 Feb 2014 09:13:18 -0800 Subject: [PATCH 037/107] Upgrade to handlebars@1.3.0 Now that Handlebars properly supports CommonJS builds, use the real module rather than a fork. Tested with airbnb/rendr#HEAD. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db1c74f..586b83e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "underscore": "1.5.2", - "handlebars": "git://github.com/spikebrehm/handlebars.js.git#6bbcf3f2225b0e62d249481d07456698da7da65e" + "handlebars": "1.3.0" }, "peerDependencies": { "rendr": ">=0.5.0-rc1" From ac99347544da69ba1f566e60177718f93c3448c0 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 14 Feb 2014 10:16:26 -0800 Subject: [PATCH 038/107] Add vim swapfiles to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 93f1361..858a3d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules npm-debug.log +*.sw? From fd426a44e52fae1f42371dc529fea6a5ac50f608 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Fri, 14 Feb 2014 10:17:08 -0800 Subject: [PATCH 039/107] Version 0.2.0-rc2 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 586b83e..2e0cba6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.2.0-rc1", + "version": "0.2.0-rc2", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -15,7 +15,7 @@ "handlebars": "1.3.0" }, "peerDependencies": { - "rendr": ">=0.5.0-rc1" + "rendr": ">=0.5.0-rc2" }, "keywords": [ "rendr", From 2e655f1a031c29b18023a286695566ef6e42f6f7 Mon Sep 17 00:00:00 2001 From: Alex Indigo Date: Mon, 17 Feb 2014 16:06:42 -0800 Subject: [PATCH 040/107] Removing lazyLoading circus. --- shared/helpers.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 798301d..1bce9df 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,6 +1,5 @@ var _ = require('underscore'), - isServer = typeof window === 'undefined', - isAMDEnvironment = !isServer && (typeof define !== 'undefined'); + isServer = typeof window === 'undefined'; // Lazy-required. var BaseView = null, @@ -14,14 +13,7 @@ module.exports = function(Handlebars, getTemplate) { var ViewClass, html, viewOptions, view; if (!BaseView) { - if (isAMDEnvironment) { - // it's lazy loaded, not a compile time dependency - // hiding it from r.js compiler - var lazyRequire_baseView = 'rendr/shared/base/view'; - BaseView = require(lazyRequire_baseView); - } else { - BaseView = require('rendr/shared/base/view'); - } + BaseView = require('rendr/shared/base/view'); } viewOptions = options.hash || {}; From 6ee63732a1eef91730928584204b164434644723 Mon Sep 17 00:00:00 2001 From: Mathias Schreck Date: Mon, 24 Feb 2014 18:44:16 +0100 Subject: [PATCH 041/107] change urls to new GH organization --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5496c0f..5aaa6ac 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -[![travis-ci status](https://secure.travis-ci.org/airbnb/rendr-handlebars.png)](http://travis-ci.org/#!/airbnb/rendr-handlebars/builds) -[![Dependency Status](https://david-dm.org/airbnb/rendr-handlebars.png)](https://david-dm.org/airbnb/rendr-handlebars) +[![travis-ci status](https://secure.travis-ci.org/rendrjs/rendr-handlebars.png)](http://travis-ci.org/#!/rendrjs/rendr-handlebars/builds) +[![Dependency Status](https://david-dm.org/rendrjs/rendr-handlebars.png)](https://david-dm.org/rendrjs/rendr-handlebars) rendr-handlebars ================ -[Handlebars](http://handlebarsjs.com/) template adapter for [Rendr](https://github.com/airbnb/rendr) apps. +[Handlebars](http://handlebarsjs.com/) template adapter for [Rendr](https://github.com/rendrjs/rendr) apps. From 54bb9be2a54c552fb8561b8baf46daadaa7538c3 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Mon, 24 Feb 2014 11:53:54 -0800 Subject: [PATCH 042/107] Update repo in package.json to rendrjs/rendr-handlebars --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e0cba6..a487483 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "repository": { "type": "git", - "url": "http://github.com/airbnb/rendr-handlebars.git" + "url": "http://github.com/rendrjs/rendr-handlebars.git" }, "dependencies": { "underscore": "1.5.2", From c8c17b7a81a352e038e31ab5dd33f8dc94fda1c5 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Tue, 25 Feb 2014 15:35:15 -0800 Subject: [PATCH 043/107] Bump to 0.2.0 for rendr@0.5.0 --- HISTORY.md | 6 ++++-- package.json | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c8dda23..2e8be73 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,5 @@ -# 0.2.0-rc1 -## 2013-12-13 +# 0.2.0 +## 2014-02-25 * Add support for AMD modules. +* Upgraded to Handlebars@1.3.0. +* Upgraded to Underscore@1.5.2. diff --git a/package.json b/package.json index a487483..9f4f4ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.2.0-rc2", + "version": "0.2.0", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -15,7 +15,7 @@ "handlebars": "1.3.0" }, "peerDependencies": { - "rendr": ">=0.5.0-rc2" + "rendr": ">=0.5.0" }, "keywords": [ "rendr", From 4c99355c27797dc91c29802a7034c8bac57c07c1 Mon Sep 17 00:00:00 2001 From: Spike Brehm Date: Tue, 5 Aug 2014 16:32:21 -0700 Subject: [PATCH 044/107] Add LICENSE --- LICENSE | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8c032f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013-2014 Adam Hull & Airbnb, Inc + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. From 8d1eb892ba998c23667ca82c562012f61430fd50 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 2 Oct 2014 15:29:40 -0700 Subject: [PATCH 045/107] Updated the view helper to only create an instance of the view if it's on the server side. otherwise create a wrapper div to be filled w/ a render call in attachChildViews of the render process --- shared/helpers.js | 55 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 1bce9df..0164511 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -17,28 +17,49 @@ module.exports = function(Handlebars, getTemplate) { } viewOptions = options.hash || {}; - // Pass through a reference to the app. var app = getProperty('_app', this, options); - if (app) { - viewOptions.app = app; - viewName = app.modelUtils.underscorize(viewName); - } else{ - throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") - } + if (isServer) { + // Pass through a reference to the app. + if (app) { + viewOptions.app = app; + viewName = app.modelUtils.underscorize(viewName); + } else{ + throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") + } + + // Pass through a reference to the parent view. + var parentView = getProperty('_view', this, options); + if (parentView) { + viewOptions.parentView = parentView; + } + + // get the Backbone.View based on viewName + ViewClass = BaseView.getView(viewName, app.options.entryPath); + view = new ViewClass(viewOptions); - // Pass through a reference to the parent view. - var parentView = getProperty('_view', this, options); - if (parentView) { - viewOptions.parentView = parentView; + // create the outerHTML using className, tagName + html = view.getHtml(); + return new Handlebars.SafeString(html); } - // get the Backbone.View based on viewName - ViewClass = BaseView.getView(viewName, app.options.entryPath); - view = new ViewClass(viewOptions); + // only create an element to pass attributes to a single point for view instantiation for client-side - // create the outerHTML using className, tagName - html = view.getHtml(); - return new Handlebars.SafeString(html); + // Builds a fetch_summary attribute + viewOptions = BaseView.parseModelAndCollection(app.modelUtils, viewOptions); + fetchSummary = BaseView.extractFetchSummary(app.modelUtils, viewOptions); + fetchSummary = JSON.stringify(fetchSummary) + + viewOptions['fetch_summary'] = fetchSummary + viewOptions = _.omit(viewOptions, ['model', 'collection']) + + // create a list of data attributes + attrString = _.inject(viewOptions, function(memo, value, key) { + return memo += " data-" + key + "=\"" + _.escape(value) + "\""; + }, ''); + + return new Handlebars.SafeString( + '
' + ) }, partial: function(templateName, options) { From 553b45b534e2dfe7f6874cd3ab6c80c719e552dc Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Fri, 17 Oct 2014 14:50:58 -0700 Subject: [PATCH 046/107] make sure that app is set for setting up models, but isn't passed into the attrString --- shared/helpers.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index 0164511..fb24679 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -18,15 +18,15 @@ module.exports = function(Handlebars, getTemplate) { viewOptions = options.hash || {}; var app = getProperty('_app', this, options); - if (isServer) { - // Pass through a reference to the app. - if (app) { - viewOptions.app = app; - viewName = app.modelUtils.underscorize(viewName); - } else{ - throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") - } + // Pass through a reference to the app. + if (app) { + viewOptions.app = app; + viewName = app.modelUtils.underscorize(viewName); + } else{ + throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") + } + if (isServer) { // Pass through a reference to the parent view. var parentView = getProperty('_view', this, options); if (parentView) { @@ -50,7 +50,7 @@ module.exports = function(Handlebars, getTemplate) { fetchSummary = JSON.stringify(fetchSummary) viewOptions['fetch_summary'] = fetchSummary - viewOptions = _.omit(viewOptions, ['model', 'collection']) + viewOptions = _.omit(viewOptions, ['model', 'collection', 'app']) // create a list of data attributes attrString = _.inject(viewOptions, function(memo, value, key) { From 5029737a4603709a16db440ba05365cf57968a40 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 23 Oct 2014 12:27:41 -0700 Subject: [PATCH 047/107] globals are bad --- shared/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/helpers.js b/shared/helpers.js index fb24679..d641a66 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -46,14 +46,14 @@ module.exports = function(Handlebars, getTemplate) { // Builds a fetch_summary attribute viewOptions = BaseView.parseModelAndCollection(app.modelUtils, viewOptions); - fetchSummary = BaseView.extractFetchSummary(app.modelUtils, viewOptions); + var fetchSummary = BaseView.extractFetchSummary(app.modelUtils, viewOptions); fetchSummary = JSON.stringify(fetchSummary) viewOptions['fetch_summary'] = fetchSummary viewOptions = _.omit(viewOptions, ['model', 'collection', 'app']) // create a list of data attributes - attrString = _.inject(viewOptions, function(memo, value, key) { + var attrString = _.inject(viewOptions, function(memo, value, key) { return memo += " data-" + key + "=\"" + _.escape(value) + "\""; }, ''); From 473c7f72425fba752edf076ef69b70fb8ba9de87 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 4 Nov 2014 13:13:37 -0800 Subject: [PATCH 048/107] update the version to be 0.2.1 of rendr, and this fix requires rendr to be at 0.5.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9f4f4ca..3a88645 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.2.0", + "version": "0.2.1", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -15,7 +15,7 @@ "handlebars": "1.3.0" }, "peerDependencies": { - "rendr": ">=0.5.0" + "rendr": ">=0.5.1" }, "keywords": [ "rendr", From b80b2014030b0a69703c4c94bec003ee61be0151 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 11 Dec 2014 18:40:14 -0800 Subject: [PATCH 049/107] Update HISTORY.md --- HISTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 2e8be73..6d5418e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,7 @@ +# 0.2.1 +## 2014-12-11 +* Removed need to initialize the view twice on the client-side. This fixed leaking listeners and makes the code perform as expected + # 0.2.0 ## 2014-02-25 * Add support for AMD modules. From 4f0efa743d52b2dccc1a02cee17d0e0dc5982d29 Mon Sep 17 00:00:00 2001 From: Matt Pardee Date: Mon, 15 Dec 2014 11:03:01 -0800 Subject: [PATCH 050/107] Stringify nested object options --- shared/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/helpers.js b/shared/helpers.js index d641a66..1105160 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -54,7 +54,7 @@ module.exports = function(Handlebars, getTemplate) { // create a list of data attributes var attrString = _.inject(viewOptions, function(memo, value, key) { - return memo += " data-" + key + "=\"" + _.escape(value) + "\""; + return memo += " data-" + key + "=\"" + _.escape(typeof value === 'object' ? JSON.stringify(value) : value) + "\""; }, ''); return new Handlebars.SafeString( From b0c1d899a2734ef44b83fc10c8b37f2e4e130cd7 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 6 Jan 2015 10:30:57 -0800 Subject: [PATCH 051/107] created a forEach helper that will add things like indx, total, isFirst element, isLast element, and iterate over objects, collections, or arrays --- shared/helpers.js | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/shared/helpers.js b/shared/helpers.js index d641a66..cbf441b 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -108,6 +108,59 @@ module.exports = function(Handlebars, getTemplate) { // Call the original helper with new context. return oldEach.call(this, context, options); + }, + + /** + * Create a `forEach` helper that works on a few more cases and gives more flexibility + * when dealing with arrays, objects, or collections + */ + forEach: function(collection, opts) { + var len = collection && collection.length, + app = this._app || this.app, + isCollection = app.modelUtils.isCollection(collection), + buffer = ''; + + if (_.isEmpty(collection)) { + return opts.inverse(_.extend({}, this, { + _app: app, + _model: this._model || this.model, + _collection: this._collection || this.collection, + _view: this._view || this.view + })); + } + + // iterate the models on a collection + if (isCollection) { + collection = collection.models + } + + _.each(collection, function (value, key) { + if (isCollection && opts.hash.toJSON) { + value = value.toJSON(); + } + + var item = _.extend({ + key: key, + value: value, + _app: this._app || this.app, + _model: this._model || this.model, + _collection: this._collection || this.collection, + _view: this._view || this.view + }, opts.hash); + + // adding extra attributes to an item for array traversal + if (_.isArray(collection) || isCollection) { + item = _.extend(item, { + _total: len, + _isFirst: key === 0, + _isLast: key === (len - 1) + }); + } + + buffer += opts.fn(item); + }.bind(this)); + + return buffer; } }; }; From c23ab434cf761b91a88e13121b009e12101fb953 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Wed, 7 Jan 2015 09:57:05 -0800 Subject: [PATCH 052/107] restuctured the helpers --- lib/getOptions.js | 24 +++++ lib/getProperty.js | 9 ++ shared/helpers.js | 202 +------------------------------------- shared/helpers/each.js | 21 ++++ shared/helpers/forEach.js | 53 ++++++++++ shared/helpers/json.js | 5 + shared/helpers/partial.js | 37 +++++++ shared/helpers/view.js | 60 +++++++++++ 8 files changed, 214 insertions(+), 197 deletions(-) create mode 100644 lib/getOptions.js create mode 100644 lib/getProperty.js create mode 100644 shared/helpers/each.js create mode 100644 shared/helpers/forEach.js create mode 100644 shared/helpers/json.js create mode 100644 shared/helpers/partial.js create mode 100644 shared/helpers/view.js diff --git a/lib/getOptions.js b/lib/getOptions.js new file mode 100644 index 0000000..89a43c2 --- /dev/null +++ b/lib/getOptions.js @@ -0,0 +1,24 @@ +/** + * Grab important underscored properties from the current context. + * These properties come from BaseView::decorateTemplateData(). + */ +module.exports = function (obj) { + var options, keys, value; + + keys = [ + '_app', + '_view', + '_model', + '_collection' + ]; + + options = keys.reduce(function(memo, key) { + value = obj[key]; + if (value) { + memo[key] = value; + } + return memo; + }, {}); + + return options; +}; diff --git a/lib/getProperty.js b/lib/getProperty.js new file mode 100644 index 0000000..00e1987 --- /dev/null +++ b/lib/getProperty.js @@ -0,0 +1,9 @@ +/** + * Get a property that is being passed down through helpers, such as `_app` + * or `_view`. It can either live on the context, i.e. `this._app`, or in the + * `options.data` object passed to the helper, i.e. `options.data._app`, in the + * case of a block helper like `each`. + */ +module.exports = function (key, context, options) { + return context[key] || (options.data || {})[key]; +} diff --git a/shared/helpers.js b/shared/helpers.js index e2a88e3..46c3942 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,201 +1,9 @@ -var _ = require('underscore'), - isServer = typeof window === 'undefined'; - -// Lazy-required. -var BaseView = null, - oldEach; - module.exports = function(Handlebars, getTemplate) { - oldEach = oldEach || Handlebars.helpers.each; - return { - view: function(viewName, options) { - var ViewClass, html, viewOptions, view; - - if (!BaseView) { - BaseView = require('rendr/shared/base/view'); - } - viewOptions = options.hash || {}; - - var app = getProperty('_app', this, options); - // Pass through a reference to the app. - if (app) { - viewOptions.app = app; - viewName = app.modelUtils.underscorize(viewName); - } else{ - throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") - } - - if (isServer) { - // Pass through a reference to the parent view. - var parentView = getProperty('_view', this, options); - if (parentView) { - viewOptions.parentView = parentView; - } - - // get the Backbone.View based on viewName - ViewClass = BaseView.getView(viewName, app.options.entryPath); - view = new ViewClass(viewOptions); - - // create the outerHTML using className, tagName - html = view.getHtml(); - return new Handlebars.SafeString(html); - } - - // only create an element to pass attributes to a single point for view instantiation for client-side - - // Builds a fetch_summary attribute - viewOptions = BaseView.parseModelAndCollection(app.modelUtils, viewOptions); - var fetchSummary = BaseView.extractFetchSummary(app.modelUtils, viewOptions); - fetchSummary = JSON.stringify(fetchSummary) - - viewOptions['fetch_summary'] = fetchSummary - viewOptions = _.omit(viewOptions, ['model', 'collection', 'app']) - - // create a list of data attributes - var attrString = _.inject(viewOptions, function(memo, value, key) { - return memo += " data-" + key + "=\"" + _.escape(typeof value === 'object' ? JSON.stringify(value) : value) + "\""; - }, ''); - - return new Handlebars.SafeString( - '
' - ) - }, - - partial: function(templateName, options) { - var data, html, context, template; - - template = getTemplate(templateName); - - context = options.hash || {}; - - // First try to use Handlebars' hash arguments as the context for the - // partial, if present. - // - // ex: `{{partial "users/photo" user=user}}` - if (_.isEmpty(context)) { - // If there are no hash arguments given, then inherit the parent context. - // - // ex: `{{partial "users/photo"}}` - context = this; - } else { - // If a hash argument is given with key `context`, then use that as the context. - // - // ex: `{{partial "users/photo" context=user}}` - if (context.hasOwnProperty('context')) { - context = context.context; - } - } - context = _.clone(context); - - context._app = getProperty('_app', this, options); - html = template(context); - return new Handlebars.SafeString(html); - }, - - json: function(object, spacing) { - return new Handlebars.SafeString(JSON.stringify(object, null, spacing) || 'null'); - }, - - /** - * Extend `each` to pass through important context. - */ - each: function(context, options) { - options.data = Handlebars.createFrame(options.data || {}); - - // Make sure `this._app`, `this._view`, etc are available. - _.extend(options.data, getOptionsFromContext(this)); - - // Call the original helper with new context. - return oldEach.call(this, context, options); - }, - - /** - * Create a `forEach` helper that works on a few more cases and gives more flexibility - * when dealing with arrays, objects, or collections - */ - forEach: function(collection, opts) { - var len = collection && collection.length, - app = this._app || this.app, - isCollection = app.modelUtils.isCollection(collection), - buffer = ''; - - if (_.isEmpty(collection)) { - return opts.inverse(_.extend({}, this, { - _app: app, - _model: this._model || this.model, - _collection: this._collection || this.collection, - _view: this._view || this.view - })); - } - - // iterate the models on a collection - if (isCollection) { - collection = collection.models - } - - _.each(collection, function (value, key) { - if (isCollection && opts.hash.toJSON) { - value = value.toJSON(); - } - - var item = _.extend({ - key: key, - value: value, - _app: this._app || this.app, - _model: this._model || this.model, - _collection: this._collection || this.collection, - _view: this._view || this.view - }, opts.hash); - - // adding extra attributes to an item for array traversal - if (_.isArray(collection) || isCollection) { - item = _.extend(item, { - _total: len, - _isFirst: key === 0, - _isLast: key === (len - 1) - }); - } - - buffer += opts.fn(item); - }.bind(this)); - - return buffer; - } + view: require('./helpers/view')(Handlebars), + partial: require('./helpers/partial')(Handlebars, getTemplate), + json: require('./helpers/json')(Handlebars), + each: require('./helpers/each')(Handlebars), + forEach: require('./helpers/forEach') }; }; - -/** - * Grab important underscored properties from the current context. - * These properties come from BaseView::decorateTemplateData(). - */ -function getOptionsFromContext(obj) { - var options, keys, value; - - keys = [ - '_app', - '_view', - '_model', - '_collection' - ]; - - options = keys.reduce(function(memo, key) { - value = obj[key]; - if (value) { - memo[key] = value; - } - return memo; - }, {}); - - return options; -} - -/** - * Get a property that is being passed down through helpers, such as `_app` - * or `_view`. It can either live on the context, i.e. `this._app`, or in the - * `options.data` object passed to the helper, i.e. `options.data._app`, in the - * case of a block helper like `each`. - */ -function getProperty(key, context, options) { - return context[key] || (options.data || {})[key]; -} diff --git a/shared/helpers/each.js b/shared/helpers/each.js new file mode 100644 index 0000000..73083d9 --- /dev/null +++ b/shared/helpers/each.js @@ -0,0 +1,21 @@ +/** +* Extend `each` to pass through important context. +*/ + +var _ = require('underscore'), + getOptionsFromContext = require('../../lib/getOptions'), + oldEach; + +module.exports = function(Handlebars) { + oldEach = oldEach || Handlebars.helpers.each; + + return function (context, options) { + options.data = Handlebars.createFrame(options.data || {}); + + // Make sure `this._app`, `this._view`, etc are available. + _.extend(options.data, getOptionsFromContext(this)); + + // Call the original helper with new context. + return oldEach.call(this, context, options); + } +}; diff --git a/shared/helpers/forEach.js b/shared/helpers/forEach.js new file mode 100644 index 0000000..1549758 --- /dev/null +++ b/shared/helpers/forEach.js @@ -0,0 +1,53 @@ +/** +* Create a `forEach` helper that works on a few more cases and gives more flexibility +* when dealing with arrays, objects, or collections +*/ + +module.exports = function (collection, opts) { + var len = collection && collection.length, + app = this._app || this.app, + isCollection = app.modelUtils.isCollection(collection), + buffer = ''; + + if (_.isEmpty(collection)) { + return opts.inverse(_.extend({}, this, { + _app: app, + _model: this._model || this.model, + _collection: this._collection || this.collection, + _view: this._view || this.view + })); + } + + // iterate the models on a collection + if (isCollection) { + collection = collection.models + } + + _.each(collection, function (value, key) { + if (isCollection && opts.hash.toJSON) { + value = value.toJSON(); + } + + var item = _.extend({ + key: key, + value: value, + _app: this._app || this.app, + _model: this._model || this.model, + _collection: this._collection || this.collection, + _view: this._view || this.view + }, opts.hash); + + // adding extra attributes to an item for array traversal + if (_.isArray(collection) || isCollection) { + item = _.extend(item, { + _total: len, + _isFirst: key === 0, + _isLast: key === (len - 1) + }); + } + + buffer += opts.fn(item); + }.bind(this)); + + return buffer; +}; diff --git a/shared/helpers/json.js b/shared/helpers/json.js new file mode 100644 index 0000000..f95670b --- /dev/null +++ b/shared/helpers/json.js @@ -0,0 +1,5 @@ +module.exports = function (Handlebars) { + return function (object, spacing) { + return new Handlebars.SafeString(JSON.stringify(object, null, spacing) || 'null'); + } +} diff --git a/shared/helpers/partial.js b/shared/helpers/partial.js new file mode 100644 index 0000000..4175b77 --- /dev/null +++ b/shared/helpers/partial.js @@ -0,0 +1,37 @@ +/** + * create an html partial + */ +var getProperty = require('../../lib/getProperty'); + +module.exports = function (Handlebars, getTemplate) { + return function (templateName, options) { + var data, html, context, template; + + template = getTemplate(templateName); + + context = options.hash || {}; + + // First try to use Handlebars' hash arguments as the context for the + // partial, if present. + // + // ex: `{{partial "users/photo" user=user}}` + if (_.isEmpty(context)) { + // If there are no hash arguments given, then inherit the parent context. + // + // ex: `{{partial "users/photo"}}` + context = this; + } else { + // If a hash argument is given with key `context`, then use that as the context. + // + // ex: `{{partial "users/photo" context=user}}` + if (context.hasOwnProperty('context')) { + context = context.context; + } + } + context = _.clone(context); + + context._app = getProperty('_app', this, options); + html = template(context); + return new Handlebars.SafeString(html); + }; +}; diff --git a/shared/helpers/view.js b/shared/helpers/view.js new file mode 100644 index 0000000..db97e1f --- /dev/null +++ b/shared/helpers/view.js @@ -0,0 +1,60 @@ +/** + * Helper to create new views in the templates + */ + +var getProperty = require('../../lib/getProperty'); + +module.exports = function (Handlebars) { + return function (viewName, options) { + var ViewClass, html, viewOptions, view; + + if (!BaseView) { + BaseView = require('rendr/shared/base/view'); + } + viewOptions = options.hash || {}; + + var app = getProperty('_app', this, options); + // Pass through a reference to the app. + if (app) { + viewOptions.app = app; + viewName = app.modelUtils.underscorize(viewName); + } else{ + throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") + } + + if (isServer) { + // Pass through a reference to the parent view. + var parentView = getProperty('_view', this, options); + if (parentView) { + viewOptions.parentView = parentView; + } + + // get the Backbone.View based on viewName + ViewClass = BaseView.getView(viewName, app.options.entryPath); + view = new ViewClass(viewOptions); + + // create the outerHTML using className, tagName + html = view.getHtml(); + return new Handlebars.SafeString(html); + } + + // only create an element to pass attributes to a single point for view instantiation for client-side + + // Builds a fetch_summary attribute + viewOptions = BaseView.parseModelAndCollection(app.modelUtils, viewOptions); + var fetchSummary = BaseView.extractFetchSummary(app.modelUtils, viewOptions); + fetchSummary = JSON.stringify(fetchSummary) + + viewOptions['fetch_summary'] = fetchSummary + viewOptions = _.omit(viewOptions, ['model', 'collection', 'app']) + + // create a list of data attributes + var attrString = _.inject(viewOptions, function(memo, value, key) { + return memo += " data-" + key + "=\"" + _.escape(value) + "\""; + }, ''); + + return new Handlebars.SafeString( + '
' + ) + }; +}; From 8ed0aa878954548bbd7d7d5b6d7dca1ccaa9947c Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Wed, 7 Jan 2015 16:25:43 -0800 Subject: [PATCH 053/107] cleaned up the view function --- shared/helpers/view.js | 73 +++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/shared/helpers/view.js b/shared/helpers/view.js index db97e1f..b9c837c 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -2,18 +2,17 @@ * Helper to create new views in the templates */ -var getProperty = require('../../lib/getProperty'); +var getProperty = require('../../lib/getProperty'), + isServer = typeof window === 'undefined', + BaseView; module.exports = function (Handlebars) { return function (viewName, options) { - var ViewClass, html, viewOptions, view; + var ViewClass, html, viewOptions, view, app; - if (!BaseView) { - BaseView = require('rendr/shared/base/view'); - } viewOptions = options.hash || {}; + app = getProperty('_app', this, options); - var app = getProperty('_app', this, options); // Pass through a reference to the app. if (app) { viewOptions.app = app; @@ -23,38 +22,46 @@ module.exports = function (Handlebars) { } if (isServer) { - // Pass through a reference to the parent view. var parentView = getProperty('_view', this, options); - if (parentView) { - viewOptions.parentView = parentView; - } + html = getServerHtml(viewName, viewOptions, parentView); + } else { + html = getClientPlaceholder(viewName, viewOptions); + } - // get the Backbone.View based on viewName - ViewClass = BaseView.getView(viewName, app.options.entryPath); - view = new ViewClass(viewOptions); + return new Handlebars.SafeString(html); + }; +}; - // create the outerHTML using className, tagName - html = view.getHtml(); - return new Handlebars.SafeString(html); - } +function getServerHtml(viewName, viewOptions, parentView) { + if (!BaseView) { BaseView = require('rendr/shared/base/view'); } - // only create an element to pass attributes to a single point for view instantiation for client-side + // Pass through a reference to the parent view. + if (parentView) { viewOptions.parentView = parentView; } - // Builds a fetch_summary attribute - viewOptions = BaseView.parseModelAndCollection(app.modelUtils, viewOptions); - var fetchSummary = BaseView.extractFetchSummary(app.modelUtils, viewOptions); - fetchSummary = JSON.stringify(fetchSummary) + // get the Backbone.View based on viewName + ViewClass = BaseView.getView(viewName, viewOptions.app.options.entryPath); + view = new ViewClass(viewOptions); - viewOptions['fetch_summary'] = fetchSummary - viewOptions = _.omit(viewOptions, ['model', 'collection', 'app']) + // create the outerHTML using className, tagName + return view.getHtml(); +} - // create a list of data attributes - var attrString = _.inject(viewOptions, function(memo, value, key) { - return memo += " data-" + key + "=\"" + _.escape(value) + "\""; - }, ''); +function getClientPlaceholder(viewName, viewOptions) { + if (!BaseView) { BaseView = require('rendr/shared/base/view'); } + var fetchSummary; - return new Handlebars.SafeString( - '
' - ) - }; -}; + // Builds a fetch_summary attribute + viewOptions = BaseView.parseModelAndCollection(viewOptions.app.modelUtils, viewOptions); + fetchSummary = BaseView.extractFetchSummary(viewOptions.app.modelUtils, viewOptions); + fetchSummary = JSON.stringify(fetchSummary) + + viewOptions['fetch_summary'] = fetchSummary + viewOptions = _.omit(viewOptions, ['model', 'collection', 'app']) + + // create a list of data attributes + var attrString = _.inject(viewOptions, function(memo, value, key) { + return memo += " data-" + key + "=\"" + _.escape(value) + "\""; + }, ''); + + return '
'; +} From 474605c542dfc8cde7ce6680cc58520d7143fdfa Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 11:32:03 -0800 Subject: [PATCH 054/107] allow partials and views to be passed block elements --- lib/getOptions.js | 3 ++- shared/helpers/partial.js | 9 +++++++-- shared/helpers/view.js | 5 +++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/getOptions.js b/lib/getOptions.js index 89a43c2..8a5fb0d 100644 --- a/lib/getOptions.js +++ b/lib/getOptions.js @@ -9,7 +9,8 @@ module.exports = function (obj) { '_app', '_view', '_model', - '_collection' + '_collection', + '_block' ]; options = keys.reduce(function(memo, key) { diff --git a/shared/helpers/partial.js b/shared/helpers/partial.js index 4175b77..bdf4c92 100644 --- a/shared/helpers/partial.js +++ b/shared/helpers/partial.js @@ -1,7 +1,8 @@ /** * create an html partial */ -var getProperty = require('../../lib/getProperty'); +var getProperty = require('../../lib/getProperty'), + _ = require('underscore'); module.exports = function (Handlebars, getTemplate) { return function (templateName, options) { @@ -28,9 +29,13 @@ module.exports = function (Handlebars, getTemplate) { context = context.context; } } - context = _.clone(context); + context = _.clone(context); context._app = getProperty('_app', this, options); + if (_.isFunction(options.fn)) { + context._block = options.fn(context); + } + html = template(context); return new Handlebars.SafeString(html); }; diff --git a/shared/helpers/view.js b/shared/helpers/view.js index b9c837c..664c78e 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -21,6 +21,11 @@ module.exports = function (Handlebars) { throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.") } + // allow views to be passed optional block elements + if (_.isFunction(options.fn)) { + viewOptions._block = options.fn(viewOptions); + } + if (isServer) { var parentView = getProperty('_view', this, options); html = getServerHtml(viewName, viewOptions, parentView); From 429bfa08b7a640a4f94f34767d0907d0ddf28b5a Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 13:31:03 -0800 Subject: [PATCH 055/107] added dev dependencies for testing --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3a88645..aac2303 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,11 @@ "npm": ">=1.2.10" }, "devDependencies": { - "mocha": "~1.12.0", - "sinon-chai": "~2.5.0", + "backbone": "~1.1.2", "chai": "~1.9.0", - "sinon": "~1.8.2" + "memo-is": "0.0.2", + "mocha": "~1.12.0", + "sinon": "~1.8.2", + "sinon-chai": "~2.5.0" } } From 58427cad4da57d35712c2d9cf1d762bbcd466803 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 13:31:16 -0800 Subject: [PATCH 056/107] added tests for the forEach helper --- shared/helpers/forEach.js | 1 + test/shared/helpers/each.test.js | 0 test/shared/helpers/forEach.test.js | 209 ++++++++++++++++++++++++++++ test/shared/helpers/json.test.js | 0 test/shared/helpers/partial.test.js | 0 test/shared/helpers/view.test.js | 0 6 files changed, 210 insertions(+) create mode 100644 test/shared/helpers/each.test.js create mode 100644 test/shared/helpers/forEach.test.js create mode 100644 test/shared/helpers/json.test.js create mode 100644 test/shared/helpers/partial.test.js create mode 100644 test/shared/helpers/view.test.js diff --git a/shared/helpers/forEach.js b/shared/helpers/forEach.js index 1549758..78c2081 100644 --- a/shared/helpers/forEach.js +++ b/shared/helpers/forEach.js @@ -2,6 +2,7 @@ * Create a `forEach` helper that works on a few more cases and gives more flexibility * when dealing with arrays, objects, or collections */ +var _ = require('underscore'); module.exports = function (collection, opts) { var len = collection && collection.length, diff --git a/test/shared/helpers/each.test.js b/test/shared/helpers/each.test.js new file mode 100644 index 0000000..e69de29 diff --git a/test/shared/helpers/forEach.test.js b/test/shared/helpers/forEach.test.js new file mode 100644 index 0000000..6080fdf --- /dev/null +++ b/test/shared/helpers/forEach.test.js @@ -0,0 +1,209 @@ +var Handlebars = require('handlebars').create(), + Collection = require('backbone').Collection, + Model = require('backbone').Model, + memo = require('memo-is'), + sinon = require('sinon'), + chai = require('chai'), + expect = chai.expect, + subject = require('../../../shared/helpers/forEach'); + +describe('forEach', function () { + var data, spy, scope; + var opts = memo().is(function () { + return { + fn: function () {}, + inverse: function () {}, + hash: {} + } + }); + + var isCollection = memo().is(function() { + return function () { + return false; + } + }); + + var app = memo().is(function() { + return { modelUtils: { isCollection: isCollection() } }; + }); + + beforeEach(function () { + spy = sinon.spy(opts(), 'fn'); + scope = { app: app() }; + }) + + context('is an object', function () { + beforeEach(function () { + data = { 'a': 'b', 'c': 'd' }; + }); + + it('calls opts.fn with a key / value item', function () { + scope = { app: app() }; + + subject.call(scope, data, opts()); + expect(spy).to.have.been.calledTwice; + + var thisCall = spy.getCall(0), + args = thisCall.args[0]; + + expect(args.key).to.equal('a'); + expect(args.value).to.equal('b'); + }) + + it('will have the private properties attached', function () { + scope = { + app: app(), + view: { 'test': 'view' }, + model: { 'test': 'model' }, + collection: { 'test': 'collection' } + }; + + subject.call(scope, data, opts()); + expect(spy).to.have.been.calledTwice; + + var thisCall = spy.getCall(0), + args = thisCall.args[0]; + + expect(args._app).to.deep.equal(scope.app); + expect(args._view).to.deep.equal(scope.view); + expect(args._model).to.deep.equal(scope.model); + expect(args._collection).to.deep.equal(scope.collection); + }) + + it('will have the private properties attached if it is nested in another helper', function () { + scope = { + _app: app(), + _view: { 'test': 'view' }, + _model: { 'test': 'model' }, + _collection: { 'test': 'collection' } + }; + + subject.call(scope, data, opts()); + expect(spy).to.have.been.calledTwice; + + var thisCall = spy.getCall(0), + args = thisCall.args[0]; + + expect(args._app).to.deep.equal(scope._app); + expect(args._view).to.deep.equal(scope._view); + expect(args._model).to.deep.equal(scope._model); + expect(args._collection).to.deep.equal(scope._collection); + }); + }); + + context('is an array', function () { + beforeEach(function () { + data = ['a', 'b', 'c']; + }); + + it('calls opts.fn correctly for the first element', function () { + subject.call(scope, data, opts()); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(0), + args = thisCall.args[0]; + + expect(args).to.deep.property('key', 0); + expect(args).to.deep.property('value', 'a'); + expect(args).to.deep.property('_isFirst', true); + expect(args).to.deep.property('_isLast', false); + expect(args).to.deep.property('_total', 3); + }); + + it('calls opts.fn correctly for the middle element', function () { + subject.call(scope, data, opts()); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(1), + args = thisCall.args[0]; + + expect(args).to.deep.property('key', 1); + expect(args).to.deep.property('_isFirst', false); + expect(args).to.deep.property('_isLast', false); + }); + + it('calls opts.fn correctly for the last element', function () { + subject.call(scope, data, opts()); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(2), + args = thisCall.args[0]; + + expect(args).to.deep.property('key', 2); + expect(args).to.deep.property('_isLast', true); + }); + }); + + context('empty array', function () { + it('should call teh inverse function', function () { + var inverseSpy = sinon.spy(opts(), 'inverse'); + subject.call(scope, [], opts()); + + expect(inverseSpy).to.have.been.called; + }) + }) + + context('is a collection', function () { + data = { 'a': 'b', 'c': 'd' }; + var currentCollection; + + var collection = memo().is(function () { + var retVal = new Collection(); + for (var i = 0; i < 3; i++) { + data.id = i; + retVal.add(new Model(data)); + } + + return retVal; + }); + isCollection.is(function () { + return function () { + return true; + }; + }); + + context('default', function () { + beforeEach(function () { + currentCollection = collection(); + }) + + it('will set the _ array properties', function () { + subject.call(scope, currentCollection, opts()); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(0), + args = thisCall.args[0]; + + expect(args._isFirst).to.equal(true); + expect(args._isLast).to.equal(false); + expect(args._total).to.equal(3); + }) + + it('should pass a model as the value', function () { + subject.call(scope, currentCollection, opts()); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(0); + expect(thisCall.args[0].value).to.deep.equal(currentCollection.first()); + }) + }) + + context('is toJSON', function () { + var jsonOpts; + + beforeEach(function () { + currentCollection = collection(); + jsonOpts = opts(); + jsonOpts.hash.toJSON = true; + }) + + it('should pass a jsonified version of the model', function () { + subject.call(scope, currentCollection, jsonOpts); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(0); + expect(thisCall.args[0].value).to.deep.equal(currentCollection.first().toJSON()); + }) + }) + }) +}); diff --git a/test/shared/helpers/json.test.js b/test/shared/helpers/json.test.js new file mode 100644 index 0000000..e69de29 diff --git a/test/shared/helpers/partial.test.js b/test/shared/helpers/partial.test.js new file mode 100644 index 0000000..e69de29 diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js new file mode 100644 index 0000000..e69de29 From 956632d107345254406828d2e0af7a5722325c40 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 13:35:35 -0800 Subject: [PATCH 057/107] run all tests with *.test.js in teh test directory --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aac2303..30691b5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { - "test": "mocha test/*.test.js test/**/*.test.js" + "test": "mocha \"test/**/*.test.js\"" }, "repository": { "type": "git", From 60ff9c47cd481befad86eecc40318bc43381ba81 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 13:43:36 -0800 Subject: [PATCH 058/107] added a test for json helper --- test/shared/helpers/json.test.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/shared/helpers/json.test.js b/test/shared/helpers/json.test.js index e69de29..5d5c2bb 100644 --- a/test/shared/helpers/json.test.js +++ b/test/shared/helpers/json.test.js @@ -0,0 +1,23 @@ +var Handlebars = require('handlebars').create(), + memo = require('memo-is'), + sinon = require('sinon'), + chai = require('chai'), + expect = chai.expect, + subject = require('../../../shared/helpers/json')(Handlebars); + +describe('json', function () { + var data = memo().is(function () { + return { + 'a': true, + 'b': false, + 'c': [0, 1, 2] + } + }); + + it('stringifys an object to json', function () { + var result = subject(data()).string; + + expect(result).to.be.a('string'); + expect(result).to.equal(JSON.stringify(data())); + }); +}); From 1b84a2954ed13212099ee5fe515ee1aa004884f7 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 14:12:14 -0800 Subject: [PATCH 059/107] moved the each test from helpers.test.js to each.test.js, changed helpers.js to verify the helpers are added correctly --- test/shared/helpers.test.js | 37 +++++++++++--------------------- test/shared/helpers/each.test.js | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/test/shared/helpers.test.js b/test/shared/helpers.test.js index dc2b70d..b729a03 100644 --- a/test/shared/helpers.test.js +++ b/test/shared/helpers.test.js @@ -3,36 +3,23 @@ var chai = require('chai'), sinon = require('sinon'), sinonChai = require('sinon-chai'), - should = chai.should(), + expect = chai.expect, helpers = require('../../shared/helpers'), - Handlebars = require('handlebars').create(); + Handlebars = require('handlebars').create(), + subject; chai.use(sinonChai); describe('helpers', function () { + beforeEach(function () { + subject = helpers(Handlebars); + }); - function createRendrHandlebarsHelpers() { - var newRendrHelpers = helpers(Handlebars); - sinon.spy(newRendrHelpers, 'each'); - return newRendrHelpers; - } - - describe('each', function () { - it('should only call the original Handlebars each function on subsequent helper creation', function () { - var helperInstance0, - helperInstance1, - handlebarsOptions = { inverse: function () {} }, - handlebarsSpy = sinon.spy(); - - helperInstance0 = createRendrHandlebarsHelpers(); - helperInstance1 = createRendrHandlebarsHelpers(); - - helperInstance1.each(handlebarsSpy, handlebarsOptions); - - helperInstance0.each.should.have.not.been.called; - handlebarsSpy.should.have.been.called; - }); + it('has the helpers we expect', function () { + expect(subject.json).to.be.a('function'); + expect(subject.each).to.be.a('function'); + expect(subject.forEach).to.be.a('function'); + expect(subject.partial).to.be.a('function'); + expect(subject.view).to.be.a('function'); }); }); - - diff --git a/test/shared/helpers/each.test.js b/test/shared/helpers/each.test.js index e69de29..7cec13f 100644 --- a/test/shared/helpers/each.test.js +++ b/test/shared/helpers/each.test.js @@ -0,0 +1,22 @@ +var Handlebars = require('handlebars').create(), + memo = require('memo-is'), + sinon = require('sinon'), + chai = require('chai'), + expect = chai.expect, + subject = require('../../../shared/helpers/each')(Handlebars); + +describe('each', function () { + var spy; + var opts = memo().is(function () { + return { inverse: function () {} }; + }); + + beforeEach(function () { + spy = sinon.spy(); + }); + + it('should invoke the oldEach function', function () { + subject(spy, opts()); + expect(spy).to.have.been.called + }); +}); From c5e402f833c957a884cbaccd54b62c4d7fa9ebba Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 14:50:09 -0800 Subject: [PATCH 060/107] added tests for partial helper --- shared/helpers/partial.js | 1 - test/shared/helpers/partial.test.js | 34 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/shared/helpers/partial.js b/shared/helpers/partial.js index bdf4c92..e08a438 100644 --- a/shared/helpers/partial.js +++ b/shared/helpers/partial.js @@ -9,7 +9,6 @@ module.exports = function (Handlebars, getTemplate) { var data, html, context, template; template = getTemplate(templateName); - context = options.hash || {}; // First try to use Handlebars' hash arguments as the context for the diff --git a/test/shared/helpers/partial.test.js b/test/shared/helpers/partial.test.js index e69de29..a9f6519 100644 --- a/test/shared/helpers/partial.test.js +++ b/test/shared/helpers/partial.test.js @@ -0,0 +1,34 @@ +var Handlebars = require('handlebars').create(), + memo = require('memo-is'), + sinon = require('sinon'), + chai = require('chai'), + expect = chai.expect, + template = memo().is(function () { + return '
'; + }); + +describe('partial', function () { + var subject, stub, templateStub; + + beforeEach(function () { + templateStub = sinon.stub().returns(template()); + stub = sinon.stub().returns(templateStub); + subject = require('../../../shared/helpers/partial')(Handlebars, stub); + }); + + it('it returns the template, calling it with the given hash as the context', function () { + var result = subject('test', { hash: 'test' }).string; + + expect(result).to.equal(template()); + expect(templateStub).to.have.been.calledWith('test'); + }); + + it('creates a block if there is one passed in', function () { + var fnStub = sinon.stub().returns(template()), + result = subject('test', { hash: {test: true}, fn: fnStub }).string; + + expect(fnStub).to.have.been.called; + expect(templateStub.args[0][0]._block).to.equal(template()); + }); + +}); From 7181b003eae23687cce838f761114d609b92d1c5 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 15:00:09 -0800 Subject: [PATCH 061/107] removed the test for the view for now because it's really intense and hard to test right now --- test/shared/helpers/view.test.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/shared/helpers/view.test.js diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js deleted file mode 100644 index e69de29..0000000 From 1bb4610ff00f58dc22fd9d1d15178c009df06451 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 15:53:42 -0800 Subject: [PATCH 062/107] Update README.md Added documentation for use of all helpers. --- README.md | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/README.md b/README.md index 5aaa6ac..d65d631 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,127 @@ rendr-handlebars ================ [Handlebars](http://handlebarsjs.com/) template adapter for [Rendr](https://github.com/rendrjs/rendr) apps. + + +## Helpers + +#### view +------ + +The view helper is used to insert a new Rendr view. This is done on the server-side by generating the html and inserting it inline. On the client-side it creates a placeholder, and the `attach` step in Rendr to create the view instance and insert the HTML. If you don't pass any attributes to the helper, it set the context (or scope) of the helper to the same as the parents. You can also pass a block into the helper and it will be available inside of the created view as `_block`. + +Example: +``` +// no attributes +{{ view "viewName" }} + +{{ view "viewName" model=_model an_option="my option" }} + +// with a block +{{ view "viewName }} +
My Block
+{{/view}} +``` + +#### partial +------ + +A partial is HTML only, and it is inserted at compile time of the templates, making them more performant than a view. These are good to use in cases where you don't have any view interaction and just want to reduce the amount of copied HTML. Again, this will inherit the parents context if no attributes are passed into the helper. Again, you can pass a block into the partial and access the `_block` variable inside of the partial. + +Example: +``` +// no attributes +{{ partial "partialName" }} + +{{ partial "partialName" attr="example" }} + +// with block +{{ partial "partialName" }} +
My Block
+{{/partial}} +``` + +#### json +------ + +This helper simply takes an object and runs `JSON.stringify` on the object. You can also pass the [spacing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#space_argument) into the helper. +Example: +``` +{{json myObject}} + +// with spacing +{{ json myObject " " }} +``` + +#### each +------ + +Works as you would expect the Handlebars [each](http://handlebarsjs.com/builtin_helpers.html#iteration) helper to work. + +Example: +```html +
    + {{ each arr }} +
  • this
  • + {{/each}} +
+``` + +#### forEach +------ + +Another helper to iterate items in the template, this has a bit more included though. You can iterate objects, arrays, or Collections with this, setting the value and the key attributes in the scope. + +Always available in the `forEach` helper +- `key` - The key on the object, or the index in the array +- `value` - The value of the item being iterated. + +If iterating arrays or collections there is extra data included: +- `_isFirst` - Boolean, is true if it's the first item in the array / collection +- `_isLast` - Boolean, is true if it's the last item in the array / collection +- `_total` - The number of items in the array / collection + +If iterating an instance of a collection, you can set the `toJSON` attribute to convert the model being iterated into JSON instead of an instance of the model. By default the `value` will be set to a Backbone model when iterating a collection. + +Example: +```html + +

for each obj test

+{{forEach obj}} +
{{ key }} :::: {{ value }}
+{{/forEach}} + + +{{forEach arr}} +
{{ value }}
+{{else}} +
Array is empty
+{{/forEach}} + + +{{forEach arr attribute="test"}} +
{{attribute}}
+{{/forEach}} + + +

for each array of objs test

+{{forEach arr}} + {{if _isFirst}} +
Will only display for first element in array and collection
+ {{if _isLast}} +
Will only display for last element in array and collection
+ {{ value }} +{{/forEach}} + + +

for each array of objs test

+{{forEach myCollection}} + {{ key }} :::: {{ value.attributes.someAttr }} +{{/forEach}} + + +

for each array of objs test toJSON

+{{forEach myCollection toJSON=true}} + {{ key }} :::: {{ value.someAttr }} +{{/forEach}} +``` From 19138dbc46d3a6358b8d8398d51ca0008147f293 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 16:05:50 -0800 Subject: [PATCH 063/107] Update README.md cleanup from the doc changes --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d65d631..f06abb5 100644 --- a/README.md +++ b/README.md @@ -107,13 +107,17 @@ Example:
{{attribute}}
{{/forEach}} - +

for each array of objs test

{{forEach arr}} {{if _isFirst}}
Will only display for first element in array and collection
+ {{/if}} + {{if _isLast}}
Will only display for last element in array and collection
+ {{/if}} {{ value }} {{/forEach}} From 2c44a2524932df8cdf8a33f68250993778265394 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 16:12:04 -0800 Subject: [PATCH 064/107] upgrade dependency tree --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 30691b5..93affc9 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "url": "http://github.com/rendrjs/rendr-handlebars.git" }, "dependencies": { - "underscore": "1.5.2", + "underscore": "~1.7.0", "handlebars": "1.3.0" }, "peerDependencies": { @@ -29,10 +29,10 @@ }, "devDependencies": { "backbone": "~1.1.2", - "chai": "~1.9.0", + "chai": "~1.10.0", "memo-is": "0.0.2", - "mocha": "~1.12.0", - "sinon": "~1.8.2", - "sinon-chai": "~2.5.0" + "mocha": "~2.1.0", + "sinon": "~1.12.2", + "sinon-chai": "~2.6.0" } } From 8fc708140f26c41be75a8292d4f0af220b15618b Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 16:32:16 -0800 Subject: [PATCH 065/107] Update README.md fixed a few grammar / punctuation problems, added a run-down of the variables that are available in views / partials. --- README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f06abb5..12b0b04 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,18 @@ rendr-handlebars ## Helpers +There are a number of variables that are set by default to these helpers and are passed in the context. Those variables include: +- `_app` - The instance of the Rendr application +- `_model` - when setting the `model` attribute, the `_model` variable will be set as the instance of a Rendr model. +- `_collection` - when using a `collection` attribute, the `_collection` variable will be set to the instance of the Rendr collection. +- `_block` - when passing a block to the `view` or `partial` helper, the `_block` variable will be available. This `_block` variable will be the HTML passed in. + #### view ------ -The view helper is used to insert a new Rendr view. This is done on the server-side by generating the html and inserting it inline. On the client-side it creates a placeholder, and the `attach` step in Rendr to create the view instance and insert the HTML. If you don't pass any attributes to the helper, it set the context (or scope) of the helper to the same as the parents. You can also pass a block into the helper and it will be available inside of the created view as `_block`. +The view helper is used to insert a new Rendr view. This is done on the server-side by generating the html and inserting it inline. On the client-side it creates a placeholder, and then in Rendr it will call the `attach` function to create a view instance and insert the HTML. If you don't pass any attributes to the helper, it sets the context (or scope) of the helper to the same as the parents. + +You can also pass a block into the helper and it will be available inside of the created view as `_block`. This is helpful when you want to have a chunk of HTML differ in a view, but have the majority of it stay the same. Example: ``` @@ -30,7 +38,9 @@ Example: #### partial ------ -A partial is HTML only, and it is inserted at compile time of the templates, making them more performant than a view. These are good to use in cases where you don't have any view interaction and just want to reduce the amount of copied HTML. Again, this will inherit the parents context if no attributes are passed into the helper. Again, you can pass a block into the partial and access the `_block` variable inside of the partial. +A partial is HTML only, and it is inserted at compile time of the templates, making them more performant than a view. These are good to use in cases where you don't have any view interaction and just want to reduce the amount of copied HTML. This will inherit the parents context if no attributes are passed into the helper. + +Again, you can pass a block into the partial and access the `_block` variable inside of the partial. This is helpful when you want to set a variable chunk of HTML inside of a partial Example: ``` @@ -49,6 +59,7 @@ Example: ------ This helper simply takes an object and runs `JSON.stringify` on the object. You can also pass the [spacing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#space_argument) into the helper. + Example: ``` {{json myObject}} @@ -60,13 +71,13 @@ Example: #### each ------ -Works as you would expect the Handlebars [each](http://handlebarsjs.com/builtin_helpers.html#iteration) helper to work. +Iterates an array setting the context to the value of the array element. Works as you would expect the Handlebars [each](http://handlebarsjs.com/builtin_helpers.html#iteration) helper to work, but this adds Rendr specific options set in the context (`_app`, `_model`, `_collection`, `_block`) Example: ```html
    {{ each arr }} -
  • this
  • +
  • {{this}}
  • {{/each}}
``` From 88bc3b8d20dfab1983729cf3bcd096a584d5a227 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 16:45:53 -0800 Subject: [PATCH 066/107] version bump to 0.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 93affc9..4fd8d75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.2.1", + "version": "0.2.2", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From 2a3745cbdfccaac33f4a7ff5084d8b38c2b1cb89 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 16:48:30 -0800 Subject: [PATCH 067/107] Update README.md cleaned up the spacing between the headers for the helpers. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 12b0b04..20203fe 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ There are a number of variables that are set by default to these helpers and are - `_collection` - when using a `collection` attribute, the `_collection` variable will be set to the instance of the Rendr collection. - `_block` - when passing a block to the `view` or `partial` helper, the `_block` variable will be available. This `_block` variable will be the HTML passed in. + #### view ------ - The view helper is used to insert a new Rendr view. This is done on the server-side by generating the html and inserting it inline. On the client-side it creates a placeholder, and then in Rendr it will call the `attach` function to create a view instance and insert the HTML. If you don't pass any attributes to the helper, it sets the context (or scope) of the helper to the same as the parents. You can also pass a block into the helper and it will be available inside of the created view as `_block`. This is helpful when you want to have a chunk of HTML differ in a view, but have the majority of it stay the same. @@ -35,9 +35,9 @@ Example: {{/view}} ``` + #### partial ------ - A partial is HTML only, and it is inserted at compile time of the templates, making them more performant than a view. These are good to use in cases where you don't have any view interaction and just want to reduce the amount of copied HTML. This will inherit the parents context if no attributes are passed into the helper. Again, you can pass a block into the partial and access the `_block` variable inside of the partial. This is helpful when you want to set a variable chunk of HTML inside of a partial @@ -55,9 +55,9 @@ Example: {{/partial}} ``` + #### json ------ - This helper simply takes an object and runs `JSON.stringify` on the object. You can also pass the [spacing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#space_argument) into the helper. Example: @@ -68,9 +68,9 @@ Example: {{ json myObject " " }} ``` + #### each ------ - Iterates an array setting the context to the value of the array element. Works as you would expect the Handlebars [each](http://handlebarsjs.com/builtin_helpers.html#iteration) helper to work, but this adds Rendr specific options set in the context (`_app`, `_model`, `_collection`, `_block`) Example: @@ -82,9 +82,9 @@ Example: ``` + #### forEach ------ - Another helper to iterate items in the template, this has a bit more included though. You can iterate objects, arrays, or Collections with this, setting the value and the key attributes in the scope. Always available in the `forEach` helper From e8b6b0ac967d181b02ca6053600bd7cad48de680 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 8 Jan 2015 17:00:03 -0800 Subject: [PATCH 068/107] Update README.md fixed missing " --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20203fe..f27d0b0 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Example: {{ view "viewName" model=_model an_option="my option" }} // with a block -{{ view "viewName }} +{{ view "viewName" }}
My Block
{{/view}} ``` From ac4366ce147341b9cc17aa869eef1c49bc739a62 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Fri, 9 Jan 2015 13:27:30 -0800 Subject: [PATCH 069/107] Update HISTORY.md --- HISTORY.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 6d5418e..5de0c4c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,12 @@ +# 0.2.2 +## 2015-01-8 +* Changed the shared/helpers to follow a CommonJS pattern of requiring each helper separately +* added tests for the helpers +* added a `forEach` helper +* added documentation for all the helpers +* added ability to pass blocks into partials / views +* upgrade to underscore@1.7.0 + # 0.2.1 ## 2014-12-11 * Removed need to initialize the view twice on the client-side. This fixed leaking listeners and makes the code perform as expected From e538f1b3ada0b2e78e5a6d90b22b957aa3ba2288 Mon Sep 17 00:00:00 2001 From: wvengen Date: Wed, 14 Jan 2015 16:24:09 +0100 Subject: [PATCH 070/107] add forgotten require --- shared/helpers/view.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared/helpers/view.js b/shared/helpers/view.js index 664c78e..10d56df 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -2,7 +2,8 @@ * Helper to create new views in the templates */ -var getProperty = require('../../lib/getProperty'), +var _ = require('underscore'), + getProperty = require('../../lib/getProperty'), isServer = typeof window === 'undefined', BaseView; From 2a776dba3b4b00fd6c6052c86f16aad430093838 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 22 Jan 2015 11:12:00 -0800 Subject: [PATCH 071/107] added some pending tests for the view helper. need to do some work with proxyquire to be able to test some things --- test/shared/helpers/view.test.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/shared/helpers/view.test.js diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js new file mode 100644 index 0000000..d2bafcd --- /dev/null +++ b/test/shared/helpers/view.test.js @@ -0,0 +1,30 @@ +var Handlebars = require('handlebars').create(), + memo = require('memo-is'), + sinon = require('sinon'), + chai = require('chai'), + expect = chai.expect, + subject = require('../../../shared/helpers/view')(Handlebars); + +describe('view', function () { + var app = memo().is(function() { + return { + modelUtils: { + underscorize: function (name) { return name } + } + } + }); + + context('server-side', function () { + it('should error if there is no app in the options', function () { + expect(function () { + subject('test') + }).to.throw(Error) + }); + + it('returns a string with the html'); + }); + + context('client-side', function () { + it('should create a string with data-render set to true'); + }); +}); From 8650c4832bf6431c4f15b99321312345000f405c Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 22 Jan 2015 11:13:48 -0800 Subject: [PATCH 072/107] drop node 0.8 support --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e0b37e6..cc39d7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: node_js node_js: - "0.10" - - "0.8" notifications: email: false From 9a6dba9f56f9621b371fbcb78a4272ee475d8a69 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 2 Feb 2015 11:39:24 -0800 Subject: [PATCH 073/107] Stringify arrays when returning string for client placeholder This fixes an issue where arrays of objects that were passed into a view via the `view` helper would get incorrectly set. Before, an array of objects would get returned as `data-some-array="[Object object, Object object]"`. Also updated the test suite for the view helper and introduced proxyquire to stub out the Rendr dependencies. --- package.json | 3 +- shared/helpers/view.js | 9 ++- test/shared/helpers/view.test.js | 110 +++++++++++++++++++++++++++---- 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 4fd8d75..79d7760 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "memo-is": "0.0.2", "mocha": "~2.1.0", "sinon": "~1.12.2", - "sinon-chai": "~2.6.0" + "sinon-chai": "~2.6.0", + "proxyquire": "^1.3.1" } } diff --git a/shared/helpers/view.js b/shared/helpers/view.js index 10d56df..9063166 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -4,12 +4,12 @@ var _ = require('underscore'), getProperty = require('../../lib/getProperty'), - isServer = typeof window === 'undefined', BaseView; module.exports = function (Handlebars) { return function (viewName, options) { - var ViewClass, html, viewOptions, view, app; + var isServer = typeof window === 'undefined', + html, viewOptions, view, app; viewOptions = options.hash || {}; app = getProperty('_app', this, options); @@ -39,6 +39,8 @@ module.exports = function (Handlebars) { }; function getServerHtml(viewName, viewOptions, parentView) { + var ViewClass, view; + if (!BaseView) { BaseView = require('rendr/shared/base/view'); } // Pass through a reference to the parent view. @@ -66,8 +68,9 @@ function getClientPlaceholder(viewName, viewOptions) { // create a list of data attributes var attrString = _.inject(viewOptions, function(memo, value, key) { + if (_.isArray(value)) { value = JSON.stringify(value); } return memo += " data-" + key + "=\"" + _.escape(value) + "\""; }, ''); - return '
'; + return '
'; } diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js index d2bafcd..f477c36 100644 --- a/test/shared/helpers/view.test.js +++ b/test/shared/helpers/view.test.js @@ -1,30 +1,114 @@ var Handlebars = require('handlebars').create(), memo = require('memo-is'), - sinon = require('sinon'), chai = require('chai'), + proxyquire = require('proxyquire').noCallThru(), expect = chai.expect, - subject = require('../../../shared/helpers/view')(Handlebars); + BaseViewStub = { + getView: function (viewName, entryPath) { + return ViewClass; + }, + parseModelAndCollection: function (modelUtils, viewOptions) { + return viewOptions; + }, + extractFetchSummary: memo().is(function() { + return {}; + }) + }, + subject = proxyquire('../../../shared/helpers/view', { + 'rendr/shared/base/view': BaseViewStub + })(Handlebars); + +function ViewClass() {} + +ViewClass.prototype.getHtml = function () { + return '

Foo

' +}; describe('view', function () { - var app = memo().is(function() { - return { - modelUtils: { - underscorize: function (name) { return name } - } - } - }); + var app = memo().is(function () { + return { + options: { entryPath: '/path' }, + modelUtils: { + underscorize: function (name) { return name } + } + }; + }); context('server-side', function () { + before(function () { + GLOBAL.window = undefined; + }); + it('should error if there is no app in the options', function () { expect(function () { - subject('test') - }).to.throw(Error) + subject('test'); + }).to.throw(Error); }); - it('returns a string with the html'); + it("returns the result of the view instance's getHtml method", function() { + var result = subject('test', { + data: { '_app': app() } + }); + expect(result.string).to.eq('

Foo

'); + }); }); context('client-side', function () { - it('should create a string with data-render set to true'); + before(function () { + GLOBAL.window = true; + }); + + after(function () { + GLOBAL.window = undefined; + }); + + context('when extractFetchSummary returns an empty object', function () { + it('creates a string with data-render set to true', function () { + var result = subject('test', { + data: {'_app': app() } + }); + expect(result.string).to.eq( + '
' + ) + }); + }); + + context('when extractFetchSummary returns a model', function () { + BaseViewStub.extractFetchSummary.is(function () { + return { + model: { + model: 'SomeModel', + id: 1 + } + }; + }); + + it('includes the fetch summary for the model', function () { + var result = subject('test', { + data: { '_app': app() } + }); + expect(result.string).to.eq( + '
' + ) + }); + }); + + context('when the viewOptions contain arrays', function () { + it('serializes the arrays correctly', function () { + var result = subject('test', { + data: { + '_app': app() + }, + hash: { + number_array: [1, 2, 3], + string_array: ['foo', 'bar'], + object_array: [{ foo: 'bar', foo: 'baz' }] + } + }); + expect(result.string).to.eq( + '
' + ); + }); + }); }); }); From e0120e95f874e1371a0fd76f38d5dfd3fc01dd66 Mon Sep 17 00:00:00 2001 From: Matt Pardee Date: Mon, 2 Feb 2015 13:14:41 -0800 Subject: [PATCH 074/107] Serialize objects and added test --- shared/helpers/view.js | 2 +- test/shared/helpers/view.test.js | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/shared/helpers/view.js b/shared/helpers/view.js index 9063166..8abb40e 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -68,7 +68,7 @@ function getClientPlaceholder(viewName, viewOptions) { // create a list of data attributes var attrString = _.inject(viewOptions, function(memo, value, key) { - if (_.isArray(value)) { value = JSON.stringify(value); } + if (_.isArray(value) || _.isObject(value)) { value = JSON.stringify(value); } return memo += " data-" + key + "=\"" + _.escape(value) + "\""; }, ''); diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js index f477c36..ddd4e3e 100644 --- a/test/shared/helpers/view.test.js +++ b/test/shared/helpers/view.test.js @@ -110,5 +110,25 @@ describe('view', function () { ); }); }); + + context('when the viewOptions contains an object', function () { + it('serializes the objects correctly', function () { + var result = subject('test', { + data: { + '_app': app() + }, + hash: { + generic_object: { + a: 1, + b: 2, + c: 3 + } + } + }); + expect(result.string).to.eq( + '
' + ); + }); + }); }); }); From 31937c0f42e667d182cd929676a97d87423980ed Mon Sep 17 00:00:00 2001 From: Matt Pardee Date: Mon, 2 Feb 2015 13:17:13 -0800 Subject: [PATCH 075/107] Spellcheck --- test/shared/helpers/view.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js index ddd4e3e..5b989ec 100644 --- a/test/shared/helpers/view.test.js +++ b/test/shared/helpers/view.test.js @@ -112,7 +112,7 @@ describe('view', function () { }); context('when the viewOptions contains an object', function () { - it('serializes the objects correctly', function () { + it('serializes the object correctly', function () { var result = subject('test', { data: { '_app': app() From dc9d6e1677424981ed73c77766e98c9087d87188 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 3 Feb 2015 11:09:57 -0800 Subject: [PATCH 076/107] minor rev for a few bug fixes --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79d7760..eb3c87e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.2.2", + "version": "0.2.3", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From 112c43c6a273e77e0c9235c8bf4593c5e48c6abd Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 3 Feb 2015 11:47:15 -0800 Subject: [PATCH 077/107] Update HISTORY.md --- HISTORY.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 5de0c4c..3043f0c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,11 @@ +# 0.2.3 +## 2015-02-03 +* stringify objects +* added view tests +* fixed missing require for underscore + # 0.2.2 -## 2015-01-8 +## 2015-01-08 * Changed the shared/helpers to follow a CommonJS pattern of requiring each helper separately * added tests for the helpers * added a `forEach` helper From d679f2d8b16a58f5403e81ed7111415a004a4e7b Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 3 Feb 2015 15:32:16 -0800 Subject: [PATCH 078/107] fix bug where '_' attributes aren't defined for blocks. --- shared/helpers/view.js | 3 ++- test/shared/helpers/view.test.js | 20 +++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/shared/helpers/view.js b/shared/helpers/view.js index 8abb40e..b90c791 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -24,7 +24,8 @@ module.exports = function (Handlebars) { // allow views to be passed optional block elements if (_.isFunction(options.fn)) { - viewOptions._block = options.fn(viewOptions); + var blockOptions = _.extend({}, this, viewOptions); + viewOptions._block = options.fn(blockOptions); } if (isServer) { diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js index 5b989ec..3b9cdef 100644 --- a/test/shared/helpers/view.test.js +++ b/test/shared/helpers/view.test.js @@ -1,6 +1,7 @@ var Handlebars = require('handlebars').create(), memo = require('memo-is'), chai = require('chai'), + sinon = require('sinon'), proxyquire = require('proxyquire').noCallThru(), expect = chai.expect, BaseViewStub = { @@ -34,9 +35,22 @@ describe('view', function () { }; }); + context('has a block', function () { + it('will pass the correct options to for a block', function () { + var stub = sinon.stub().returns('
test
'); + + var result = subject.call({ + _app: app() + }, 'test', { fn: stub }); + + expect(stub).to.have.been.called; + expect(stub.firstCall.args[0]).to.have.ownProperty('_app'); + }); + }) + context('server-side', function () { before(function () { - GLOBAL.window = undefined; + global.window = undefined; }); it('should error if there is no app in the options', function () { @@ -55,11 +69,11 @@ describe('view', function () { context('client-side', function () { before(function () { - GLOBAL.window = true; + global.window = true; }); after(function () { - GLOBAL.window = undefined; + global.window = undefined; }); context('when extractFetchSummary returns an empty object', function () { From cbaa22917b22ec3cfd00c6f447823659f99565f0 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 3 Feb 2015 15:34:42 -0800 Subject: [PATCH 079/107] upgrade to 0.2.4 with the fix for blocks --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb3c87e..b2fa710 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.2.3", + "version": "0.2.4", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From ec840e37e88e4d371edc6251cd1c5b87050c4e30 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 3 Feb 2015 15:40:36 -0800 Subject: [PATCH 080/107] Update HISTORY.md --- HISTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 3043f0c..bd58c2b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,7 @@ +# 0.2.4 +## 2015-02-03 +* invoke block views with the parent scope + # 0.2.3 ## 2015-02-03 * stringify objects From bc939c6106c05559d7b9d07d690f9226c9d18d26 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 3 Feb 2015 17:30:01 -0800 Subject: [PATCH 081/107] Don't include data attributes that are present in the fetch summary When returning the client placeholder for a view, we currently add data attributes that are later ignored because the models/collections are actually fetched via the fetch_params. This PR updates the view helper so that it doesn't add these unnecessary data attributes, resulting in a much smaller DOM. --- shared/helpers/view.js | 4 +-- test/shared/helpers/view.test.js | 46 +++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/shared/helpers/view.js b/shared/helpers/view.js index b90c791..c5d67b3 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -62,10 +62,8 @@ function getClientPlaceholder(viewName, viewOptions) { // Builds a fetch_summary attribute viewOptions = BaseView.parseModelAndCollection(viewOptions.app.modelUtils, viewOptions); fetchSummary = BaseView.extractFetchSummary(viewOptions.app.modelUtils, viewOptions); - fetchSummary = JSON.stringify(fetchSummary) - viewOptions['fetch_summary'] = fetchSummary - viewOptions = _.omit(viewOptions, ['model', 'collection', 'app']) + viewOptions = _.omit(viewOptions, _.keys(fetchSummary).concat(['model', 'collection', 'app'])); // create a list of data attributes var attrString = _.inject(viewOptions, function(memo, value, key) { diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js index 3b9cdef..86e55d2 100644 --- a/test/shared/helpers/view.test.js +++ b/test/shared/helpers/view.test.js @@ -25,12 +25,16 @@ ViewClass.prototype.getHtml = function () { return '

Foo

' }; +function ModelClass() {} + +function CollectionClass() {} + describe('view', function () { var app = memo().is(function () { return { options: { entryPath: '/path' }, modelUtils: { - underscorize: function (name) { return name } + underscorize: function (name) { return name; } } }; }); @@ -90,19 +94,49 @@ describe('view', function () { context('when extractFetchSummary returns a model', function () { BaseViewStub.extractFetchSummary.is(function () { return { - model: { - model: 'SomeModel', + some_model: { + model: 'ModelClass', + id: 1 + } + }; + }); + + it('includes the fetch summary for the model and does not include a data-attribute for the model', function () { + var result = subject('test', { + data: { + '_app': app() + }, + hash: { + some_model: new ModelClass() + } + }); + expect(result.string).to.eq( + '
' + ) + }); + }); + + context('when extractFetchSummary returns a collection', function () { + BaseViewStub.extractFetchSummary.is(function () { + return { + some_collection: { + collection: 'CollectionClass', id: 1 } }; }); - it('includes the fetch summary for the model', function () { + it('includes the fetch summary for the collection and does not include a data-attribute for the collection', function () { var result = subject('test', { - data: { '_app': app() } + data: { + '_app': app() + }, + hash: { + some_collection: new CollectionClass() + } }); expect(result.string).to.eq( - '
' + '
' ) }); }); From 9ff4ced36c8740ce076974d777efa2545f9cb43d Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Wed, 4 Feb 2015 13:12:01 -0800 Subject: [PATCH 082/107] Set _app by binding context instead of in data --- test/shared/helpers/view.test.js | 42 +++++++++++++++----------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js index 86e55d2..6fc0ded 100644 --- a/test/shared/helpers/view.test.js +++ b/test/shared/helpers/view.test.js @@ -64,9 +64,10 @@ describe('view', function () { }); it("returns the result of the view instance's getHtml method", function() { - var result = subject('test', { - data: { '_app': app() } - }); + var result = subject.call({ + _app: app() + }, 'test', {}); + expect(result.string).to.eq('

Foo

'); }); }); @@ -82,9 +83,10 @@ describe('view', function () { context('when extractFetchSummary returns an empty object', function () { it('creates a string with data-render set to true', function () { - var result = subject('test', { - data: {'_app': app() } - }); + var result = subject.call({ + _app: app() + }, 'test', {}); + expect(result.string).to.eq( '
' ) @@ -102,10 +104,9 @@ describe('view', function () { }); it('includes the fetch summary for the model and does not include a data-attribute for the model', function () { - var result = subject('test', { - data: { - '_app': app() - }, + var result = subject.call({ + _app: app() + }, 'test', { hash: { some_model: new ModelClass() } @@ -127,10 +128,9 @@ describe('view', function () { }); it('includes the fetch summary for the collection and does not include a data-attribute for the collection', function () { - var result = subject('test', { - data: { - '_app': app() - }, + var result = subject.call({ + _app: app() + }, 'test', { hash: { some_collection: new CollectionClass() } @@ -143,10 +143,9 @@ describe('view', function () { context('when the viewOptions contain arrays', function () { it('serializes the arrays correctly', function () { - var result = subject('test', { - data: { - '_app': app() - }, + var result = subject.call({ + _app: app() + }, 'test', { hash: { number_array: [1, 2, 3], string_array: ['foo', 'bar'], @@ -161,10 +160,9 @@ describe('view', function () { context('when the viewOptions contains an object', function () { it('serializes the object correctly', function () { - var result = subject('test', { - data: { - '_app': app() - }, + var result = subject.call({ + _app: app() + }, 'test', { hash: { generic_object: { a: 1, From 5f3f50f81a422c5a8cf20b277c89d185d408daeb Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 24 Feb 2015 10:35:31 -0800 Subject: [PATCH 083/107] add serverToClientJson helper - this should be set when you pass any data from the server to the client --- package.json | 4 ++-- shared/helpers.js | 1 + shared/helpers/serverToClientJson.js | 6 ++++++ test/shared/helpers.test.js | 1 + test/shared/helpers/serverToClientJson.test.js | 14 ++++++++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 shared/helpers/serverToClientJson.js create mode 100644 test/shared/helpers/serverToClientJson.test.js diff --git a/package.json b/package.json index b2fa710..2e499de 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "url": "http://github.com/rendrjs/rendr-handlebars.git" }, "dependencies": { - "underscore": "~1.7.0", - "handlebars": "1.3.0" + "handlebars": "1.3.0", + "underscore": "~1.7.0" }, "peerDependencies": { "rendr": ">=0.5.1" diff --git a/shared/helpers.js b/shared/helpers.js index 46c3942..f824d89 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -4,6 +4,7 @@ module.exports = function(Handlebars, getTemplate) { partial: require('./helpers/partial')(Handlebars, getTemplate), json: require('./helpers/json')(Handlebars), each: require('./helpers/each')(Handlebars), + serverToClientJson: require('./helpers/serverToClientJson')(Handlebars), forEach: require('./helpers/forEach') }; }; diff --git a/shared/helpers/serverToClientJson.js b/shared/helpers/serverToClientJson.js new file mode 100644 index 0000000..bdcc328 --- /dev/null +++ b/shared/helpers/serverToClientJson.js @@ -0,0 +1,6 @@ +module.exports = function (Handlebars) { + return function (obj) { + var data = escape(JSON.stringify(obj)); + return new Handlebars.SafeString('JSON.parse(unescape("' + data + '"))'); + }; +}; diff --git a/test/shared/helpers.test.js b/test/shared/helpers.test.js index b729a03..abbdbe2 100644 --- a/test/shared/helpers.test.js +++ b/test/shared/helpers.test.js @@ -21,5 +21,6 @@ describe('helpers', function () { expect(subject.forEach).to.be.a('function'); expect(subject.partial).to.be.a('function'); expect(subject.view).to.be.a('function'); + expect(subject.serverToClientJson).to.be.a('function'); }); }); diff --git a/test/shared/helpers/serverToClientJson.test.js b/test/shared/helpers/serverToClientJson.test.js new file mode 100644 index 0000000..f4678d6 --- /dev/null +++ b/test/shared/helpers/serverToClientJson.test.js @@ -0,0 +1,14 @@ +var Handlebars = require('handlebars').create(), + sinon = require('sinon'), + chai = require('chai'), + expect = chai.expect, + subject = require('../../../shared/helpers/serverToClientJson')(Handlebars); + +describe('serverToClientJson', function () { + var data = { key: 'права' } + + it('should add JSON.parse and unescape to the string', function () { + var result = subject(data); + expect(eval(result.string)).to.deep.equal(data); + }); +}); From 7631e80ba44854f431bf4c7e08d9716cf4901c58 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 24 Feb 2015 16:44:58 -0800 Subject: [PATCH 084/107] verify that the string is what we'd want and the data is parsed the way we expect --- test/shared/helpers/serverToClientJson.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/shared/helpers/serverToClientJson.test.js b/test/shared/helpers/serverToClientJson.test.js index f4678d6..dff3809 100644 --- a/test/shared/helpers/serverToClientJson.test.js +++ b/test/shared/helpers/serverToClientJson.test.js @@ -8,7 +8,15 @@ describe('serverToClientJson', function () { var data = { key: 'права' } it('should add JSON.parse and unescape to the string', function () { + var result = subject(data), + expectedResult = 'JSON.parse(unescape("%7B%22key%22%3A%22%u043F%u0440%u0430%u0432%u0430%22%7D"))'; + + expect(expectedResult).to.equal(result.string); + }); + + it('should result in the same data after eval', function () { var result = subject(data); expect(eval(result.string)).to.deep.equal(data); }); + }); From d45a71abf56ed0faab7f3892ea2cb14edcade71f Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 24 Feb 2015 17:08:13 -0800 Subject: [PATCH 085/107] upgrade dependencies and fix tests that broke when upgrading chai --- package.json | 10 +++++----- test/shared/helpers/forEach.test.js | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 2e499de..d69b6c5 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,11 @@ "url": "http://github.com/rendrjs/rendr-handlebars.git" }, "dependencies": { - "handlebars": "1.3.0", - "underscore": "~1.7.0" + "handlebars": "^2.0.0", + "underscore": "^1.8.2" }, "peerDependencies": { - "rendr": ">=0.5.1" + "rendr": ">=1.0.0" }, "keywords": [ "rendr", @@ -29,11 +29,11 @@ }, "devDependencies": { "backbone": "~1.1.2", - "chai": "~1.10.0", + "chai": "^2.1.0", "memo-is": "0.0.2", "mocha": "~2.1.0", "sinon": "~1.12.2", - "sinon-chai": "~2.6.0", + "sinon-chai": "^2.7.0", "proxyquire": "^1.3.1" } } diff --git a/test/shared/helpers/forEach.test.js b/test/shared/helpers/forEach.test.js index 6080fdf..c5fa3ba 100644 --- a/test/shared/helpers/forEach.test.js +++ b/test/shared/helpers/forEach.test.js @@ -103,11 +103,11 @@ describe('forEach', function () { var thisCall = spy.getCall(0), args = thisCall.args[0]; - expect(args).to.deep.property('key', 0); - expect(args).to.deep.property('value', 'a'); - expect(args).to.deep.property('_isFirst', true); - expect(args).to.deep.property('_isLast', false); - expect(args).to.deep.property('_total', 3); + expect(args.key).to.equal(0); + expect(args.value).to.equal('a'); + expect(args._isFirst).to.equal(true); + expect(args._isLast).to.equal(false); + expect(args._total).to.equal(3); }); it('calls opts.fn correctly for the middle element', function () { @@ -117,9 +117,9 @@ describe('forEach', function () { var thisCall = spy.getCall(1), args = thisCall.args[0]; - expect(args).to.deep.property('key', 1); - expect(args).to.deep.property('_isFirst', false); - expect(args).to.deep.property('_isLast', false); + expect(args.key).to.equal(1); + expect(args._isFirst).to.equal(false); + expect(args._isLast).to.equal(false); }); it('calls opts.fn correctly for the last element', function () { @@ -129,8 +129,8 @@ describe('forEach', function () { var thisCall = spy.getCall(2), args = thisCall.args[0]; - expect(args).to.deep.property('key', 2); - expect(args).to.deep.property('_isLast', true); + expect(args.key).to.equal(2); + expect(args._isLast).to.equal(true); }); }); From 36045af412710600f96bcaeb25c1d73450f023c1 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 24 Feb 2015 17:18:38 -0800 Subject: [PATCH 086/107] fixed tests to use .to.have.property instead of .to.equal --- test/shared/helpers/forEach.test.js | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/shared/helpers/forEach.test.js b/test/shared/helpers/forEach.test.js index c5fa3ba..422bb7d 100644 --- a/test/shared/helpers/forEach.test.js +++ b/test/shared/helpers/forEach.test.js @@ -46,8 +46,8 @@ describe('forEach', function () { var thisCall = spy.getCall(0), args = thisCall.args[0]; - expect(args.key).to.equal('a'); - expect(args.value).to.equal('b'); + expect(args).to.have.property('key', 'a'); + expect(args).to.have.property('value', 'b'); }) it('will have the private properties attached', function () { @@ -103,11 +103,11 @@ describe('forEach', function () { var thisCall = spy.getCall(0), args = thisCall.args[0]; - expect(args.key).to.equal(0); - expect(args.value).to.equal('a'); - expect(args._isFirst).to.equal(true); - expect(args._isLast).to.equal(false); - expect(args._total).to.equal(3); + expect(args).to.have.property('key', 0); + expect(args).to.have.property('value', 'a'); + expect(args).to.have.property('_isFirst', true); + expect(args).to.have.property('_isLast', false); + expect(args).to.have.property('_total', 3); }); it('calls opts.fn correctly for the middle element', function () { @@ -117,9 +117,9 @@ describe('forEach', function () { var thisCall = spy.getCall(1), args = thisCall.args[0]; - expect(args.key).to.equal(1); - expect(args._isFirst).to.equal(false); - expect(args._isLast).to.equal(false); + expect(args).to.have.property('key', 1); + expect(args).to.have.property('_isFirst', false); + expect(args).to.have.property('_isLast', false); }); it('calls opts.fn correctly for the last element', function () { @@ -129,8 +129,8 @@ describe('forEach', function () { var thisCall = spy.getCall(2), args = thisCall.args[0]; - expect(args.key).to.equal(2); - expect(args._isLast).to.equal(true); + expect(args).to.have.property('key', 2); + expect(args).to.have.property('_isLast', true); }); }); @@ -174,9 +174,9 @@ describe('forEach', function () { var thisCall = spy.getCall(0), args = thisCall.args[0]; - expect(args._isFirst).to.equal(true); - expect(args._isLast).to.equal(false); - expect(args._total).to.equal(3); + expect(args).to.have.property('_isFirst', true); + expect(args).to.have.property('_isLast', false); + expect(args).to.have.property('_total', 3); }) it('should pass a model as the value', function () { From b5be7544d0dd6c5ef031f2aab7559c8d8c78284f Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 24 Feb 2015 17:30:25 -0800 Subject: [PATCH 087/107] cleanup on the to.deep.equal tests to hceck the property and it's value --- test/shared/helpers/forEach.test.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/shared/helpers/forEach.test.js b/test/shared/helpers/forEach.test.js index 422bb7d..26bbb66 100644 --- a/test/shared/helpers/forEach.test.js +++ b/test/shared/helpers/forEach.test.js @@ -64,10 +64,10 @@ describe('forEach', function () { var thisCall = spy.getCall(0), args = thisCall.args[0]; - expect(args._app).to.deep.equal(scope.app); - expect(args._view).to.deep.equal(scope.view); - expect(args._model).to.deep.equal(scope.model); - expect(args._collection).to.deep.equal(scope.collection); + expect(args).to.have.property('_app').that.deep.equals(scope.app); + expect(args).to.have.property('_view').that.deep.equals(scope.view); + expect(args).to.have.property('_model').that.deep.equals(scope.model); + expect(args).to.have.property('_collection').that.deep.equals(scope.collection); }) it('will have the private properties attached if it is nested in another helper', function () { @@ -84,10 +84,10 @@ describe('forEach', function () { var thisCall = spy.getCall(0), args = thisCall.args[0]; - expect(args._app).to.deep.equal(scope._app); - expect(args._view).to.deep.equal(scope._view); - expect(args._model).to.deep.equal(scope._model); - expect(args._collection).to.deep.equal(scope._collection); + expect(args).to.have.property('_app').that.deep.equals(scope._app); + expect(args).to.have.property('_view').that.deep.equals(scope._view); + expect(args).to.have.property('_model').that.deep.equals(scope._model); + expect(args).to.have.property('_collection').that.deep.equals(scope._collection); }); }); From 44c134577f00d4e2b81f8168c139f393179548dd Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 24 Feb 2015 20:29:50 -0800 Subject: [PATCH 088/107] bump to version 1.0.0 because of breaking changes with handlebars 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d69b6c5..33d1b92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "0.2.4", + "version": "1.0.0", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From a9f4bca628d54c7567f46c860cb7157e9f9eacc7 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Tue, 24 Feb 2015 20:31:41 -0800 Subject: [PATCH 089/107] Update HISTORY.md add version 1.0.0 --- HISTORY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index bd58c2b..c720662 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,10 @@ +# 1.0.0 +## 2015-02-24 +* update to handlebars 2.0.0 +* update to underscore 1.8.2 +* add serverToClientJson for an example of how to escape JSON between the client and server +* omit anything in the fetch params that as a data attribute + # 0.2.4 ## 2015-02-03 * invoke block views with the parent scope From d98c3f6d2e8d7cbdfaf3660f93abe4fd72b81c2f Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Wed, 25 Feb 2015 12:06:29 -0800 Subject: [PATCH 090/107] Update README.md added documentation for passing a model to a subview within a forEach loop --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f27d0b0..5cb57b2 100644 --- a/README.md +++ b/README.md @@ -144,3 +144,9 @@ and access them on the value with the dot operator --> {{ key }} :::: {{ value.someAttr }} {{/forEach}} ``` + + +

forEach collection -> subview

+{{forEach myCollection }} + {{view model=value model_name="ExampleModel"}} +{{/forEach}} From 4dd4ef8cc5c910d08789c1355b897c9e13563924 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Wed, 25 Feb 2015 12:06:49 -0800 Subject: [PATCH 091/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5cb57b2..1c1ef1a 100644 --- a/README.md +++ b/README.md @@ -143,10 +143,10 @@ and access them on the value with the dot operator --> {{forEach myCollection toJSON=true}} {{ key }} :::: {{ value.someAttr }} {{/forEach}} -```

forEach collection -> subview

{{forEach myCollection }} {{view model=value model_name="ExampleModel"}} {{/forEach}} +``` From 792edf4bd2e582d293ca7de0f453a4a8e3b0ed61 Mon Sep 17 00:00:00 2001 From: Jon Merrifield Date: Sat, 28 Feb 2015 09:26:04 -0900 Subject: [PATCH 092/107] Only ship the handlebars runtime to the client I'm making a big assumption that everyone is using precompiled templates with this module. --- index.js | 6 ++++++ package.json | 3 +++ 2 files changed, 9 insertions(+) diff --git a/index.js b/index.js index 6b3723e..e400054 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,11 @@ var Handlebars = require('handlebars'); +if (Handlebars['default']) { + // If we only have the Handlebars runtime available, use that here. + // Until Handlebars 3, we have to use 'default' instead of just requiring 'handlebars'. + Handlebars = Handlebars['default']; +} + module.exports = function(options){ var localExports = {}, templateFinder = require('./shared/templateFinder')(Handlebars); diff --git a/package.json b/package.json index 33d1b92..f52dc2f 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,8 @@ "sinon": "~1.12.2", "sinon-chai": "^2.7.0", "proxyquire": "^1.3.1" + }, + "browser": { + "handlebars": "handlebars/runtime" } } From c475c50e7cb8c885a5018681227abdc44aaac514 Mon Sep 17 00:00:00 2001 From: Jon Merrifield Date: Tue, 3 Mar 2015 14:43:08 -0800 Subject: [PATCH 093/107] Allow an instance of templateFinder to be passed in --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6b3723e..a1ce381 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ var Handlebars = require('handlebars'); module.exports = function(options){ var localExports = {}, - templateFinder = require('./shared/templateFinder')(Handlebars); + templateFinder = options.templateFinder || require('./shared/templateFinder')(Handlebars); /** * Export the `Handlebars` object, so other modules can add helpers, partials, etc. From a52726ba66ef5382b525df692c8e5e75ab1c4171 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Wed, 4 Mar 2015 10:31:27 -0800 Subject: [PATCH 094/107] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1c1ef1a..626741f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ rendr-handlebars [Handlebars](http://handlebarsjs.com/) template adapter for [Rendr](https://github.com/rendrjs/rendr) apps. +## Configuration options + +- `entryPath` *optional* - change the location of the application for the given rendr-app (same as the `entryPath` in the rendr configuration options +- `templateFinder` *optional* - allows the ability to require a different template finder. Commonly used to make a non-dynamic version of `require` to make buid systems more efficient. ## Helpers From 2f9c955f532a58b8d90ea2b56617f53afb8ef8c2 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Wed, 4 Mar 2015 10:32:23 -0800 Subject: [PATCH 095/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 626741f..17b37c7 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ rendr-handlebars ## Configuration options - `entryPath` *optional* - change the location of the application for the given rendr-app (same as the `entryPath` in the rendr configuration options -- `templateFinder` *optional* - allows the ability to require a different template finder. Commonly used to make a non-dynamic version of `require` to make buid systems more efficient. +- `templateFinder` *optional* - allows the ability to require a different template finder. Commonly used to make a non-dynamic version of `require` to make build systems more efficient. ## Helpers From fa4daf446ed84f1a62eadac061261cb2f3cd2289 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Wed, 4 Mar 2015 10:33:33 -0800 Subject: [PATCH 096/107] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17b37c7..36fcc08 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ rendr-handlebars ## Configuration options -- `entryPath` *optional* - change the location of the application for the given rendr-app (same as the `entryPath` in the rendr configuration options +- `entryPath` *optional* - change the location of the application for the given rendr-app. - `templateFinder` *optional* - allows the ability to require a different template finder. Commonly used to make a non-dynamic version of `require` to make build systems more efficient. ## Helpers From e9e75ac69c4fa9b17b396e924a381462d9145226 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 26 Mar 2015 14:21:48 -0700 Subject: [PATCH 097/107] update to 1.0.1 --- HISTORY.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index c720662..14e8172 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +# 1.0.1 +## 2015-03-26 +* allow instance of templateFinder to be passed in +* only ship handlbars runtime, greatly reduces client bundle size + # 1.0.0 ## 2015-02-24 * update to handlebars 2.0.0 diff --git a/package.json b/package.json index f52dc2f..a56c002 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "1.0.0", + "version": "1.0.1", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From 4065e4a9909e2d5270cc4eb50284be5cd2c751e7 Mon Sep 17 00:00:00 2001 From: Giulio Grillanda Date: Wed, 29 Apr 2015 16:51:14 +0200 Subject: [PATCH 098/107] remove handlebars as a dependency, get it as init param --- index.js | 15 +++++++-------- package.json | 2 +- test/index.test.js | 11 ++++++----- test/shared/templateFinder.test.js | 3 ++- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index 7f9c4fa..f315032 100644 --- a/index.js +++ b/index.js @@ -1,14 +1,13 @@ -var Handlebars = require('handlebars'); +module.exports = function(options, Handlebars) { -if (Handlebars['default']) { - // If we only have the Handlebars runtime available, use that here. - // Until Handlebars 3, we have to use 'default' instead of just requiring 'handlebars'. - Handlebars = Handlebars['default']; -} + if (Handlebars['default']) { + // If we only have the Handlebars runtime available, use that here. + // Until Handlebars 3, we have to use 'default' instead of just requiring 'handlebars'. + Handlebars = Handlebars['default']; + } -module.exports = function(options){ var localExports = {}, - templateFinder = options.templateFinder || require('./shared/templateFinder')(Handlebars); + templateFinder = options.templateFinder || require('./shared/templateFinder')(Handlebars); /** * Export the `Handlebars` object, so other modules can add helpers, partials, etc. diff --git a/package.json b/package.json index a56c002..acfd3f4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "url": "http://github.com/rendrjs/rendr-handlebars.git" }, "dependencies": { - "handlebars": "^2.0.0", "underscore": "^1.8.2" }, "peerDependencies": { @@ -30,6 +29,7 @@ "devDependencies": { "backbone": "~1.1.2", "chai": "^2.1.0", + "handlebars": "^2.0.0", "memo-is": "0.0.2", "mocha": "~2.1.0", "sinon": "~1.12.2", diff --git a/test/index.test.js b/test/index.test.js index b3b1c3a..e433cfc 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,9 +1,10 @@ -var assert = require('assert'); +var assert = require('assert'), + Handlebars = require('handlebars'); describe("require('rendr-handlebars')", function() { it('returns a new templateAdapter', function() { var templateAdapter, firstPatternSrc; - templateAdapter = require('../index')({entryPath: '/some/place/'}) + templateAdapter = require('../index')({entryPath: '/some/place/'}, Handlebars); assert.equal(templateAdapter.templatePatterns.length, 1); firstPatternSrc = templateAdapter.templatePatterns[0].src; assert.equal(firstPatternSrc, '/some/place/app/templates/compiledTemplates'); @@ -11,8 +12,8 @@ describe("require('rendr-handlebars')", function() { it('does not squash an old templateAdapter', function() { var templateAdapter1, templateAdapter2, firstPatternSrc, secondPatternSrc; - templateAdapter1 = require('../index')({entryPath: '/some/place/'}) - templateAdapter2 = require('../index')({entryPath: '/some/other/place/'}) + templateAdapter1 = require('../index')({entryPath: '/some/place/'}, Handlebars); + templateAdapter2 = require('../index')({entryPath: '/some/other/place/'}, Handlebars); assert.equal(templateAdapter1.templatePatterns.length, 1); assert.equal(templateAdapter2.templatePatterns.length, 1); firstPatternSrc = templateAdapter1.templatePatterns[0].src; @@ -20,4 +21,4 @@ describe("require('rendr-handlebars')", function() { assert.equal(firstPatternSrc, '/some/place/app/templates/compiledTemplates'); assert.equal(secondPatternSrc, '/some/other/place/app/templates/compiledTemplates'); }); -}); +}); \ No newline at end of file diff --git a/test/shared/templateFinder.test.js b/test/shared/templateFinder.test.js index 80afca8..4f1d0eb 100644 --- a/test/shared/templateFinder.test.js +++ b/test/shared/templateFinder.test.js @@ -1,6 +1,7 @@ var assert = require('assert'), entryPath = process.cwd() + '/test/fixtures/', - templateAdapter = require('../../index')({entryPath: entryPath}); + Handlebars = require('handlebars'), + templateAdapter = require('../../index')({entryPath: entryPath}, Handlebars); describe('templateFinder', function() { describe('getTemplate', function() { From bf8b39ed5aa69518e1d8ab1178786f3f2682176f Mon Sep 17 00:00:00 2001 From: Giulio Grillanda Date: Sat, 27 Jun 2015 20:27:22 -0700 Subject: [PATCH 099/107] add supported Handlebars version to README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 36fcc08..5cd0395 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ rendr-handlebars ================ [Handlebars](http://handlebarsjs.com/) template adapter for [Rendr](https://github.com/rendrjs/rendr) apps. +This library has been tested using [Handlebars 2.0.0](https://github.com/wycats/handlebars.js/tree/v2.0.0). + +If you'd like to use a more recent version number of `Handlebars` for your app, just specify it in the [package.json](https://github.com/rendrjs/rendr/blame/master/README.md#L315) of your app and start testing it. + ## Configuration options From 816c74ebea5a73a490fa194bfe5415c14168b391 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Fri, 17 Jul 2015 14:33:45 -0700 Subject: [PATCH 100/107] Update to 2.0.0, because we are removing handlebars as a dependency this is a breaking (major) change. --- HISTORY.md | 3 +++ package.json | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 14e8172..28ab57a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,6 @@ +# 2.0.0 +* handlebars is no longer included here, it needs to be installed at the app level. + # 1.0.1 ## 2015-03-26 * allow instance of templateFinder to be passed in diff --git a/package.json b/package.json index acfd3f4..572f9fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "1.0.1", + "version": "2.0.0", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { @@ -14,7 +14,7 @@ "underscore": "^1.8.2" }, "peerDependencies": { - "rendr": ">=1.0.0" + "rendr": ">=1.2.0" }, "keywords": [ "rendr", From 06d51d2cfd48c9a4848881baee718004a8c8bce1 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Mon, 20 Jul 2015 15:53:07 -0700 Subject: [PATCH 101/107] Update package.json Rendr version needs to be greater than 1.1.0, not 1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 572f9fd..e6dd03e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "underscore": "^1.8.2" }, "peerDependencies": { - "rendr": ">=1.2.0" + "rendr": ">=1.1.0" }, "keywords": [ "rendr", From cbca1b19b6e07d399ff97ea637f2d7bcf3c783e6 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 30 Jul 2015 20:55:18 -0700 Subject: [PATCH 102/107] version 2.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6dd03e..40bb69f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "2.0.0", + "version": "2.0.1", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": { From b770611dbaad8032544642ca5f65dadb53dd0e4e Mon Sep 17 00:00:00 2001 From: Santiago Archila Date: Wed, 7 Oct 2015 11:04:34 -0700 Subject: [PATCH 103/107] check if collection is a collection object before checking whether it is empty --- shared/helpers/forEach.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shared/helpers/forEach.js b/shared/helpers/forEach.js index 78c2081..4e31e7a 100644 --- a/shared/helpers/forEach.js +++ b/shared/helpers/forEach.js @@ -10,6 +10,11 @@ module.exports = function (collection, opts) { isCollection = app.modelUtils.isCollection(collection), buffer = ''; + // iterate the models on a collection + if (isCollection) { + collection = collection.models + } + if (_.isEmpty(collection)) { return opts.inverse(_.extend({}, this, { _app: app, @@ -19,11 +24,6 @@ module.exports = function (collection, opts) { })); } - // iterate the models on a collection - if (isCollection) { - collection = collection.models - } - _.each(collection, function (value, key) { if (isCollection && opts.hash.toJSON) { value = value.toJSON(); From a6bdf02b1298eec3d45fb1496690cb6578b75532 Mon Sep 17 00:00:00 2001 From: Santiago Archila Date: Wed, 7 Oct 2015 15:00:42 -0700 Subject: [PATCH 104/107] add test for empty collection calling the inverse function --- test/shared/helpers/forEach.test.js | 99 +++++++++++++++++------------ 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/test/shared/helpers/forEach.test.js b/test/shared/helpers/forEach.test.js index 26bbb66..1f03caa 100644 --- a/test/shared/helpers/forEach.test.js +++ b/test/shared/helpers/forEach.test.js @@ -135,7 +135,7 @@ describe('forEach', function () { }); context('empty array', function () { - it('should call teh inverse function', function () { + it('should call the inverse function', function () { var inverseSpy = sinon.spy(opts(), 'inverse'); subject.call(scope, [], opts()); @@ -144,65 +144,86 @@ describe('forEach', function () { }) context('is a collection', function () { - data = { 'a': 'b', 'c': 'd' }; - var currentCollection; + var currentCollection, + collection = memo().is(function () {}); - var collection = memo().is(function () { - var retVal = new Collection(); - for (var i = 0; i < 3; i++) { - data.id = i; - retVal.add(new Model(data)); - } - - return retVal; - }); isCollection.is(function () { return function () { return true; }; }); - context('default', function () { - beforeEach(function () { - currentCollection = collection(); + context('collection is not empty', function () { + data = { 'a': 'b', 'c': 'd' }; + collection.is(function () { + var retVal = new Collection(); + for (var i = 0; i < 3; i++) { + data.id = i; + retVal.add(new Model(data)); + } + + return retVal; + }); + + context('default', function () { + beforeEach(function () { + currentCollection = collection(); + }) + + it('will set the _ array properties', function () { + subject.call(scope, currentCollection, opts()); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(0), + args = thisCall.args[0]; + + expect(args).to.have.property('_isFirst', true); + expect(args).to.have.property('_isLast', false); + expect(args).to.have.property('_total', 3); + }) + + it('should pass a model as the value', function () { + subject.call(scope, currentCollection, opts()); + expect(spy).to.have.been.calledThrice; + + var thisCall = spy.getCall(0); + expect(thisCall.args[0].value).to.deep.equal(currentCollection.first()); + }) }) - it('will set the _ array properties', function () { - subject.call(scope, currentCollection, opts()); - expect(spy).to.have.been.calledThrice; + context('is toJSON', function () { + var jsonOpts; - var thisCall = spy.getCall(0), - args = thisCall.args[0]; + beforeEach(function () { + currentCollection = collection(); + jsonOpts = opts(); + jsonOpts.hash.toJSON = true; + }) - expect(args).to.have.property('_isFirst', true); - expect(args).to.have.property('_isLast', false); - expect(args).to.have.property('_total', 3); - }) + it('should pass a jsonified version of the model', function () { + subject.call(scope, currentCollection, jsonOpts); + expect(spy).to.have.been.calledThrice; - it('should pass a model as the value', function () { - subject.call(scope, currentCollection, opts()); - expect(spy).to.have.been.calledThrice; - - var thisCall = spy.getCall(0); - expect(thisCall.args[0].value).to.deep.equal(currentCollection.first()); + var thisCall = spy.getCall(0); + expect(thisCall.args[0].value).to.deep.equal(currentCollection.first().toJSON()); + }) }) }) - context('is toJSON', function () { - var jsonOpts; + context('collection is empty', function () { + collection.is(function () { + return new Collection(); + }); beforeEach(function () { currentCollection = collection(); - jsonOpts = opts(); - jsonOpts.hash.toJSON = true; }) - it('should pass a jsonified version of the model', function () { - subject.call(scope, currentCollection, jsonOpts); - expect(spy).to.have.been.calledThrice; + it('should call the inverse function', function () { + var inverseSpy = sinon.spy(opts(), 'inverse'); + subject.call(scope, currentCollection, opts()); - var thisCall = spy.getCall(0); - expect(thisCall.args[0].value).to.deep.equal(currentCollection.first().toJSON()); + expect(inverseSpy).to.have.been.called; }) }) }) From afe3c41766526fba9088a71b428ab6a56be6ce54 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Thu, 11 Feb 2016 14:31:59 -0800 Subject: [PATCH 105/107] fix nested partial view client render issue --- shared/helpers/view.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/shared/helpers/view.js b/shared/helpers/view.js index c5d67b3..e2de6ee 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -32,7 +32,7 @@ module.exports = function (Handlebars) { var parentView = getProperty('_view', this, options); html = getServerHtml(viewName, viewOptions, parentView); } else { - html = getClientPlaceholder(viewName, viewOptions); + html = getClientPlaceholder(viewName, viewOptions, Handlebars); } return new Handlebars.SafeString(html); @@ -55,7 +55,7 @@ function getServerHtml(viewName, viewOptions, parentView) { return view.getHtml(); } -function getClientPlaceholder(viewName, viewOptions) { +function getClientPlaceholder(viewName, viewOptions, Handlebars) { if (!BaseView) { BaseView = require('rendr/shared/base/view'); } var fetchSummary; @@ -67,7 +67,13 @@ function getClientPlaceholder(viewName, viewOptions) { // create a list of data attributes var attrString = _.inject(viewOptions, function(memo, value, key) { - if (_.isArray(value) || _.isObject(value)) { value = JSON.stringify(value); } + if (_.isArray(value) || _.isObject(value)) { + if (key === '_block' && value.contructor === Handlebars.SafeString) { + value = value.string; + } else { + value = JSON.stringify(value); + } + } return memo += " data-" + key + "=\"" + _.escape(value) + "\""; }, ''); From 7e92d651ba0b0b2fad984cf89bdc93c21829189c Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Mon, 22 Feb 2016 17:27:19 -0800 Subject: [PATCH 106/107] changed type compare to instanceof and added test --- shared/helpers/view.js | 2 +- test/shared/helpers/view.test.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/shared/helpers/view.js b/shared/helpers/view.js index e2de6ee..c53c93b 100644 --- a/shared/helpers/view.js +++ b/shared/helpers/view.js @@ -68,7 +68,7 @@ function getClientPlaceholder(viewName, viewOptions, Handlebars) { // create a list of data attributes var attrString = _.inject(viewOptions, function(memo, value, key) { if (_.isArray(value) || _.isObject(value)) { - if (key === '_block' && value.contructor === Handlebars.SafeString) { + if (key === '_block' && value instanceof Handlebars.SafeString) { value = value.string; } else { value = JSON.stringify(value); diff --git a/test/shared/helpers/view.test.js b/test/shared/helpers/view.test.js index 6fc0ded..79e5e6a 100644 --- a/test/shared/helpers/view.test.js +++ b/test/shared/helpers/view.test.js @@ -175,6 +175,22 @@ describe('view', function () { '
' ); }); + + context('when the key is _block and is of type Handlebars.SafeString', function () { + it('extracts the string correctly', function () { + var html = '
something
', + result = subject.call({ + _app: app() + }, 'test', { + hash: { + _block: new Handlebars.SafeString(html) + } + }); + expect(result.string).to.eq( + '
' + ); + }); + }); }); }); }); From df63328f94f9382bac6b3af760ea167ef501c401 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Mon, 29 Feb 2016 17:22:01 -0800 Subject: [PATCH 107/107] Update the package / HISTORY --- HISTORY.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 28ab57a..e2d535c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,10 @@ +# 2.0.2 +* bugfix for the forEach / else helper +* bugfix for parital -> view nesting + +# 2.0.1 +* Bug fix for the peerDependency of rendr to be at 1.1.0 instead of 1.2.0. + # 2.0.0 * handlebars is no longer included here, it needs to be installed at the app level. diff --git a/package.json b/package.json index 40bb69f..33a3ec3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rendr-handlebars", - "version": "2.0.1", + "version": "2.0.2", "description": "Glue handlebars templates into a Rendr app.", "main": "index.js", "scripts": {