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'),
|
_ = require('lodash'),
|
||||||
logging = proxy.logging,
|
logging = proxy.logging,
|
||||||
i18n = proxy.i18n,
|
i18n = proxy.i18n,
|
||||||
validAttrs = ['tag', 'author', 'slug', 'id', 'number', 'index'];
|
validAttrs = ['tag', 'author', 'slug', 'id', 'number', 'index', 'any', 'all'];
|
||||||
|
|
||||||
function evaluateTagList(expr, tags) {
|
function evaluateTagList(expr, tags) {
|
||||||
return expr.split(',').map(function (v) {
|
return expr.split(',').map(function (v) {
|
||||||
|
@ -49,6 +49,25 @@ function evaluateStringMatch(expr, str, ci) {
|
||||||
return expr === str;
|
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) {
|
module.exports = function has(options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.hash = options.hash || {};
|
options.hash = options.hash || {};
|
||||||
|
@ -56,13 +75,16 @@ module.exports = function has(options) {
|
||||||
|
|
||||||
var self = this,
|
var self = this,
|
||||||
attrs = _.pick(options.hash, validAttrs),
|
attrs = _.pick(options.hash, validAttrs),
|
||||||
|
data = _.pick(options.data, ['blog', 'config', 'labs']),
|
||||||
checks = {
|
checks = {
|
||||||
tag: function () { return attrs.tag && evaluateTagList(attrs.tag, _.map(self.tags, 'name')) || false; },
|
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; },
|
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; },
|
number: function () { return attrs.number && evaluateIntegerMatch(attrs.number, options.data.number) || false; },
|
||||||
index: function () { return attrs.index && evaluateIntegerMatch(attrs.index, options.data.index) || 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; },
|
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;
|
result;
|
||||||
|
|
||||||
|
|
|
@ -249,6 +249,24 @@ describe('{{#has}} helper', function () {
|
||||||
inverse.called.should.be.true();
|
inverse.called.should.be.true();
|
||||||
inverse.callCount.should.eql(6);
|
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 () {
|
describe('index match (0-based index)', function () {
|
||||||
|
@ -343,6 +361,15 @@ describe('{{#has}} helper', function () {
|
||||||
inverse.called.should.be.true();
|
inverse.called.should.be.true();
|
||||||
inverse.callCount.should.eql(5);
|
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 () {
|
describe('slug match', function () {
|
||||||
|
@ -408,4 +435,252 @@ describe('{{#has}} helper', function () {
|
||||||
inverse.called.should.be.true();
|
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