Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

version 0.1.0

  • Loading branch information...
commit 0aca51343801edb2f1265753136922a635e762ea 1 parent 4801f86
@petejkim authored
View
44 .gitignore
@@ -0,0 +1,44 @@
+# App
+tmp/
+node_modules/
+*.swp
+**.orig
+*.pid
+*.log
+
+# OSX
+.DS_Store
+._*
+.Spotlight-V100
+.Trashes
+
+# Linux
+*~
+.directory
+
+# Windows
+Thumbs.db
+Desktop.ini
+
+# RubyMine
+.idea/
+
+# TextMate
+*.tmproj
+*.tmproject
+tmtags
+
+# Vim
+.*.sw[a-z]
+*.un~
+Session.vim
+
+# Emacs
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+.elc
+auto-save-list
+tramp
+.\#*
+
View
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Peter Jihoon Kim
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
View
5 Makefile
@@ -0,0 +1,5 @@
+test:
+ @./node_modules/.bin/mocha --reporter dot --ui bdd
+
+.PHONY: test
+
View
104 README.md
@@ -0,0 +1,104 @@
+# factory-lady.js
+
+Factory-lady is a factory library for [Node.js](http://nodejs.org/) / JavaScript inspired by [Factory\_girl](http://github.com/thoughtbot/factory_girl). It works asynchronously and supports lazy attributes as well as associations.
+
+It works as long as `new` keyword is used on the model to instantiate new objects and `save` method is used to persist objects. For example, [Mongoose](http://github.com/LearnBoost/mongoose.git) models follow such convention.
+
+## Installation
+
+Node.js:
+
+```
+npm install factory-lady
+```
+
+To use Factory-lady in the browser or other JavaScript environments, just copy and include `factory-lady.js` under `lib` directory.
+
+## Defining Factories
+
+JavaScript:
+
+```javascript
+var Factory = require('factory-lady')
+ , User = require('../../app/models/user')
+ , Post = require('../../app/models/post');
+
+var emailCounter = 1;
+
+Factory.define('user', User, {
+ email : function(cb) { cb('user' + emailCounter++ + '@example.com'); } // lazy attribute
+, state : 'activated'
+, password : '123456'
+});
+
+Factory.define('post', Post, {
+ user_id : Factory.assoc('user', 'id') // simply Factory.assoc('user') for user object itself
+, subject : 'Hello World'
+, content : 'Lorem ipsum dolor sit amet...'
+});
+```
+
+CoffeeScript:
+
+```coffeescript
+Factory = require 'factory-lady'
+User = require '../../app/models/user'
+Post = require '../../app/models/post'
+
+emailCounter = 1
+
+Factory.define 'user', User,
+ email : (cb) -> cb("user#{emailCounter++}@example.com") # lazy attribute
+ state : 'activated'
+ password : '123456'
+
+Factory.define 'post', Post,
+ user_id : Factory.assoc 'user', 'id' # simply Factory.assoc 'user' for user object itself
+ title : 'Hello World'
+ content : 'Lorem ipsum dolor sit amet...'
+```
+
+## Using Factories
+
+JavaScript:
+
+```javascript
+Factory.build('post', function(post) {
+ // post is a Post instance that is not saved
+});
+
+Factory.build('post', { title: 'Foo', content: 'Bar' }, function(post) {
+ // build a post and override title and content
+});
+
+Factory.create('post', function(post) {
+ // post is a saved Post instance
+});
+
+Factory('post', function(post) {
+ // post is a saved Post instance
+ // same as Factory.create
+});
+```
+
+CoffeeScript:
+
+```coffeescript
+Factory.build 'post', (post) ->
+ # post is a Post instance that is not saved
+
+Factory.build 'post', title: 'Foo', content: 'Bar', (post) ->
+ # post is a Post instance that is not saved
+
+Factory.create 'post', (post) ->
+ # post is a saved Post instance
+
+Factory 'post', (post) ->
+ # post is a saved Post instance
+ # same as Factory.create
+```
+
+## License
+
+Copyright (c) 2011 Peter Jihoon Kim. This software is licensed under the [MIT License](http://github.com/petejkim/factory-lady/raw/master/LICENSE).
+
View
2  index.js
@@ -0,0 +1,2 @@
+module.exports = require('./lib/factory-lady');
+
View
129 lib/factory-lady.js
@@ -0,0 +1,129 @@
+(function() {
+ var hash = {
+ merge: function(obj1, obj2) {
+ if(obj1 && obj2) {
+ var key;
+ for(key in obj2) {
+ if(obj2.hasOwnProperty(key)) {
+ obj1[key] = obj2[key];
+ }
+ }
+ }
+ return obj1;
+ }
+ , copy: function(obj) {
+ var newObj = {};
+ if(obj) {
+ hash.merge(newObj, obj);
+ }
+ return newObj;
+ }
+ , keys: function(obj) {
+ var keys = [], key;
+ for(key in obj) {
+ if(obj.hasOwnProperty(key)) {
+ keys.push(key);
+ }
+ }
+ return keys;
+ }
+ };
+
+ var asyncForEach = function(array, handler, callback) {
+ var length = array.length, index = -1;
+
+ var processNext = function() {
+ index ++;
+ if(index < length) {
+ var item = array[index];
+ handler(item, processNext);
+ } else {
+ callback();
+ }
+ };
+
+ processNext();
+ };
+
+ var factories = {};
+
+ var define = function(name, model, attributes) {
+ factories[name] = {
+ model: model
+ , attributes: attributes
+ };
+ };
+
+ var build = function(name, userAttrs, callback) {
+ if(typeof userAttrs === 'function') {
+ callback = userAttrs;
+ userAttrs = {};
+ }
+
+ var model = factories[name].model;
+ var attrs = hash.copy(factories[name].attributes);
+ hash.merge(attrs, userAttrs);
+
+ asyncForEach(hash.keys(attrs), function(key, cb) {
+ var fn = attrs[key];
+ if(typeof fn === 'function') {
+ fn(function(value) {
+ attrs[key] = value;
+ cb();
+ });
+ } else {
+ cb();
+ }
+ }, function() {
+ var doc = new model();
+ var key;
+ for(key in attrs) {
+ if(attrs.hasOwnProperty(key)) {
+ doc[key] = attrs[key];
+ }
+ }
+ callback(doc);
+ });
+ };
+
+ var create = function(name, userAttrs, callback) {
+ if(typeof userAttrs === 'function') {
+ callback = userAttrs;
+ userAttrs = {};
+ }
+
+ build(name, userAttrs, function(doc) {
+ doc.save(function(err) {
+ if(err) {
+ throw err;
+ }
+ callback(doc);
+ });
+ });
+ };
+
+ var assoc = function(name, attr) {
+ return function(callback) {
+ create(name, function(doc) {
+ if(attr) {
+ callback(doc[attr]);
+ } else {
+ callback(doc);
+ }
+ });
+ };
+ };
+
+ var Factory = create;
+ Factory.define = define;
+ Factory.build = build;
+ Factory.create = create;
+ Factory.assoc = assoc;
+
+ if(typeof module !== 'undefined' && module.exports) {
+ module.exports = Factory;
+ } else {
+ this.Factory = Factory;
+ }
+}());
+
View
26 package.json
@@ -0,0 +1,26 @@
+{
+ "name": "factory-lady"
+, "description": "a factory library for javascript / node.js inspired by factory_girl"
+, "main": "./index"
+, "author": "Peter Jihoon Kim"
+, "version": "0.1.0"
+, "keywords": ["factory", "test", "bdd", "tdd", "fixture"]
+, "repository" : {
+ "type" : "git"
+ , "url" : "http://github.com/petejkim/factory-lady.git"
+ }
+, "bugs" : {
+ "url" : "http://github.com/petejkim/factory-lady/issues"
+ }
+, "licenses" : [
+ { "type" : "MIT"
+ , "url" : "http://github.com/petejkim/factory-lady/raw/master/LICENSE"
+ }
+ ]
+, "dependencies": {}
+, "devDependencies": {
+ "mocha" : ">= 0.7.1"
+ , "should" : ">= 0.4.2"
+ }
+}
+
View
116 test/factory-test.js
@@ -0,0 +1,116 @@
+var Factory = require('..');
+var should = require('should');
+var context = describe;
+
+describe('Factory', function() {
+ var Model, Person, Job;
+
+ before(function() {
+ Model = function() {};
+
+ Model.prototype.save = function(callback) {
+ this.saveCalled = true;
+ callback();
+ };
+
+ Person = function() {};
+ Person.protoype = new Model();
+
+ Job = function() {};
+ Job.prototype = new Model();
+ });
+
+ beforeEach(function() {
+ var nameCounter = 1;
+
+ Factory.define('person', Person, {
+ name: function(cb) { cb("person " + nameCounter++); }
+ , age: 25
+ , job: Factory.assoc('job')
+ , title: Factory.assoc('job', 'title')
+ });
+
+ Factory.define('job', Job, {
+ title: 'Engineer'
+ , company: 'Foobar Inc.'
+ });
+ });
+
+ describe('#build', function() {
+ it('builds, but does not save the object', function(done) {
+ Factory.build('job', function(job) {
+ (job instanceof Job).should.be.true;
+ job.title.should.eql('Engineer');
+ job.company.should.eql('Foobar Inc.');
+ job.should.not.have.property('saveCalled');
+ done();
+ });
+
+ context('passing attributes as second argument', function() {
+ it('sets them', function(done) {
+ Factory.build('job', { title: "Artist", company: "Bazqux Co." }, function(job) {
+ (job instanceof Job).should.be.true;
+ job.title.should.eql('Artist');
+ job.company.should.eql('Bazqux Co.');
+ job.should.not.have.property('saveCalled');
+ done();
+ });
+ });
+ });
+
+ context('factory containing an association', function() {
+ it('is able to handle that', function(done) {
+ Factory.build('person', { age: 30 }, function(person) {
+ (person instanceof Person).should.be.true;
+ person.should.not.have.property('saveCalled');
+ person.name.should.eql('person 1');
+ person.age.should.eql(30);
+ (person.job instanceof Job).should.be.true;
+ person.job.title.should.eql('Engineer');
+ person.job.company.should.eql('Foobar Inc.');
+ person.job.saveCalled.should.be.true;
+ person.title.should.eql('Engineer');
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ describe('#create', function() {
+ it('builds and saves the object', function(done) {
+ Factory.create('job', function(job) {
+ (job instanceof Job).should.be.true;
+ job.title.should.eql('Engineer');
+ job.company.should.eql('Foobar Inc.');
+ job.saveCalled.should.be.true;
+ done();
+ });
+ });
+
+ context('passing attributes as second argument', function() {
+ it('sets them', function(done) {
+ Factory.create('job', { title: "Artist", company: "Bazqux Co." }, function(job) {
+ (job instanceof Job).should.be.true;
+ job.title.should.eql('Artist');
+ job.company.should.eql('Bazqux Co.');
+ job.saveCalled.should.be.true;
+ done();
+ });
+ });
+ });
+
+ context('Factory(...) instead of Factory.create(...)', function() {
+ it('is aliased, so it does the same thing as #create', function(done) {
+ Factory('job', function(job) {
+ (job instanceof Job).should.be.true;
+ job.title.should.eql('Engineer');
+ job.company.should.eql('Foobar Inc.');
+ job.saveCalled.should.be.true;
+ done();
+ });
+ });
+ });
+ });
+});
+
Please sign in to comment.
Something went wrong with that request. Please try again.