Giter Club home page Giter Club logo

rosie's People

Contributors

bkeepers avatar dependabot[bot] avatar dishwasha avatar dj0nes avatar eventualbuddha avatar freshtonic avatar frozenfung avatar hpcsc avatar jonstorer avatar kylezeeuwen avatar landonschropp avatar lexicality avatar maspwr avatar mdarnall avatar mewdriller avatar mkuklis avatar okuramasafumi avatar rodmax avatar rustytoms avatar shilman avatar subakva avatar uniservo1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rosie's Issues

add optional singleton methods

I would really like a batteries included way to quickly use a factory as a fixture, and reset it when I want to. I think something like Factory.singleton(name, attributes, options), myFactory.singleton(attributes, options) and myFactory.reset() is a simple enough contract that dont effect the regular use-case of rosie.

One can of course use rosie in a singleton manner without these changes, but I think it would be a valuable addition to the lib. If the maintainers like the idea, I would happily build up a pr.

Build with nested objects

I have an Factory that has attrs which is being passed this object:

fetching: false,
  submittedRequest: null,
  error: null,
  alerts: {
    editFailure: {
      display: false,
      canBeToggled: true,
      color: "danger",
      title: "Error",
      children:
        "Failed to edit. Please try again. If you continue to receive this error, " +
        "please contact support."
    }
  }

Is it possible to run factoryName.build(...) while only changing the value of display to true without importing and extending the object?

Less restrictive license?

Any chance you'd consider switching from GPL 2 to an LGPL or MIT license? I'd like to use rosie at work but that would mean we'd have to release our software under GPL as well.

Sequences and calculated attributes don't work in buildLists when attributes are specified

This is a great library that I have found very useful! However, I often like to create lists of objects with unique attributes. Unfortunately, when attributes are set using buildList the sequences no longer work. All the objects have identical values, even if they are sequences or calculated using functions. This is because the attributes have been mutated and include all the attributes of the first object. It appears that these attributes have been passed in, and are set on all subsequent objects. This only happens if attributes are specified, because if attributes are not specified the attribute variable here is set to a new, empty object for each object created.

To reproduce the error:

Factory.define('test')
  .sequence('id')
  .attr('random', function() {
    return Math.floor(Math.random() * 100);
  })
  .attr('constant', 'same');

// this produces 4 objects with different ids and 'random' attributes 
Factory.buildList('test', 4);

// this produces 4 objects with identical attributes
Factory.buildList('test', 4, {constant: 'different'});

I am submitting a pull request with a simple fix

Error occurs when try to build using dependent attributes that are not defined

I want to have a factory as such:

var settingsForThemes = {
   'long': {
       duration: 30,
       content: 'a longer theme'
   },
   'short': {
       duration: 10,
       content: 'a shorter theme'
   }
}

var factory = new Factory()
                    .attr('settings', ['theme'], function(theme) {
                         return settingsForThemes[them]
                     }) 

When I try to build using this factory, by passing it a theme:

var built = factory.build({theme: 'long'})

You get an error:

TypeError: Cannot read property 'dependencies' of undefined
at Object.Factory._alwaysCallBuilder (.../node_modules/rosie/src/rosie.js:230:20)

If I define the attribute theme on the factory, the error doesn't occur. However, I don't want to define that attribute, because it should never exist on the object. If extra attributes are added to the object that shouldn't be there, other parts of the program throw an error.

You should be able to define attributes that depend on other attributes that haven't necessarily been defined on the factory.

Allow factories to set the builder method

Some common ones might be

function buildByPassingArguments(klass, properties) {
  return new klass(properties);
}

function buildBySettingProperties(klass, properties) {
  var result = new klass();
  Object.keys(properties).forEach(function(property) {
    result[property] = properties[property];
  });
  return result;
}

function buildForEmber(klass, properties) {
  return klass.create(propertes);
}

Support or Recommended Alternative

Just writing to see if there is any intention of long term support or a recommended alternative to this library. The factory pattern is something I like but if it has fallen out of favor I would like to see what would be a nice alternative.

Feature Request: Support promises

Hey folks,

we would like to use Rosie.js to setup a database in a nodejs environment. The API of our database connection returns promises and so do our factories now. I have seen in the code that Rosie.js never waits for promises.

Would you accept a breaking change and return a promises for instance.build and instance.buildList?

Or would you accept a feature like this: Factory.defineAsync('model') which would then a factory that returns a promise for the two methods above?

We work around this issue by never having more than one after callback and never using buildList.

I would be up to contribute a feature as mentioned above and would like to get an update about the status of this library.

Prettier

We should format project code with prettier.

"Factory" not exported in browser

In a browser environment, rosie.js only exports its global Factory object if it's executed at the top level scope. But when it's not, for instance because it's wrapped like so ...

loader.register('rosie', function(
  // ... rosie.js contents ...
});

... then there is no way to get at the Factory object. That's because Factory is defined using var and never exported to window.

Could we have some code that tacks it onto the global window object? (Not sure what the most idiomatic way is, or I would've sent a pull request.)

Does this remain in active development?

I want to use a factory library for MongoDB in NodeJS. But I want this factory library to be able to work with both the Node MongoDB driver (which I am currently using) as well as an ORM like Mongoose (which I am not currently using). Does this work with both? Also will this work with SQL ORMs? Is this in active maintenance?

Using same factory overwrites multiple assigned instances

  import BookFactory from '../factories/Book';
  const entry1 = BookFactory.build({ id: '1', attributes: { title: 'Journal' } }); 
  const entry2 = BookFactory.build({ id: '2', attributes: { title: 'Book'} });

I've been seeing this weird behaviour for awhile now and trying to understand if it's Rosie or something else. I am trying to create 2 separate objects, from the same factory, but with different attributes. Hence the code above.

But, after defining entry2, entry1 title gets's overwritten to Book as well... Interesting id gets assigned correctly, but nested attributes is what causing the problem...

Unregistered pattern allows options through as params

Unclear if this library is still supported, but just in case:

Using the 'unregistered' pattern from the README for node, in a pretty simple way:

exports.Task = new Factory()
  .sequence('name', n => `factory-task-${n}`)
  .attrs({
    expectedDuration: 15,
    recurrence: null
  })
  .option('tagCount', 2)
  .attr('tags', ['tagCount'], function(tagCount) {return [{name: 'awesome'}, {name: 'tag'}]})

However, even though options are not supposed to carry through to the output object, they do. Testing a graphQL query:

variables = {task: factory.Task.build({tagCount: 2})}

The test run returns:

[GraphQLError: Variable "$task" got invalid value {"tagCount":2,"name":"factory-task-2","expectedDuration":15,"recurrence":null,"tags":[{"name":"awesome"},{"name":"tag"}]}; Field "tagCount" is not defined by type TaskInput.]

This seems pretty straightforward, so I'm not sure what I'm missing.

Provide .after callback

I modified Rosie to provide a simple .after callback that takes two arguments, the object just built and an options object. The options object is passed through from Factory.build. Sample usage:

Factory.define('Parent').after(function(obj, options) {
  var attrs = options.child;
  attrs.parent_id = obj.id;
  obj.child = Factory.build('Child', attrs);
});

Factory.build('Parent', {}, {child: {name: 'Jr.'}});

If you would be interested in including something like this, I'll add some tests, docs, and open a pull request.

Nested buildList() doesn't create new arrays

Hi rosie team,

I'm a little new to rosie, but have been using FactoryBot for a long time. So forgive me if I'm misunderstanding how things work or if this is expected behaviour.

The basic behaviour I'm seeing is that when you have a parent factory that has a collection of children - the array of children that gets generated by buildList doesn't generate a new list every time.

Where this really becomes an issue is if you have a beforeEach say with Jest specs - where you're regenerated the parent everytime - the children don't seem to get regenerated. So if you edit the children, it gets carried over to other specs.

Here's some code that illustrates my point better:

const Factory = require('rosie').Factory;

const child = Factory.define('child')
  .sequence('id')
  .attrs({
    name: 'test'
  });

const parent = Factory.define('parent')
  .sequence('id')
  .attrs({
    children: child.buildList(3)
  });

let parent1 = parent.build();
let parent2 = parent.build();

console.log("Are both parents referentially the same? ");
console.log(parent1 === parent2);
console.log("Are both parent.children referentially the same?");
console.log(parent1.children === parent2.children);

console.log("IDs before change: parent1.children[0] has an ID of " + parent1.children[0].id + " and parent2.children[0] has an ID of " + parent2.children[0].id); 

/* ONLY CHANGE parent1 */
parent1.children[0].id = 100;

console.log("IDs before change: parent1.children[0] has an ID of " + parent1.children[0].id + " and parent2.children[0] has an ID of " + parent2.children[0].id); 

let childrenList1 = child.buildList(2);
let childrenList2 = child.buildList(2);

console.log("Are both standalone children lists referentially the same?");
console.log(childrenList1 === childrenList2);

And here's the output

    Are both parents referentially the same? 
    false

    Are both parent.children referentially the same?
    true

    IDs before change: parent1.children[0] has an ID of 1 and parent2.children[0] has an ID of 1

    IDs before change: parent1.children[0] has an ID of 100 and parent2.children[0] has an ID of 100


    Are both standalone children lists referentially the same?
    false

So as you see, if you wanted to create two parents, and then independently edit each of its children - you wouldn't be able to.

Again, let me know if this is expected behaviour and if there is a workaround?

Thanks! Great library!

Depends Syntax Issue

Hi there,

I'm trying to write a property that depends on another field based on the first example in the readme, but it doesn't seem to be working as expected.

Factory.define('photo')
  .sequence('id')
  .attr('title', function() {return Faker.Lorem.sentence()})
  .attr('color', 'ff00ff'),
  .attr('url', [ 'color' ], function(color) {
    return 'http://placehold.it/600/' + color
  })

I'd expect to see

{
      "id": 2,
      "title": "aut recusandae a eveniet et laborum sunt",
      "color": "ff00ff",
      "url": "http://placehold.it/600/ff00ff"
}

But instead I'm getting

{
      "id": 2,
      "title": "aut recusandae a eveniet et laborum sunt",
      "color": "ff00ff",
      "url": [
        "color"
      ],
}

What am I doing wrong here?

How to build recursively?

Hi, I have a case where I want to create some items recursively. Like this:

[
    { id: '1', label: 'Item 1', children: [] },
    { id: '2', label: 'Item 1', children: [
        { id: '2-1', label: 'Item 2-1', children: [] },
        { id: '2-2', label: 'Item 2-1', children: [
            { id: '2-1-1', label: 'Item 2-1-1', children: [] },
        ] },
    ] },
]

I tried with:

export const MenuValueFactory = new Factory<Value>()
  .sequence('label', (i) => `Menu Item ${i}`)
  .sequence('slug', (i) => `menu-item-${i}`)
  .attrs({
    id: () => uuidv4(),
    children: () => MenuValueFactory.buildList(3),
  });

Then I got RangeError: Maximum call stack size exceeded (not surprise ๐Ÿ™ˆ )

I tried to Google and search issues here but have no luck. Can someone help, please?

Thanks so much.

Conditional properties - Proposal

It would be useful for some of my use cases to have the option to not return some properties on the object that is generated. Right now my specific use case is for generating a list of menu links. Our menu links can either be nested and have sub links or be normal links. Currently I have something working with the current code base, doing

const { Factory } = require('rosie');
const Faker = require('faker');

module.exports = new Factory()
    .sequence('id')
    .option('menuType', () => Faker.random.number(100))
    .option('subMenuChance', .5)
    .attr('label', () => Faker.random.word())
    .attr('sub', ['menuType', 'subMenuChance'], (menuType, subMenuChance) => {
        return menuType/100 <= subMenuChance
            ? module.exports.buildList(Faker.random.number(10), {}, { subMenuChance: Math.floor(subMenuChance * 50) / 100 })
            : undefined;
    })
    .attr('href', ['menuType', 'subMenuChance'], (menuType, subMenuChance) => {
        return menuType/100 > subMenuChance
            ? Faker.random.words().replace(/ /g, '/').toLowerCase()
            : undefined;
    })
    .after(menu => {
        if (typeof menu.sub === 'undefined') delete menu.sub
        else delete menu.href;
    });

I needed the after hook because our assertion library chai was making the assertion that an undefined property did not equal a property that does not exist (copying via JSON.stringify and parse removes all undefined properties).

I would propose a similar solution to the above and depending on the return value the attribute would not be added to the built object. Whether that be undefined (may break peoples code if they actually expect undefined to be returned) or a symbol that is defined on the global object which the user then returns (better as not possible for collisions with existing user data).

I can look at implementing and submitting a pull request if there is any interest.

Support rosie Flow types on flow-typed

Bringing rosie support to flow-typed in order to be able to check conformance of factories with Flow types would really helpful for maintaining heavy-weight projects.

I just tried to give it a try on a playground project (cf. this PR), but I'm struggling a lot to make it work in a satisfying/helpful way.

Has anyone out there given it a try or willing to collaborate on this subject?

Stack overflow on option that depends on itself

const Factory = require("rosie").Factory;
const myFactory = new Factory().option(
  "wantThat",
  ["wantThat"],
  (wantThat) => {
    if (wantThat == null) {
      return "want it anyway";
    }
    return wantThat;
  }
);
myFactory.build(); // Stack overflow

It works with an attribute, however. The documentation doesn't warn against that case, which makes me think it's an unintended behaviour.

Between 0.2.0 and 0.30, sequences are no longer inherited correctly

The following code produces different output in versions 0.2.0 and 0.3.0.

Factory = require('rosie').Factory;

Factory.define('Base').sequence('id');
Factory.define('Derived1').extend('Base');
Factory.define('Derived2').extend('Base');

derived1 = Factory.build('Derived1');
derived2 = Factory.build('Derived2');

console.warn('derived1.id', derived1.id);
console.warn('derived2.id', derived2.id);

Output for 0.2.0

derived1.id 1
derived2.id 2

Output for version 0.3.0

derived1.id 1
derived2.id 1

The sequence function and the extend function are different between those two versions, but I'm finding it difficult to reason about the breakage.

I think the 0.2.0 behaviour is the correct behaviour. For example, if I had a User factory and an AdminUser factory extending it, I would expect the sequence to be shared rather than reset so that they can be inserted into the same table.

Edit:

It actually looks like sequence was changed to refer to this instead of the original factory, which will cause the sequence to be reset. I'll submit a PR shortly.

Nesting factories

It would be cool if you can nest factories. So write .attr('myName', myFactory) would be equivalent to .attr('myName', () => myFactory.build())

`define()` undefined

Hi,

I'm attempting to use Rosie and running into the following error:

Factory.define('User', User)
        ^
TypeError: undefined is not a function
    at Object.<anonymous> (/Users/jamesdixon/Projects/scout/platform/test/index.js:13:9)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

The code is as follows:

var Factory = require('rosie');

var User = function () {
  return {
    firstName: '',
    lastName: '',
    email: '',
    password: '',
    active: false
  };
};

Factory.define('User', User)
  .sequence('id')
  .attr('firstName', 'john')
  .attr('lastName', 'doe')
  .sequence('email', function(i) {
    return 'test_' + i + '@gmail.com';
  })
  .attr('password', '12345678')
  .attr('active', true);

I'm running node 0.12.2 and running the file via node index.js.

Any suggestions?

Best,
James

Lazy attributes - proposal

Hi guys, I really like the project, but I think It could use some "lazy attributes".
Let's say we want to mock the DB & we need to have consistent relations (A->B & B->A)
Therefore it would be nice to be able to create relations only after the related objects have been created. Otherwise we don't know how many related object there would be or what ids would they have. Also we would have to take care of handling relations ourselves.

I have created some wrappers (using underscore.js), that I found useful:

https://gist.github.com/4301663

since there are dynamically computed properties in JS, I have no idea how else could one solve this problem?

GraphQL, Circular dependencies cause stack errors

I am using a GraphQL API which requires explicit declaration of data requirement. It also allows for types to create circular references for this reason.

While not necessarily an issue with this library (It's a problem with factories in general), I would be interested in knowing the recommended way of handling model data which could contain circular types.

An example which will throw stack errors:

Factory.define('organization')
  .sequence('id')
  .attr('users', ['users'], (users) => {
    if (!users) users = [{}, {}]
    return users.map(user => {
      return Factory.attributes('user', user)
    })
  })

Factory.define('user')
  .sequence('id')
  .attr('organizations', ['organizations'], (organizations) => {
    if (!organizations) organizations = [{}, {}]
    return organizations.map(organization => {
      return Factory.attributes('organization', organization)
    })
  })

Any help highly appreciated!

npm package

Hey, this module is a real life-saver for me, but there's no npm package?!

Allow Factory instance to define sub-factories

I would like to be able to define a set of factories in a Factory instance, and not just by using the singleton.

Example:

// fixtures/car.ts

const carFixture = new Factory();

carFixture.define('car:slow').attrs({
// ...
});

carFixture.define('car:fast').attrs({
// ...
});

export default const CarFixture = carFixture;

Use case:
A directory of models, each having a fixture:

/models
- car.ts
- car.fixture.ts
- engine.ts
- engine.fixture.ts
- boat.ts
- boat.fixture.ts

In the current implementation, all factories are loaded into the Factory singleton once a directory is imported.

If I would like to test, say, car.ts, which uses an engine fixture, I would not like the factories of boat.fixture to be loaded.

Changelog?

Hey guys, love the work you're doing with this library. Is there a changelog somewhere? I see a 2.0.1 release but no context for what changed without looking through the commit log.

Option to spread attributes to constructor

Imagine a simple class that looks like this:

export class MyKlass {
  constructor(
    public foo,
    public bar
  ) {}
}

I'd imagine being able to create an instance of this class like this:

Factory.define('MyKlass', MyKlass)
  .attr('foo', 'something')
  .attr('bar', 'something else');

However, as implemented today, this would pass both attributes into the constructor as foo and you'd end up with an object that looks like this:

Factory.build('MyKlass');
MyKlass {
  foo: { foo: 'something', bar: 'something else' },
  bar: undefined
}

It'd be nice if there were a way to spread the object, so you could build and get the following result.

Factory.build('MyKlass');
MyKlass {
  foo: 'something',
  bar: 'something else'
}

Add an option to merge properties instead of overriding?

E.g. if you can pass a merging property to .build() that will merge the attributes with the ones from the factory. E.g. If the factory would return {foo: {bar: false, baz: true}} and you pass {foo: {baz: 3}} that it will return {foo: {bar: false, baz: 3}}

how to exclude attributes when inheriting?

let's say I have this example factory definition

Factory.define('example')
.sequence('id')
.attr('ignored', false)
.attr('taskId', '666')

now I want to define another factory and inherit from 'example' factory
but removing some attributes

Factory.define('second-example').extend('example')
.removeAttr('ignored')

the .removeAttr method does not exist, is there such concept? or how could I achieve my goal?

Update NPM version

It looks like the version of Rosie that's on NPM doesn't have the buildList functionality yet. It would be great if it could be updated.

Build reuses the same values each time

If I have a User factory:

import {Factory} from 'rosie';
import faker from 'faker';
import Patient from './patient';

let User = new Factory().sequence('id');
User.attr('uuid', () => faker.random.uuid())
    .attr('patients', (patients) => {
        return Patient.buildList(2);
    });


export default User;

and a Patient factory:

import {Factory} from 'rosie';
import faker from 'faker';

let Patient = new Factory().sequence('id');
Patient.attr('uuid', () => faker.random.uuid())
    .attr('first_name', faker.name.firstName())
    .attr('last_name', faker.name.lastName());

export default Patient;

I want the user.patients to be an array with unique names. But the names are the same:

{ id: 1,
  uuid: '27610653-59d3-4961-9266-825684828328',
  patients:
   [ { id: 1,
       uuid: 'a4af8777-cea1-4d61-af45-1b5c56ba652f',
       first_name: 'Mabelle',
       last_name: 'Rowe' },
     { id: 2,
       uuid: 'c61739d3-193e-49bd-b63c-a2e7a638e9a9',
       first_name: 'Mabelle',
       last_name: 'Rowe' } ] }

How can I accomplish this?

Should .build() invoke this.construct.create()?

I'm trying to migrate from rosie 0.2.0 to 0.3.1.

The object I'm creating a factory for has a .create() method, but this method is async (it's a database object). Because this .create() is async, retval on line 293 will always be undefined, which results in Factory.build() always returning undefined instead of an object.

This seems like a bug. Can rosie support asynchronous .create()?

Cannot override attributes when associating with existing class

I am unable to override the attributes when I associate the factory with an existing class. Here is my code:
This is my class: car.model.js

class Car {
    constructor() {
        this.year = '2017';
        this.make = 'Toyota';
        this.model = 'Corolla';
        this.color = 'Black';
    }
}
module.exports = Car;

This is my factory: car.factory.js

var Factory = require('rosie').Factory;
var Car = require('../models/car.model.js');
CarFactory = Factory.define('car', Car);
module.exports = CarFactory;

This is my test file: test.js

var CarFactory = require('../../lib/factories/car.factory.js');
var test = CarFactory.build({color: 'Blue'});

test is an object with the same default values of the class. The override seems to fail. Perhaps I am using this incorrectly.

No declared license

Any chance you could add a formal license to this? Guessing you mean to have this under MIT/BSD like your other projects, but would be nice to be certain.

Middleware

Same comment as on PR #22. Added here to keep track of it separately from PR #22.

I would like to add better callbacks to rosie to act as middlewares. So I am able to use rosie to create database records with associations.

These are my current work arounds:

// setup
var mongoose  = require("mongoose")
var userModel = mongoose.model("User", new mongoose.Schema({ name: String }));
var Factory   = require("rosie").Factory;

// #1
Factory.define("User", userModel).attr("first_name", "Steve")

var user = Factory.build("User", { name: "Greg" });
user.save(function(err, user){
  // perform test
});

// #2
var saveIfCallback = function(newRecord, options){
  if(options.callback && typeof options.callback == "function"){
    newRecord.save(callback);
  }
}

Factory.define("Record").after(saveIfCallback);
Factory.define("User", userModel).extend("Record").attr("name", "Steve")

Factory.build("User", { name: "Greg" }, { callback: function(err, user){
  // perform test
}})

// #3
var clientModel      = mongoose.model("Client",      new mongoose.Schema({ name: String, key: String, secret: String }));
var accessTokenModel = mongoose.model("AccessToken", new mongoose.Schema({ token: { type: String, "default": genToken }, client_id: ObjectId, user_id: ObjectId }));

var saveIfCallback = function(newRecord, options){
  if(options.callback && typeof options.callback == "function"){
    newRecord.save(callback);
  }
}

Factory.define("Record").after(saveIfCallback);

Factory.define("User",        userModel).extend("Record").attr("first_name", "Steve")
Factory.define("AccessToken", accessTokenModel).extend("Record")
Factory.define("Client",      clientModel).extend("Record")

Factory.build("Client", { }, { callback: function(err, client) {
  // Note the {} as the 2nd arg cannot be removed
  if(err){ throw err }
  Factory.build("User", { }, { callback: function(err, user) {
    // Again the {} as the 2nd arg cannot be removed
    if(err){ throw err }
    Factory.build("AccessToken", { client_id: client.id, user_id: user.id }, { callback: function(err, accessToken) {
      if(err){ throw err }
      // test user who needed an access token
    }});
  }});
}});

But what I would really like is:

Factory.define("Record")
  .onBuild(buildAssociations)
  .onCreate(persistAssociations)
  .onCreate(persistRecord)

Factory.define("Client").extend("Record")
  .attr("name", "Acme")
  .many("access_tokens", "AccessToken")

Factory.define("AccessToken").extend("Record")
  .belongsTo("client", "Client")
  .belongsTo("user",   "User")

Factory.define("User", userModel).extend("Record")
  .many("access_tokens", "AccessToken")
  .many("posts", "Post", [ { /* attrs */ } , { /* other attrs */ })

Factory.create("User", function(err, user){
  // notice create mmethod & attributes variable not needed!!
  var access_token = user.access_tokens[0]
  var client       = access_token.client
  var params       = { key: client.key, secret: client.secret, access_token: access_token.token }

  request('/my/posts', params, function(err, posts){
    // posts made from factories!
    // expect(posts).to.equal(user.posts);
  });
})

Create sequences based on other attributes, or options and attributes passed in through `build`

When defining an attribute, you can use an existing attribute or option to calculate a value. For example, this factory creates an object that uses a startTime (using moment.js) to create a day with a random count associated with it:

Factory.define("day")
  .option("startTime", moment().subtract("days", 30))
  .attr("count", function() {
    return Math.floor(Math.random() * 100);
  })
  .attr("time", ["startTime"], function(startTime) {
    return moment(startTime).add("days", 1).valueOf();
  });

var startTime = moment().subtract("days", 10);
Factory.build("day", {}, {startTime: startTime});
// Object {count: 26, time: 1407607275019}

However, if I want to create a list of "days", which use the startTime as a reference point I cannot do that. Only attributes can list dependencies, and they do not receive the sequence, and sequences cannot access dependencies. It would be nice if I could do this:

Factory.define("day")
  .option("startTime", moment().subtract("days", 30))
  .attr("count", function() {
    return Math.floor(Math.random() * 100);
  })
  .sequence("time", ["startTime"], function(i, startTime) {
    return moment(startTime).add("days", i - 1).valueOf();
  });

var startTime = moment().subtract("days", 10);
Factory.buildList("day", 10, {}, {startTime: startTime});
// [{count: 26, time: 1407607275019}, {count: 53, time: 1407693675019}...{count: 12, time: 1408384875019}]

An additional problem associated with this is that the iterator passed into sequence might not start at 1, depending on if the factory has already been used. So associated with this would be to have a way to increment starting at 0 or 1 for an attribute or a property each time buildList is called. The sort of hacky way that I got around this was by creating the "startTime" option, and mutating it. This was only possible because startTime is an object, rather than a string or a number.

Factory.define("day")
  .option("startTime")
  .attr("count", function() {
    return Math.floor(Math.random() * 100);
  })
  .attr("time", ["startTime"], function(startTime) {
    var time = startTime.valueOf();
    startTime.add("days", 1);
    return time;
  });

var startTime = moment().subtract("days", 10);
Factory.buildList("day", 36, {}, {startTime: startTime}); // startTime is mutated
// [{count: 26, time: 1407607275019}, {count: 53, time: 1407693675019}...{count: 12, time: 1408384875019}]

This has the unfortunate side effect of changing the value that is passed in as the initial "startTime" which may cause trouble for someone else using this later as I would consider this unexpected behavior. Of course, trouble can be simply avoided by cloning the object passed in as the startTime, Factory.buildList("day", 36, {}, {startTime: moment(startTime)}); but this requires the knowledge that the value will be mutated. So I think this is not really a good solution because it won't work on immutable objects, and it mutates mutable objects.

"builder.apply is not a function" error when user makes subtle mistake

Using rosie v1.3.0 with node v6.1.0.

I was just writing up practice docs for a project that uses rosie and I found a scenario where user error could result in a subtle bug.

If I define a factory:

  • with a sequence and erroneously pass a default value to it (i.e., .sequence('myid', 1)
  • with an attribute that relies on that sequence ID

Then when I invoke factory.build with all defaults it will cause a builder.apply is not a function error.

I think it could be a bug in the handling of calls to sequence, but in any case I thought I would write it up for posterity:

var Factory = require('rosie').Factory;
var dump = function(o) { console.log(JSON.stringify(o, {}, 2)) }

// //Case 1 - Fail
Factory.define('failingSequenceExample')
  .sequence('propertyId', 1) // HERE!!! Look here where I pass 1 to the sequence
  .attr('reference', ['propertyId'], function(propertyId) { return `REFPROP${propertyId}` })
  .attr('bedroomNr', 1)

try {
  dump(Factory.build('failingSequenceExample'));
}
catch (err) {
  console.log("This is the apply error, caused because you must explicitly set anything you use as a dependency")
  console.log(err.message)
}
// OUTPUT:
// This is the apply error, caused because you must explicitly set anything you use as a dependency
// "builder.apply is not a function"


// //Case 2 - Pass
Factory.define('passingSequenceExample')
  .sequence('propertyId') // HERE! There is no default provided, and now it works
  .attr('reference', ['propertyId'], function(propertyId) { return `REFPROP${propertyId}` })
  .attr('bedroomNr', 1)

dump(Factory.build('passingSequenceExample'));
dump(Factory.build('passingSequenceExample'));
// OUTPUT:
//{
//  "propertyId": 1,
//  "reference": "REFPROP1",
//  "bedroomNr": 1
//}
//{
//  "propertyId": 2,
//  "reference": "REFPROP2",
//  "bedroomNr": 1
//}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.