mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
✨ Added any & all matching to {{#has}} helper
closes #8901 - Adds support for ``` {{#has any="twitter, facebook, website"}} {{#has any="author.facebook, author.twitter,author.website"}} {{#has any="@blog.facebook, @blog.twitter, @labs.subscribers"}} {{#has all="@labs.subscribers,@labs.publicAPI"}} ```
This commit is contained in:
parent
746ac2db4d
commit
ff15dc1667
2 changed files with 299 additions and 2 deletions
|
@ -7,7 +7,7 @@ var proxy = require('./proxy'),
|
|||
_ = require('lodash'),
|
||||
logging = proxy.logging,
|
||||
i18n = proxy.i18n,
|
||||
validAttrs = ['tag', 'author', 'slug', 'id', 'number', 'index'];
|
||||
validAttrs = ['tag', 'author', 'slug', 'id', 'number', 'index', 'any', 'all'];
|
||||
|
||||
function evaluateTagList(expr, tags) {
|
||||
return expr.split(',').map(function (v) {
|
||||
|
@ -49,6 +49,25 @@ function evaluateStringMatch(expr, str, ci) {
|
|||
return expr === str;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} type - either some or every - the lodash function to use
|
||||
* @param {String} expr - the attribute value passed into {{#has}}
|
||||
* @param {Object} obj - "this" context from the helper
|
||||
* @param {Object} data - global params
|
||||
*/
|
||||
function evaluateList(type, expr, obj, data) {
|
||||
return expr.split(',').map(function (prop) {
|
||||
return prop.trim().toLocaleLowerCase();
|
||||
})[type](function (prop) {
|
||||
if (prop.match(/^@/)) {
|
||||
return _.has(data, prop.replace(/@/, '')) && !_.isEmpty(_.get(data, prop.replace(/@/, '')));
|
||||
} else {
|
||||
return _.has(obj, prop) && !_.isEmpty(_.get(obj, prop));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function has(options) {
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
|
@ -56,13 +75,16 @@ module.exports = function has(options) {
|
|||
|
||||
var self = this,
|
||||
attrs = _.pick(options.hash, validAttrs),
|
||||
data = _.pick(options.data, ['blog', 'config', 'labs']),
|
||||
checks = {
|
||||
tag: function () { return attrs.tag && evaluateTagList(attrs.tag, _.map(self.tags, 'name')) || false; },
|
||||
author: function () { return attrs.author && evaluateAuthorList(attrs.author, _.get(self, 'author.name')) || false; },
|
||||
number: function () { return attrs.number && evaluateIntegerMatch(attrs.number, options.data.number) || false; },
|
||||
index: function () { return attrs.index && evaluateIntegerMatch(attrs.index, options.data.index) || false; },
|
||||
slug: function () { return attrs.slug && evaluateStringMatch(attrs.slug, self.slug, true) || false; },
|
||||
id: function () { return attrs.id && evaluateStringMatch(attrs.id, self.id, true) || false; }
|
||||
id: function () { return attrs.id && evaluateStringMatch(attrs.id, self.id, true) || false; },
|
||||
any: function () { return attrs.any && evaluateList('some', attrs.any, self, data) || false; },
|
||||
all: function () { return attrs.all && evaluateList('every', attrs.all, self, data) || false; }
|
||||
},
|
||||
result;
|
||||
|
||||
|
|
|
@ -249,6 +249,24 @@ describe('{{#has}} helper', function () {
|
|||
inverse.called.should.be.true();
|
||||
inverse.callCount.should.eql(6);
|
||||
});
|
||||
|
||||
it('fails gracefully if there is no number property', function () {
|
||||
handlebarsOptions.data = {};
|
||||
|
||||
callHasHelper(thisCtx, {number: 'nth:3'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
|
||||
it('fails gracefully if there is no data property', function () {
|
||||
handlebarsOptions.data = null;
|
||||
|
||||
callHasHelper(thisCtx, {number: 'nth:3'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('index match (0-based index)', function () {
|
||||
|
@ -343,6 +361,15 @@ describe('{{#has}} helper', function () {
|
|||
inverse.called.should.be.true();
|
||||
inverse.callCount.should.eql(5);
|
||||
});
|
||||
|
||||
it('fails gracefully if there is no index property', function () {
|
||||
handlebarsOptions.data = {};
|
||||
|
||||
callHasHelper(thisCtx, {index: 'nth:3'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('slug match', function () {
|
||||
|
@ -408,4 +435,252 @@ describe('{{#has}} helper', function () {
|
|||
inverse.called.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('any match', function () {
|
||||
it('matches on a single property (pass)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has any="twitter"}}
|
||||
callHasHelper(thisCtx, {any: 'twitter'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on a single property (fail)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has any="facebook"}}
|
||||
callHasHelper(thisCtx, {any: 'facebook'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
|
||||
it('matches on multiple properties (pass)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has any="twitter, facebook,website"}}
|
||||
callHasHelper(thisCtx, {any: 'twitter, facebook,website'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on multiple properties (fail)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has any="facebook,website, foo"}}
|
||||
callHasHelper(thisCtx, {any: 'facebook,website, foo'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
|
||||
it('matches on global properties (pass)', function () {
|
||||
thisCtx = {};
|
||||
handlebarsOptions.data = {
|
||||
blog: {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has any="@blog.twitter, @blog.facebook,@blog.website"}}
|
||||
callHasHelper(thisCtx, {any: '@blog.twitter, @blog.facebook,@blog.website'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on global properties (fail)', function () {
|
||||
thisCtx = {};
|
||||
handlebarsOptions.data = {
|
||||
blog: {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has any="@blog.facebook,@blog.website, @blog.foo"}}
|
||||
callHasHelper(thisCtx, {any: '@blog.facebook,@blog.website, @not.foo'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
|
||||
it('matches on path expressions (pass)', function () {
|
||||
thisCtx = {
|
||||
author: {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has any="author.twitter, author.facebook,author.website"}}
|
||||
callHasHelper(thisCtx, {any: 'author.twitter, author.facebook,author.website'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on path expressions (fail)', function () {
|
||||
thisCtx = {
|
||||
author: {
|
||||
twitter: 'foo',
|
||||
facebook: '',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has any="author.facebook,author.website, author.foo"}}
|
||||
callHasHelper(thisCtx, {any: 'author.facebook,author.website, fred.foo'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('all match', function () {
|
||||
it('matches on a single property (pass)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has all="twitter"}}
|
||||
callHasHelper(thisCtx, {all: 'twitter'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on a single property (fail)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has all="website"}}
|
||||
callHasHelper(thisCtx, {all: 'website'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
|
||||
it('matches on multiple properties (pass)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has all="twitter, facebook"}}
|
||||
callHasHelper(thisCtx, {all: 'twitter, facebook'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on multiple properties (fail)', function () {
|
||||
thisCtx = {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
};
|
||||
|
||||
// {{#has all="facebook,website, foo"}}
|
||||
callHasHelper(thisCtx, {all: 'facebook,website, foo'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
|
||||
it('matches on global properties (pass)', function () {
|
||||
thisCtx = {};
|
||||
handlebarsOptions.data = {
|
||||
blog: {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has all="@blog.twitter, @blog.facebook"}}
|
||||
callHasHelper(thisCtx, {all: '@blog.twitter, @blog.facebook'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on global properties (fail)', function () {
|
||||
thisCtx = {};
|
||||
handlebarsOptions.data = {
|
||||
blog: {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has all="@blog.facebook,@blog.website, @blog.foo"}}
|
||||
callHasHelper(thisCtx, {all: '@blog.facebook,@blog.website, @not.foo'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
|
||||
it('matches on path expressions (pass)', function () {
|
||||
thisCtx = {
|
||||
author: {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has all="author.twitter, author.facebook"}}
|
||||
callHasHelper(thisCtx, {all: 'author.twitter, author.facebook'});
|
||||
|
||||
fn.called.should.be.true();
|
||||
inverse.called.should.be.false();
|
||||
});
|
||||
|
||||
it('matches on path expressions (fail)', function () {
|
||||
thisCtx = {
|
||||
author: {
|
||||
twitter: 'foo',
|
||||
facebook: 'bar',
|
||||
website: null
|
||||
}
|
||||
};
|
||||
|
||||
// {{#has all="author.facebook,author.website, author.foo"}}
|
||||
callHasHelper(thisCtx, {all: 'author.facebook,author.website, fred.foo'});
|
||||
|
||||
fn.called.should.be.false();
|
||||
inverse.called.should.be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue