ember-data-factory-guy 0.8.3 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6cec722b2159388b70506a21e8a83f4023b7af30
4
- data.tar.gz: 35e7f0e16e331d54fca3e5e9d783b411db4b459d
3
+ metadata.gz: a62de689fff74d16df3fb9b94729bc70cc8a6732
4
+ data.tar.gz: 6efd7d089935309736957886b63d9b3494e4f02f
5
5
  SHA512:
6
- metadata.gz: 935baa72d03b250f8164f8861a8d0ca46d97e4acd731c2088222a71fa69f7e97f1e2cf4823e0ec4f04af0f2b035843cce9b9b86c3080689678511ecf067b1bf8
7
- data.tar.gz: 499d2a19b0118865c5fff04b64ea9258f4ff6f190aaf8f7defd0cce946b9e7443ca34f7b12f9ced4cf8a77afe63b78231a409a23da4a947d5fdf1ae33f53e7a4
6
+ metadata.gz: 04df59360622690f76bc7980592b77262b32190d4420aad6d99084d579abaae5fa94e77e18f21767ed32c4537bd0ad35b07f484ada08b388e873e9ffb2c7386a
7
+ data.tar.gz: b3ed6abf13aaf67a201a9d711defe20d3695f3cedcb9cb98f6a9d79190ac9125514f376a5773f95078798574add538a4ae2b968e4fe7a689eb5babafdd207106
data/Gruntfile.js CHANGED
@@ -6,6 +6,16 @@ module.exports = function(grunt) {
6
6
 
7
7
  concat: {
8
8
  dist: {
9
+ src: [
10
+ 'src/sequence.js',
11
+ 'src/model_definition.js',
12
+ 'src/factory_guy.js',
13
+ 'src/store.js',
14
+ 'src/factory_guy_test_mixin.js',
15
+ ],
16
+ dest: "dist/ember-data-factory-guy.js"
17
+ },
18
+ amd: {
9
19
  src: [
10
20
  'src/prologue.js',
11
21
  'src/sequence.js',
@@ -15,7 +25,7 @@ module.exports = function(grunt) {
15
25
  'src/factory_guy_test_mixin.js',
16
26
  'src/epilogue.js'
17
27
  ],
18
- dest: "dist/ember-data-factory-guy.js"
28
+ dest: "dist/amd/factory-guy.js"
19
29
  },
20
30
  extra: {
21
31
  src: ['src/has_many.js'],
data/README.md CHANGED
@@ -9,18 +9,25 @@ of ember-data-factory-guy.
9
9
  - Versions:
10
10
  - 0.6.4 -> ember-data-1.0.0-beta.8 and under
11
11
  - 0.7.1.1 -> ember-data-1.0.0-beta.10
12
- - 0.8.2 -> ember-data-1.0.0-beta.11
13
- - 0.8.2 -> ember-data-1.0.0-beta.12
12
+ - 0.8.3 -> ember-data-1.0.0-beta.11
13
+ - 0.8.3 -> ember-data-1.0.0-beta.12
14
14
 
15
- **For versions ( 0.7.1 -> 0.8.2 ), support for the fixture adapter is currently broken.**
15
+ **For versions ( 0.7.1 -> 0.8.3 ), support for the fixture adapter is currently broken.**
16
16
 
17
- *Version 0.8.2 has many bug fixes and improvements that the earlier versions don't have, so
17
+ *Version 0.8.3 has many bug fixes and improvements that the earlier versions don't have, so
18
18
  hopefully you can switch to newer version of ember-data and therefore the best
19
19
  ember-data-factory-guy version, but if not, send me bug report and I will try and go back
20
20
  and fix the older version you are using.*
21
21
 
22
22
  **Version 0.8.0 introduces amd and commonjs support**
23
23
 
24
+ ## Using with Ember Cli
25
+ - https://github.com/igorrKurr/ember-cli-factory-guy-example
26
+ An example of how to manually set up ember-data-factory-guy with ember cli
27
+
28
+ - https://github.com/cristinawithout/ember-cli-data-factory-guy
29
+ A wrapper around ember-data-factory-guy for ember-cli for even easier setup
30
+
24
31
  ## Using as Gem
25
32
 
26
33
  To Use with in Rails project or project with sprockets:
@@ -34,7 +41,7 @@ gem 'ember-data-factory-guy', group: test
34
41
  or for particular version:
35
42
 
36
43
  ```ruby
37
- gem 'ember-data-factory-guy', '0.8.2', group: test
44
+ gem 'ember-data-factory-guy', '0.8.3', group: test
38
45
  ```
39
46
 
40
47
  then:
@@ -69,7 +76,7 @@ or for particular version:
69
76
  "dependencies": {
70
77
  "foo-dependency": "latest",
71
78
  "other-foo-dependency": "latest",
72
- "ember-data-factory-guy": "0.8.2"
79
+ "ember-data-factory-guy": "0.8.3"
73
80
  }
74
81
  ```
75
82
 
@@ -614,14 +621,15 @@ and this is already bundled for you when you use the ember-data-factory-guy libr
614
621
  - returns - attributes to include in response json
615
622
  - succeed - flag to indicate if the request should succeed ( default is true )
616
623
 
617
- *Note*
618
- That any attributes in match will be added to the response json automatically,
619
- so you don't need to include them in the returns hash as well.
624
+ **Note**
625
+
626
+ *Any attributes in match will be added to the response json automatically,
627
+ so you don't need to include them in the returns hash as well.*
620
628
 
621
- If you don't use match options for exact match, there will be no id returned to the model.
629
+ *If you don't use match options for exact match, there will be no id returned to the model.*
622
630
 
623
- If you match on a belongsTo association, you don't have to include that in the
624
- returns hash.
631
+ *If you match on a belongsTo association, you don't have to include that in the
632
+ returns hash.*
625
633
 
626
634
 
627
635
  Realistically, you will have code in a view action or controller action that will
@@ -645,9 +653,15 @@ to a particular user. To mock this createRecord call here are a few ways to do t
645
653
  match and or returns options.
646
654
 
647
655
  ```javascript
648
- // note that you don't need the returns option since the
649
- // match options are returned automatically
656
+ // Simplest case
657
+ // Don't care about a match just handle createRecord for any project
658
+ testHelper.handleCreate('project')
659
+ // Exactly matching attributes
650
660
  testHelper.handleCreate('project', {match: {name: "Moo", user: user}})
661
+ // Exactly matching attributes, and returning extra attributes
662
+ testHelper.handleCreate('project', {
663
+ match: {name: "Moo", user: user}, returns: {created_at: new Date()}
664
+ })
651
665
 
652
666
  ```
653
667
 
@@ -655,11 +669,17 @@ match and or returns options.
655
669
 
656
670
  ```javascript
657
671
  // set the succeed flag to 'false'
658
- testHelper.handleCreate('profile', {succeed: false});
672
+ testHelper.handleCreate('project', {succeed: false});
659
673
 
660
674
  // when the createRecord on the 'project' is called, it will fail
661
675
  store.createRecord('project').save() //=> fails
662
676
 
677
+ // or fail only if the attribues match exactly
678
+ testHelper.handleCreate('project', {
679
+ match: {name: "Moo", user: user}, {succeed: false}
680
+ })
681
+
682
+ store.createRecord('project', {name: "Moo", user: user}).save() //=> fails
663
683
  ```
664
684
 
665
685
 
data/bower.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-data-factory-guy",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "authors": [
5
5
  "Daniel Sudol <dansudol@yahoo.com>",
6
6
  "Opak Alex <opak.alexandr@gmail.com>"
@@ -0,0 +1,921 @@
1
+ define("factory-guy", ["ember-data"], function(__d1__) {
2
+ "use strict";
3
+
4
+ var DS = __d1__["default"] || __d1__;
5
+
6
+ var Sequence = function (fn) {
7
+ var index = 1;
8
+ this.next = function () {
9
+ return fn.call(this, index++);
10
+ };
11
+ this.reset = function () {
12
+ index = 1;
13
+ };
14
+ };
15
+
16
+ var MissingSequenceError = function(message) {
17
+ this.toString = function () {
18
+ return message;
19
+ };
20
+ };
21
+
22
+ if (FactoryGuy !== undefined) {
23
+ FactoryGuy.sequence = Sequence;
24
+ FactoryGuy.missingSequenceError = MissingSequenceError;
25
+ };
26
+
27
+ /**
28
+ A ModelDefinition encapsulates a model's definition
29
+
30
+ @param model
31
+ @param config
32
+ @constructor
33
+ */
34
+ var ModelDefinition = function (model, config) {
35
+ var sequences = {};
36
+ var traits = {};
37
+ var defaultAttributes = {};
38
+ var namedModels = {};
39
+ var modelId = 1;
40
+ var sequenceName = null;
41
+ this.model = model;
42
+ /**
43
+ @param {String} name model name like 'user' or named type like 'admin'
44
+ @returns {Boolean} true if name is this definitions model or this definition
45
+ contains a named model with that name
46
+ */
47
+ this.matchesName = function (name) {
48
+ return model == name || namedModels[name];
49
+ };
50
+ // Increment id
51
+ this.nextId = function () {
52
+ return modelId++;
53
+ };
54
+ /**
55
+ Call the next method on the named sequence function. If the name
56
+ is a function, create the sequence with that function
57
+
58
+ @param {String} name previously declared sequence name or
59
+ an the random name generate for inline functions
60
+ @param {Function} sequenceFn optional function to use as sequence
61
+ @returns {String} output of sequence function
62
+ */
63
+ this.generate = function (name, sequenceFn) {
64
+ if (sequenceFn) {
65
+ if (!sequences[name]) {
66
+ // create and add that sequence function on the fly
67
+ sequences[name] = new Sequence(sequenceFn);
68
+ }
69
+ }
70
+ var sequence = sequences[name];
71
+ if (!sequence) {
72
+ throw new MissingSequenceError('Can not find that sequence named [' + sequenceName + '] in \'' + model + '\' definition');
73
+ }
74
+ return sequence.next();
75
+ };
76
+ /**
77
+ Build a fixture by name
78
+
79
+ @param {String} name fixture name
80
+ @param {Object} opts attributes to override
81
+ @param {String} traitArgs array of traits
82
+ @returns {Object} json
83
+ */
84
+ this.build = function (name, opts, traitArgs) {
85
+ var traitsObj = {};
86
+ traitArgs.forEach(function (trait) {
87
+ $.extend(traitsObj, traits[trait]);
88
+ });
89
+ var modelAttributes = namedModels[name] || {};
90
+ // merge default, modelAttributes, traits and opts to get the rough fixture
91
+ var fixture = $.extend({}, defaultAttributes, modelAttributes, traitsObj, opts);
92
+ // deal with attributes that are functions or objects
93
+ for (var attribute in fixture) {
94
+ if (Ember.typeOf(fixture[attribute]) == 'function') {
95
+ // function might be a sequence of a named association
96
+ fixture[attribute] = fixture[attribute].call(this, fixture);
97
+ } else if (Ember.typeOf(fixture[attribute]) == 'object') {
98
+ // if it's an object it's for a model association, so build the json
99
+ // for the association and replace the attribute with that json
100
+ fixture[attribute] = FactoryGuy.build(attribute, fixture[attribute]);
101
+ }
102
+ }
103
+ // set the id, unless it was already set in opts
104
+ if (!fixture.id) {
105
+ fixture.id = this.nextId();
106
+ }
107
+ return fixture;
108
+ };
109
+ /**
110
+ Build a list of fixtures
111
+
112
+ @param {String} name model name or named model type
113
+ @param {Integer} number of fixtures to build
114
+ @param {Array} array of traits to build with
115
+ @param {Object} opts attribute options
116
+ @returns array of fixtures
117
+ */
118
+ this.buildList = function (name, number, traits, opts) {
119
+ var arr = [];
120
+ for (var i = 0; i < number; i++) {
121
+ arr.push(this.build(name, opts, traits));
122
+ }
123
+ return arr;
124
+ };
125
+ // Set the modelId back to 1, and reset the sequences
126
+ this.reset = function () {
127
+ modelId = 1;
128
+ for (var name in sequences) {
129
+ sequences[name].reset();
130
+ }
131
+ };
132
+ var parseDefault = function (object) {
133
+ if (!object) {
134
+ return;
135
+ }
136
+ defaultAttributes = object;
137
+ };
138
+ var parseTraits = function (object) {
139
+ if (!object) {
140
+ return;
141
+ }
142
+ traits = object;
143
+ };
144
+ var parseSequences = function (object) {
145
+ if (!object) {
146
+ return;
147
+ }
148
+ for (sequenceName in object) {
149
+ var sequenceFn = object[sequenceName];
150
+ if (Ember.typeOf(sequenceFn) != 'function') {
151
+ throw new Error('Problem with [' + sequenceName + '] sequence definition. Sequences must be functions');
152
+ }
153
+ object[sequenceName] = new Sequence(sequenceFn);
154
+ }
155
+ sequences = object;
156
+ };
157
+ var parseConfig = function (config) {
158
+ parseSequences(config.sequences);
159
+ delete config.sequences;
160
+ parseTraits(config.traits);
161
+ delete config.traits;
162
+ parseDefault(config.default);
163
+ delete config.default;
164
+ namedModels = config;
165
+ };
166
+ // initialize
167
+ parseConfig(config);
168
+ };
169
+
170
+ if (FactoryGuy !== undefined) {
171
+ FactoryGuy.modelDefiniton = ModelDefinition;
172
+ };
173
+
174
+ var FactoryGuy = {
175
+ modelDefinitions: {},
176
+ /**
177
+ ```javascript
178
+
179
+ Person = DS.Model.extend({
180
+ type: DS.attr('string'),
181
+ name: DS.attr('string')
182
+ })
183
+
184
+ FactoryGuy.define('person', {
185
+ sequences: {
186
+ personName: function(num) {
187
+ return 'person #' + num;
188
+ },
189
+ personType: function(num) {
190
+ return 'person type #' + num;
191
+ }
192
+ },
193
+ default: {
194
+ type: 'normal',
195
+ name: FactoryGuy.generate('personName')
196
+ },
197
+ dude: {
198
+ type: FactoryGuy.generate('personType')
199
+ },
200
+ });
201
+
202
+ ```
203
+
204
+ For the Person model, you can define named fixtures like 'dude' or
205
+ just use 'person' and get default values.
206
+
207
+ And to get those fixtures you would call them this way:
208
+
209
+ FactoryGuy.build('dude') or FactoryGuy.build('person')
210
+
211
+ @param {String} model the model to define
212
+ @param {Object} config your model definition
213
+ */
214
+ define: function (model, config) {
215
+ if (this.modelDefinitions[model]) {
216
+ this.modelDefinitions[model].merge(config);
217
+ } else {
218
+ this.modelDefinitions[model] = new ModelDefinition(model, config);
219
+ }
220
+ },
221
+ /**
222
+ Used in model definitions to declare use of a sequence. For example:
223
+
224
+ ```
225
+
226
+ FactoryGuy.define('person', {
227
+ sequences: {
228
+ personName: function(num) {
229
+ return 'person #' + num;
230
+ }
231
+ },
232
+ default: {
233
+ name: FactoryGuy.generate('personName')
234
+ }
235
+ });
236
+
237
+ ```
238
+
239
+ @param {String|Function} value previously declared sequence name or
240
+ an inline function to use as the sequence
241
+ @returns {Function} wrapper function that is called by the model
242
+ definition containing the sequence
243
+ */
244
+ generate: function (nameOrFunction) {
245
+ var sortaRandomName = Math.floor((1 + Math.random()) * 65536).toString(16) + Date.now();
246
+ return function () {
247
+ if (Em.typeOf(nameOrFunction) == 'function') {
248
+ return this.generate(sortaRandomName, nameOrFunction);
249
+ } else {
250
+ return this.generate(nameOrFunction);
251
+ }
252
+ };
253
+ },
254
+ /**
255
+ Used in model definitions to define a belongsTo association attribute.
256
+ For example:
257
+
258
+ ```
259
+ FactoryGuy.define('project', {
260
+ default: {
261
+ title: 'Project'
262
+ },
263
+
264
+ // setup named project with built in associated user
265
+ project_with_admin: {
266
+ user: FactoryGuy.belongsTo('admin')
267
+ }
268
+
269
+ // or use as a trait
270
+ traits: {
271
+ with_admin: {
272
+ user: FactoryGuy.belongsTo('admin')
273
+ }
274
+ }
275
+ })
276
+ ```
277
+
278
+ @param {String} fixtureName fixture name
279
+ @param {Object} opts options
280
+ @returns {Function} wrapper function that will build the association json
281
+ */
282
+ belongsTo: function (fixtureName, opts) {
283
+ return function () {
284
+ return FactoryGuy.build(fixtureName, opts);
285
+ };
286
+ },
287
+ association: function (fixtureName, opts) {
288
+ console.log('DEPRECATION Warning: use FactoryGuy.belongsTo instead');
289
+ return this.belongsTo(fixtureName, opts);
290
+ },
291
+ /**
292
+ Used in model definitions to define a hasMany association attribute.
293
+ For example:
294
+
295
+ ```
296
+ FactoryGuy.define('user', {
297
+ default: {
298
+ name: 'Bob'
299
+ },
300
+
301
+ // define the named user type that will have projects
302
+ user_with_projects: { FactoryGuy.hasMany('project', 2) }
303
+
304
+ // or use as a trait
305
+ traits: {
306
+ with_projects: {
307
+ projects: FactoryGuy.hasMany('project', 2)
308
+ }
309
+ }
310
+ })
311
+
312
+ ```
313
+
314
+ @param {String} fixtureName fixture name
315
+ @param {Number} number of hasMany association items to build
316
+ @param {Object} opts options
317
+ @returns {Function} wrapper function that will build the association json
318
+ */
319
+ hasMany: function (fixtureName, number, opts) {
320
+ return function () {
321
+ return FactoryGuy.buildList(fixtureName, number, opts);
322
+ };
323
+ },
324
+ /**
325
+ Given a fixture name like 'person' or 'dude' determine what model this name
326
+ refers to. In this case it's 'person' for each one.
327
+
328
+ @param {String} name a fixture name could be model name like 'person'
329
+ or a named person in model definition like 'dude'
330
+ @returns {String} model name associated with fixture name or undefined if not found
331
+ */
332
+ lookupModelForFixtureName: function (name) {
333
+ var definition = this.lookupDefinitionForFixtureName(name);
334
+ if (definition) {
335
+ return definition.model;
336
+ }
337
+ },
338
+ /**
339
+
340
+ @param {String} name a fixture name could be model name like 'person'
341
+ or a named person in model definition like 'dude'
342
+ @returns {ModelDefinition} ModelDefinition associated with model or undefined if not found
343
+ */
344
+ lookupDefinitionForFixtureName: function (name) {
345
+ for (var model in this.modelDefinitions) {
346
+ var definition = this.modelDefinitions[model];
347
+ if (definition.matchesName(name)) {
348
+ return definition;
349
+ }
350
+ }
351
+ },
352
+ /**
353
+ Build fixtures for model or specific fixture name. For example:
354
+
355
+ FactoryGuy.build('user') for User model
356
+ FactoryGuy.build('bob') for a 'bob' User
357
+ FactoryGuy.build('bob', 'dude') for a 'bob' User with dude traits
358
+ FactoryGuy.build('bob', 'dude', 'funny') for a 'bob' User with dude and funny traits
359
+ FactoryGuy.build('bob', 'dude', {name: 'wombat'}) for a 'bob' User with dude trait and custom attribute name of 'wombat'
360
+
361
+ @param {String} name fixture name
362
+ @param {String} trait optional trait names ( one or more )
363
+ @param {Object} opts optional fixture options that will override default fixture values
364
+ @returns {Object} json fixture
365
+ */
366
+ build: function () {
367
+ var args = Array.prototype.slice.call(arguments);
368
+ var opts = {};
369
+ var name = args.shift();
370
+ if (!name) {
371
+ throw new Error('Build needs a factory name to build');
372
+ }
373
+ if (Ember.typeOf(args[args.length - 1]) == 'object') {
374
+ opts = args.pop();
375
+ }
376
+ var traits = args;
377
+ // whatever is left are traits
378
+ var definition = this.lookupDefinitionForFixtureName(name);
379
+ if (!definition) {
380
+ throw new Error('Can\'t find that factory named [' + name + ']');
381
+ }
382
+ return definition.build(name, opts, traits);
383
+ },
384
+ /**
385
+ Build list of fixtures for model or specific fixture name. For example:
386
+
387
+ FactoryGuy.buildList('user', 2) for 2 User models
388
+ FactoryGuy.build('bob', 2) for 2 User model with bob attributes
389
+
390
+ @param {String} name fixture name
391
+ @param {Number} number number of fixtures to create
392
+ @param {String} trait optional traits (one or more)
393
+ @param {Object} opts optional fixture options that will override default fixture values
394
+ @returns {Array} list of fixtures
395
+ */
396
+ buildList: function () {
397
+ var args = Array.prototype.slice.call(arguments);
398
+ var name = args.shift();
399
+ var number = args.shift();
400
+ if (!name || !number) {
401
+ throw new Error('buildList needs a name and a number ( at least ) to build with');
402
+ }
403
+ var opts = {};
404
+ if (Ember.typeOf(args[args.length - 1]) == 'object') {
405
+ opts = args.pop();
406
+ }
407
+ var traits = args;
408
+ // whatever is left are traits
409
+ var definition = this.lookupDefinitionForFixtureName(name);
410
+ if (!definition) {
411
+ throw new Error('Can\'t find that factory named [' + name + ']');
412
+ }
413
+ return definition.buildList(name, number, traits, opts);
414
+ },
415
+
416
+ /**
417
+ Clear model instances from FIXTURES array, and from store cache.
418
+ Reset the id sequence for the models back to zero.
419
+ */
420
+ resetModels: function (store) {
421
+ for (var model in this.modelDefinitions) {
422
+ var definition = this.modelDefinitions[model];
423
+ definition.reset();
424
+ try {
425
+ var modelType = store.modelFor(definition.model);
426
+ if (store.usingFixtureAdapter()) {
427
+ modelType.FIXTURES = [];
428
+ }
429
+ store.unloadAll(modelType);
430
+ } catch (e) {
431
+ console.log('resetModels',e)
432
+ }
433
+ }
434
+ },
435
+ /**
436
+ Push fixture to model's FIXTURES array.
437
+ Used when store's adapter is a DS.FixtureAdapter.
438
+
439
+ @param {DS.Model} modelClass
440
+ @param {Object} fixture the fixture to add
441
+ @returns {Object} json fixture data
442
+ */
443
+ pushFixture: function (modelClass, fixture) {
444
+ if (!modelClass.FIXTURES) {
445
+ modelClass.FIXTURES = [];
446
+ }
447
+ modelClass.FIXTURES.push(fixture);
448
+ return fixture;
449
+ },
450
+ /**
451
+ Clears all model definitions
452
+ */
453
+ clear: function (opts) {
454
+ if (!opts) {
455
+ this.modelDefinitions = {};
456
+ }
457
+ }
458
+ };
459
+ (function () {
460
+ DS.Store.reopen({
461
+ /**
462
+ @returns {Boolean} true if store's adapter is DS.FixtureAdapter
463
+ */
464
+ usingFixtureAdapter: function () {
465
+ var adapter = this.adapterFor('application');
466
+ return adapter instanceof DS.FixtureAdapter;
467
+ },
468
+ /**
469
+ Make new fixture and save to store. If the store is using FixtureAdapter,
470
+ will push to FIXTURE array, otherwise will use push method on adapter to load
471
+ the record into the store
472
+
473
+ @param {String} name fixture name
474
+ @param {String} trait optional trait names ( one or more )
475
+ @param {Object} opts optional fixture options that will override default fixture values
476
+ @returns {Object|DS.Model} json or record depending on the adapter type
477
+ */
478
+ makeFixture: function () {
479
+ var store = this;
480
+ var fixture = FactoryGuy.build.apply(FactoryGuy, arguments);
481
+ var name = arguments[0];
482
+ var modelName = FactoryGuy.lookupModelForFixtureName(name);
483
+ var modelType = store.modelFor(modelName);
484
+ if (this.usingFixtureAdapter()) {
485
+ this.setAssociationsForFixtureAdapter(modelType, modelName, fixture);
486
+ return FactoryGuy.pushFixture(modelType, fixture);
487
+ } else {
488
+ return store.makeModel(modelType, fixture);
489
+ }
490
+ },
491
+ makeModel: function (modelType, fixture) {
492
+ var store = this, modelName = store.modelFor(modelType).typeKey, model;
493
+ Em.run(function () {
494
+ store.findEmbeddedAssociationsForRESTAdapter(modelType, fixture);
495
+ if (fixture.type) {
496
+ // assuming its polymorphic if there is a type attribute
497
+ // is this too bold an assumption?
498
+ modelName = fixture.type.underscore();
499
+ modelType = store.modelFor(modelName);
500
+ }
501
+ model = store.push(modelName, fixture);
502
+ store.setAssociationsForRESTAdapter(modelType, modelName, model);
503
+ });
504
+ return model;
505
+ },
506
+ /**
507
+ Make a list of Fixtures
508
+
509
+ @param {String} name name of fixture
510
+ @param {Number} number number to create
511
+ @param {Object} options fixture options
512
+ @returns {Array} list of json fixtures or records depending on the adapter type
513
+ */
514
+ makeList: function () {
515
+ var arr = [];
516
+ var number = arguments[1];
517
+ for (var i = 0; i < number; i++) {
518
+ arr.push(this.makeFixture.apply(this, arguments));
519
+ }
520
+ return arr;
521
+ },
522
+ /**
523
+ Set the hasMany and belongsTo associations for FixtureAdapter.
524
+
525
+ For example, assuming a user hasMany projects, if you make a project,
526
+ then a user with that project in the users list of project, then this method
527
+ will go back and set the user.id on each project that the user hasMany of,
528
+ so that the project now has the belongsTo user association setup.
529
+ As in this scenario:
530
+
531
+ ```js
532
+ var projectJson = store.makeFixture('project');
533
+ var userJson = store.makeFixture('user', {projects: [projectJson]});
534
+ ```
535
+
536
+ Or if you make a project with a user, then set this project in
537
+ the users list of 'projects' it hasMany of. As in this scenario:
538
+
539
+ ```js
540
+ var userJson = store.makeFixture('user');
541
+ var projectJson = store.makeFixture('project', {user: userJson});
542
+ ```
543
+
544
+ @param {DS.Model} modelType model type like User
545
+ @param {String} modelName model name like 'user'
546
+ @param {Object} fixture to check for needed association assignments
547
+ */
548
+ setAssociationsForFixtureAdapter: function (modelType, modelName, fixture) {
549
+ var self = this;
550
+ var adapter = this.adapterFor('application');
551
+ Ember.get(modelType, 'relationshipsByName').forEach(function (relationship, name) {
552
+ if (relationship.kind == 'hasMany') {
553
+ var hasManyRelation = fixture[relationship.key];
554
+ if (hasManyRelation) {
555
+ $.each(fixture[relationship.key], function (index, object) {
556
+ // used to require that the relationship was set by id,
557
+ // but now, you can set it as the json object, and this will
558
+ // normalize that back to the id
559
+ var id = object;
560
+ if (Ember.typeOf(object) == 'object') {
561
+ id = object.id;
562
+ hasManyRelation[index] = id;
563
+ }
564
+ var hasManyfixtures = adapter.fixturesForType(relationship.type);
565
+ var fixture = adapter.findFixtureById(hasManyfixtures, id);
566
+ fixture[modelName] = fixture.id;
567
+ });
568
+ }
569
+ }
570
+ if (relationship.kind == 'belongsTo') {
571
+ var belongsToRecord = fixture[relationship.key];
572
+ if (belongsToRecord) {
573
+ if (typeof belongsToRecord == 'object') {
574
+ FactoryGuy.pushFixture(relationship.type, belongsToRecord);
575
+ fixture[relationship.key] = belongsToRecord.id;
576
+ }
577
+ var hasManyName = self.findHasManyRelationshipNameForFixtureAdapter(relationship.type, relationship.parentType);
578
+ var belongsToFixtures = adapter.fixturesForType(relationship.type);
579
+ var belongsTofixture = adapter.findFixtureById(belongsToFixtures, fixture[relationship.key]);
580
+ if (!belongsTofixture[hasManyName]) {
581
+ belongsTofixture[hasManyName] = [];
582
+ }
583
+ belongsTofixture[hasManyName].push(fixture.id);
584
+ }
585
+ }
586
+ });
587
+ },
588
+ /**
589
+ Before pushing the fixture to the store, do some preprocessing. Descend into the tree
590
+ of object data, and convert child objects to record instances recursively.
591
+
592
+ If its a belongs to association, and the fixture has an object there,
593
+ then push that model to the store and set the id of that new model
594
+ as the attribute value in the fixture
595
+
596
+ @param modelType
597
+ @param fixture
598
+ */
599
+ findEmbeddedAssociationsForRESTAdapter: function (modelType, fixture) {
600
+ var store = this;
601
+ Ember.get(modelType, 'relationshipsByName').forEach(function (relationship, name) {
602
+ if (relationship.kind == 'belongsTo') {
603
+ var belongsToRecord = fixture[relationship.key];
604
+ if (Ember.typeOf(belongsToRecord) == 'object') {
605
+ store.findEmbeddedAssociationsForRESTAdapter(relationship.type, belongsToRecord);
606
+ belongsToRecord = store.push(relationship.type, belongsToRecord);
607
+ fixture[relationship.key] = belongsToRecord;
608
+ }
609
+ }
610
+ if (relationship.kind == 'hasMany') {
611
+ var hasManyRecords = fixture[relationship.key];
612
+ if (Ember.typeOf(hasManyRecords) == 'array') {
613
+ if (Ember.typeOf(hasManyRecords[0]) == 'object') {
614
+ var records = Em.A();
615
+ hasManyRecords.map(function (object) {
616
+ store.findEmbeddedAssociationsForRESTAdapter(relationship.type, object);
617
+ var record = store.push(relationship.type, object);
618
+ records.push(record);
619
+ return record;
620
+ });
621
+ fixture[relationship.key] = records;
622
+ }
623
+ }
624
+ }
625
+ });
626
+ },
627
+ /**
628
+ For the REST type models:
629
+
630
+ For example if a user hasMany projects, then set the user
631
+ on each project that the user hasMany of, so that the project
632
+ now has the belongsTo user association setup. As in this scenario:
633
+
634
+ ```js
635
+ var project = store.makeFixture('project');
636
+ var user = store.makeFixture('user', {projects: [project]});
637
+ ```
638
+
639
+ Or if you make a user, then a project with that user, then set the project
640
+ in the users list of 'projects' it hasMany of. As in this scenario:
641
+
642
+ ```js
643
+ var user = store.makeFixture('user');
644
+ var project = store.makeFixture('project', {user: user});
645
+ ```
646
+
647
+ NOTE:
648
+ As of ember-data-1.0.0-beta.10 and ember-data-1.0.0-beta.12,
649
+ this method is only needed because the belongsTo is not assigned when
650
+ there is a self referential polymorphic has many association.
651
+
652
+ @param {DS.Model} modelType model type like 'User'
653
+ @param {String} modelName model name like 'user'
654
+ @param {DS.Model} model model to check for needed association assignments
655
+ */
656
+ setAssociationsForRESTAdapter: function (modelType, modelName, model) {
657
+ var self = this;
658
+ Ember.get(modelType, 'relationshipsByName').forEach(function (relationship, name) {
659
+ if (relationship.kind == 'hasMany') {
660
+ var children = model.get(name) || [];
661
+ children.forEach(function (child) {
662
+ var belongsToName = self.findRelationshipName('belongsTo', child.constructor, model);
663
+ if (belongsToName) {
664
+ child.set(belongsToName, model);
665
+ }
666
+ });
667
+ }
668
+ });
669
+ },
670
+ findRelationshipName: function (kind, belongToModelType, childModel) {
671
+ var relationshipName;
672
+ Ember.get(belongToModelType, 'relationshipsByName').forEach(function (relationship, name) {
673
+ if (relationship.kind == kind && childModel instanceof relationship.type) {
674
+ relationshipName = relationship.key;
675
+ }
676
+ });
677
+ return relationshipName;
678
+ },
679
+ findHasManyRelationshipNameForFixtureAdapter: function (belongToModelType, childModelType) {
680
+ var relationshipName;
681
+ Ember.get(belongToModelType, 'relationshipsByName').forEach(function (relationship, name) {
682
+ if (relationship.kind == 'hasMany' && childModelType == relationship.type) {
683
+ relationshipName = relationship.key;
684
+ }
685
+ });
686
+ return relationshipName;
687
+ },
688
+ /**
689
+ Adding a pushPayload for FixtureAdapter, but using the original with
690
+ other adapters that support pushPayload.
691
+
692
+ @param {String} type
693
+ @param {Object} payload
694
+ */
695
+ pushPayload: function (type, payload) {
696
+ if (this.usingFixtureAdapter()) {
697
+ var model = this.modelFor(modelName);
698
+ FactoryGuy.pushFixture(model, payload);
699
+ } else {
700
+ this._super(type, payload);
701
+ }
702
+ }
703
+ });
704
+ })();
705
+ var FactoryGuyTestMixin = Em.Mixin.create({
706
+ // Pass in the app root, which typically is App.
707
+ setup: function (app) {
708
+ this.set('container', app.__container__);
709
+ return this;
710
+ },
711
+ useFixtureAdapter: function (app) {
712
+ app.ApplicationAdapter = DS.FixtureAdapter;
713
+ this.getStore().adapterFor('application').simulateRemoteResponse = false;
714
+ },
715
+ /**
716
+ @param {String} model type like user for model User
717
+ @return {boolean} true if model's serializer is ActiveModelSerializer based
718
+ */
719
+ usingActiveModelSerializer: function (type) {
720
+ var store = this.getStore();
721
+ var modelType = store.modelFor(type);
722
+ var serializer = store.serializerFor(modelType.typeKey);
723
+ return serializer instanceof DS.ActiveModelSerializer;
724
+ },
725
+ /**
726
+ Proxy to store's find method
727
+
728
+ @param {String or subclass of DS.Model} type
729
+ @param {Object|String|Integer|null} id
730
+ @return {Promise} promise
731
+ */
732
+ find: function (type, id) {
733
+ return this.getStore().find(type, id);
734
+ },
735
+ /**
736
+ Proxy to store's makeFixture method
737
+
738
+ */
739
+ make: function () {
740
+ var store = this.getStore();
741
+ return store.makeFixture.apply(store, arguments);
742
+ },
743
+ getStore: function () {
744
+ return this.get('container').lookup('store:main');
745
+ },
746
+ pushPayload: function (type, hash) {
747
+ return this.getStore().pushPayload(type, hash);
748
+ },
749
+ pushRecord: function (type, hash) {
750
+ return this.getStore().push(type, hash);
751
+ },
752
+ /**
753
+ Using mockjax to stub an http request.
754
+
755
+ @param {String} url request url
756
+ @param {Object} json response
757
+ @param {Object} options ajax request options
758
+ */
759
+ stubEndpointForHttpRequest: function (url, json, options) {
760
+ options = options || {};
761
+ var request = {
762
+ url: url,
763
+ dataType: 'json',
764
+ responseText: json,
765
+ type: options.type || 'GET',
766
+ status: options.status || 200
767
+ };
768
+ if (options.data) {
769
+ request.data = options.data;
770
+ }
771
+ $.mockjax(request);
772
+ },
773
+ /**
774
+ Build url for the mockjax call. Proxy to the adapters buildURL method.
775
+
776
+ @param {String} typeName model type name like 'user' for User model
777
+ @param {String} id
778
+ @return {String} url
779
+ */
780
+ buildURL: function (typeName, id) {
781
+ var type = this.getStore().modelFor(typeName);
782
+ return this.getStore().adapterFor(type).buildURL(type.typeKey, id);
783
+ },
784
+ /**
785
+ Handling ajax GET for finding all records for a type of model.
786
+ You can mock failed find by passing in success argument as false.
787
+
788
+ @param {String} name name of the fixture ( or model ) to find
789
+ @param {Number} number number of fixtures to create
790
+ @param {String} trait optional traits (one or more)
791
+ @param {Object} opts optional fixture options
792
+ */
793
+ handleFindMany: function () {
794
+ var store = this.getStore();
795
+ // make the records and load them in the store
796
+ store.makeList.apply(store, arguments);
797
+ var name = arguments[0];
798
+ var modelName = FactoryGuy.lookupModelForFixtureName(name);
799
+ var responseJson = {};
800
+ responseJson[modelName] = [];
801
+ var url = this.buildURL(modelName);
802
+ // mock the ajax call, but return nothing, since the records will be
803
+ // retrieved from the store where they were just loaded above
804
+ this.stubEndpointForHttpRequest(url, responseJson, { type: 'GET' });
805
+ },
806
+ /**
807
+ Handling ajax POST ( create record ) for a model. You can mock
808
+ failed create by passing in success argument as false.
809
+
810
+ ```js
811
+ handleCreate('post')
812
+ handleCreate('post', { match: {title: 'foo'} )
813
+ handleCreate('post', { match: {title: 'foo', user: user} )
814
+ handleCreate('post', { returns: {createdAt: new Date()} )
815
+ handleCreate('post', { match: {title: 'foo'}, returns: {createdAt: new Date()} )
816
+ handleCreate('post', { match: {title: 'foo'}, success: false} } )
817
+ ```
818
+
819
+ match - attributes that must be in request json,
820
+ returns - attributes to include in response json,
821
+ succeed - flag to indicate if the request should succeed ( default is true )
822
+
823
+ Note:
824
+ 1) that any attributes in match will be added to the response json automatically,
825
+ so you don't need to include them in the returns hash as well.
826
+
827
+ 2) If you don't use match options for exact match, there will be no id returned to the model.
828
+
829
+ 3) If you match on a belongsTo association, you don't have to include that in the
830
+ returns hash.
831
+
832
+ @param {String} modelName name of model your creating like 'profile' for Profile
833
+ @param {Object} options hash of options for handling request
834
+ */
835
+ handleCreate: function (modelName, options) {
836
+ var opts = options === undefined ? {} : options;
837
+ var succeed = opts.succeed === undefined ? true : opts.succeed;
838
+ var match = opts.match || {};
839
+ var returnArgs = opts.returns || {};
840
+
841
+ var url = this.buildURL(modelName);
842
+ var definition = FactoryGuy.modelDefinitions[modelName];
843
+
844
+ var httpOptions = {type: 'POST'};
845
+ if (opts.match) {
846
+ var expectedRequest = {};
847
+ var record = this.getStore().createRecord(modelName, match);
848
+ expectedRequest[modelName] = record.serialize();
849
+ httpOptions.data = JSON.stringify(expectedRequest);
850
+ }
851
+
852
+ var modelType = this.getStore().modelFor(modelName)
853
+
854
+ var responseJson = {};
855
+ if (succeed) {
856
+ if (options) {
857
+ responseJson[modelName] = $.extend({id: definition.nextId()}, match, returnArgs);
858
+ // Remove belongsTo associations since they will already be set when you called
859
+ // createRecord, and included them in those attributes
860
+ Ember.get(modelType, 'relationshipsByName').forEach(function (relationship) {
861
+ if (relationship.kind == 'belongsTo') {
862
+ delete responseJson[modelName][relationship.key];
863
+ }
864
+ })
865
+ }
866
+ } else {
867
+ httpOptions.status = 500;
868
+ }
869
+
870
+ this.stubEndpointForHttpRequest(url, responseJson, httpOptions);
871
+ },
872
+ /**
873
+ Handling ajax PUT ( update record ) for a model type. You can mock
874
+ failed update by passing in success argument as false.
875
+
876
+ @param {String} type model type like 'user' for User model
877
+ @param {String} id id of record to update
878
+ @param {Boolean} succeed optional flag to indicate if the request
879
+ should succeed ( default is true )
880
+ */
881
+ handleUpdate: function (type, id, succeed) {
882
+ succeed = succeed === undefined ? true : succeed;
883
+ this.stubEndpointForHttpRequest(this.buildURL(type, id), {}, {
884
+ type: 'PUT',
885
+ status: succeed ? 200 : 500
886
+ });
887
+ },
888
+ /**
889
+ Handling ajax DELETE ( delete record ) for a model type. You can mock
890
+ failed delete by passing in success argument as false.
891
+
892
+ @param {String} type model type like 'user' for User model
893
+ @param {String} id id of record to update
894
+ @param {Boolean} succeed optional flag to indicate if the request
895
+ should succeed ( default is true )
896
+ */
897
+ handleDelete: function (type, id, succeed) {
898
+ succeed = succeed === undefined ? true : succeed;
899
+ this.stubEndpointForHttpRequest(this.buildURL(type, id), {}, {
900
+ type: 'DELETE',
901
+ status: succeed ? 200 : 500
902
+ });
903
+ },
904
+ teardown: function () {
905
+ FactoryGuy.resetModels(this.getStore());
906
+ $.mockjax.clear();
907
+ }
908
+ });
909
+
910
+ if (FactoryGuy !== undefined) {
911
+ FactoryGuy.testMixin = FactoryGuyTestMixin;
912
+ };
913
+
914
+
915
+ return {
916
+ default: FactoryGuy,
917
+ modelDefinition: FactoryGuy.modelDefinition,
918
+ sequence: FactoryGuy.sequence,
919
+ testMixin: FactoryGuy.testMixin
920
+ }
921
+ });