Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ build/Release
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules

# WebStorm project files
.idea
7 changes: 7 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"undef" : true,
"unused" : true,
"predef" : ["__dirname", "require", "module", "console", "process", "describe", "before", "it", "after", "afterEach"],
"exported" : ["should"],
"globalstrict": true
}
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
slugin
======
# moorea-mongoose-slugin

[![Dependency Status](https://david-dm.org/pdesterlich/moorea-mongoose-slugin.svg?theme=shields.io)](https://david-dm.org/pdesterlich/moorea-mongoose-slugin) [![devDependency Status](https://david-dm.org/pdesterlich/moorea-mongoose-slugin/dev-status.svg?theme=shields.io)](https://david-dm.org/pdesterlich/moorea-mongoose-slugin#info=devDependencies)

Unique URL-friendly slugs plugin for mongoose that is lightweight, concurrency safe, but also follows the normal mongoose plugin pattern. Unlike some of the other offerings in NPM, this one also requires minimal effort on your part by using the standard mongoose plugin syntax.

based on Paul Vencill's [slugin](https://github.com/pvencill/slugin) plugin

## Getting started
Install the plugin with npm:

```sh
npm install slugin
npm install moorea-mongoose-slugin
```

Add the plugin to your schema:

```javascript
var slugin = require('slugin');
var slugin = require('moorea-mongoose-slugin');

// Your awesome schema building here

Expand All @@ -35,5 +38,5 @@ Why the random numbers? Well, I decided that for a few edge cases if you had tw
In most cases you'll use the Schema syntax of YourSchema.plugin(slugin, options) instead. Options are, as one would expect, optional.

* `slugName` - What property you want created on your schema to store the completed slug. Defaults to 'slug'. Also creates a pair of properties to hold the string and numeric parts of the slug for more efficient querying. Those will be {slugName}_base and {slugName}_it respectively.
* `source` - The property or properties on your schema that you want to use as the source of the slug. Can be a string property name or array of properties. Defaults to 'title'.
* `modelName` - The name you use to store your schema in mongoose when you call mongoose.model('modelName', schema). Defaults to pascal-casing the mongodb collection name for your model (e.g. the mongodb collection name "posts" will be interpreted as a modelName 'Posts').
* `source` - The property or properties on your schema that you want to use as the source of the slug. Can be a string property name or array of properties. Defaults to 'title'.
* `modelName` - The name you use to store your schema in mongoose when you call mongoose.model('modelName', schema). Defaults to pascal-casing the mongodb collection name for your model (e.g. the mongodb collection name "posts" will be interpreted as a modelName 'Posts').
150 changes: 76 additions & 74 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,94 +1,96 @@
'use strict';

var mongoose = require('mongoose'),
_ = require('lodash'),
inflection = require('inflection'),
slugs = require('slugs');

function slugify(model, options){
var slugParts = _.values(_.pick(model, options.source));
return slugs(slugParts.join(' '));
var
mongoose = require('mongoose'),
_ = require('lodash'),
inflection = require('inflection'),
slugs = require('slugs');

function slugify(model, options) {
var slugParts = _.map(options.source, function(path) {
return _.get(model, path);
});
return slugs(slugParts.join(' '));
}

function getModel(document, options){
var modelName = options.modelName || inflection.singularize(inflection.camelize(document.collection.name));
return document.collection.conn.model(modelName);
function getModel(document, options) {
var modelName = options.modelName || inflection.singularize(inflection.camelize(document.collection.name));
return document.collection.conn.model(modelName);
}

function incrementAndSave(document, options, cb){
var Model = getModel(document, options);
var params = {};
var slugbaseKey = options.slugBase;
var itKey = options.slugIt;
params[slugbaseKey] = document[slugbaseKey];
function incrementAndSave(document, options, cb) {
var Model = getModel(document, options);
var params = {};
var slugbaseKey = options.slugBase;
var itKey = options.slugIt;
params[slugbaseKey] = document[slugbaseKey];

Model.findOne(params).sort('-'+itKey).exec(function(e, doc){
if(e) return cb(e);
Model.findOne(params).sort('-'+itKey).exec(function(e, doc) {
if(e) return cb(e);

var it = (doc[itKey] || 0) + Math.ceil(Math.random()*10);
var it = (doc[itKey] || 0) + Math.ceil(Math.random()*10);

document[itKey] = it;
document[options.slugName] = document[slugbaseKey]+'-'+it;
document.markModified(slugbaseKey);
document[itKey] = it;
document[options.slugName] = document[slugbaseKey]+'-'+it;
document.markModified(slugbaseKey);

_.forEach(options.source, function(item){
document.markModified(item);
});

return document.save(cb);
_.forEach(options.source, function(item) {
document.markModified(item);
});

return document.save(cb);
});
}

function Slugin(schema, options){
options = _.defaults(options || {}, Slugin.defaultOptions);
if(_.isString(options.source))
options.source = [options.source];
options.slugIt = options.slugName + '_it';
options.slugBase = options.slugName + '_base';
var fields = {};
fields[options.slugName] = {
type : String,
unique: true
};

fields[options.slugBase] = {
type: String,
index:true
};

fields[options.slugIt] = {
type: Number
};

schema.add(fields);

schema.pre('save', function(next){
var self = this;
var slugBase = slugify(this,options);
if(this[options.slugBase] !== slugBase){
this[options.slugName] = slugBase;
this[options.slugBase] = slugBase;
delete this[options.slugIt];
}
next();
function Slugin(schema, options) {
options = _.defaults(options || {}, Slugin.defaultOptions);
if (_.isString(options.source)) options.source = [options.source];
options.slugIt = options.slugName + '_it';
options.slugBase = options.slugName + '_base';
var fields = {};
fields[options.slugName] = {
type : String,
unique: true
};

fields[options.slugBase] = {
type: String,
index:true
};

fields[options.slugIt] = {
type: Number
};

schema.add(fields);

schema.pre('save', function(next){
var self = this;
var slugBase = slugify(self, options);
if (self[options.slugBase] !== slugBase) {
self[options.slugName] = slugBase;
self[options.slugBase] = slugBase;
delete self[options.slugIt];
}
next();
});

schema.methods.save = function(cb){
var self = this;
mongoose.Model.prototype.save.call(self, function(e, model, num) {
if (e && (e.code === 11000 || e.code === 11001) && !!~e.errmsg.indexOf(self[options.slugName])) {
incrementAndSave(self, options, cb);
} else {
cb(e,model,num);
}
});

schema.methods.save = function(cb){
var self = this;
mongoose.Model.prototype.save.call(self, function(e, model, num){
if(e && (e.code === 11000 || e.code === 11001) && !!~e.err.indexOf(self[options.slugName])){
incrementAndSave(self, options, cb);
}else{
cb(e,model,num);
}
});
};
};
}

Slugin.defaultOptions = {
slugName : 'slug',
source : 'title',
modelName : null
slugName : 'slug',
source : 'title',
modelName : null
};

module.exports = Slugin;
25 changes: 14 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
{
"name": "slugin",
"version": "0.0.12",
"name": "moorea-mongoose-slugin",
"version": "0.1.6",
"description": "Unique slugs plugin for mongoose that is lightweight, concurrency safe, but also follows the normal mongoose plugin pattern.",
"main": "index.js",
"scripts": {
"test": "mocha test --reporter spec --require should"
"test": "mocha test"
},
"repository": {
"type": "git",
"url": "git://github.com/pvencill/slugin.git"
"url": "https://github.com/pdesterlich/moorea-mongoose-slugin.git"
},
"keywords": [
"mongoose",
"unique",
"slug",
"plugin"
],
"author": "Paul Vencill",
"author": "Phelipe de Sterlich <p.desterlich@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/pvencill/slugin/issues"
"url": "https://github.com/pdesterlich/slugin/issues"
},
"homepage": "https://github.com/pvencill/slugin",
"homepage": "https://github.com/pdesterlich/slugin",
"dependencies": {
"inflection": "^1.3.5",
"lodash": "~2.4.1",
"mongoose": "~3.8.9",
"slugs": "~0.1.2"
"inflection": "^1.7.0",
"lodash": "^3.7.0",
"slugs": "^0.1.3"
},
"devDependencies": {
"chai": "^3.3.0",
"mongoose": "^4.0.1"
}
}
Loading