Permalink
Browse files

added callback style functions for attributes

  • Loading branch information...
1 parent 10b1567 commit 9bf86ff2988af3eeb8a2ba5b5ea60cb1e3b2375a @gregormelhorn gregormelhorn committed Feb 5, 2012
Showing with 129 additions and 31 deletions.
  1. +23 −3 README.md
  2. +60 −15 index.js
  3. +1 −0 package.json
  4. +44 −13 spec/factory_spec.js
  5. +1 −0 spec/spec_helper.js
View
@@ -23,17 +23,37 @@ To begin, we need to define our Factory. We do this with the Factory.define met
"created_at": function(){
return new Date();
}
- })
+ });
```
In the example above, we've created the "profile" Factory for the class Profile with our default attributes.
The "created_at" attribute will be filled dynamically by evaluating the function at object build time. This is useful to dynamically fill attributes with a name generator, e.g. https://github.com/marak/Faker.js/
-*build(factory, [attributes])*
+*build(factory, [attributes], [callback])*
-Builds an instance of the factory, overriding the factory with the attributes hash if passed.
+Builds an instance of the factory, overriding the factory with the attributes hash if passed.
+
+If a callback is passed, it is possible to use callback functions for building attributes. The callback is detected by checking if the attribute function will accept arguments. Here is an example that will create
+BlogEntry for a Comment object and set the foreign key accordingly.
+
+```js
+ var Factory = require('factory-worker');
+
+ Factory.define("comment", Comment, {
+ "text": "Comment text",
+ "blogEntry": function(callback){
+ Factory.create("BlogEntry", function(err, entry){
+ callback(err, entry._id);
+ });
+ }
+ });
+
+ Factory.build("comment", {text: "some other comment text"}, function(err, comment){
+ console.log(err, comment);
+ });
+```
*create(factory, [attributes], callback)*
View
@@ -1,4 +1,5 @@
var Hash = require('hashish');
+var Seq = require('seq');
module.exports = {
patterns: {},
@@ -8,32 +9,76 @@ module.exports = {
attributes: def
}
},
- build: function(model, data) {
+ build: function(model, data, callback) {
+ if (typeof(data) == 'function'){
+ callback = data;
+ data = {}
+ }
+
if (data === undefined) {
data = {}
}
var attributes = this.patterns[model].attributes
var values = Hash.merge(attributes, data);
- values = Hash.map(values, function(value, key){
- if (typeof(value) == 'function'){
- return value.call(data);
- } else {
- return value;
- }
- });
- var obj = new(this.patterns[model].class)(values);
- return obj;
+ // no callback given so use direct style
+ if (callback === undefined){
+ values = Hash.map(values, function(value, key){
+ if (typeof(value) == 'function'){
+ if (value.length > 0){
+ throw('you need to pass a callback to the build function - setter for attribute "' + key + '" is asynchronous');
+ } else {
+ return value.call();
+ }
+ } else {
+ return value;
+ }
+ });
+ var obj = new(this.patterns[model].class)(values);
+ return obj;
+ } else {
+ // callback given, convert everything to function style
+ callbacks = []
+ Hash.forEach(values, function(value, key){
+ if (typeof(value) == 'function'){
+ if (value.length == 0){
+ callbacks.push({key: key, cbk: function(cbk){
+ return cbk(null, value.call());
+ }});
+ } else {
+ callbacks.push({key: key, cbk: value});
+ }
+ } else {
+ callbacks.push({key: key, cbk: function(cbk){
+ return cbk(null, value);
+ }});
+ }
+ });
+ Seq(callbacks)
+ .parEach(function(value){
+ value.cbk(this.into(value.key));
+ })
+ .seq(function(){
+ var obj = new(module.exports.patterns[model].class)(this.vars);
+ callback(null, obj);
+ })
+ .catch(function(err){
+ callback(err);
+ });
+ }
},
create: function(model, data, callback) {
if (typeof(data) == 'function') {
callback = data;
- data = {}
+ data = {};
}
- var obj = this.build(model, data)
- obj.save(function(error) {
- callback(error, obj)
- })
+ this.build(model, data, function(err, obj){
+ if (err)
+ throw(err);
+ obj.save(function(error) {
+ callback(error, obj);
+ })
+ });
}
}
View
@@ -17,6 +17,7 @@
},
"dependencies": {
"hashish": ">=0.0.3"
+ , "seq": ">= 0.3.5"
},
"devDependencies": {
"jessie": ">=0.3.7"
View
@@ -4,17 +4,23 @@ describe('Factory#define', function() {
return "dynamic";
}
+ callback_function = function(callback){
+ return callback(null, 'called');
+ }
+
Factory.define('test', TestModel, {
name: 'Test Model',
real: false,
- dynamic: dynamic_function})
+ dynamic: dynamic_function,
+ called: callback_function})
expect(Factory.patterns.test).toEqual({
class: TestModel,
attributes: {
name: 'Test Model',
real: false,
- dynamic: dynamic_function
+ dynamic: dynamic_function,
+ called: callback_function
}
})
})
@@ -32,45 +38,70 @@ describe('Factory#build', function() {
})
it('uses the stored definition to seed attribute values', function() {
- object = Factory.build('test')
+ var object = Factory.build('test')
expect(object.name).toEqual('Test Model')
expect(object.real).toEqual(false)
expect(object.dynamic).toEqual("dynamic")
})
it('takes an optional parameter to override stores attributes', function() {
- object = Factory.build('test', { real: true })
+ var object = Factory.build('test', { real: true })
expect(object.name).toEqual('Test Model')
expect(object.real).toEqual(true)
})
})
+describe('Factory#build_with_callback', function() {
+ beforeEach(function() {
+ Factory.define('test_callback', TestModel, {
+ called: function(cbk) {
+ return cbk(null, 'called');
+ }
+ })
+ })
+
+ it('uses the stored definition to seed attribute values', function() {
+ var object = null;
+ Factory.build('test_callback', function(err, obj){
+ object = obj;
+ });
+ waitsFor(function() { return object != null}, 'object creation failed', 1000);
+ runs(function(){
+ expect(object.called).toEqual("called");
+ });
+ })
+
+ it("throws an error if no callback is given", function(){
+ expect(function(){
+ Factory.build('test_callback');
+ }).toThrow('you need to pass a callback to the build function - setter for attribute "called" is asynchronous');
+ });
+})
+
describe('Factory#create', function() {
beforeEach(function() {
Factory.define('test', TestModel, {
name: 'Test Model',
real: false,
dynamic: function() {
return "dynamic";
+ },
+ called: function(cbk) {
+ return cbk(null, 'called');
}
- })
- })
-
- it('should call Factory.build, passing the factory name and data', function() {
- spyOn(Factory, 'build').andCallThrough()
- Factory.create('test', function(error) {})
- expect(Factory.build).toHaveBeenCalledWith('test', {})
+ })
})
- it('can also override attributes like Factory#build', function() {
+ it('should override attributes like Factory#build', function() {
var object;
- Factory.create('test', { name: function(){return 'Test Object';}, dynamic: "another value" }, function(e, o) {
+ Factory.create('test', { name: function(){return 'Test Object';}, dynamic: "another value", called: function(cbk){return cbk(null, 'new value');} }, function(e, o) {
object = o;
})
waitsFor(function() { return object != null}, 'object creation failed', 1000);
runs(function() {
expect(object.name).toEqual('Test Object');
expect(object.dynamic).toEqual('another value');
+ expect(object.called).toEqual('new value');
})
})
})
View
@@ -3,6 +3,7 @@ TestModel = function TestModel(attributes) {
this.name = attributes.name;
this.real = attributes.real;
this.dynamic = attributes.dynamic;
+ this.called = attributes.called;
}
TestModel.prototype.save = function(callback) {
callback(null)

0 comments on commit 9bf86ff

Please sign in to comment.