0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Implements is-helper

closes #2249
- Added context to res.locals
- Added context aware is helper
- Added unit tests
This commit is contained in:
Fabian Becker 2014-09-04 16:07:12 +00:00
parent 438444df3d
commit aa5cf6ed3b
4 changed files with 179 additions and 9 deletions

View file

@ -88,6 +88,35 @@ function handleError(next) {
};
}
function setResponseContext(req, res, data) {
var contexts = [],
pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1;
// paged context
if (!isNaN(pageParam) && pageParam > 1) {
contexts.push('paged');
}
if (req.route.path === '/page/:page/') {
contexts.push('index');
} else if (req.route.path === '/') {
contexts.push('home');
contexts.push('index');
} else if (/\/rss\/(:page\/)?$/.test(req.route.path)) {
contexts.push('rss');
} else if (/^\/tag\//.test(req.route.path)) {
contexts.push('tag');
} else if (/^\/author\//.test(req.route.path)) {
contexts.push('author');
} else if (data && data.post && data.post.page) {
contexts.push('page');
} else {
contexts.push('post');
}
res.locals.context = contexts;
}
// Add Request context parameter to the data object
// to be passed down to the templates
function setReqCtx(req, data) {
@ -146,6 +175,7 @@ frontendControllers = {
view = 'index';
}
setResponseContext(req, res);
res.render(view, formatPageResponse(posts, page));
});
});
@ -200,6 +230,7 @@ frontendControllers = {
if (!result.tag) {
return next();
}
setResponseContext(req, res);
res.render(view, result);
});
});
@ -254,6 +285,8 @@ frontendControllers = {
if (!result.author) {
return next();
}
setResponseContext(req, res);
res.render(view, result);
});
});
@ -325,9 +358,12 @@ frontendControllers = {
filters.doFilter('prePostsRender', post).then(function (post) {
getActiveThemePaths().then(function (paths) {
var view = template.getThemeViewForPost(paths, post);
var view = template.getThemeViewForPost(paths, post),
response = formatResponse(post);
res.render(view, formatResponse(post));
setResponseContext(req, res, response);
res.render(view, response);
});
});
}
@ -470,6 +506,7 @@ frontendControllers = {
}
setReqCtx(req, page.posts);
setResponseContext(req, res);
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
posts.forEach(function (post) {

View file

@ -677,6 +677,34 @@ coreHelpers.foreach = function (context, options) {
return ret;
};
// ### Is Helper
// `{{#is "paged"}}`
// `{{#is "index, paged"}}`
// Checks whether we're in a given context.
coreHelpers.is = function (context, options) {
options = options || {};
var currentContext = options.data.root.context;
if (!_.isString(context)) {
errors.logWarn('Invalid or no attribute given to is helper');
return;
}
function evaluateContext(expr) {
return expr.split(',').map(function (v) {
return v.trim();
}).reduce(function (p, c) {
return p || _.contains(currentContext, c);
}, false);
}
if (evaluateContext(context)) {
return options.fn(this);
}
return options.inverse(this);
};
// ### Has Helper
// `{{#has tag="video, music"}}`
// `{{#has author="sam, pat"}}`
@ -855,6 +883,8 @@ registerHelpers = function (adminHbs, assetHash) {
registerThemeHelper('foreach', coreHelpers.foreach);
registerThemeHelper('is', coreHelpers.is);
registerThemeHelper('has', coreHelpers.has);
registerThemeHelper('page_url', coreHelpers.page_url);

View file

@ -202,6 +202,7 @@ describe('Frontend Controller', function () {
route: {}
},
res = {
locals: {},
render: function (view) {
assert.equal(view, 'home');
done();
@ -220,6 +221,7 @@ describe('Frontend Controller', function () {
route: {}
},
res = {
locals: {},
render: function (view) {
assert.equal(view, 'index');
done();
@ -251,6 +253,7 @@ describe('Frontend Controller', function () {
route: {}
},
res = {
locals: {},
render: function (view) {
assert.equal(view, 'index');
done();
@ -360,9 +363,13 @@ describe('Frontend Controller', function () {
it('it will render custom tag template if it exists', function (done) {
var req = {
path: '/tag/' + mockTags[0].slug,
params: {}
params: {},
route: {
path: '/tag/:slug'
}
},
res = {
locals: {},
render: function (view, context) {
assert.equal(view, 'tag');
assert.equal(context.tag, mockTags[0]);
@ -576,9 +583,14 @@ describe('Frontend Controller', function () {
it('it will render custom page template if it exists', function (done) {
var req = {
path: '/' + mockPosts[2].posts[0].slug
path: '/' + mockPosts[2].posts[0].slug,
route: {
path: '*'
},
params: {}
},
res = {
locals: {},
render: function (view, context) {
assert.equal(view, 'page-' + mockPosts[2].posts[0].slug);
assert.equal(context.post, mockPosts[2].posts[0]);
@ -601,9 +613,14 @@ describe('Frontend Controller', function () {
it('will render static page via /:slug', function (done) {
var req = {
path: '/' + mockPosts[0].posts[0].slug
path: '/' + mockPosts[0].posts[0].slug,
route: {
path: '*'
},
params: {}
},
res = {
locals: {},
render: function (view, context) {
assert.equal(view, 'page');
assert.equal(context.post, mockPosts[0].posts[0]);
@ -620,6 +637,7 @@ describe('Frontend Controller', function () {
path: '/' + ['2012/12/30', mockPosts[0].posts[0].slug].join('/')
},
res = {
locals: {},
render: sinon.spy()
};
@ -634,6 +652,7 @@ describe('Frontend Controller', function () {
path: '/' + [mockPosts[0].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: function (arg) {
res.render.called.should.be.false;
@ -650,6 +669,7 @@ describe('Frontend Controller', function () {
path: '/' + ['2012/12/30', mockPosts[0].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: sinon.spy()
};
@ -673,9 +693,14 @@ describe('Frontend Controller', function () {
it('will render static page via /:slug', function (done) {
var req = {
path: '/' + mockPosts[0].posts[0].slug
path: '/' + mockPosts[0].posts[0].slug,
route: {
path: '*'
},
params: {}
},
res = {
locals: {},
render: function (view, context) {
assert.equal(view, 'page');
assert.equal(context.post, mockPosts[0].posts[0]);
@ -705,6 +730,7 @@ describe('Frontend Controller', function () {
path: '/' + [mockPosts[0].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: function (arg) {
res.render.called.should.be.false;
@ -721,6 +747,7 @@ describe('Frontend Controller', function () {
path: '/' + ['2012/12/30', mockPosts[0].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: sinon.spy()
};
@ -746,9 +773,14 @@ describe('Frontend Controller', function () {
it('will render post via /:slug', function (done) {
var req = {
path: '/' + mockPosts[1].posts[0].slug
path: '/' + mockPosts[1].posts[0].slug,
route: {
path: '*'
},
params: {}
},
res = {
locals: {},
render: function (view, context) {
assert.equal(view, 'post');
assert(context.post, 'Context object has post attribute');
@ -766,6 +798,7 @@ describe('Frontend Controller', function () {
path: '/' + ['2012/12/30', mockPosts[1].posts[0].slug].join('/')
},
res = {
locals: {},
render: sinon.spy()
};
@ -781,6 +814,7 @@ describe('Frontend Controller', function () {
path: '/' + [mockPosts[1].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: function (arg) {
res.render.called.should.be.false;
@ -797,6 +831,7 @@ describe('Frontend Controller', function () {
path: '/' + ['2012/12/30', mockPosts[1].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: sinon.spy()
};
@ -821,9 +856,14 @@ describe('Frontend Controller', function () {
it('will render post via /YYYY/MM/DD/:slug', function (done) {
var date = moment(mockPosts[1].posts[0].published_at).format('YYYY/MM/DD'),
req = {
path: '/' + [date, mockPosts[1].posts[0].slug].join('/')
path: '/' + [date, mockPosts[1].posts[0].slug].join('/'),
route: {
path: '*'
},
params: {}
},
res = {
locals: {},
render: function (view, context) {
assert.equal(view, 'post');
assert(context.post, 'Context object has post attribute');
@ -842,6 +882,7 @@ describe('Frontend Controller', function () {
path: '/' + [date, mockPosts[1].posts[0].slug].join('/')
},
res = {
locals: {},
render: sinon.spy()
};
@ -856,6 +897,7 @@ describe('Frontend Controller', function () {
path: '/' + mockPosts[1].posts[0].slug
},
res = {
locals: {},
render: sinon.spy()
};
@ -872,6 +914,7 @@ describe('Frontend Controller', function () {
path: '/' + [dateFormat, mockPosts[1].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: function (arg) {
res.render.called.should.be.false;
@ -888,6 +931,7 @@ describe('Frontend Controller', function () {
path: '/' + [mockPosts[1].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: sinon.spy()
};
@ -912,9 +956,14 @@ describe('Frontend Controller', function () {
it('will render post via /:year/:slug', function (done) {
var date = moment(mockPosts[1].posts[0].published_at).format('YYYY'),
req = {
path: '/' + [date, mockPosts[1].posts[0].slug].join('/')
path: '/' + [date, mockPosts[1].posts[0].slug].join('/'),
route: {
path: '*'
},
params: {}
},
res = {
locals: {},
render: function (view, context) {
assert.equal(view, 'post');
assert(context.post, 'Context object has post attribute');
@ -933,6 +982,7 @@ describe('Frontend Controller', function () {
path: '/' + [date, mockPosts[1].posts[0].slug].join('/')
},
res = {
locals: {},
render: sinon.spy()
};
@ -948,6 +998,7 @@ describe('Frontend Controller', function () {
path: '/' + [date, mockPosts[1].posts[0].slug].join('/')
},
res = {
locals: {},
render: sinon.spy()
};
@ -962,6 +1013,7 @@ describe('Frontend Controller', function () {
path: '/' + mockPosts[1].posts[0].slug
},
res = {
locals: {},
render: sinon.spy()
};
@ -978,6 +1030,7 @@ describe('Frontend Controller', function () {
path: '/' + [date, mockPosts[1].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: function (arg) {
res.render.called.should.be.false;
@ -994,6 +1047,7 @@ describe('Frontend Controller', function () {
path: '/' + [mockPosts[1].posts[0].slug, 'edit'].join('/')
},
res = {
locals: {},
render: sinon.spy(),
redirect: sinon.spy()
};

View file

@ -669,6 +669,55 @@ describe('Core Helpers', function () {
});
});
describe('is Block Helper', function () {
it('has loaded is block helper', function () {
should.exist(handlebars.helpers.is);
});
// All positive tests
it('should match single context "index"', function () {
var fn = sinon.spy(),
inverse = sinon.spy();
helpers.is.call(
{},
'index',
{fn: fn, inverse: inverse, data: {root: {context: ['home', 'index']}}}
);
fn.called.should.be.true;
inverse.called.should.be.false;
});
it('should match OR context "index, paged"', function () {
var fn = sinon.spy(),
inverse = sinon.spy();
helpers.is.call(
{},
'index, paged',
{fn: fn, inverse: inverse, data: {root: {context: ['tag', 'paged']}}}
);
fn.called.should.be.true;
inverse.called.should.be.false;
});
it('should not match "paged"', function () {
var fn = sinon.spy(),
inverse = sinon.spy();
helpers.is.call(
{},
'paged',
{fn: fn, inverse: inverse, data: {root: {context: ['index', 'home']}}}
);
fn.called.should.be.false;
inverse.called.should.be.true;
});
});
describe('has Block Helper', function () {
it('has loaded has block helper', function () {
should.exist(handlebars.helpers.has);