0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Merge pull request #6220 from ErisDS/issue-6205-limit

Add `limit` attribute to tags & foreach helpers
This commit is contained in:
Sebastian Gierlinger 2015-12-15 11:37:44 +01:00
commit 432f8610c8
4 changed files with 102 additions and 91 deletions

View file

@ -3,6 +3,7 @@
//
// Block helper designed for looping through posts
var hbs = require('express-hbs'),
_ = require('lodash'),
errors = require('../errors'),
hbsUtils = hbs.handlebars.Utils,
@ -15,9 +16,10 @@ foreach = function (context, options) {
var fn = options.fn,
inverse = options.inverse,
i = 0,
columns = options.hash.columns,
ret = '',
length = _.size(context),
limit = options.hash.limit || length,
output = '',
data,
contextPath;
@ -50,53 +52,32 @@ foreach = function (context, options) {
}
}
ret = ret + fn(context[field], {
output = output + fn(context[field], {
data: data,
blockParams: hbsUtils.blockParams([context[field], field], [contextPath + field, null])
});
}
function iterateArray(context) {
var j;
for (j = context.length; i < j; i += 1) {
execIteration(i, i, i === context.length - 1);
}
}
function iterateCollection(context) {
var count = 1;
function iterateObject(context) {
var priorKey,
key;
for (key in context) {
if (context.hasOwnProperty(key)) {
// We're running the iterations one step out of sync so we can detect
// the last iteration without have to scan the object twice and create
// an itermediate keys array.
if (priorKey) {
execIteration(priorKey, i - 1);
}
priorKey = key;
i += 1;
_.each(context, function (item, key) {
if (count <= limit) {
execIteration(key, count - 1, count === length);
}
}
if (priorKey) {
execIteration(priorKey, i - 1, true);
}
count += 1;
});
}
if (context && typeof context === 'object') {
if (hbsUtils.isArray(context)) {
iterateArray(context);
} else {
iterateObject(context);
}
iterateCollection(context);
}
if (i === 0) {
ret = inverse(this);
if (length === 0) {
output = inverse(this);
}
return ret;
return output;
};
module.exports = foreach;

View file

@ -16,28 +16,33 @@ tags = function (options) {
options = options || {};
options.hash = options.hash || {};
var autolink = options.hash && _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
separator = options.hash && _.isString(options.hash.separator) ? options.hash.separator : ', ',
prefix = options.hash && _.isString(options.hash.prefix) ? options.hash.prefix : '',
suffix = options.hash && _.isString(options.hash.suffix) ? options.hash.suffix : '',
var autolink = !(_.isString(options.hash.autolink) && options.hash.autolink === 'false'),
separator = _.isString(options.hash.separator) ? options.hash.separator : ', ',
prefix = _.isString(options.hash.prefix) ? options.hash.prefix : '',
suffix = _.isString(options.hash.suffix) ? options.hash.suffix : '',
limit = options.hash.limit ? parseInt(options.hash.limit, 10) : undefined,
output = '';
function createTagList(tags) {
var tagNames = _.pluck(tags, 'name');
if (autolink) {
return _.map(tags, function (tag) {
return utils.linkTemplate({
url: config.urlFor('tag', {tag: tag}),
text: _.escape(tag.name)
});
}).join(separator);
});
}
return _.escape(tagNames.join(separator));
return _(tags).pluck('name').each(_.escape);
}
if (this.tags && this.tags.length) {
output = prefix + createTagList(this.tags) + suffix;
output = createTagList(this.tags);
if (limit) {
output = output.slice(0, limit);
}
output = prefix + output.join(separator) + suffix;
}
return new hbs.handlebars.SafeString(output);

View file

@ -261,6 +261,15 @@ describe('{{#foreach}} helper', function () {
});
describe('(compile)', function () {
var objectHash = {posts: {
first: {title: 'first'}, second: {title: 'second'}, third: {title: 'third'}, fourth: {title: 'fourth'}, fifth: {title: 'fifth'}
}},
arrayHash = {posts: [
{title: 'first'}, {title: 'second'}, {title: 'third'}, {title: 'fourth'}, {title: 'fifth'}
]},
arrayHash2 = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
objectHash2 = {goodbyes: {foo: {text: 'goodbye'}, bar: {text: 'Goodbye'}, baz: {text: 'GOODBYE'}}, world: 'world'};
function shouldCompileToExpected(templateString, hash, expected) {
var template = handlebars.compile(templateString),
result = template(hash);
@ -269,92 +278,97 @@ describe('{{#foreach}} helper', function () {
}
/** Many of these are copied direct from the handlebars spec */
it('foreach with object and @key', function () {
it('object and @key', function () {
var templateString = '<ul>{{#foreach posts}}<li>{{@key}} {{title}}</li>{{/foreach}}</ul>',
hash = {posts: {first: {title: 'first'}, second: {title: 'second'}}},
expected = '<ul><li>first first</li><li>second second</li></ul>';
expected = '<ul><li>first first</li><li>second second</li><li>third third</li><li>fourth fourth</li><li>fifth fifth</li></ul>';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, objectHash, expected);
});
it('foreach with @index', function () {
it('@index', function () {
var templateString = '<ul>{{#foreach posts}}<li>{{@index}} {{title}}</li>{{/foreach}}</ul>',
hash = {posts: [{title: 'first'}, {title: 'second'}]},
expected = '<ul><li>0 first</li><li>1 second</li></ul>';
expected = '<ul><li>0 first</li><li>1 second</li><li>2 third</li><li>3 fourth</li><li>4 fifth</li></ul>';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash, expected);
shouldCompileToExpected(templateString, objectHash, expected);
});
it('foreach with @number', function () {
it('@number', function () {
var templateString = '<ul>{{#foreach posts}}<li>{{@number}} {{title}}</li>{{/foreach}}</ul>',
hash = {posts: [{title: 'first'}, {title: 'second'}]},
expected = '<ul><li>1 first</li><li>2 second</li></ul>';
expected = '<ul><li>1 first</li><li>2 second</li><li>3 third</li><li>4 fourth</li><li>5 fifth</li></ul>';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash, expected);
shouldCompileToExpected(templateString, objectHash, expected);
});
it('foreach with nested @index', function () {
it('nested @index', function () {
var templateString = '{{#foreach goodbyes}}{{@index}}. {{text}}! {{#foreach ../goodbyes}}{{@index}} {{/foreach}}After {{@index}} {{/foreach}}{{@index}}cruel {{world}}!',
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
expected = '0. goodbye! 0 1 2 After 0 1. Goodbye! 0 1 2 After 1 2. GOODBYE! 0 1 2 After 2 cruel world!';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash2, expected);
shouldCompileToExpected(templateString, objectHash2, expected);
});
it('foreach with block params', function () {
it('array block params', function () {
var templateString = '{{#foreach goodbyes as |value index|}}{{index}}. {{value.text}}! {{#foreach ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/foreach}} After {{index}} {{/foreach}}{{index}}cruel {{world}}!',
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}], world: 'world'},
expected = '0. goodbye! 0 0 0 1 After 0 1. Goodbye! 1 0 1 1 After 1 cruel world!';
expected = '0. goodbye! 0 0 0 1 0 2 After 0 1. Goodbye! 1 0 1 1 1 2 After 1 2. GOODBYE! 2 0 2 1 2 2 After 2 cruel world!';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash2, expected);
});
it('foreach with @first', function () {
it('object block params', function () {
var templateString = '{{#foreach goodbyes as |value index|}}{{index}}. {{value.text}}! {{#foreach ../goodbyes as |childValue childIndex|}} {{index}} {{childIndex}}{{/foreach}} After {{index}} {{/foreach}}{{index}}cruel {{world}}!',
expected = 'foo. goodbye! foo foo foo bar foo baz After foo bar. Goodbye! bar foo bar bar bar baz After bar baz. GOODBYE! baz foo baz bar baz baz After baz cruel world!';
shouldCompileToExpected(templateString, objectHash2, expected);
});
it('@first', function () {
var templateString = '{{#foreach goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
expected = 'goodbye! cruel world!';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash2, expected);
shouldCompileToExpected(templateString, objectHash2, expected);
});
it('foreach with nested @first', function () {
it('nested @first', function () {
var templateString = '{{#foreach goodbyes}}({{#if @first}}{{text}}! {{/if}}{{#foreach ../goodbyes}}{{#if @first}}{{text}}!{{/if}}{{/foreach}}{{#if @first}} {{text}}!{{/if}}) {{/foreach}}cruel {{world}}!',
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
expected = '(goodbye! goodbye! goodbye!) (goodbye!) (goodbye!) cruel world!';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash2, expected);
shouldCompileToExpected(templateString, objectHash2, expected);
});
it('foreach object with @first', function () {
var templateString = '{{#foreach goodbyes}}{{#if @first}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
hash = {goodbyes: {foo: {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'},
expected = 'goodbye! cruel world!';
shouldCompileToExpected(templateString, hash, expected);
});
it('foreach with @last', function () {
it('@last', function () {
var templateString = '{{#foreach goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
expected = 'GOODBYE! cruel world!';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash2, expected);
shouldCompileToExpected(templateString, objectHash2, expected);
});
it('foreach object with @last', function () {
var templateString = '{{#foreach goodbyes}}{{#if @last}}{{text}}! {{/if}}{{/foreach}}cruel {{world}}!',
hash = {goodbyes: {foo: {text: 'goodbye'}, bar: {text: 'Goodbye'}}, world: 'world'},
expected = 'Goodbye! cruel world!';
shouldCompileToExpected(templateString, hash, expected);
});
it('foreach with nested @last', function () {
it('nested @last', function () {
var templateString = '{{#foreach goodbyes}}({{#if @last}}{{text}}! {{/if}}{{#foreach ../goodbyes}}{{#if @last}}{{text}}!{{/if}}{{/foreach}}{{#if @last}} {{text}}!{{/if}}) {{/foreach}}cruel {{world}}!',
hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'},
expected = '(GOODBYE!) (GOODBYE!) (GOODBYE! GOODBYE! GOODBYE!) cruel world!';
shouldCompileToExpected(templateString, hash, expected);
shouldCompileToExpected(templateString, arrayHash2, expected);
shouldCompileToExpected(templateString, objectHash2, expected);
});
it('foreach with limit 1', function () {
var templateString = '<ul>{{#foreach posts limit="1"}}<li>{{title}}</li>{{else}}not this{{/foreach}}</ul>',
expected = '<ul><li>first</li></ul>';
shouldCompileToExpected(templateString, arrayHash, expected);
shouldCompileToExpected(templateString, objectHash, expected);
});
it('foreach with limit 3', function () {
var templateString = '<ul>{{#foreach posts limit="3"}}<li>{{title}}</li>{{else}}not this{{/foreach}}</ul>',
expected = '<ul><li>first</li><li>second</li><li>third</li></ul>';
shouldCompileToExpected(templateString, arrayHash, expected);
shouldCompileToExpected(templateString, objectHash, expected);
});
});
});

View file

@ -109,4 +109,15 @@ describe('{{tags}} helper', function () {
String(rendered).should.equal('<a href="/tag/foo-bar/">foo</a>, <a href="/tag/bar/">bar</a>');
});
it('can limit no. tags output to 1', function () {
var tags = [{name: 'foo', slug: 'foo-bar'}, {name: 'bar', slug: 'bar'}],
rendered = helpers.tags.call(
{tags: tags},
{hash: {limit: '1'}}
);
should.exist(rendered);
String(rendered).should.equal('<a href="/tag/foo-bar/">foo</a>');
});
});