ember-data-factory-guy 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +405 -191
- data/bower.json +1 -1
- data/dist/ember-data-factory-guy.js +5 -2
- data/dist/ember-data-factory-guy.min.js +1 -1
- data/ember-data-factory-guy.gemspec +1 -1
- data/package.json +1 -1
- data/src/factory_guy.js +1 -0
- data/src/store.js +4 -2
- data/tests/active_model_adapter_factory_test.js +1 -1
- data/tests/factory_guy_test.js +3 -1
- data/tests/fixture_adapter_factory_test.js +7 -3
- data/vendor/assets/javascripts/ember_data_factory_guy.js +10 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 473a275179e818f802dbabd583b165881041075d
|
4
|
+
data.tar.gz: fa6ee1183d3633585c1a550720b3c4c26a7edc69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edf890374037db93120303bca6e74f023514526c4b6f53c73f94bf835ef0cf8f907d7bd5666cde33e35f875106c2b497a69e9b9de0140bfcd327a5482ab26cd8
|
7
|
+
data.tar.gz: 991e511cd9dcd2a451fb2dcea9cdbf82ba0a76b58f0a291cf8cc3d26e6ad5d53c2a9ca0ce54975f14a4da9090ff3f76cd59b94fd201318714f384a2fe2a68a7e
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Ember Data Factory Guy
|
1
|
+
# Ember Data Factory Guy
|
2
2
|
|
3
3
|
## Using as Gem
|
4
4
|
|
@@ -18,15 +18,17 @@ $ bundle install
|
|
18
18
|
|
19
19
|
then:
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
require the 'ember_data_factory_guy' javascript file in your test helper
|
22
|
+
|
23
|
+
```
|
23
24
|
//= require ember_data_factory_guy
|
24
25
|
```
|
25
26
|
|
26
27
|
## Using as bower component
|
27
28
|
|
28
29
|
Add as one of your dependencies in bower.json file:
|
29
|
-
|
30
|
+
|
31
|
+
```json
|
30
32
|
"dependencies": {
|
31
33
|
"foo-dependency": "latest",
|
32
34
|
"other-foo-dependency": "latest",
|
@@ -36,18 +38,35 @@ Add as one of your dependencies in bower.json file:
|
|
36
38
|
|
37
39
|
then:
|
38
40
|
```
|
39
|
-
$ bower install
|
41
|
+
$ bower install
|
40
42
|
```
|
41
43
|
|
42
|
-
|
44
|
+
### How this works
|
45
|
+
|
46
|
+
- Using DS.RestAdapter / DS.ActiveModelAdapter
|
47
|
+
- Add record instances to the store
|
48
|
+
- Faster, since models can be accessed synchronously
|
49
|
+
- Using DS.FixtureAdapter
|
50
|
+
- Add fixtures to the store
|
51
|
+
- Slower, since models are accessed asynchronously
|
52
|
+
|
53
|
+
|
54
|
+
##### DS.RestAdapter / DS.ActiveModelAdapter
|
55
|
+
|
56
|
+
The preferred way to use this project is to use the default adapter for your project,
|
57
|
+
which is usually going to be the RESTAdapter/ActiveModelAdapter.
|
58
|
+
*In other words, it is NOT recommended to use the DS.FixtureAdapter.*
|
43
59
|
|
44
|
-
|
60
|
+
When you call: store.makeFixture('user'), you create model in the store and this method
|
61
|
+
returns this model instance
|
45
62
|
|
46
|
-
|
47
|
-
|
48
|
-
|
63
|
+
*Since you are synchronously getting model instances, you can immediately start asking
|
64
|
+
for data from the model, and its associations, which is why it is faster to use
|
65
|
+
the REST/ActiveModel adapter than the FixtureAdapter*
|
49
66
|
|
50
|
-
|
67
|
+
##### Using DS.FixtureAdapter
|
68
|
+
|
69
|
+
The benefit of using FactoryGuy is that you can run your tests with the
|
51
70
|
default adapter that your application's store normally uses. In other words:
|
52
71
|
You do not have to use the DS.FixtureAdapter. But if you do choose to use the Fixture adapter,
|
53
72
|
which does not run any faster, and does not handle associations as elegantly
|
@@ -57,15 +76,18 @@ Error: Assertion Failed: You looked up the 'projects' relationship on '<User:emb
|
|
57
76
|
|
58
77
|
If you do get these types of errors try requiring the factory_guy_has_many.js file
|
59
78
|
( located in dist dir and vendor dir ) AFTER you require ember-data,
|
60
|
-
but BEFORE you require your models.
|
79
|
+
but BEFORE you require your models.
|
61
80
|
|
62
|
-
Let's say you have a few models like these:
|
63
81
|
|
64
|
-
|
82
|
+
### Setup
|
83
|
+
|
84
|
+
In the following examples, assume the models look like this:
|
65
85
|
|
86
|
+
```javascript
|
87
|
+
// standard models
|
66
88
|
User = DS.Model.extend({
|
67
89
|
name: DS.attr('string'),
|
68
|
-
|
90
|
+
style: DS.attr('string'),
|
69
91
|
projects: DS.hasMany('project'),
|
70
92
|
hats: DS.hasMany('hat', {polymorphic: true})
|
71
93
|
});
|
@@ -74,81 +96,65 @@ Let's say you have a few models like these:
|
|
74
96
|
title: DS.attr('string'),
|
75
97
|
user: DS.belongsTo('user')
|
76
98
|
});
|
77
|
-
|
99
|
+
|
100
|
+
// polymorphic models
|
78
101
|
Hat = DS.Model.extend({
|
79
102
|
type: DS.attr('string'),
|
80
103
|
user: DS.belongsTo('user')
|
81
104
|
});
|
82
|
-
|
105
|
+
|
83
106
|
BigHat = Hat.extend();
|
84
107
|
SmallHat = Hat.extend();
|
85
|
-
|
86
108
|
```
|
87
109
|
|
88
110
|
|
89
|
-
### Defining
|
111
|
+
### Defining Factories
|
112
|
+
- A factory has a name and a set of attributes.
|
113
|
+
- The name should match the model type name. So, for 'User' model, the name would be 'user'
|
114
|
+
|
115
|
+
|
116
|
+
##### Standard models
|
90
117
|
|
91
118
|
```javascript
|
92
|
-
////////////////////////////////////////////
|
93
|
-
// FactoryGuy definitions for models
|
94
119
|
|
95
120
|
FactoryGuy.define('user', {
|
96
|
-
//
|
97
|
-
sequences: {
|
98
|
-
userName: function(num) {
|
99
|
-
return 'User' + num;
|
100
|
-
}
|
101
|
-
},
|
102
|
-
|
103
|
-
// default 'user' attributes
|
121
|
+
// Put default 'user' attributes in the default section
|
104
122
|
default: {
|
105
|
-
|
106
|
-
|
107
|
-
name: FactoryGuy.generate('userName')
|
123
|
+
style: 'normal',
|
124
|
+
name: 'Dude'
|
108
125
|
},
|
109
|
-
//
|
110
|
-
// named 'user' type with custom attributes
|
111
|
-
//
|
126
|
+
// Create a named 'user' with custom attributes
|
112
127
|
admin: {
|
113
|
-
|
128
|
+
style: 'super',
|
114
129
|
name: 'Admin'
|
115
130
|
}
|
116
|
-
//
|
117
|
-
// using a function for an attribute that refers to other attributes
|
118
|
-
//
|
119
|
-
funny_user: {
|
120
|
-
type: function(f) { return 'funny ' + f.name }
|
121
|
-
}
|
122
131
|
});
|
123
132
|
|
124
|
-
|
133
|
+
```
|
134
|
+
|
135
|
+
|
136
|
+
##### Polymorphic models
|
137
|
+
|
138
|
+
It is better to define each polymorphic model in it's own typed definition:
|
139
|
+
|
140
|
+
```
|
141
|
+
FactoryGuy.define('small_hat', {
|
125
142
|
default: {
|
126
|
-
|
127
|
-
},
|
128
|
-
//
|
129
|
-
// declaring inline sequence
|
130
|
-
//
|
131
|
-
special_project: {
|
132
|
-
title: FactoryGuy.generate(function(num) { return 'Project #' + num})
|
133
|
-
},
|
134
|
-
//
|
135
|
-
// define built in belongTo models
|
136
|
-
//
|
137
|
-
project_with_user: {
|
138
|
-
// user model with default attributes
|
139
|
-
user: {}
|
140
|
-
},
|
141
|
-
project_with_dude: {
|
142
|
-
// user model with custom attributes
|
143
|
-
user: {name: 'Dude'}
|
144
|
-
},
|
145
|
-
project_with_admin: {
|
146
|
-
// for named association, use the FactoryGuy.belongsTo helper method
|
147
|
-
user: FactoryGuy.belongsTo('admin')
|
143
|
+
type: 'SmallHat'
|
148
144
|
}
|
145
|
+
})
|
146
|
+
|
147
|
+
FactoryGuy.define('big_hat', {
|
148
|
+
default: {
|
149
|
+
type: 'BigHat'
|
150
|
+
}
|
151
|
+
})
|
149
152
|
|
150
|
-
|
153
|
+
```
|
151
154
|
|
155
|
+
rather than doing this:
|
156
|
+
|
157
|
+
```
|
152
158
|
FactoryGuy.define('hat', {
|
153
159
|
default: {},
|
154
160
|
small_hat: {
|
@@ -160,178 +166,341 @@ Let's say you have a few models like these:
|
|
160
166
|
})
|
161
167
|
```
|
162
168
|
|
169
|
+
Since there are times that the latter can cause problems when
|
170
|
+
the store is looking up the correct model type name
|
171
|
+
|
172
|
+
|
173
|
+
### Using Factories
|
174
|
+
- FactoryGuy.build
|
175
|
+
- Builds json
|
176
|
+
- store.makeFixture
|
177
|
+
- Loads model instance into the store
|
178
|
+
- Can override default attributes by passing in a hash
|
179
|
+
- Can add attributes with traits ( see traits section )
|
180
|
+
|
181
|
+
```javascript
|
182
|
+
|
183
|
+
// returns json
|
184
|
+
var json = FactoryGuy.build('user');
|
185
|
+
json // => {id: 1, name: 'Dude', style: 'normal'}
|
186
|
+
|
187
|
+
// returns a User instance that is loaded into your application's store
|
188
|
+
var user = store.makeFixture('user');
|
189
|
+
user.toJSON() // => {id: 2, name: 'Dude', style: 'normal'}
|
190
|
+
|
191
|
+
var json = FactoryGuy.build('admin');
|
192
|
+
json // => {id: 3, name: 'Admin', style: 'super'}
|
193
|
+
|
194
|
+
var user = store.makeFixture('admin');
|
195
|
+
user.toJSON() // => {id: 4, name: 'Admin', style: 'super'}
|
196
|
+
|
197
|
+
```
|
198
|
+
|
199
|
+
You can override the default attributes by passing in a hash
|
200
|
+
|
201
|
+
```javascript
|
202
|
+
|
203
|
+
var json = FactoryGuy.build('user', {name: 'Fred'});
|
204
|
+
// json.name => 'Fred'
|
205
|
+
|
206
|
+
```
|
207
|
+
|
163
208
|
|
164
|
-
###
|
209
|
+
### Sequences
|
210
|
+
|
211
|
+
- For generating unique attribute values.
|
212
|
+
- Can be defined:
|
213
|
+
- In the model definition's sequences hash
|
214
|
+
- Inline on the attribute
|
215
|
+
- Values are generated by calling FactoryGuy.generate
|
216
|
+
|
217
|
+
##### Declaring sequences in sequences hash
|
165
218
|
|
166
219
|
```javascript
|
167
|
-
//////////////////////////////////////////////////////////////////
|
168
|
-
//
|
169
|
-
// building json with FactoryGuy.build
|
170
|
-
//
|
171
220
|
|
172
|
-
FactoryGuy.
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
221
|
+
FactoryGuy.define('user', {
|
222
|
+
sequences: {
|
223
|
+
userName: function(num) {
|
224
|
+
return 'User' + num;
|
225
|
+
}
|
226
|
+
},
|
227
|
+
|
228
|
+
default: {
|
229
|
+
// use the 'userName' sequence for this attribute
|
230
|
+
name: FactoryGuy.generate('userName')
|
231
|
+
}
|
232
|
+
});
|
233
|
+
|
234
|
+
var json = FactoryGuy.build('user');
|
235
|
+
json.name // => 'User1'
|
236
|
+
|
237
|
+
var user = store.makeFixture('user');
|
238
|
+
user.get('name') // => 'User2'
|
239
|
+
|
240
|
+
```
|
180
241
|
|
181
|
-
|
182
|
-
FactoryGuy.build('project') // {id: 1, title: 'Project'}
|
242
|
+
##### Declaring an inline sequence on attribute
|
183
243
|
|
184
|
-
|
185
|
-
|
244
|
+
```javascript
|
245
|
+
|
246
|
+
FactoryGuy.define('project', {
|
247
|
+
special_project: {
|
248
|
+
title: FactoryGuy.generate(function(num) { return 'Project #' + num})
|
249
|
+
},
|
250
|
+
});
|
186
251
|
|
187
|
-
|
188
|
-
|
189
|
-
// project with user that has custom attributes
|
190
|
-
FactoryGuy.build('project_with_dude') // {id: 4, title: 'Project', user: {id:7, name: 'Dude', type: 'normal'}
|
191
|
-
// project with user that has a named user
|
192
|
-
FactoryGuy.build('project_with_admin') // {id: 3, title: 'Project', user: {id:8, name: 'Admin', type: 'superuser'}
|
252
|
+
var json = FactoryGuy.build('special_project');
|
253
|
+
json.title // => 'Project 1'
|
193
254
|
|
194
|
-
|
195
|
-
//
|
196
|
-
// building json with FactoryGuy.buildList
|
197
|
-
//
|
255
|
+
var project = store.makeFixture('special_project');
|
256
|
+
project.get('title') // => 'Project 2'
|
198
257
|
|
199
|
-
FactoryGuy.buildList('user', 2) // [ {id: 1, name: 'User1', type: 'normal'}, {id: 2, name: 'User2', type: 'normal'} ]
|
200
258
|
```
|
201
259
|
|
260
|
+
### Inline Functions
|
261
|
+
|
262
|
+
- Declare a function for an attribute
|
263
|
+
- Can reference other attributes
|
264
|
+
|
265
|
+
```javascript
|
266
|
+
|
267
|
+
FactoryGuy.define('user', {
|
268
|
+
// Assume that this definition includes the same sequences and default section
|
269
|
+
// from the user definition in: "Declaring sequences in sequences hash" section.
|
270
|
+
|
271
|
+
funny_user: {
|
272
|
+
style: function(f) { return 'funny ' + f.name }
|
273
|
+
}
|
274
|
+
});
|
275
|
+
|
276
|
+
var json = FactoryGuy.build('funny_user');
|
277
|
+
json.name = 'User1'
|
278
|
+
json.style = 'funny User1'
|
279
|
+
|
280
|
+
var user = store.makeFixture('funny_user');
|
281
|
+
user.get('name') // => 'User2'
|
282
|
+
user.get('style') // => 'funny User2'
|
202
283
|
|
203
|
-
|
284
|
+
```
|
204
285
|
|
205
|
-
|
286
|
+
*Note the style attribute was built from a function which depends on the name
|
287
|
+
and the name is a generated attribute from a sequence function*
|
206
288
|
|
207
|
-
###### The Basics
|
208
289
|
|
209
|
-
|
210
|
-
*NOTE* since you are getting a model instances, you can synchronously
|
211
|
-
start asking for data from the model, and its associations
|
290
|
+
### Traits
|
212
291
|
|
292
|
+
- For grouping attributes together
|
293
|
+
- Can use one or more traits in a row
|
294
|
+
- The last trait included overrides any values in traits before it
|
295
|
+
|
213
296
|
```javascript
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
297
|
+
|
298
|
+
FactoryGuy.define('user', {
|
299
|
+
traits: {
|
300
|
+
big: { name: 'Big Guy' }
|
301
|
+
friendly: { style: 'Friendly' }
|
302
|
+
}
|
303
|
+
});
|
304
|
+
|
305
|
+
var json = FactoryGuy.build('user', 'big', 'friendly');
|
306
|
+
json.name // => 'Big Guy'
|
307
|
+
json.style // => 'Friendly'
|
308
|
+
|
309
|
+
var user = store.makeFixture('user', 'big', 'friendly');
|
310
|
+
user.get('name') // => 'Big Guy'
|
311
|
+
user.get('style') // => 'Friendly'
|
312
|
+
|
221
313
|
```
|
222
314
|
|
223
|
-
|
315
|
+
You can still pass in a hash of options when using traits. This hash of
|
316
|
+
attributes will override any trait attributes or default attributes
|
317
|
+
|
318
|
+
```javascript
|
224
319
|
|
225
|
-
|
226
|
-
//
|
227
|
-
|
228
|
-
var user = store.makeFixture('user', {projects: [project]});
|
229
|
-
// OR
|
230
|
-
// setting a model on the belongsTo association
|
231
|
-
var user = store.makeFixture('user');
|
232
|
-
var project = store.makeFixture('project', {user: user});
|
320
|
+
var user = store.makeFixture('user', 'big', 'friendly', {name: 'Dave'});
|
321
|
+
user.get('name') // => 'Dave'
|
322
|
+
user.get('style') // => 'Friendly'
|
233
323
|
|
234
|
-
// will get you the same results, since FactoryGuy makes sure the associations
|
235
|
-
// are created in both directions
|
236
|
-
// user.get('projects.length') == 1;
|
237
|
-
// user.get('projects.firstObject.user') == user;
|
238
324
|
```
|
239
325
|
|
240
|
-
###### Polymorphic hasMany associations
|
241
326
|
|
327
|
+
### Associations
|
328
|
+
|
329
|
+
- Can setup belongsTo or hasMany associations in factory definitions
|
330
|
+
- As inline attribute definition
|
331
|
+
- With traits
|
332
|
+
- Can setup belongsTo or hasMany associations manually
|
333
|
+
- The inverse association is being set up for you
|
334
|
+
|
335
|
+
##### Setup belongsTo associations in Factory Definition
|
336
|
+
|
242
337
|
```javascript
|
243
|
-
//
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
// user.get('hats.length') == 2;
|
248
|
-
// (user.get('hats.firstObject') instanceof BigHat) == true
|
249
|
-
// (user.get('hats.lastObject') instanceof SmallHat) == true
|
338
|
+
// Recall ( from above setup ) that there is a user belongsTo on the Project model
|
339
|
+
// Also, assume 'user' factory is same as from 'user' factory definition above in
|
340
|
+
// 'Defining Factories' section
|
341
|
+
FactoryGuy.define('project', {
|
250
342
|
|
251
|
-
|
343
|
+
project_with_user: {
|
344
|
+
// create user model with default attributes
|
345
|
+
user: {}
|
346
|
+
},
|
347
|
+
project_with_dude: {
|
348
|
+
// create user model with custom attributes
|
349
|
+
user: {name: 'Bob'}
|
350
|
+
},
|
351
|
+
project_with_admin: {
|
352
|
+
// create a named user model with the FactoryGuy.belongsTo helper method
|
353
|
+
user: FactoryGuy.belongsTo('admin')
|
354
|
+
}
|
355
|
+
});
|
356
|
+
|
357
|
+
var json = FactoryGuy.build('project_with_user');
|
358
|
+
json.user // => {id:1, name: 'Dude', style: 'normal'}
|
252
359
|
|
253
|
-
|
254
|
-
|
255
|
-
store.makeFixture('big_hat', {user: user});
|
256
|
-
store.makeFixture('small_hat', {user: user});
|
360
|
+
var json = FactoryGuy.build('project_with_dude');
|
361
|
+
json.user // => {id:1, name: 'Dude', style: 'normal'}
|
257
362
|
|
258
|
-
|
259
|
-
//
|
260
|
-
|
261
|
-
// (user.get('hats.firstObject') instanceof BigHat) == true
|
262
|
-
// (user.get('hats.lastObject') instanceof SmallHat) == true
|
363
|
+
var project = store.makeFixture('project_with_admin');
|
364
|
+
project.get('user.name') // => 'Admin'
|
365
|
+
project.get('user.style') // => 'super'
|
263
366
|
|
264
367
|
```
|
265
368
|
|
266
|
-
|
369
|
+
*You could also accomplish the above with traits:*
|
267
370
|
|
268
371
|
```javascript
|
269
|
-
|
372
|
+
|
373
|
+
FactoryGuy.define('project', {
|
374
|
+
traits: {
|
375
|
+
with_user: { user: {} },
|
376
|
+
with_admin: { user: FactoryGuy.belongsTo('admin') }
|
377
|
+
}
|
378
|
+
});
|
379
|
+
|
380
|
+
var user = store.makeFixture('project', 'with_user');
|
381
|
+
project.get('user').toJSON() // => {id:1, name: 'Dude', style: 'normal'}
|
382
|
+
|
270
383
|
```
|
271
384
|
|
272
|
-
#####DS.Fixture adapter
|
273
385
|
|
274
|
-
|
386
|
+
##### Setup belongsTo associations manually
|
275
387
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
388
|
+
```javascript
|
389
|
+
var user = store.makeFixture('user');
|
390
|
+
var project = store.makeFixture('project', {user: user});
|
391
|
+
|
392
|
+
project.get('user').toJSON() // => {id:1, name: 'Dude', style: 'normal'}
|
393
|
+
```
|
394
|
+
|
395
|
+
*Note that though you are setting the 'user' belongsTo association on a project,
|
396
|
+
the reverse user hasMany 'projects' association is being setup for you on the user
|
397
|
+
( for both manual and factory defined belongsTo associations ) as well*
|
280
398
|
|
281
399
|
```javascript
|
400
|
+
user.get('projects.length') // => 1
|
401
|
+
```
|
282
402
|
|
283
|
-
store.makeFixture('user'); // user.FIXTURES = [{id: 1, name: 'User1', type: 'normal'}]
|
284
|
-
store.makeFixture('user', {name: 'bob'}); // user.FIXTURES = [{id: 2, name: 'bob', type: 'normal'}]
|
285
|
-
store.makeFixture('admin'); // user.FIXTURES = [{id: 3, name: 'Admin', type: 'superuser'}]
|
286
|
-
store.makeFixture('admin', {name: 'Fred'}); // user.FIXTURES = [{id: 4, name: 'Fred', type: 'superuser'}]
|
287
403
|
|
288
404
|
|
289
|
-
|
290
|
-
// you use the ActiveModelAdapter or RESTAdapter the record is returned so you don't
|
291
|
-
// have to then go and find it )
|
292
|
-
var userJson = store.makeFixture('user');
|
293
|
-
store.find('user', userJson.id).then(function(user) {
|
294
|
-
user.toJSON() ( pretty much equals ) userJson;
|
295
|
-
});
|
405
|
+
##### Setup hasMany associations in Factory Definition
|
296
406
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
var
|
407
|
+
``` javascript
|
408
|
+
FactoryGuy.define('user', {
|
409
|
+
user_with_projects: { FactoryGuy.hasMany('project', 2) }
|
410
|
+
});
|
411
|
+
|
412
|
+
var user = store.makeFixture('user_with_projects');
|
413
|
+
user.get('projects.length') // => 2
|
414
|
+
|
415
|
+
```
|
303
416
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
417
|
+
*You could also accomplish the above with traits:*
|
418
|
+
|
419
|
+
```javascript
|
420
|
+
|
421
|
+
FactoryGuy.define('project', {
|
422
|
+
traits: {
|
423
|
+
with_projects: {
|
424
|
+
projects: FactoryGuy.hasMany('project', 2)
|
425
|
+
}
|
426
|
+
}
|
313
427
|
});
|
314
|
-
|
428
|
+
|
429
|
+
var user = store.makeFixture('user', 'with_projects');
|
430
|
+
user.get('projects.length') // => 2
|
431
|
+
|
432
|
+
```
|
433
|
+
|
434
|
+
##### Setup hasMany associations manually
|
315
435
|
|
316
|
-
|
317
|
-
store.
|
436
|
+
```javascript
|
437
|
+
var project1 = store.makeFixture('project');
|
438
|
+
var project2 = store.makeFixture('project');
|
439
|
+
var user = store.makeFixture('user', {projects: [project1,project2]});
|
440
|
+
user.get('projects.length') // => 2
|
441
|
+
|
442
|
+
// or
|
443
|
+
var projects = store.makeList('project', 2);
|
444
|
+
var user = store.makeFixture('user', {projects: projects});
|
445
|
+
user.get('projects.length') // => 2
|
446
|
+
|
447
|
+
```
|
448
|
+
|
449
|
+
*Note that though you are setting the 'projects' hasMany association on a user,
|
450
|
+
the reverse 'user' belongsTo association is being setup for you on the project
|
451
|
+
( for both manual and factory defined hasMany associations ) as well*
|
452
|
+
|
453
|
+
```javascript
|
454
|
+
projects.get('firstObject.user') // => user
|
318
455
|
```
|
319
456
|
|
320
|
-
###Testing models, controllers, views
|
321
457
|
|
458
|
+
#### Building many models at once
|
322
459
|
|
323
|
-
|
460
|
+
- FactoryGuy.buildList
|
461
|
+
- Builds an array of one or more json objects
|
462
|
+
- store.makeList
|
463
|
+
- Loads one or more instances into store
|
324
464
|
|
325
|
-
|
326
|
-
|
465
|
+
|
466
|
+
##### Building json array
|
327
467
|
|
328
468
|
```javascript
|
469
|
+
var json = FactoryGuy.buildList('user', 2)
|
470
|
+
json.length // => 2
|
471
|
+
json[0] // => {id: 1, name: 'User1', style: 'normal'}
|
472
|
+
json[1] // => {id: 2, name: 'User2', style: 'normal'}
|
329
473
|
|
330
|
-
|
474
|
+
```
|
331
475
|
|
332
|
-
|
476
|
+
##### Building model instances
|
477
|
+
|
478
|
+
```javascript
|
479
|
+
var users = store.makeList('user', 2)
|
480
|
+
users.get('length') // => 2
|
481
|
+
users[0].toJSON() // => {id: 3, name: 'User3', style: 'normal'}
|
482
|
+
users[1].toJSON() // => {id: 4, name: 'User4', style: 'normal'}
|
483
|
+
|
484
|
+
```
|
485
|
+
|
486
|
+
|
487
|
+
###Testing models, controllers, views
|
488
|
+
|
489
|
+
- Testing the models, controllers and views in isolation
|
490
|
+
- Use FactoryGuyTestMixin to help with testing
|
333
491
|
|
334
492
|
|
493
|
+
##### Using FactoryGuyTestMixin
|
494
|
+
|
495
|
+
- Using FactoryGuyTestMixin helper methods:
|
496
|
+
- make
|
497
|
+
- teardown
|
498
|
+
|
499
|
+
```javascript
|
500
|
+
|
501
|
+
// Create a helper class using FactoryGuyTestMixin.
|
502
|
+
TestHelper = Ember.Object.createWithMixins(FactoryGuyTestMixin);
|
503
|
+
|
335
504
|
// Then in your tests you can use it like so:
|
336
505
|
|
337
506
|
var testHelper, store;
|
@@ -350,16 +519,6 @@ module('User Model', {
|
|
350
519
|
// to be even more concise in tests you could add this method to your tests
|
351
520
|
var make = function(name, opts) { return testHelper.make(name, opts); }
|
352
521
|
|
353
|
-
|
354
|
-
test("make a user using fixture adapter", function() {
|
355
|
-
// useFixtureAdapter method is built into FactoryGuyTestMixin, and usually
|
356
|
-
// this would be called in the setup function
|
357
|
-
testHelper.useFixtureAdapter();
|
358
|
-
var json = make('user');
|
359
|
-
equal(User.FIXTURES.length, 1);
|
360
|
-
equal(User.FIXTURES[0], json);
|
361
|
-
});
|
362
|
-
|
363
522
|
// assuming your default adapter is ActiveModelAdapter or RESTAdapter
|
364
523
|
test("make a user using your applications default adapter", function() {
|
365
524
|
var user = make('user');
|
@@ -373,6 +532,16 @@ test("make a user using your applications default adapter", function() {
|
|
373
532
|
|
374
533
|
###Integration Tests
|
375
534
|
|
535
|
+
|
536
|
+
##### Using FactoryGuyTestMixin
|
537
|
+
|
538
|
+
- Uses mockjax
|
539
|
+
- Has helper methods
|
540
|
+
- handleFind
|
541
|
+
- handleCreate
|
542
|
+
- handleUpdate
|
543
|
+
- handleDelete
|
544
|
+
|
376
545
|
Since it is recommended to use your normal adapter ( which is usually a subclass of RESTAdapter, )
|
377
546
|
FactoryGuyTestMixin assumes you will want to use that adapter to do your integration tests.
|
378
547
|
|
@@ -453,3 +622,48 @@ test("Creates new project", function() {
|
|
453
622
|
```
|
454
623
|
|
455
624
|
|
625
|
+
### Using DS.Fixture adapter
|
626
|
+
|
627
|
+
- Not recommended
|
628
|
+
- store.makeFixture ... creates model in the store and returns json
|
629
|
+
|
630
|
+
Technically when you call store.makeFixture with a store using the DS.FixtureAdapter,
|
631
|
+
the fixture is actually added to the models FIXTURE array. It just seems to be added
|
632
|
+
to the store because when you call store.find to get that record, the adapter looks
|
633
|
+
in that FIXTURE array to find it and then puts it in the store.
|
634
|
+
|
635
|
+
```javascript
|
636
|
+
|
637
|
+
|
638
|
+
store.makeFixture('user'); // user.FIXTURES = [{id: 1, name: 'User1', style: 'normal'}]
|
639
|
+
store.makeFixture('user', {name: 'bob'}); // user.FIXTURES = [{id: 2, name: 'bob', style: 'normal'}]
|
640
|
+
store.makeFixture('admin'); // user.FIXTURES = [{id: 3, name: 'Admin', style: 'super'}]
|
641
|
+
store.makeFixture('admin', {name: 'Fred'}); // user.FIXTURES = [{id: 4, name: 'Fred', style: 'super'}]
|
642
|
+
|
643
|
+
|
644
|
+
// Use store.find to get the model instance ( Remember this is the Fixture adapter, if
|
645
|
+
// you use the ActiveModelAdapter or RESTAdapter the record is returned so you don't
|
646
|
+
// have to then go and find it )
|
647
|
+
var userJson = store.makeFixture('user');
|
648
|
+
store.find('user', userJson.id).then(function(user) {
|
649
|
+
user.toJSON() ( pretty much equals ) userJson;
|
650
|
+
});
|
651
|
+
|
652
|
+
// and to setup associations ...
|
653
|
+
var projectJson = store.makeFixture('project');
|
654
|
+
var userJson = store.makeFixture('user', {projects: [projectJson.id]});
|
655
|
+
// OR
|
656
|
+
var userJson = store.makeFixture('user');
|
657
|
+
var projectJson = store.makeFixture('project', {user: userJson.id});
|
658
|
+
|
659
|
+
// will give you the same result, but with fixture adapter all associations
|
660
|
+
// are treated as async ( by factory_guy_has_many.js fix ), so it's
|
661
|
+
// a bit clunky to get this associated data. When using DS.FixtureAdapter
|
662
|
+
// in view specs though, this clunk is dealt with for you. But remember,
|
663
|
+
// you DON'T have to use the Fixture adapter.
|
664
|
+
store.find('user', 1).then(function(user) {
|
665
|
+
user.toJSON() (pretty much equals) userJson;
|
666
|
+
user.get('projects').then(function(projects) {
|
667
|
+
projects.length == 1;
|
668
|
+
});
|
669
|
+
});
|
data/bower.json
CHANGED
@@ -438,6 +438,7 @@ FactoryGuy = {
|
|
438
438
|
}
|
439
439
|
},
|
440
440
|
|
441
|
+
|
441
442
|
/**
|
442
443
|
Push fixture to model's FIXTURES array.
|
443
444
|
Used when store's adapter is a DS.FixtureAdapter.
|
@@ -821,11 +822,13 @@ DS.FixtureAdapter.reopen({
|
|
821
822
|
if (belongsToRecord && belongsToRecord.get('length') > 0) {
|
822
823
|
var hasManyName = store.findRelationshipName(
|
823
824
|
'hasMany',
|
824
|
-
belongsToRecord.constructor,
|
825
|
+
belongsToRecord.get('firstObject').constructor,
|
825
826
|
record
|
826
827
|
);
|
827
828
|
belongsToRecord.forEach(function (child){
|
828
|
-
child.get(hasManyName).
|
829
|
+
Em.RSVP.resolve(child.get(hasManyName)).then( function(value) {
|
830
|
+
value.addObjects(record);
|
831
|
+
});
|
829
832
|
});
|
830
833
|
}
|
831
834
|
});
|
@@ -1 +1 @@
|
|
1
|
-
Sequence=function(fn){var index=1;this.next=function(){return fn.call(this,index++)};this.reset=function(){index=1}};function MissingSequenceError(message){this.toString=function(){return message}}ModelDefinition=function(model,config){var sequences={};var traits={};var defaultAttributes={};var namedModels={};var modelId=1;this.model=model;this.matchesName=function(name){return model==name||namedModels[name]};this.merge=function(config){};this.generate=function(name,sequenceFn){if(sequenceFn){if(!sequences[name]){sequences[name]=new Sequence(sequenceFn)}}var sequence=sequences[name];if(!sequence){throw new MissingSequenceError("Can not find that sequence named ["+sequenceName+"] in '"+model+"' definition")}return sequence.next()};this.build=function(name,opts,traitArgs){var traitsObj={};traitArgs.forEach(function(trait){$.extend(traitsObj,traits[trait])});var modelAttributes=namedModels[name]||{};var fixture=$.extend({},defaultAttributes,modelAttributes,traitsObj,opts);for(attribute in fixture){if(Ember.typeOf(fixture[attribute])=="function"){fixture[attribute]=fixture[attribute].call(this,fixture)}else if(Ember.typeOf(fixture[attribute])=="object"){fixture[attribute]=FactoryGuy.build(attribute,fixture[attribute])}}if(!fixture.id){fixture.id=modelId++}return fixture};this.buildList=function(name,number,traits,opts){var arr=[];for(var i=0;i<number;i++){arr.push(this.build(name,opts,traits))}return arr};this.reset=function(){modelId=1;for(name in sequences){sequences[name].reset()}};var parseDefault=function(object){if(!object){return}defaultAttributes=object};var parseTraits=function(object){if(!object){return}traits=object};var parseSequences=function(object){if(!object){return}for(sequenceName in object){var sequenceFn=object[sequenceName];if(Ember.typeOf(sequenceFn)!="function"){throw new Error("Problem with ["+sequenceName+"] sequence definition. Sequences must be functions")}object[sequenceName]=new Sequence(sequenceFn)}sequences=object};var parseConfig=function(config){parseSequences(config.sequences);delete config.sequences;parseTraits(config.traits);delete config.traits;parseDefault(config.default);delete config.default;namedModels=config};parseConfig(config)};FactoryGuy={modelDefinitions:{},define:function(model,config){if(this.modelDefinitions[model]){this.modelDefinitions[model].merge(config)}else{this.modelDefinitions[model]=new ModelDefinition(model,config)}},generate:function(nameOrFunction){var sortaRandomName=Math.floor((1+Math.random())*65536).toString(16)+Date.now();return function(){if(Em.typeOf(nameOrFunction)=="function"){return this.generate(sortaRandomName,nameOrFunction)}else{return this.generate(nameOrFunction)}}},belongsTo:function(fixtureName,opts){return function(){return FactoryGuy.build(fixtureName,opts)}},hasMany:function(fixtureName,number,opts){return function(){return FactoryGuy.buildList(fixtureName,number,opts)}},lookupModelForFixtureName:function(name){var definition=this.lookupDefinitionForFixtureName(name);if(definition){return definition.model}},lookupDefinitionForFixtureName:function(name){for(model in this.modelDefinitions){var definition=this.modelDefinitions[model];if(definition.matchesName(name)){return definition}}},build:function(){var args=Array.prototype.slice.call(arguments);var opts={};var name=args.shift();if(!name){throw new Error("Build needs a factory name to build")}if(Ember.typeOf(args[args.length-1])=="object"){opts=args.pop()}var traits=args;var definition=this.lookupDefinitionForFixtureName(name);if(!definition){throw new Error("Can't find that factory named ["+name+"]")}return definition.build(name,opts,traits)},buildList:function(){var args=Array.prototype.slice.call(arguments);var name=args.shift();var number=args.shift();if(!name||!number){throw new Error("buildList needs a name and a number ( at least ) to build with")}var opts={};if(Ember.typeOf(args[args.length-1])=="object"){opts=args.pop()}var traits=args;var definition=this.lookupDefinitionForFixtureName(name);if(!definition){throw new Error("Can't find that factory named ["+name+"]")}return definition.buildList(name,number,traits,opts)},resetModels:function(store){for(model in this.modelDefinitions){var definition=this.modelDefinitions[model];definition.reset();try{var modelType=store.modelFor(definition.model);if(store.usingFixtureAdapter()){modelType.FIXTURES=[]}store.unloadAll(modelType)}catch(e){}}},pushFixture:function(modelClass,fixture){if(!modelClass["FIXTURES"]){modelClass["FIXTURES"]=[]}modelClass["FIXTURES"].push(fixture);return fixture},clear:function(opts){if(!opts){this.modelDefinitions={}}}};DS.Store.reopen({usingFixtureAdapter:function(){var adapter=this.adapterFor("application");return adapter instanceof DS.FixtureAdapter},makeFixture:function(name,options){var store=this;var modelName=FactoryGuy.lookupModelForFixtureName(name);var fixture=FactoryGuy.build(name,options);var modelType=store.modelFor(modelName);if(this.usingFixtureAdapter()){this.setAssociationsForFixtureAdapter(modelType,modelName,fixture);return FactoryGuy.pushFixture(modelType,fixture)}else{var store=this;var model;Em.run(function(){store.findEmbeddedBelongsToAssociationsForRESTAdapter(modelType,fixture);if(fixture.type){modelName=fixture.type.underscore();modelType=store.modelFor(modelName)}model=store.push(modelName,fixture);store.setAssociationsForRESTAdapter(modelType,modelName,model)});return model}},makeList:function(name,number,options){var arr=[];for(var i=0;i<number;i++){arr.push(this.makeFixture(name,options))}return arr},setAssociationsForFixtureAdapter:function(modelType,modelName,fixture){var self=this;var adapter=this.adapterFor("application");Ember.get(modelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="hasMany"){if(fixture[relationship.key]){fixture[relationship.key].forEach(function(id){var hasManyfixtures=adapter.fixturesForType(relationship.type);var fixture=adapter.findFixtureById(hasManyfixtures,id);fixture[modelName]=fixture.id})}}if(relationship.kind=="belongsTo"){var belongsToRecord=fixture[relationship.key];if(belongsToRecord){if(typeof belongsToRecord=="object"){FactoryGuy.pushFixture(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord.id}var hasManyName=self.findHasManyRelationshipNameForFixtureAdapter(relationship.type,relationship.parentType);var belongsToFixtures=adapter.fixturesForType(relationship.type);var belongsTofixture=adapter.findFixtureById(belongsToFixtures,fixture[relationship.key]);if(!belongsTofixture[hasManyName]){belongsTofixture[hasManyName]=[]}belongsTofixture[hasManyName].push(fixture.id)}}})},findEmbeddedBelongsToAssociationsForRESTAdapter:function(modelType,fixture){var store=this;Ember.get(modelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="belongsTo"){var belongsToRecord=fixture[relationship.key];if(Ember.typeOf(belongsToRecord)=="object"){belongsToRecord=store.push(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord}}if(relationship.kind=="hasMany"){var hasManyRecords=fixture[relationship.key];if(Ember.typeOf(hasManyRecords)=="array"&&Ember.typeOf(hasManyRecords[0])=="object"){var records=Em.A();hasManyRecords.forEach(function(record){var record=store.push(relationship.type,record);records.push(record)});fixture[relationship.key]=records}}})},setAssociationsForRESTAdapter:function(modelType,modelName,model){var self=this;Ember.get(modelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="hasMany"){var children=model.get(name)||[];children.forEach(function(child){var belongsToName=self.findRelationshipName("belongsTo",child.constructor,model);var hasManyName=self.findRelationshipName("hasMany",child.constructor,model);var inverseName=relationship.options&&relationship.options.inverse;if(belongsToName){child.set(belongsToName||inverseName,model)}else if(hasManyName){relation=child.get(hasManyName||inverseName)||[];relation.pushObject(model)}})}if(relationship.kind=="belongsTo"){var belongsToRecord=model.get(name);if(belongsToRecord){var setAssociations=function(){var hasManyName=self.findRelationshipName("hasMany",belongsToRecord.constructor,model);if(hasManyName){belongsToRecord.get(hasManyName).addObject(model);return}var oneToOneName=self.findRelationshipName("belongsTo",belongsToRecord.constructor,model);if(oneToOneName&&!(belongsToRecord.constructor==model.constructor)){belongsToRecord.set(oneToOneName,model)}};if(belongsToRecord.then){belongsToRecord.then(function(record){belongsToRecord=record;setAssociations()})}else{setAssociations()}}}})},findRelationshipName:function(kind,belongToModelType,childModel){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind==kind&&childModel instanceof relationship.type){relationshipName=relationship.key}});return relationshipName},findHasManyRelationshipNameForFixtureAdapter:function(belongToModelType,childModelType){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="hasMany"&&childModelType==relationship.type){relationshipName=relationship.key}});return relationshipName},pushPayload:function(type,payload){if(this.usingFixtureAdapter()){var model=this.modelFor(modelName);FactoryGuy.pushFixture(model,payload)}else{this._super(type,payload)}}});DS.FixtureAdapter.reopen({createRecord:function(store,type,record){var promise=this._super(store,type,record);promise.then(function(){Em.RSVP.Promise.resolve(Ember.get(type,"relationshipNames")).then(function(relationShips){if(relationShips.belongsTo){relationShips.belongsTo.forEach(function(relationship){Em.RSVP.Promise.resolve(record.get(relationship)).then(function(belongsToRecord){if(belongsToRecord){var hasManyName=store.findRelationshipName("hasMany",belongsToRecord.constructor,record);if(hasManyName){Ember.RSVP.resolve(belongsToRecord.get(hasManyName)).then(function(relationship){relationship.addObject(record)})}}})})}if(relationShips.hasMany){relationShips.hasMany.forEach(function(relationship){Em.RSVP.Promise.resolve(record.get(relationship)).then(function(belongsToRecord){if(belongsToRecord&&belongsToRecord.get("length")>0){var hasManyName=store.findRelationshipName("hasMany",belongsToRecord.constructor,record);belongsToRecord.forEach(function(child){child.get(hasManyName).addObject(record)})}})})}})});return promise}});FactoryGuyTestMixin=Em.Mixin.create({setup:function(app){this.set("container",app.__container__);return this},useFixtureAdapter:function(app){app.ApplicationAdapter=DS.FixtureAdapter;this.getStore().adapterFor("application").simulateRemoteResponse=false},usingActiveModelSerializer:function(type){var store=this.getStore();var type=store.modelFor(type);var serializer=store.serializerFor(type.typeKey);return serializer instanceof DS.ActiveModelSerializer},find:function(type,id){return this.getStore().find(type,id)},make:function(name,opts){return this.getStore().makeFixture(name,opts)},getStore:function(){return this.get("container").lookup("store:main")},pushPayload:function(type,hash){return this.getStore().pushPayload(type,hash)},pushRecord:function(type,hash){return this.getStore().push(type,hash)},stubEndpointForHttpRequest:function(url,json,options){options=options||{};var request={url:url,dataType:"json",responseText:json,type:options.type||"GET",status:options.status||200};if(options.data){request.data=options.data}$.mockjax(request)},buildAjaxHttpResponse:function(name,opts){var fixture=FactoryGuy.build(name,opts);var modelName=FactoryGuy.lookupModelForFixtureName(name);if(this.usingActiveModelSerializer(modelName)){this.toSnakeCase(fixture)}var hash={};hash[modelName]=fixture;return hash},toSnakeCase:function(fixture){for(key in fixture){if(key!=Em.String.decamelize(key)){var value=fixture[key];delete fixture[key];fixture[Em.String.decamelize(key)]=value}}},buildURL:function(type,id){return this.getStore().adapterFor("application").buildURL(type,id)},handleFind:function(name,opts,status){var modelName=FactoryGuy.lookupModelForFixtureName(name);var responseJson=this.buildAjaxHttpResponse(name,opts);var id=responseJson[modelName].id;var url=this.buildURL(modelName,id);this.stubEndpointForHttpRequest(url,responseJson,{type:"GET",status:status||200});return responseJson},handleCreate:function(name,opts,status){var modelName=FactoryGuy.lookupModelForFixtureName(name);var responseJson=this.buildAjaxHttpResponse(name,opts);var url=this.buildURL(modelName);this.stubEndpointForHttpRequest(url,responseJson,{type:"POST",status:status||200});return responseJson},handleUpdate:function(type,id,status){this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"PUT",status:status||200})},handleDelete:function(type,id,status){this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"DELETE",status:status||200})},teardown:function(){FactoryGuy.resetModels(this.getStore())}});
|
1
|
+
Sequence=function(fn){var index=1;this.next=function(){return fn.call(this,index++)};this.reset=function(){index=1}};function MissingSequenceError(message){this.toString=function(){return message}}ModelDefinition=function(model,config){var sequences={};var traits={};var defaultAttributes={};var namedModels={};var modelId=1;this.model=model;this.matchesName=function(name){return model==name||namedModels[name]};this.merge=function(config){};this.generate=function(name,sequenceFn){if(sequenceFn){if(!sequences[name]){sequences[name]=new Sequence(sequenceFn)}}var sequence=sequences[name];if(!sequence){throw new MissingSequenceError("Can not find that sequence named ["+sequenceName+"] in '"+model+"' definition")}return sequence.next()};this.build=function(name,opts,traitArgs){var traitsObj={};traitArgs.forEach(function(trait){$.extend(traitsObj,traits[trait])});var modelAttributes=namedModels[name]||{};var fixture=$.extend({},defaultAttributes,modelAttributes,traitsObj,opts);for(attribute in fixture){if(Ember.typeOf(fixture[attribute])=="function"){fixture[attribute]=fixture[attribute].call(this,fixture)}else if(Ember.typeOf(fixture[attribute])=="object"){fixture[attribute]=FactoryGuy.build(attribute,fixture[attribute])}}if(!fixture.id){fixture.id=modelId++}return fixture};this.buildList=function(name,number,traits,opts){var arr=[];for(var i=0;i<number;i++){arr.push(this.build(name,opts,traits))}return arr};this.reset=function(){modelId=1;for(name in sequences){sequences[name].reset()}};var parseDefault=function(object){if(!object){return}defaultAttributes=object};var parseTraits=function(object){if(!object){return}traits=object};var parseSequences=function(object){if(!object){return}for(sequenceName in object){var sequenceFn=object[sequenceName];if(Ember.typeOf(sequenceFn)!="function"){throw new Error("Problem with ["+sequenceName+"] sequence definition. Sequences must be functions")}object[sequenceName]=new Sequence(sequenceFn)}sequences=object};var parseConfig=function(config){parseSequences(config.sequences);delete config.sequences;parseTraits(config.traits);delete config.traits;parseDefault(config.default);delete config.default;namedModels=config};parseConfig(config)};FactoryGuy={modelDefinitions:{},define:function(model,config){if(this.modelDefinitions[model]){this.modelDefinitions[model].merge(config)}else{this.modelDefinitions[model]=new ModelDefinition(model,config)}},generate:function(nameOrFunction){var sortaRandomName=Math.floor((1+Math.random())*65536).toString(16)+Date.now();return function(){if(Em.typeOf(nameOrFunction)=="function"){return this.generate(sortaRandomName,nameOrFunction)}else{return this.generate(nameOrFunction)}}},belongsTo:function(fixtureName,opts){return function(){return FactoryGuy.build(fixtureName,opts)}},association:function(fixtureName,opts){console.log("DEPRECATION Warning: use FactoryGuy.belongsTo instead");return this.belongsTo(fixtureName,opts)},hasMany:function(fixtureName,number,opts){return function(){return FactoryGuy.buildList(fixtureName,number,opts)}},lookupModelForFixtureName:function(name){var definition=this.lookupDefinitionForFixtureName(name);if(definition){return definition.model}},lookupDefinitionForFixtureName:function(name){for(model in this.modelDefinitions){var definition=this.modelDefinitions[model];if(definition.matchesName(name)){return definition}}},build:function(){var args=Array.prototype.slice.call(arguments);var opts={};var name=args.shift();if(!name){throw new Error("Build needs a factory name to build")}if(Ember.typeOf(args[args.length-1])=="object"){opts=args.pop()}var traits=args;var definition=this.lookupDefinitionForFixtureName(name);if(!definition){throw new Error("Can't find that factory named ["+name+"]")}return definition.build(name,opts,traits)},buildList:function(){var args=Array.prototype.slice.call(arguments);var name=args.shift();var number=args.shift();if(!name||!number){throw new Error("buildList needs a name and a number ( at least ) to build with")}var opts={};if(Ember.typeOf(args[args.length-1])=="object"){opts=args.pop()}var traits=args;var definition=this.lookupDefinitionForFixtureName(name);if(!definition){throw new Error("Can't find that factory named ["+name+"]")}return definition.buildList(name,number,traits,opts)},resetModels:function(store){for(model in this.modelDefinitions){var definition=this.modelDefinitions[model];definition.reset();try{var modelType=store.modelFor(definition.model);if(store.usingFixtureAdapter()){modelType.FIXTURES=[]}store.unloadAll(modelType)}catch(e){}}},pushFixture:function(modelClass,fixture){if(!modelClass["FIXTURES"]){modelClass["FIXTURES"]=[]}modelClass["FIXTURES"].push(fixture);return fixture},clear:function(opts){if(!opts){this.modelDefinitions={}}}};DS.Store.reopen({usingFixtureAdapter:function(){var adapter=this.adapterFor("application");return adapter instanceof DS.FixtureAdapter},makeFixture:function(name,options){var store=this;var modelName=FactoryGuy.lookupModelForFixtureName(name);var fixture=FactoryGuy.build(name,options);var modelType=store.modelFor(modelName);if(this.usingFixtureAdapter()){this.setAssociationsForFixtureAdapter(modelType,modelName,fixture);return FactoryGuy.pushFixture(modelType,fixture)}else{var store=this;var model;Em.run(function(){store.findEmbeddedBelongsToAssociationsForRESTAdapter(modelType,fixture);if(fixture.type){modelName=fixture.type.underscore();modelType=store.modelFor(modelName)}model=store.push(modelName,fixture);store.setAssociationsForRESTAdapter(modelType,modelName,model)});return model}},makeList:function(name,number,options){var arr=[];for(var i=0;i<number;i++){arr.push(this.makeFixture(name,options))}return arr},setAssociationsForFixtureAdapter:function(modelType,modelName,fixture){var self=this;var adapter=this.adapterFor("application");Ember.get(modelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="hasMany"){if(fixture[relationship.key]){fixture[relationship.key].forEach(function(id){var hasManyfixtures=adapter.fixturesForType(relationship.type);var fixture=adapter.findFixtureById(hasManyfixtures,id);fixture[modelName]=fixture.id})}}if(relationship.kind=="belongsTo"){var belongsToRecord=fixture[relationship.key];if(belongsToRecord){if(typeof belongsToRecord=="object"){FactoryGuy.pushFixture(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord.id}var hasManyName=self.findHasManyRelationshipNameForFixtureAdapter(relationship.type,relationship.parentType);var belongsToFixtures=adapter.fixturesForType(relationship.type);var belongsTofixture=adapter.findFixtureById(belongsToFixtures,fixture[relationship.key]);if(!belongsTofixture[hasManyName]){belongsTofixture[hasManyName]=[]}belongsTofixture[hasManyName].push(fixture.id)}}})},findEmbeddedBelongsToAssociationsForRESTAdapter:function(modelType,fixture){var store=this;Ember.get(modelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="belongsTo"){var belongsToRecord=fixture[relationship.key];if(Ember.typeOf(belongsToRecord)=="object"){belongsToRecord=store.push(relationship.type,belongsToRecord);fixture[relationship.key]=belongsToRecord}}if(relationship.kind=="hasMany"){var hasManyRecords=fixture[relationship.key];if(Ember.typeOf(hasManyRecords)=="array"&&Ember.typeOf(hasManyRecords[0])=="object"){var records=Em.A();hasManyRecords.forEach(function(record){var record=store.push(relationship.type,record);records.push(record)});fixture[relationship.key]=records}}})},setAssociationsForRESTAdapter:function(modelType,modelName,model){var self=this;Ember.get(modelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="hasMany"){var children=model.get(name)||[];children.forEach(function(child){var belongsToName=self.findRelationshipName("belongsTo",child.constructor,model);var hasManyName=self.findRelationshipName("hasMany",child.constructor,model);var inverseName=relationship.options&&relationship.options.inverse;if(belongsToName){child.set(belongsToName||inverseName,model)}else if(hasManyName){relation=child.get(hasManyName||inverseName)||[];relation.pushObject(model)}})}if(relationship.kind=="belongsTo"){var belongsToRecord=model.get(name);if(belongsToRecord){var setAssociations=function(){var hasManyName=self.findRelationshipName("hasMany",belongsToRecord.constructor,model);if(hasManyName){belongsToRecord.get(hasManyName).addObject(model);return}var oneToOneName=self.findRelationshipName("belongsTo",belongsToRecord.constructor,model);if(oneToOneName&&!(belongsToRecord.constructor==model.constructor)){belongsToRecord.set(oneToOneName,model)}};if(belongsToRecord.then){belongsToRecord.then(function(record){belongsToRecord=record;setAssociations()})}else{setAssociations()}}}})},findRelationshipName:function(kind,belongToModelType,childModel){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind==kind&&childModel instanceof relationship.type){relationshipName=relationship.key}});return relationshipName},findHasManyRelationshipNameForFixtureAdapter:function(belongToModelType,childModelType){var relationshipName;Ember.get(belongToModelType,"relationshipsByName").forEach(function(name,relationship){if(relationship.kind=="hasMany"&&childModelType==relationship.type){relationshipName=relationship.key}});return relationshipName},pushPayload:function(type,payload){if(this.usingFixtureAdapter()){var model=this.modelFor(modelName);FactoryGuy.pushFixture(model,payload)}else{this._super(type,payload)}}});DS.FixtureAdapter.reopen({createRecord:function(store,type,record){var promise=this._super(store,type,record);promise.then(function(){Em.RSVP.Promise.resolve(Ember.get(type,"relationshipNames")).then(function(relationShips){if(relationShips.belongsTo){relationShips.belongsTo.forEach(function(relationship){Em.RSVP.Promise.resolve(record.get(relationship)).then(function(belongsToRecord){if(belongsToRecord){var hasManyName=store.findRelationshipName("hasMany",belongsToRecord.constructor,record);if(hasManyName){Ember.RSVP.resolve(belongsToRecord.get(hasManyName)).then(function(relationship){relationship.addObject(record)})}}})})}if(relationShips.hasMany){relationShips.hasMany.forEach(function(relationship){Em.RSVP.Promise.resolve(record.get(relationship)).then(function(belongsToRecord){if(belongsToRecord&&belongsToRecord.get("length")>0){var hasManyName=store.findRelationshipName("hasMany",belongsToRecord.get("firstObject").constructor,record);belongsToRecord.forEach(function(child){Em.RSVP.resolve(child.get(hasManyName)).then(function(value){value.addObjects(record)})})}})})}})});return promise}});FactoryGuyTestMixin=Em.Mixin.create({setup:function(app){this.set("container",app.__container__);return this},useFixtureAdapter:function(app){app.ApplicationAdapter=DS.FixtureAdapter;this.getStore().adapterFor("application").simulateRemoteResponse=false},usingActiveModelSerializer:function(type){var store=this.getStore();var type=store.modelFor(type);var serializer=store.serializerFor(type.typeKey);return serializer instanceof DS.ActiveModelSerializer},find:function(type,id){return this.getStore().find(type,id)},make:function(name,opts){return this.getStore().makeFixture(name,opts)},getStore:function(){return this.get("container").lookup("store:main")},pushPayload:function(type,hash){return this.getStore().pushPayload(type,hash)},pushRecord:function(type,hash){return this.getStore().push(type,hash)},stubEndpointForHttpRequest:function(url,json,options){options=options||{};var request={url:url,dataType:"json",responseText:json,type:options.type||"GET",status:options.status||200};if(options.data){request.data=options.data}$.mockjax(request)},buildAjaxHttpResponse:function(name,opts){var fixture=FactoryGuy.build(name,opts);var modelName=FactoryGuy.lookupModelForFixtureName(name);if(this.usingActiveModelSerializer(modelName)){this.toSnakeCase(fixture)}var hash={};hash[modelName]=fixture;return hash},toSnakeCase:function(fixture){for(key in fixture){if(key!=Em.String.decamelize(key)){var value=fixture[key];delete fixture[key];fixture[Em.String.decamelize(key)]=value}}},buildURL:function(type,id){return this.getStore().adapterFor("application").buildURL(type,id)},handleFind:function(name,opts,status){var modelName=FactoryGuy.lookupModelForFixtureName(name);var responseJson=this.buildAjaxHttpResponse(name,opts);var id=responseJson[modelName].id;var url=this.buildURL(modelName,id);this.stubEndpointForHttpRequest(url,responseJson,{type:"GET",status:status||200});return responseJson},handleCreate:function(name,opts,status){var modelName=FactoryGuy.lookupModelForFixtureName(name);var responseJson=this.buildAjaxHttpResponse(name,opts);var url=this.buildURL(modelName);this.stubEndpointForHttpRequest(url,responseJson,{type:"POST",status:status||200});return responseJson},handleUpdate:function(type,id,status){this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"PUT",status:status||200})},handleDelete:function(type,id,status){this.stubEndpointForHttpRequest(this.buildURL(type,id),{},{type:"DELETE",status:status||200})},teardown:function(){FactoryGuy.resetModels(this.getStore())}});
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = "ember-data-factory-guy"
|
4
|
-
s.version = "0.6.
|
4
|
+
s.version = "0.6.2"
|
5
5
|
s.platform = Gem::Platform::RUBY
|
6
6
|
s.authors = ["Daniel Sudol", "Alex Opak"]
|
7
7
|
s.email = ["dansudol@yahoo.com", "opak.alexandr@gmail.com"]
|
data/package.json
CHANGED
data/src/factory_guy.js
CHANGED
data/src/store.js
CHANGED
@@ -356,11 +356,13 @@ DS.FixtureAdapter.reopen({
|
|
356
356
|
if (belongsToRecord && belongsToRecord.get('length') > 0) {
|
357
357
|
var hasManyName = store.findRelationshipName(
|
358
358
|
'hasMany',
|
359
|
-
belongsToRecord.constructor,
|
359
|
+
belongsToRecord.get('firstObject').constructor,
|
360
360
|
record
|
361
361
|
);
|
362
362
|
belongsToRecord.forEach(function (child){
|
363
|
-
child.get(hasManyName).
|
363
|
+
Em.RSVP.resolve(child.get(hasManyName)).then( function(value) {
|
364
|
+
value.addObjects(record);
|
365
|
+
});
|
364
366
|
});
|
365
367
|
}
|
366
368
|
});
|
data/tests/factory_guy_test.js
CHANGED
@@ -173,14 +173,18 @@ asyncTest("#createRecord can work for one-to-none associations", function () {
|
|
173
173
|
asyncTest("#createRecord adds hasMany association to records it hasMany of ", function () {
|
174
174
|
var usersJson = store.makeList('user', 3);
|
175
175
|
|
176
|
-
|
176
|
+
var user1Promise = store.find('user', usersJson[0].id)
|
177
|
+
var user2Promise = store.find('user', usersJson[1].id)
|
178
|
+
var user3Promise = store.find('user', usersJson[2].id)
|
179
|
+
Em.RSVP.all([user1Promise, user2Promise, user3Promise]).then(function (users) {
|
177
180
|
|
178
181
|
var propertyJson = {name: 'beach front property'};
|
179
182
|
|
180
|
-
property = store.createRecord('property', propertyJson);
|
183
|
+
var property = store.createRecord('property', propertyJson);
|
181
184
|
property.get('owners').then(function (owners) {
|
182
185
|
owners.addObjects(users);
|
183
|
-
|
186
|
+
return property.save();
|
187
|
+
}).then(function (property) {
|
184
188
|
return property.get('owners');
|
185
189
|
}).then(function (users) {
|
186
190
|
equal(users.get('length'), usersJson.length);
|
@@ -285,6 +285,11 @@ FactoryGuy = {
|
|
285
285
|
}
|
286
286
|
},
|
287
287
|
|
288
|
+
association: function(fixtureName, opts) {
|
289
|
+
console.log('DEPRECATION Warning: use FactoryGuy.belongsTo instead')
|
290
|
+
return this.belongsTo(fixtureName, opts);
|
291
|
+
},
|
292
|
+
|
288
293
|
/**
|
289
294
|
Used in model definitions to define a hasMany association attribute.
|
290
295
|
For example:
|
@@ -433,6 +438,7 @@ FactoryGuy = {
|
|
433
438
|
}
|
434
439
|
},
|
435
440
|
|
441
|
+
|
436
442
|
/**
|
437
443
|
Push fixture to model's FIXTURES array.
|
438
444
|
Used when store's adapter is a DS.FixtureAdapter.
|
@@ -816,11 +822,13 @@ DS.FixtureAdapter.reopen({
|
|
816
822
|
if (belongsToRecord && belongsToRecord.get('length') > 0) {
|
817
823
|
var hasManyName = store.findRelationshipName(
|
818
824
|
'hasMany',
|
819
|
-
belongsToRecord.constructor,
|
825
|
+
belongsToRecord.get('firstObject').constructor,
|
820
826
|
record
|
821
827
|
);
|
822
828
|
belongsToRecord.forEach(function (child){
|
823
|
-
child.get(hasManyName).
|
829
|
+
Em.RSVP.resolve(child.get(hasManyName)).then( function(value) {
|
830
|
+
value.addObjects(record);
|
831
|
+
});
|
824
832
|
});
|
825
833
|
}
|
826
834
|
});
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ember-data-factory-guy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Sudol
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-09-04 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Easily create Fixtures for Ember Data
|
15
15
|
email:
|