From 62cbd3f3eda1852c0c6814f1cb3d6202dd071258 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 29 Aug 2017 21:58:00 -0500 Subject: [PATCH 1/5] Hierarchical categories can be assigned The code still needs some cleanup, but the new test passes without breaking the old ones. --- lib/models/post.js | 90 +++++++++++++++++++++---------------- test/scripts/models/post.js | 27 ++++++++++- 2 files changed, 78 insertions(+), 39 deletions(-) diff --git a/lib/models/post.js b/lib/models/post.js index cf83a33e5c..7e608efac2 100644 --- a/lib/models/post.js +++ b/lib/models/post.js @@ -133,62 +133,76 @@ module.exports = function(ctx) { }); Post.method('setCategories', function(cats) { - cats = removeEmptyTag(cats); - + // Remove empty categories, preserving hierarchies + cats = cats.filter(cat => + Array.isArray(cat) ? true : cat != null && cat !== '' + ).map(cat => + Array.isArray(cat) ? removeEmptyTag(cat) : cat + '' + ); + var PostCategory = ctx.model('PostCategory'); var Category = ctx.model('Category'); var id = this._id; var arr = []; var existed = PostCategory.find({post_id: id}, {lean: true}).map(pickID); - - // Don't use "Promise.map". It doesn't run in series. - // MUST USE "Promise.each". - return Promise.each(cats, function(cat, i) { - // Find the category by name - var data = Category.findOne({ - name: cat, - parent: i ? arr[i - 1] : {$exists: false} - }, {lean: true}); - - if (data) { - arr.push(data._id); - return data; - } - - // Insert the category if not exist - var obj = {name: cat}; - if (i) obj.parent = arr[i - 1]; - - return Category.insert(obj).catch(function(err) { - // Try to find the category again. Throw the error if not found + var hasHierarchy = cats.filter(cat => Array.isArray(cat)).length > 0; + + // Add a hierarchy of categories + var addHierarchy = function(catHierarchy) { + var parentIds = []; + if (!Array.isArray(catHierarchy)) catHierarchy = [catHierarchy]; + // Don't use "Promise.map". It doesn't run in series. + // MUST USE "Promise.each". + return Promise.each(catHierarchy, function(cat, i) { + // Find the category by name var data = Category.findOne({ name: cat, - parent: i ? arr[i - 1] : {$exists: false} + parent: i ? parentIds[i - 1] : {$exists: false} }, {lean: true}); - - if (data) return data; - throw err; - }).then(function(data) { - arr.push(data._id); - return data; + + if (data) { + arr.push(data._id); + parentIds.push(data._id); + return data; + } + + // Insert the category if not exist + var obj = {name: cat}; + if (i) obj.parent = parentIds[i - 1]; + + return Category.insert(obj).catch(function(err) { + // Try to find the category again. Throw the error if not found + var data = Category.findOne({ + name: cat, + parent: i ? parentIds[i - 1] : {$exists: false} + }, {lean: true}); + + if (data) return data; + throw err; + }).then(function(data) { + arr.push(data._id); + parentIds.push(data._id); + return data; + }); }); - }).map(function() { - // Get the index from the second argument - // and get the category id from arr. - var cat = arr[arguments[1]]; + } + return (hasHierarchy + ? Promise.each(cats, addHierarchy) + : Promise.resolve(addHierarchy(cats)) + ).then(() => arr).map(function(catId) { // Find the reference - var ref = PostCategory.findOne({post_id: id, category_id: cat}, {lean: true}); + var ref = PostCategory.findOne({post_id: id, category_id: catId}, {lean: true}); if (ref) return ref; // Insert the reference if not exist return PostCategory.insert({ post_id: id, - category_id: cat + category_id: catId }); - }).then(function(cats) { + }).then(function(postCats) { // Remove old categories - var deleted = _.difference(existed, cats.map(pickID)); + var deleted = _.difference(existed, postCats.map(pickID)); return deleted; }).map(function(cat) { return PostCategory.removeById(cat); diff --git a/test/scripts/models/post.js b/test/scripts/models/post.js index 4599db273a..82d0da7015 100644 --- a/test/scripts/models/post.js +++ b/test/scripts/models/post.js @@ -127,7 +127,7 @@ describe('Post', () => { }).then(post => post.setCategories(['foo', 'bar', 'baz']) .thenReturn(Post.findById(post._id))).then(post => { var cats = post.categories; - + // Make sure the order of categories is correct cats.map((cat, i) => { // Make sure the parent reference is correct @@ -143,6 +143,31 @@ describe('Post', () => { return Post.removeById(post._id); })); + it('categories (multi hierarchy) - virtual', () => Post.insert({ + source: 'foo.md', + slug: 'bar' + }).then(post => post.setCategories([['foo', '', 'bar'], '', 'baz']) + .thenReturn(Post.findById(post._id))).then(post => { + var cats = post.categories.toArray(); + + // There should have been 3 categories set; blanks eliminated + cats.should.have.lengthOf(3); + + // Category 1 should be foo, no parent + cats[0].name.should.eql('foo'); + should.not.exist(cats[0].parent); + + // Category 2 should be bar, foo as parent + cats[1].name.should.eql('bar'); + cats[1].parent.should.eql(cats[0]._id); + + // Category 3 should be baz, no parent + cats[2].name.should.eql('baz'); + should.not.exist(cats[2].parent); + + return Post.removeById(post._id); + })); + it('setTags() - old tags should be removed', () => { var id; From b9a121fb9c6a55a496f509b8a9859ff9340c18c6 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Wed, 30 Aug 2017 04:53:02 -0500 Subject: [PATCH 2/5] more tests, cleanup added tests for category list and front-matter processor to include multiple categories; cleaned up Post.setCategories() code --- lib/models/post.js | 27 ++++--- lib/plugins/processor/post.js | 2 +- test/scripts/helpers/list_categories.js | 94 +++++++++++++++++++------ test/scripts/models/post.js | 50 ++++++------- test/scripts/processors/post.js | 25 +++++++ 5 files changed, 134 insertions(+), 64 deletions(-) diff --git a/lib/models/post.js b/lib/models/post.js index 7e608efac2..a34df832a3 100644 --- a/lib/models/post.js +++ b/lib/models/post.js @@ -143,17 +143,17 @@ module.exports = function(ctx) { var PostCategory = ctx.model('PostCategory'); var Category = ctx.model('Category'); var id = this._id; - var arr = []; + var allIds = []; var existed = PostCategory.find({post_id: id}, {lean: true}).map(pickID); var hasHierarchy = cats.filter(cat => Array.isArray(cat)).length > 0; // Add a hierarchy of categories - var addHierarchy = function(catHierarchy) { + var addHierarchy = catHierarchy => { var parentIds = []; if (!Array.isArray(catHierarchy)) catHierarchy = [catHierarchy]; // Don't use "Promise.map". It doesn't run in series. // MUST USE "Promise.each". - return Promise.each(catHierarchy, function(cat, i) { + return Promise.each(catHierarchy, (cat, i) => { // Find the category by name var data = Category.findOne({ name: cat, @@ -161,7 +161,7 @@ module.exports = function(ctx) { }, {lean: true}); if (data) { - arr.push(data._id); + allIds.push(data._id); parentIds.push(data._id); return data; } @@ -170,7 +170,7 @@ module.exports = function(ctx) { var obj = {name: cat}; if (i) obj.parent = parentIds[i - 1]; - return Category.insert(obj).catch(function(err) { + return Category.insert(obj).catch(err => { // Try to find the category again. Throw the error if not found var data = Category.findOne({ name: cat, @@ -179,18 +179,18 @@ module.exports = function(ctx) { if (data) return data; throw err; - }).then(function(data) { - arr.push(data._id); + }).then(data => { + allIds.push(data._id); parentIds.push(data._id); return data; }); }); - } + }; return (hasHierarchy ? Promise.each(cats, addHierarchy) : Promise.resolve(addHierarchy(cats)) - ).then(() => arr).map(function(catId) { + ).then(() => allIds).map(catId => { // Find the reference var ref = PostCategory.findOne({post_id: id, category_id: catId}, {lean: true}); if (ref) return ref; @@ -200,13 +200,10 @@ module.exports = function(ctx) { post_id: id, category_id: catId }); - }).then(function(postCats) { + }).then(postCats => // Remove old categories - var deleted = _.difference(existed, postCats.map(pickID)); - return deleted; - }).map(function(cat) { - return PostCategory.removeById(cat); - }); + _.difference(existed, postCats.map(pickID)) + ).map(cat => PostCategory.removeById(cat)); }); // Remove PostTag references diff --git a/lib/plugins/processor/post.js b/lib/plugins/processor/post.js index 399ed1a7de..4cc57789e5 100644 --- a/lib/plugins/processor/post.js +++ b/lib/plugins/processor/post.js @@ -53,7 +53,7 @@ module.exports = function(ctx) { var info = parseFilename(config.new_post_name, path); var keys = Object.keys(info); var key; - + data.source = file.path; data.raw = content; data.slug = info.title; diff --git a/test/scripts/helpers/list_categories.js b/test/scripts/helpers/list_categories.js index 976a0831f9..353b362a45 100644 --- a/test/scripts/helpers/list_categories.js +++ b/test/scripts/helpers/list_categories.js @@ -19,12 +19,14 @@ describe('list_categories', () => { {source: 'foo', slug: 'foo'}, {source: 'bar', slug: 'bar'}, {source: 'baz', slug: 'baz'}, - {source: 'boo', slug: 'boo'} + {source: 'boo', slug: 'boo'}, + {source: 'bat', slug: 'bat'} ])).then(posts => Promise.each([ ['baz'], ['baz', 'bar'], ['foo'], - ['baz'] + ['baz'], + ['bat', ['baz', 'bar']] ], (cats, i) => posts[i].setCategories(cats))).then(() => { hexo.locals.invalidate(); ctx.site = hexo.locals.toObject(); @@ -37,10 +39,13 @@ describe('list_categories', () => { result.should.eql([ '