ember-data-factory-guy 0.8.3 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
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
+ });