Skip to content
This repository was archived by the owner on Dec 10, 2019. It is now read-only.

Commit 5c4d065

Browse files
committed
Underscore engine with basic support and unit tests!
1 parent 0150b79 commit 5c4d065

File tree

9 files changed

+216
-3
lines changed

9 files changed

+216
-3
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* underscore pattern engine for patternlab-node - v0.15.1 - 2015
3+
*
4+
* Geoffrey Pursell, Brian Muenzenmeyer, and the web community.
5+
* Licensed under the MIT license.
6+
*
7+
* Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice.
8+
*
9+
*/
10+
11+
12+
/*
13+
* ENGINE SUPPORT LEVEL:
14+
*
15+
* Basic. We can't call partials from inside underscore templates yet, but we
16+
* can render templates with backing JSON.
17+
*
18+
*/
19+
20+
(function () {
21+
"use strict";
22+
23+
var _ = require('underscore');
24+
25+
// extend underscore with partial-ing methods
26+
// HANDLESCORE! UNDERBARS!
27+
_.mixin({
28+
renderPartial: function(partial, data) {
29+
var data = data || {};
30+
var compiled = _.template(partial);
31+
return compiled(data);
32+
},
33+
assignContext: function(viewModel, data) {
34+
return viewModel(data);
35+
}
36+
});
37+
38+
var engine_underscore = {
39+
engine: _,
40+
engineName: 'underscore',
41+
engineFileExtension: '.underscore',
42+
43+
// partial expansion is only necessary for Mustache templates that have
44+
// style modifiers or pattern parameters (I think)
45+
expandPartials: false,
46+
47+
// regexes, stored here so they're only compiled once
48+
findPartialsRE: /<%= _.renderPartial\((.*?)\).*?%>/g, // TODO,
49+
findPartialsWithStyleModifiersRE: /<%= _.renderPartial\((.*?)\).*?%>/g, // TODO
50+
findPartialsWithPatternParametersRE: /<%= _.renderPartial\((.*?)\).*?%>/g, // TODO
51+
findListItemsRE: /({{#( )?)(list(I|i)tems.)(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)( )?}}/g,
52+
53+
// render it
54+
renderPattern: function renderPattern(template, data, partials) {
55+
var compiled = _.template(template);
56+
return compiled(_.extend(data, {
57+
_allData: data,
58+
_partials: partials
59+
}));
60+
},
61+
62+
// registerPartial: function (oPattern) {
63+
// debugger;
64+
// _.registerPartial(oPattern.key, oPattern.template);
65+
// },
66+
67+
// find and return any {{> template-name }} within pattern
68+
findPartials: function findPartials(pattern) {
69+
var matches = pattern.template.match(this.findPartialsRE);
70+
return matches;
71+
},
72+
findPartialsWithStyleModifiers: function(pattern) {
73+
return [];
74+
},
75+
// returns any patterns that match {{> value(foo:"bar") }} or {{>
76+
// value:mod(foo:"bar") }} within the pattern
77+
findPartialsWithPatternParameters: function(pattern) {
78+
return [];
79+
},
80+
findListItems: function(pattern) {
81+
var matches = pattern.template.match(this.findListItemsRE);
82+
return matches;
83+
},
84+
// given a pattern, and a partial string, tease out the "pattern key" and
85+
// return it.
86+
findPartialKey: function(partialString) {
87+
var partialKey = partialString.replace(this.findPartialsRE, '$1');
88+
return partialKey;
89+
}
90+
};
91+
92+
module.exports = engine_underscore;
93+
})();

test/engine_underscore_tests.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
(function () {
2+
"use strict";
3+
4+
var path = require('path');
5+
var pa = require('../builder/pattern_assembler');
6+
var testPatternsPath = path.resolve(__dirname, 'files', '_underscore-test-patterns');
7+
8+
try {
9+
require('underscore');
10+
} catch (err) {
11+
console.log('underscore renderer not installed; skipping tests');
12+
return;
13+
}
14+
15+
// fake pattern lab constructor:
16+
// sets up a fake patternlab object, which is needed by the pattern processing
17+
// apparatus.
18+
function fakePatternLab() {
19+
var fpl = {
20+
partials: {},
21+
patterns: [],
22+
footer: '',
23+
header: '',
24+
listitems: {},
25+
listItemArray: [],
26+
data: {
27+
link: {}
28+
},
29+
config: require('../config.json'),
30+
package: {}
31+
};
32+
33+
// patch the pattern source so the pattern assembler can correctly determine
34+
// the "subdir"
35+
fpl.config.paths.source.patterns = testPatternsPath;
36+
37+
return fpl;
38+
}
39+
40+
exports['engine_underscore'] = {
41+
'hello world underscore pattern renders': function (test) {
42+
test.expect(1);
43+
44+
var patternPath = path.resolve(
45+
testPatternsPath,
46+
'00-atoms',
47+
'00-global',
48+
'00-helloworld.underscore'
49+
);
50+
51+
// do all the normal processing of the pattern
52+
var patternlab = new fakePatternLab();
53+
var assembler = new pa();
54+
var helloWorldPattern = assembler.process_pattern_iterative(patternPath, patternlab);
55+
assembler.process_pattern_recursive(patternPath, patternlab);
56+
57+
test.equals(helloWorldPattern.render(), 'Hello world!\n');
58+
test.done();
59+
},
60+
'underscore partials can render JSON values': function (test) {
61+
test.expect(1);
62+
63+
// pattern paths
64+
var pattern1Path = path.resolve(
65+
testPatternsPath,
66+
'00-atoms',
67+
'00-global',
68+
'00-helloworld-withdata.underscore'
69+
);
70+
71+
// set up environment
72+
var patternlab = new fakePatternLab(); // environment
73+
var assembler = new pa();
74+
75+
// do all the normal processing of the pattern
76+
var helloWorldWithData = assembler.process_pattern_iterative(pattern1Path, patternlab);
77+
assembler.process_pattern_recursive(pattern1Path, patternlab);
78+
79+
// test
80+
test.equals(helloWorldWithData.render(), 'Hello world!\nYeah, we got the subtitle from the JSON.\n');
81+
test.done();
82+
}
83+
};
84+
})();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"subtitle": "Yeah, we got the subtitle from the JSON."
3+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Hello world!
2+
<%= subtitle %>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello world!
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"subtitle": "from the default JSON.",
3+
"otherBlob": {
4+
"subtitle": "from a totally different blob."
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<h2>Call with default JSON environment:</h2>
2+
This is {{> atoms-helloworld-withdata }}
3+
4+
<h2>Call with passed parameter:</h2>
5+
However, this is {{> atoms-helloworld-withdata otherBlob }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= _.template(_partials['atoms-helloworld'])(_data) %> and <%= _.template(_partials['atoms-helloworld'])(_data) %>

test/pattern_engines_tests.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,27 @@
9898
function testProps(object, propTests, test) {
9999

100100
// function to test each expected property is present and the correct type
101-
function testProp(propName, typeString) {
101+
function testProp(propName, types) {
102+
103+
var possibleTypes;
104+
105+
// handle "types" being a string or an array of strings
106+
if (types instanceof Array) {
107+
possibleTypes = types;
108+
} else {
109+
// "types" is just a single string, load it into an array; the rest of
110+
// the code expects it!
111+
possibleTypes = [types];
112+
}
113+
114+
var isOneOfTheseTypes = possibleTypes.map(function (type) {
115+
return typeof object[propName] === type;
116+
}).reduce(function(isPrevType, isCurrentType) {
117+
return isPrevType || isCurrentType;
118+
});
119+
102120
test.ok(object.hasOwnProperty(propName), '"' + propName + '" prop should be present');
103-
test.ok(typeof object[propName] === typeString, '"' + propName + '" prop should be of type ' + typeString);
121+
test.ok(isOneOfTheseTypes, '"' + propName + '" prop should be one of types ' + possibleTypes);
104122
}
105123

106124
// go over each property test and run it
@@ -129,7 +147,7 @@
129147
'engine contains expected properties and methods': function (test) {
130148

131149
var propertyTests = {
132-
'engine': 'object',
150+
'engine': ['object', 'function'],
133151
'engineName': 'string',
134152
'engineFileExtension': 'string',
135153
'renderPattern': 'function',

0 commit comments

Comments
 (0)