├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js ├── package.json ├── scripts ├── mocha_runner.js └── prepublish.sh └── src ├── __tests__ └── index.js └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"] 3 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | 4 | "plugins": [ 5 | "babel" 6 | ], 7 | 8 | "env": { 9 | "es6": true, 10 | "node": true 11 | }, 12 | 13 | "ecmaFeatures": { 14 | "arrowFunctions": true, 15 | "binaryLiterals": true, 16 | "blockBindings": true, 17 | "classes": true, 18 | "defaultParams": true, 19 | "destructuring": true, 20 | "experimentalObjectRestSpread": true, 21 | "forOf": true, 22 | "generators": true, 23 | "globalReturn": true, 24 | "jsx": true, 25 | "modules": true, 26 | "objectLiteralComputedProperties": true, 27 | "objectLiteralDuplicateProperties": true, 28 | "objectLiteralShorthandMethods": true, 29 | "objectLiteralShorthandProperties": true, 30 | "octalLiterals": true, 31 | "regexUFlag": true, 32 | "regexYFlag": true, 33 | "restParams": true, 34 | "spread": true, 35 | "superInFunctions": true, 36 | "templateStrings": true, 37 | "unicodeCodePointEscapes": true 38 | }, 39 | 40 | "rules": { 41 | "babel/arrow-parens": [2, "as-needed"], 42 | 43 | "array-bracket-spacing": [2, "always"], 44 | "arrow-spacing": 2, 45 | "block-scoped-var": 0, 46 | "brace-style": [2, "1tbs", {"allowSingleLine": true}], 47 | "callback-return": 2, 48 | "camelcase": [2, {"properties": "always"}], 49 | "comma-dangle": 0, 50 | "comma-spacing": 0, 51 | "comma-style": [2, "last"], 52 | "complexity": 0, 53 | "computed-property-spacing": [2, "never"], 54 | "consistent-return": 0, 55 | "consistent-this": 0, 56 | "curly": [2, "all"], 57 | "default-case": 0, 58 | "dot-location": [2, "property"], 59 | "dot-notation": 0, 60 | "eol-last": 2, 61 | "eqeqeq": 2, 62 | "func-names": 0, 63 | "func-style": 0, 64 | "generator-star-spacing": [0, {"before": true, "after": false}], 65 | "guard-for-in": 2, 66 | "handle-callback-err": [2, "error"], 67 | "id-length": 0, 68 | "id-match": [2, "^(?:_?[a-zA-Z0-9]*)|[_A-Z0-9]+$"], 69 | "indent": [2, 2, {"SwitchCase": 1}], 70 | "init-declarations": 0, 71 | "key-spacing": [2, {"beforeColon": false, "afterColon": true}], 72 | "linebreak-style": 2, 73 | "lines-around-comment": 0, 74 | "max-depth": 0, 75 | "max-len": [2, 80, 4], 76 | "max-nested-callbacks": 0, 77 | "max-params": 0, 78 | "max-statements": 0, 79 | "new-cap": 0, 80 | "new-parens": 2, 81 | "newline-after-var": 0, 82 | "no-alert": 2, 83 | "no-array-constructor": 2, 84 | "no-bitwise": 0, 85 | "no-caller": 2, 86 | "no-catch-shadow": 0, 87 | "no-class-assign": 2, 88 | "no-cond-assign": 2, 89 | "no-console": 1, 90 | "no-const-assign": 2, 91 | "no-constant-condition": 2, 92 | "no-continue": 0, 93 | "no-control-regex": 0, 94 | "no-debugger": 1, 95 | "no-delete-var": 2, 96 | "no-div-regex": 2, 97 | "no-dupe-args": 2, 98 | "no-dupe-keys": 2, 99 | "no-duplicate-case": 2, 100 | "no-else-return": 2, 101 | "no-empty": 2, 102 | "no-empty-character-class": 2, 103 | "no-empty-label": 2, 104 | "no-eq-null": 0, 105 | "no-eval": 2, 106 | "no-ex-assign": 2, 107 | "no-extend-native": 2, 108 | "no-extra-bind": 2, 109 | "no-extra-boolean-cast": 2, 110 | "no-extra-parens": 0, 111 | "no-extra-semi": 2, 112 | "no-fallthrough": 2, 113 | "no-floating-decimal": 2, 114 | "no-func-assign": 2, 115 | "no-implicit-coercion": 2, 116 | "no-implied-eval": 2, 117 | "no-inline-comments": 0, 118 | "no-inner-declarations": [2, "functions"], 119 | "no-invalid-regexp": 2, 120 | "no-invalid-this": 0, 121 | "no-irregular-whitespace": 2, 122 | "no-iterator": 2, 123 | "no-label-var": 2, 124 | "no-labels": 0, 125 | "no-lone-blocks": 2, 126 | "no-lonely-if": 2, 127 | "no-loop-func": 0, 128 | "no-mixed-requires": [2, true], 129 | "no-mixed-spaces-and-tabs": 2, 130 | "no-multi-spaces": 2, 131 | "no-multi-str": 2, 132 | "no-multiple-empty-lines": 0, 133 | "no-native-reassign": 0, 134 | "no-negated-in-lhs": 2, 135 | "no-nested-ternary": 0, 136 | "no-new": 2, 137 | "no-new-func": 0, 138 | "no-new-object": 2, 139 | "no-new-require": 2, 140 | "no-new-wrappers": 2, 141 | "no-obj-calls": 2, 142 | "no-octal": 2, 143 | "no-octal-escape": 2, 144 | "no-param-reassign": 2, 145 | "no-path-concat": 2, 146 | "no-plusplus": 0, 147 | "no-process-env": 0, 148 | "no-process-exit": 0, 149 | "no-proto": 2, 150 | "no-redeclare": 2, 151 | "no-regex-spaces": 2, 152 | "no-restricted-modules": 0, 153 | "no-return-assign": 2, 154 | "no-script-url": 2, 155 | "no-self-compare": 0, 156 | "no-sequences": 2, 157 | "no-shadow": 2, 158 | "no-shadow-restricted-names": 2, 159 | "no-spaced-func": 2, 160 | "no-sparse-arrays": 2, 161 | "no-sync": 2, 162 | "no-ternary": 0, 163 | "no-this-before-super": 2, 164 | "no-throw-literal": 2, 165 | "no-trailing-spaces": 2, 166 | "no-undef": 2, 167 | "no-undef-init": 2, 168 | "no-undefined": 0, 169 | "no-underscore-dangle": 0, 170 | "no-unexpected-multiline": 2, 171 | "no-unneeded-ternary": 2, 172 | "no-unreachable": 2, 173 | "no-unused-expressions": 2, 174 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], 175 | "no-use-before-define": 0, 176 | "no-useless-call": 2, 177 | "no-var": 0, 178 | "no-void": 2, 179 | "no-warning-comments": 0, 180 | "no-with": 2, 181 | "object-curly-spacing": [0, "always"], 182 | "object-shorthand": [2, "always"], 183 | "one-var": [2, "never"], 184 | "operator-assignment": [2, "always"], 185 | "operator-linebreak": [2, "after"], 186 | "padded-blocks": 0, 187 | "prefer-const": 0, 188 | "prefer-reflect": 0, 189 | "prefer-spread": 0, 190 | "quote-props": [2, "as-needed"], 191 | "quotes": [2, "single"], 192 | "radix": 2, 193 | "require-yield": 2, 194 | "semi": [2, "always"], 195 | "semi-spacing": [2, {"before": false, "after": true}], 196 | "sort-vars": 0, 197 | "space-after-keywords": [2, "always"], 198 | "space-before-blocks": [2, "always"], 199 | "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], 200 | "space-in-parens": 0, 201 | "space-infix-ops": [2, {"int32Hint": false}], 202 | "space-return-throw-case": 2, 203 | "space-unary-ops": [2, {"words": true, "nonwords": false}], 204 | "spaced-comment": [2, "always"], 205 | "strict": 0, 206 | "use-isnan": 2, 207 | "valid-jsdoc": 0, 208 | "valid-typeof": 2, 209 | "vars-on-top": 0, 210 | "wrap-iife": 2, 211 | "wrap-regex": 0, 212 | "yoda": [2, "never", {"exceptRange": true}] 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.iml 4 | .*.haste_cache.* 5 | .DS_Store 6 | .idea 7 | npm-debug.log 8 | node_modules 9 | dist -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.iml 4 | .*.haste_cache.* 5 | .DS_Store 6 | .idea 7 | .babelrc 8 | .eslintrc 9 | npm-debug.log 10 | lib 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kadira Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dr. Seeder 2 | 3 | Seed data with ease! 4 | 5 | 6 | 7 | ## Use in Node.js 8 | ``` 9 | npm install dr-seeder 10 | ``` 11 | 12 | ## Usage 13 | ````js 14 | import Seeder from 'dr-seeder'; 15 | 16 | Seeder.seed('Seed articles from array', ArticlesCollection, { 17 | data: [ 18 | {title: 'Super cool title', text: 'Hello!'}, 19 | {title: 'Another cool title', text: 'World!'} 20 | ] 21 | }); 22 | 23 | // or 24 | 25 | Seeder.seed('Seed articles with data callback', ArticlesCollection, { 26 | data(index) { 27 | return {title: 'Article ' + index}; 28 | } 29 | }); 30 | 31 | // or 32 | 33 | Seeder.seed('Advanced seed articles', ArticlesCollection, { 34 | condition() { 35 | return ArticlesCollection.find().count() < 5 36 | } 37 | min: 3, 38 | max: 10, 39 | data(index) { 40 | return { 41 | title: faker.lorem.words(), 42 | text: faker.lorem.paragraph() 43 | }; 44 | }, 45 | onStart() { 46 | console.log('Seeder: ' + this.name + ' started'); 47 | return this.collection.find().count(); 48 | }, 49 | onFinish() { 50 | const count = this.collection.find().count() 51 | console.log('Seeder: items count increased from ' + this.startResponse + ' to ' + count); 52 | } 53 | 54 | }); 55 | 56 | ```` 57 | 58 | ## Configuration 59 | 60 | ````js 61 | import Seeder from 'dr-seeder'; 62 | 63 | Seeder.config({ // default values 64 | condition() { // should data be seeded? 65 | // this (context) is an Object{name, collection} 66 | return this.collection.find().count() === 0; 67 | }, 68 | min: 1, // min rows to be seeded 69 | max: 20, // max rows to be seeded 70 | onStart() { // run before seed starts 71 | // same context as in condition() 72 | console.log('Seeder: ' + this.name + '\t => Started'); 73 | }, 74 | onFinish() { // run when seed is finished 75 | // same context as in condition() 76 | console.log('Seeder: ' + this.name + '\t => Finished'); 77 | }, 78 | onSkip() { // run when seed is skipped (if condition return false) 79 | // same context as in condition() 80 | console.log('Seeder: ' + this.name + '\t => Skipped'); 81 | } 82 | }) 83 | ```` 84 | 85 | ## API 86 | ### Seeder.config(options) 87 | Override default settings 88 | 89 | ### Seeder.seed(name, collection, options) 90 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/index'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dr-seeder", 3 | "version": "0.1.3", 4 | "description": "Seed data with ease", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/DrMongo/dr-seeder.git" 8 | }, 9 | "license": "MIT", 10 | "keywords": [ 11 | "mongoDb", 12 | "data", 13 | "seed", 14 | "meteor" 15 | ], 16 | "options": { 17 | "mocha": "--require scripts/mocha_runner src/**/__tests__/**/*.js" 18 | }, 19 | "scripts": { 20 | "prepublish": ". ./scripts/prepublish.sh", 21 | "lint": "eslint ./src", 22 | "lintfix": "eslint ./src --fix", 23 | "testonly": "mocha $npm_package_options_mocha", 24 | "test": "npm run lint && npm run testonly", 25 | "test-watch": "npm run testonly -- --watch --watch-extensions js" 26 | }, 27 | "devDependencies": { 28 | "nodemon": "1.7.x", 29 | "mocha": "2.x.x", 30 | "chai": "3.x.x", 31 | "eslint": "1.7.x", 32 | "babel-eslint": "4.x.x", 33 | "eslint-plugin-babel": "2.x.x", 34 | "babel-cli": "6.x.x", 35 | "babel-core": "6.x.x", 36 | "babel-polyfill": "6.x.x", 37 | "babel-preset-es2015": "6.x.x", 38 | "babel-preset-stage-2": "6.x.x", 39 | "babel-plugin-transform-runtime": "6.x.x" 40 | }, 41 | "dependencies": { 42 | "babel-runtime": "6.x.x" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scripts/mocha_runner.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register'); 2 | require('babel-polyfill'); 3 | 4 | process.on('unhandledRejection', function (error) { 5 | console.error('Unhandled Promise Rejection:'); 6 | console.error(error && error.stack || error); 7 | }); 8 | -------------------------------------------------------------------------------- /scripts/prepublish.sh: -------------------------------------------------------------------------------- 1 | echo "> Start transpiling ES2015" 2 | echo "" 3 | ./node_modules/.bin/babel --plugins "transform-runtime" src --ignore __tests__ --out-dir ./dist 4 | echo "" 5 | echo "> Complete transpiling ES2015" 6 | -------------------------------------------------------------------------------- /src/__tests__/index.js: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import Seeder from '../'; 3 | const {describe, it} = global; 4 | 5 | class Collection { 6 | constructor() { 7 | this.list = []; 8 | } 9 | 10 | insert(data) { 11 | this.list.push(data); 12 | } 13 | 14 | find() { 15 | const length = this.list.length; 16 | return { 17 | count() { 18 | return length; 19 | } 20 | }; 21 | } 22 | 23 | // helpers 24 | _reset() { 25 | this.list = []; 26 | } 27 | 28 | _count() { 29 | return this.list.length; 30 | } 31 | } 32 | 33 | 34 | describe('Data as array', () => { 35 | 36 | it('check seeded data', () => { 37 | const collection = new Collection(); 38 | 39 | Seeder.seed('Data as array', collection, { 40 | data: [ 41 | {name: 'foo1'}, 42 | {name: 'foo2'} 43 | ] 44 | }); 45 | 46 | expect(collection).to.have.deep.property('list[0].name', 'foo1'); 47 | expect(collection).to.have.deep.property('list[1].name', 'foo2'); 48 | }); 49 | 50 | 51 | it('custom condition', () => { 52 | const collection = new Collection(); 53 | 54 | Seeder.seed('custom condition', collection, { 55 | condition() { 56 | return true; 57 | }, 58 | data: [ {key: 'value'} ] 59 | }); 60 | 61 | expect(collection._count()).to.be.equal(1); 62 | expect(collection).to.have.deep.property('list[0].key', 'value'); 63 | }); 64 | 65 | }); 66 | 67 | 68 | describe('Data as function', () => { 69 | const collection = new Collection(); 70 | 71 | Seeder.seed('Data as function', collection, { 72 | min: 3, 73 | max: 10, 74 | data(index) { 75 | return {title: 'Article ' + index}; 76 | } 77 | }); 78 | 79 | 80 | it('check seeded data count', () => { 81 | expect(collection._count()).to.be.least(3); 82 | expect(collection._count()).to.be.most(10); 83 | }); 84 | 85 | it('check seeded data', () => { 86 | expect(collection).to.have.deep.property('list[0].title', 'Article 0'); 87 | expect(collection).to.have.deep.property('list[1].title', 'Article 1'); 88 | }); 89 | }); 90 | 91 | 92 | describe('Lifecycle callbacks', () => { 93 | 94 | it('start and finish with context check', () => { 95 | const collection = new Collection(); 96 | 97 | let stack = []; 98 | Seeder.seed('seed with lifecycle callbacks', collection, { 99 | data: [ {key: 'value'} ], 100 | onStart() { 101 | stack.push('start'); 102 | expect(this.name).to.be.equal('seed with lifecycle callbacks'); 103 | expect(this.collection).to.be.equal(collection); 104 | return 'start return data'; 105 | }, 106 | onFinish() { 107 | expect(this.name).to.be.equal('seed with lifecycle callbacks'); 108 | expect(this.collection).to.be.equal(collection); 109 | expect(this.startResponse).to.be.equal('start return data'); 110 | stack.push('finish'); 111 | }, 112 | onSkip() { 113 | stack.push('skip'); 114 | } 115 | }); 116 | 117 | expect(stack.join(';')).to.be.equal('start;finish'); 118 | }); 119 | 120 | it('start and skip with context check', () => { 121 | const collection = new Collection(); 122 | collection.insert({key: 'already seeded collection'}); 123 | 124 | let stack = []; 125 | Seeder.seed('seed with lifecycle callbacks', collection, { 126 | data: [ {key: 'value'} ], 127 | onStart() { 128 | stack.push('start'); 129 | }, 130 | onFinish() { 131 | stack.push('finish'); 132 | }, 133 | onSkip() { 134 | expect(this.name).to.be.equal('seed with lifecycle callbacks'); 135 | expect(this.collection).to.be.equal(collection); 136 | expect(this.startResponse).to.be.equal(null); 137 | stack.push('skip'); 138 | } 139 | }); 140 | 141 | expect(stack.join(';')).to.be.equal('skip'); 142 | }); 143 | 144 | }); 145 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | class Seed { 2 | 3 | constructor(name, collection, options ) { 4 | if ( !collection || !options ) { 5 | var msg = 'Please supply a collection to seed and options for seeding.'; 6 | throw new Error( msg ); 7 | } else { 8 | this.name = name; 9 | this.collection = collection; 10 | this.options = options; 11 | this.isDataArray = this.options.data instanceof Array; 12 | this.context = { 13 | name, 14 | collection, 15 | startResponse: null 16 | }; 17 | 18 | this.seed(); 19 | } 20 | } 21 | 22 | shouldSeed() { 23 | let condition = this.options.condition; 24 | 25 | return condition ? condition.call(this.context) : true; 26 | } 27 | 28 | seed() { 29 | let options = this.options; 30 | let data = options.data; 31 | let onStart = options.onStart; 32 | let onFinish = options.onFinish; 33 | let onSkip = options.onSkip; 34 | 35 | 36 | if (this.shouldSeed()) { 37 | if (onStart) { 38 | this.context.startResponse = onStart.call(this.context); 39 | } 40 | 41 | this.plant(data); 42 | 43 | if (onFinish) { 44 | onFinish.call(this.context); 45 | } 46 | } else if (onSkip) { 47 | onSkip.call(this.context); 48 | } 49 | } 50 | 51 | plant( data ) { 52 | let loopLength = this._loopLength(); 53 | 54 | for ( let i = 0; i < loopLength; i++ ) { 55 | let value = this.isDataArray ? data[i] : data.call(this.context, i); 56 | 57 | this.collection.insert( value ); 58 | } 59 | } 60 | 61 | _loopLength() { 62 | const random = (min, max) => { 63 | return Math.floor(Math.random() * (max - min + 1)) + min; 64 | }; 65 | 66 | return this.isDataArray ? 67 | this.options.data.length : 68 | random(this.options.min, this.options.max); 69 | } 70 | } 71 | 72 | const Seeder = { 73 | settings: { 74 | condition() { 75 | return this.collection.find().count() === 0; 76 | }, 77 | min: 1, 78 | max: 20, 79 | onStart() { 80 | console.log('Seeder: ' + this.name + '\t => Started'); 81 | }, 82 | onFinish() { 83 | console.log('Seeder: ' + this.name + '\t => Finished'); 84 | }, 85 | onSkip() { 86 | console.log('Seeder: ' + this.name + '\t => Skipped'); 87 | } 88 | }, 89 | 90 | config(options) { 91 | Object.assign(this.settings, options); 92 | }, 93 | 94 | seed(name, collection, options) { 95 | const finalOptions = Object.assign({}, this.settings, options); 96 | return new Seed( name, collection, finalOptions ); 97 | } 98 | }; 99 | 100 | export default Seeder; 101 | --------------------------------------------------------------------------------