New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

rosie

Package Overview
Dependencies
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rosie - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

bower.json

4

component.json
{
"name": "rosie",
"version": "0.2.0",
"version": "0.3.0",
"main": "src/rosie.js",

@@ -21,2 +21,2 @@ "ignore": [

}
}
}

@@ -1,11 +0,31 @@

{ "name": "rosie"
, "version": "0.2.0"
, "description": "factory for building JavaScript objects, mostly useful for setting up test data. Inspired by factory_girl"
, "keywords": ["factory", "rosie", "test"]
, "author": "Brandon Keepers <brandon@opensoul.org>"
, "contributors": []
, "dependencies" : {}
, "engines": { "node": "*" }
, "repository": { "type": "git", "url": "git://github.com/bkeepers/rosie.git" }
, "main": "src/rosie.js"
}
{
"name": "rosie",
"version": "0.3.0",
"description": "factory for building JavaScript objects, mostly useful for setting up test data. Inspired by factory_girl",
"keywords": [
"factory",
"rosie",
"test"
],
"author": "Brandon Keepers <brandon@opensoul.org>",
"contributors": [],
"dependencies": {},
"devDependencies": {
"karma": "^0.12.1",
"karma-chrome-launcher": "^0.1.2",
"karma-jasmine": "^0.1.5",
"karma-phantomjs-launcher": "^0.1.2"
},
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "git://github.com/bkeepers/rosie.git"
},
"main": "src/rosie.js",
"scripts": {
"test": "./node_modules/karma/bin/karma start --singleRun true",
"watch": "./node_modules/karma/bin/karma start"
}
}

@@ -16,7 +16,10 @@ # Rosie

.attr('random_seed', function() { return Math.random(); })
.attr('players', function() {
return [
Factory.attributes('player'),
Factory.attributes('player')
];
// Default to two players. If players were given, fill in
// whatever attributes might be missing.
.attr('players', ['players'], function(players) {
if (!players) { players = [{}, {}]; }
return players.map(function(data) {
return Factory.attributes('player', data);
});
});

@@ -26,4 +29,10 @@

.sequence('id')
.sequence('name', function(i) { return 'player' + i; });
.sequence('name', function(i) { return 'player' + i; })
// Define `position` to depend on `id`.
.attr('position', ['id'], function(id) {
var positions = ['pitcher', '1st base', '2nd base', '3rd base'];
return positions[id % positions.length];
});
Factory.define('disabled-player').extend('player').attr('state', 'disabled')

@@ -51,11 +60,33 @@

Factory.attributes('game') // return just the attributes
You can also define a callback function to be run after building an object:
Factory.define('coach').after(function(coach, options) { if (options.buildPlayer) { Factory.build('player', {coach_id: coach.id}; } })
Factory.define('coach')
.option('buildPlayer', false)
.sequence('id')
.attr('players', ['id', 'buildPlayer'], function(id, buildPlayer) {
if (buildPlayer) {
return [Factory.build('player', {coach_id: id})];
}
})
.after(function(coach, options) {
if (options.buildPlayer) {
console.log('built player:', coach.players[0]);
}
});
Factory.build('coach', {}, {buildPlayer: true});
## Contributing
0. Fork it
0. Create your feature branch (`git checkout -b my-new-feature`)
0. Install the test dependencies (`script/bootstrap` - requires NodeJS and npm)
0. Make your changes and make sure the tests pass (`npm test`)
0. Commit your changes (`git commit -am 'Added some feature'`)
0. Push to the branch (`git push origin my-new-feature`)
0. Create new Pull Request
## Credits
Thanks to [Daniel Morrison](http://twitter.com/danielmorrison/status/58883772040486912) for the name and [Jon Hoyt](http://twitter.com/jonmagic) for inspiration and brainstorming the idea.

@@ -7,3 +7,3 @@ describe('Factory', function() {

describe('build', function() {
describe('with a constructor', function() {
describe('with a normal constructor', function() {
var Thing = function(attrs) {

@@ -31,6 +31,42 @@ for(var attr in attrs) {

it('should run callbacks', function() {
expect(Factory.build('thing').afterCalled).toBe(true);
expect(Factory.build('thing').afterCalled).toBe(true);
});
});
describe('with a constructor with a .create() function', function() {
var afterArgs;
var createArgs;
var built;
var created;
// i.e. an Ember class
var Thing = {
create: function() {
createArgs = [].slice.call(arguments);
created = {};
return created;
}
};
beforeEach(function() {
createArgs = afterArgs = null;
Factory.define('thing', Thing).attr('name', 'Thing 1').after(function() {
afterArgs = [].slice.call(arguments);
});
built = Factory.build('thing');
});
it('should run callbacks', function() {
expect(afterArgs).toEqual([created, /* options = */undefined]);
});
it('should call .create on the class with the attributes', function() {
expect(createArgs).toEqual([{name: 'Thing 1'}]);
});
it('should return the value returned by create', function() {
expect(created).toBe(built);
});
});
describe('without a constructor', function() {

@@ -48,2 +84,7 @@ beforeEach(function() {

});
it('throws error if the factory is not defined', function() {
expect(function(){Factory.build('nothing')})
.toThrow('The "nothing" factory is not defined.');
});
});

@@ -147,2 +188,53 @@ });

});
it('should allow depending on other attributes', function() {
factory
.attr('fullName', ['firstName', 'lastName'], function(first, last) {
return first + ' ' + last;
})
.attr('firstName', 'Default')
.attr('lastName', 'Name');
expect(factory.attributes())
.toEqual({
firstName: 'Default',
lastName: 'Name',
fullName: 'Default Name'
});
expect(factory.attributes({ firstName: 'Michael', lastName: 'Bluth' }))
.toEqual({
fullName: 'Michael Bluth',
firstName: 'Michael',
lastName: 'Bluth'
});
expect(factory.attributes({ fullName: 'Buster Bluth' }))
.toEqual({
fullName: 'Buster Bluth',
firstName: 'Default',
lastName: 'Name'
});
});
it('throws when building when a dependency cycle is unbroken', function() {
factory
.option('rate', 0.0275)
.attr('fees', ['total', 'rate'], function(total, rate){ return total * rate; })
.attr('total', ['fees', 'rate'], function(fees, rate){ return fees / rate; });
expect(function(){ factory.build(); }).toThrow('detected a dependency cycle: fees -> total -> fees');
});
it('always calls dynamic attributes when they depend on themselves', function() {
factory.attr('person', ['person'], function(person) {
if (!person) { person = {}; }
if (!person.name) { person.name = 'Bob'; }
return person;
});
expect(factory.attributes({ person: { age: 55 }})).toEqual({
person: { name: 'Bob', age: 55 }
});
});
});

@@ -193,3 +285,40 @@

});
describe('option', function() {
beforeEach(function() {
factory.option('useCapsLock', false);
});
it('should return the factory', function() {
expect(factory.option('rate')).toBe(factory);
});
it('should not create attributes in the build result', function() {
expect(factory.attributes().useCapsLock).toBeUndefined();
});
it('throws when no default or value is given', function() {
factory.option('someOptionWithoutAValue');
expect(function(){ factory.attributes(); }).toThrow('option `someOptionWithoutAValue` has no default value and none was provided');
});
it('should be usable by attributes', function() {
var useCapsLockValues = [];
factory.attr('name', ['useCapsLock'], function(useCapsLock) {
useCapsLockValues.push(useCapsLock);
var name = 'Madeline';
if (useCapsLock) {
return name.toUpperCase();
} else {
return name;
}
});
// use default values
expect(factory.attributes().name).toEqual('Madeline');
// override default values
expect(factory.attributes({}, { useCapsLock: true }).name).toEqual('MADELINE');
expect(useCapsLockValues).toEqual([false, true]);
});
});
});
});

@@ -0,4 +1,13 @@

/**
* Creates a new factory with attributes, options, etc. to be used to build
* objects. Generally you should use `Factory.define()` instead of this
* constructor.
*
* @param {?Function} constructor
* @class
*/
var Factory = function(constructor) {
this.construct = constructor;
this.attrs = {};
this.opts = {};
this.sequences = {};

@@ -9,18 +18,122 @@ this.callbacks = [];

Factory.prototype = {
attr: function(attr, value) {
var callback = typeof value == 'function' ? value : function() { return value; };
this.attrs[attr] = callback;
/**
* Define an attribute on this factory. Attributes can optionally define a
* default value, either as a value (e.g. a string or number) or as a builder
* function. For example:
*
* // no default value for age
* Factory.define('Person').attr('age')
*
* // static default value for age
* Factory.define('Person').attr('age', 18)
*
* // dynamic default value for age
* Factory.define('Person').attr('age', function() {
* return Math.random() * 100;
* })
*
* Attributes with dynamic default values can depend on options or other
* attributes:
*
* Factory.define('Person').attr('age', ['name'], function(name) {
* return name === 'Brian' ? 30 : 18;
* });
*
* By default if the consumer of your factory provides a value for an
* attribute your builder function will not be called. You can override this
* behavior by declaring that your attribute depends on itself:
*
* Factory.define('Person').attr('spouse', ['spouse'], function(spouse) {
* return Factory.build('Person', spouse);
* });
*
* As in the example above, this can be a useful way to fill in
* partially-specified child objects.
*
* @param {string} attr
* @param {?Array.<string>} dependencies
* @param {*} value
* @return {Factory}
*/
attr: function(attr, dependencies, value) {
var builder;
if (arguments.length === 2) {
value = dependencies;
dependencies = null;
}
builder = typeof value === 'function' ? value : function() { return value; };
this.attrs[attr] = { dependencies: dependencies || [], builder: builder };
return this;
},
sequence: function(attr, callback) {
var factory = this;
callback = callback || function(i) { return i; };
this.attrs[attr] = function() {
factory.sequences[attr] = factory.sequences[attr] || 0;
return callback(++factory.sequences[attr]);
};
/**
* Define an option for this factory. Options are values that may inform
* dynamic attribute behavior but are not included in objects built by the
* factory. Like attributes, options may have dependencies. Unlike
* attributes, options may only depend on other options.
*
* Factory.define('Person')
* .option('includeRelationships', false)
* .attr(
* 'spouse',
* ['spouse', 'includeRelationships'],
* function(spouse, includeRelationships) {
* return includeRelationships ?
* Factory.build('Person', spouse) :
* null;
* });
*
* Factory.build('Person', null, { includeRelationships: true });
*
* Options may have either static or dynamic default values, just like
* attributes. Options without default values must have a value specified
* when building.
*
* @param {string} attr
* @param {?Array.<string>} dependencies
* @param {?*} value
* @return {Factory}
*/
option: function(opt, dependencies, value) {
var builder;
if (arguments.length === 2) {
value = dependencies;
dependencies = null;
}
if (arguments.length > 1) {
builder = typeof value === 'function' ? value : function() { return value; };
}
this.opts[opt] = { dependencies: dependencies || [], builder: builder };
return this;
},
/**
* Defines an attribute that, by default, simply has an auto-incrementing
* numeric value starting at 1. You can provide your own builder function
* that accepts the number of the sequence and returns whatever value you'd
* like it to be.
*
* Factory.define('Person').sequence('id');
*
* @param {string} attr
* @param {?function(number): *} builder
* @return {Factory}
*/
sequence: function(attr, builder) {
builder = builder || function(i) { return i; };
return this.attr(attr, function() {
this.sequences[attr] = this.sequences[attr] || 0;
return builder(++this.sequences[attr]);
});
},
/**
* Sets a post-processor callback that will receive built objects and the
* options for the build just before they are returned from the #build
* function.
*
* @param {function(object, ?object)} callback
* @return {Factory}
*/
after: function(callback) {

@@ -31,17 +144,169 @@ this.callbacks.push(callback);

attributes: function(attrs) {
attrs = attrs || {};
for(var attr in this.attrs) {
if(!attrs.hasOwnProperty(attr)) {
attrs[attr] = this.attrs[attr]();
/**
* Sets the constructor for this factory to be another factory. This can be
* used to create more specific sub-types of factories.
*
* @param {Factory}
* @return {Factory}
*/
inherits: function(parentFactory) {
this.construct = function(attributes, options) {
return Factory.build(parentFactory, attributes, options);
};
return this;
},
/**
* Builds a plain object containing values for each of the declared
* attributes. The result of this is the same as the result when using #build
* when there is no constructor registered.
*
* @param {?object} attributes
* @param {?object} options
* @return {object}
*/
attributes: function(attributes, options) {
attributes = attributes || {};
options = this.options(options);
for (var attr in this.attrs) {
this._attrValue(attr, attributes, options, [attr]);
}
return attributes;
},
/**
* Generates a value for the given named attribute and adds the result to the
* given attributes list.
*
* @private
* @param {string} attr
* @param {object} attributes
* @param {object} options
* @param {Array.<string>} stack
* @return {*}
*/
_attrValue: function(attr, attributes, options, stack) {
if (!this._alwaysCallBuilder(attr) && attributes.hasOwnProperty(attr)) {
return attributes[attr];
}
var value = this._buildWithDependencies(this.attrs[attr], function(dep) {
if (options.hasOwnProperty(dep)) {
return options[dep];
} else if (dep === attr) {
return attributes[dep];
} else if (stack.indexOf(dep) >= 0) {
throw new Error('detected a dependency cycle: '+stack.concat([dep]).join(' -> '));
} else {
return this._attrValue(dep, attributes, options, stack.concat([dep]));
}
});
attributes[attr] = value;
return value;
},
/**
* Determines whether the given named attribute has listed itself as a
* dependency.
*
* @private
* @param {string} attr
* @return {boolean}
*/
_alwaysCallBuilder: function(attr) {
var attrMeta = this.attrs[attr];
return attrMeta.dependencies.indexOf(attr) >= 0;
},
/**
* Generates values for all the registered options using the values given.
*
* @private
* @param {object} options
* @return {object}
*/
options: function(options) {
options = options || {};
for (var opt in this.opts) {
options[opt] = this._optionValue(opt, options);
}
return attrs;
return options;
},
build: function(attrs) {
var result = this.attributes(attrs);
return this.construct ? new this.construct(result) : result;
/**
* Generates a value for the given named option and adds the result to the
* given options list.
*
* @private
* @param {string}
* @param {object} options
* @return {*}
*/
_optionValue: function(opt, options) {
if (options.hasOwnProperty(opt)) {
return options[opt];
}
var optMeta = this.opts[opt];
if (!optMeta.builder) {
throw new Error('option `'+opt+'` has no default value and none was provided');
}
return this._buildWithDependencies(optMeta, function(dep) {
return this._optionValue(dep, options);
});
},
/**
* Calls the builder function with its dependencies as determined by the
* given dependency resolver.
*
* @private
* @param {{builder: function(...[*]): *, dependencies: Array.<string>}} meta
* @param {function(string): *} getDep
* @return {*}
*/
_buildWithDependencies: function(meta, getDep) {
var deps = meta.dependencies;
var self = this;
var args = deps.map(function(){ return getDep.apply(self, arguments); });
return meta.builder.apply(this, args);
},
/**
* Builds objects by getting values for all attributes and optionally passing
* the result to a constructor function.
*
* @param {object} attributes
* @param {object} options
* @return {*}
*/
build: function(attributes, options) {
var result = this.attributes(attributes, options);
var retval = null;
if (this.construct) {
if (typeof this.construct.create === 'function') {
retval = this.construct.create(result);
} else {
retval = new this.construct(result);
}
} else {
retval = result;
}
for (var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i](retval, options);
}
return retval;
},
/**
* Extends a given factory by copying over its attributes, options,
* callbacks, and constructor. This can be useful when you want to make
* different types which all share certain attributes.
*
* @param {string} name The factory to extend.
* @return {Factory}
*/
extend: function(name) {

@@ -51,11 +316,14 @@ var factory = Factory.factories[name];

if (this.construct === undefined) { this.construct = factory.construct; }
for(var attr in factory.attrs) {
if(factory.attrs.hasOwnProperty(attr)) {
for (var attr in factory.attrs) {
if (factory.attrs.hasOwnProperty(attr)) {
this.attrs[attr] = factory.attrs[attr];
}
}
for (var opt in factory.opts) {
if (factory.opts.hasOwnProperty(opt)) {
this.opts[opt] = factory.opts[opt];
}
}
// Copy the parent's callbacks
for(var i = 0; i < factory.callbacks.length; i++) {
this.callbacks.push(factory.callbacks[i]);
}
this.callbacks = factory.callbacks.slice();
return this;

@@ -67,2 +335,10 @@ }

/**
* Defines a factory by name and constructor function. Call #attr and #option
* on the result to define the properties of this factory.
*
* @param {string} name
* @param {?function(object): *} constructor
* @return {Factory}
*/
Factory.define = function(name, constructor) {

@@ -74,14 +350,29 @@ var factory = new Factory(constructor);

Factory.build = function(name, attrs, options) {
var obj = this.factories[name].build(attrs);
for(var i = 0; i < this.factories[name].callbacks.length; i++) {
this.factories[name].callbacks[i](obj, options);
}
return obj;
/**
* Locates a factory by name and calls #build on it.
*
* @param {string} name
* @param {object} attributes
* @param {object} options
* @return {*}
*/
Factory.build = function(name, attributes, options) {
if (!this.factories[name])
throw new Error('The "'+name+'" factory is not defined.');
return this.factories[name].build(attributes, options);
};
Factory.buildList = function(name, size, attrs, options) {
/**
* Builds a collection of objects using the named factory.
*
* @param {string} name
* @param {number} size
* @param {object} attributes
* @param {object} options
* @return {Array.<*>}
*/
Factory.buildList = function(name, size, attributes, options) {
var objs = [];
for(var i = 0; i < size; i++) {
objs.push(Factory.build(name, attrs, options));
for (var i = 0; i < size; i++) {
objs.push(Factory.build(name, attributes, options));
}

@@ -91,4 +382,12 @@ return objs;

Factory.attributes = function(name, attrs) {
return this.factories[name].attributes(attrs);
/**
* Locates a factory by name and calls #attributes on it.
*
* @param {string} name
* @param {object} attributes
* @param {object} options
* @return {object}
*/
Factory.attributes = function(name, attributes, options) {
return this.factories[name].attributes(attributes, options);
};

@@ -95,0 +394,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc