ember-data-factory-guy 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/Gruntfile.js +45 -0
- data/README.md +118 -0
- data/bower.json +35 -0
- data/dist/ember-data-factory-guy.js +376 -0
- data/dist/ember-data-factory-guy.min.js +1 -0
- data/ember-data-factory-guy.gemspec +20 -0
- data/lib/ember-data-factory-guy.rb +13 -0
- data/package.json +41 -0
- data/src/factory_guy.js +144 -0
- data/src/factory_guy_helper_mixin.js +89 -0
- data/src/has_many.js +188 -0
- data/src/store.js +142 -0
- data/tests/active_model_adapter_factory_test.js +63 -0
- data/tests/factory_guy_test.js +0 -0
- data/tests/fixture_adapter_factory_test.js +134 -0
- data/tests/index.html +38 -0
- data/tests/rest_adapter_factory_test.js +61 -0
- data/tests/store_test.js +35 -0
- data/tests/support/factories/project_factory.js +3 -0
- data/tests/support/factories/user_factory.js +10 -0
- data/tests/support/models/project.js +4 -0
- data/tests/support/models/user.js +4 -0
- data/tests/support/test_helper.js +23 -0
- data/tests/test_setup.js +22 -0
- data/vendor/assets/javascripts/ember_data_factory_guy.js +376 -0
- data/vendor/assets/javascripts/factory_guy_has_many.js +188 -0
- metadata +76 -0
@@ -0,0 +1 @@
|
|
1
|
+
FactoryGuy=Ember.Object.reopenClass({fixtureStore:{},fixtureLookup:{},modelIds:{},define:function(model,config){var info=this.getModelInfo(model);for(key in config){var value=config[key];info[key]=value;if(key!="default"){this.fixtureLookup[key]=model}}this.modelIds[model]=0},getModelInfo:function(model){if(!this.fixtureStore[model]){this.fixtureStore[model]={}}return this.fixtureStore[model]},lookupModelForName:function(name){var model=this.fixtureLookup[name];if(!model){if(this.fixtureStore[name]){model=name}}return model},generateId:function(model){var lastId=this.modelIds[model]||0;this.modelIds[model]=lastId+1;return this.modelIds[model]},build:function(name,opts){var model=this.lookupModelForName(name);if(!model){throw new Error("can't find that factory named ["+name+"]")}var modelInfo=this.fixtureStore[model];var modelAttributes=modelInfo[name]||{};var defaultModelAttributes=modelInfo.default;var fixture=$.extend({},defaultModelAttributes,modelAttributes,opts);fixture.id=this.generateId(model);return fixture},resetModels:function(store){var typeMaps=store.typeMaps;if(store.usingFixtureAdapter()){for(typeKey in this.fixtureStore){var modelType=store.modelFor(typeKey);modelType.FIXTURES=[];store.unloadAll(modelType)}}else{for(model in typeMaps){if(typeMaps[model].type.typeKey!="user"){store.unloadAll(typeMaps[model].type)}}}this.modelIds={}},pushFixture:function(modelClass,fixture){if(!modelClass["FIXTURES"]){modelClass["FIXTURES"]=[]}modelClass["FIXTURES"].push(fixture);return fixture}});DS.Store.reopen({usingFixtureAdapter:function(){var adapter=this.adapterFor("application");return adapter instanceof DS.FixtureAdapter},makeFixture:function(name,options){var modelName=FactoryGuy.lookupModelForName(name);var fixture=FactoryGuy.build(name,options);var modelType=this.modelFor(modelName);if(this.usingFixtureAdapter()){this.setBelongsToFixturesAssociation(modelType,modelName,fixture);return FactoryGuy.pushFixture(modelType,fixture)}else{var self=this;var model;Em.run(function(){model=self.push(modelName,fixture);self.setBelongsToRestAssociation(modelType,modelName,model)});return model}},setBelongsToFixturesAssociation:function(modelType,modelName,parentFixture){var store=this;var adapter=this.adapterFor("application");var relationShips=Ember.get(modelType,"relationshipNames");if(relationShips.hasMany){relationShips.hasMany.forEach(function(relationship){var hasManyModel=store.modelFor(Em.String.singularize(relationship));if(parentFixture[relationship]){parentFixture[relationship].forEach(function(id){var hasManyfixtures=adapter.fixturesForType(hasManyModel);var fixture=adapter.findFixtureById(hasManyfixtures,id);fixture[modelName]=parentFixture.id})}})}},setBelongsToRestAssociation:function(modelType,modelName,parent){var relationShips=Ember.get(modelType,"relationshipNames");if(relationShips.hasMany){relationShips.hasMany.forEach(function(name){var children=parent.get(name);if(children.get("length")>0){children.forEach(function(child){child.set(modelName,parent)})}})}},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(){var hasManyName=Ember.String.pluralize(type.typeKey);var relationShips=Ember.get(type,"relationshipNames");if(relationShips.belongsTo){}});return promise}});FactoryGuyHelperMixin=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},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)},handleCreate:function(name,opts){var model=FactoryGuy.lookupModelForName(name);this.stubEndpointForHttpRequest("/"+Em.String.pluralize(model),this.buildAjaxResponse(name,opts),{type:"POST"})},buildAjaxResponse:function(name,opts){var fixture=FactoryGuy.build(name,opts);var model=FactoryGuy.lookupModelForName(name);var hash={};hash[model]=fixture;return hash},handleUpdate:function(root,id){this.stubEndpointForHttpRequest("/"+Em.String.pluralize(root)+"/"+id,"{}",{type:"PUT"})},handleDelete:function(root,id){this.stubEndpointForHttpRequest("/"+Em.String.pluralize(root)+"/"+id,{},{type:"DELETE"})},teardown:function(){FactoryGuy.resetModels(this.getStore())}});
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = "ember-data-factory-guy"
|
4
|
+
s.version = "0.1"
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.authors = ["daniel sudol", "alex opak"]
|
7
|
+
s.email = ["dansudol@yahoo.com"]
|
8
|
+
s.homepage = "http://rubygems.org/gems/ember-data-factory-guy"
|
9
|
+
s.summary = "Create Fixtures for Ember Data"
|
10
|
+
s.description = "Create Fixtures for Ember Data"
|
11
|
+
s.license = "MIT"
|
12
|
+
|
13
|
+
s.required_rubygems_version = ">= 1.3.6"
|
14
|
+
s.rubyforge_project = "ember-data-factory-guy"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {tests}/*`.split("\n")
|
18
|
+
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module EmberDataFixtureFactory
|
2
|
+
if defined? ::Rails::Engine
|
3
|
+
# auto wire assets as Rails Engine
|
4
|
+
class Rails < ::Rails::Engine
|
5
|
+
end
|
6
|
+
|
7
|
+
elsif defined? ::Sprockets
|
8
|
+
root_dir = File.expand_path(File.dirname(File.dirname(__FILE__)))
|
9
|
+
# Set up asset paths for Sprockets apps
|
10
|
+
p "root_dir #{root_dir} #{File.join(root_dir, "vendor", "assets", "javascripts")}"
|
11
|
+
::Sprockets.append_path File.join(root_dir, "vendor", "assets", "javascripts")
|
12
|
+
end
|
13
|
+
end
|
data/package.json
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
{
|
2
|
+
"name": "ember-data-factory-guy",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"authors": [
|
5
|
+
"Opak Alex <opak.alexandr@gmail.com>",
|
6
|
+
"Daniel Sudol <dansudol@yahoo.com>"
|
7
|
+
],
|
8
|
+
"description": "Factory for testing Ember application",
|
9
|
+
"main": "dist/ember-data-factory-guy.js",
|
10
|
+
"keywords": [
|
11
|
+
"Factory",
|
12
|
+
"Ember",
|
13
|
+
"Data",
|
14
|
+
"FixtureAdapter"
|
15
|
+
],
|
16
|
+
"license": "MIT",
|
17
|
+
|
18
|
+
"bugs": {
|
19
|
+
"url": ""
|
20
|
+
},
|
21
|
+
|
22
|
+
"scripts": {
|
23
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
24
|
+
},
|
25
|
+
|
26
|
+
"repository": {
|
27
|
+
"type": "git",
|
28
|
+
"url": ""
|
29
|
+
},
|
30
|
+
|
31
|
+
"devDependencies": [
|
32
|
+
"grunt-contrib-coffee",
|
33
|
+
"grunt-contrib-concat",
|
34
|
+
"grunt-contrib-uglify",
|
35
|
+
"grunt-contrib-qunit"
|
36
|
+
],
|
37
|
+
|
38
|
+
"engines": {
|
39
|
+
"node": ">=v0.10.1"
|
40
|
+
}
|
41
|
+
}
|
data/src/factory_guy.js
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
FactoryGuy = Ember.Object.reopenClass({
|
2
|
+
fixtureStore: {},
|
3
|
+
fixtureLookup: {},
|
4
|
+
modelIds: {},
|
5
|
+
|
6
|
+
/**
|
7
|
+
```javascript
|
8
|
+
|
9
|
+
User = DS.Model.extend({
|
10
|
+
name: DS.attr('string'),
|
11
|
+
})
|
12
|
+
|
13
|
+
FactoryGuy.define('user', {
|
14
|
+
default: {
|
15
|
+
name: "Fred"
|
16
|
+
},
|
17
|
+
|
18
|
+
bob: {
|
19
|
+
name: "Bob"
|
20
|
+
}
|
21
|
+
});
|
22
|
+
|
23
|
+
```
|
24
|
+
|
25
|
+
For the User model, you can define fixtures like 'bob' or just use 'user'
|
26
|
+
and get default values.
|
27
|
+
|
28
|
+
And to get those fixtures you would call them this way:
|
29
|
+
|
30
|
+
FactoryGuy.build('user') or FactoryGuy.build('bob')
|
31
|
+
|
32
|
+
@param model the model to define
|
33
|
+
@param config your default and specific fixtures
|
34
|
+
*/
|
35
|
+
define: function (model, config) {
|
36
|
+
var info = this.getModelInfo(model);
|
37
|
+
for (key in config) {
|
38
|
+
var value = config[key];
|
39
|
+
info[key] = value;
|
40
|
+
if (key != 'default') {
|
41
|
+
this.fixtureLookup[key] = model;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
// setup id
|
45
|
+
this.modelIds[model] = 0;
|
46
|
+
},
|
47
|
+
|
48
|
+
/**
|
49
|
+
|
50
|
+
@param model
|
51
|
+
*/
|
52
|
+
getModelInfo: function (model) {
|
53
|
+
if (!this.fixtureStore[model]) {
|
54
|
+
this.fixtureStore[model] = {};
|
55
|
+
}
|
56
|
+
return this.fixtureStore[model];
|
57
|
+
},
|
58
|
+
|
59
|
+
/**
|
60
|
+
|
61
|
+
@param name fixture name
|
62
|
+
@returns model associated with fixture name
|
63
|
+
*/
|
64
|
+
lookupModelForName: function (name) {
|
65
|
+
var model = this.fixtureLookup[name];
|
66
|
+
if (!model) {
|
67
|
+
if (this.fixtureStore[name]) {
|
68
|
+
model = name;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
return model;
|
72
|
+
},
|
73
|
+
|
74
|
+
/**
|
75
|
+
Generate next id for model
|
76
|
+
*/
|
77
|
+
generateId: function (model) {
|
78
|
+
var lastId = this.modelIds[model] || 0;
|
79
|
+
this.modelIds[model] = lastId + 1;
|
80
|
+
return this.modelIds[model];
|
81
|
+
},
|
82
|
+
|
83
|
+
/**
|
84
|
+
Build fixtures for model or specific fixture name. For example:
|
85
|
+
|
86
|
+
FactoryGuy.build('user') for User model
|
87
|
+
FactoryGuy.build('bob') for User model with bob attributes
|
88
|
+
|
89
|
+
@param name fixture name
|
90
|
+
@param opts options that will override default fixture values
|
91
|
+
@returns {*}
|
92
|
+
*/
|
93
|
+
build: function (name, opts) {
|
94
|
+
var model = this.lookupModelForName(name);
|
95
|
+
if (!model) {
|
96
|
+
throw new Error("can't find that factory named [" + name + "]");
|
97
|
+
}
|
98
|
+
|
99
|
+
var modelInfo = this.fixtureStore[model];
|
100
|
+
var modelAttributes = modelInfo[name] || {};
|
101
|
+
var defaultModelAttributes = modelInfo.default;
|
102
|
+
var fixture = $.extend({}, defaultModelAttributes, modelAttributes, opts);
|
103
|
+
fixture.id = this.generateId(model);
|
104
|
+
return fixture;
|
105
|
+
},
|
106
|
+
|
107
|
+
/**
|
108
|
+
Clear model instances from FIXTURES array, and from store cache.
|
109
|
+
Reset the id sequence for the models back to zero.
|
110
|
+
*/
|
111
|
+
resetModels: function (store) {
|
112
|
+
var typeMaps = store.typeMaps;
|
113
|
+
if (store.usingFixtureAdapter()) {
|
114
|
+
for (typeKey in this.fixtureStore) {
|
115
|
+
var modelType = store.modelFor(typeKey);
|
116
|
+
modelType.FIXTURES = [];
|
117
|
+
store.unloadAll(modelType);
|
118
|
+
}
|
119
|
+
} else {
|
120
|
+
for (model in typeMaps) {
|
121
|
+
if (typeMaps[model].type.typeKey != 'user') {
|
122
|
+
// console.log(typeMaps[model].type.typeKey)
|
123
|
+
store.unloadAll(typeMaps[model].type);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
this.modelIds = {}
|
128
|
+
},
|
129
|
+
|
130
|
+
/**
|
131
|
+
Push fixture to model's FIXTURES array.
|
132
|
+
Used when store's adapter is a DS.FixtureAdapter.
|
133
|
+
|
134
|
+
@param modelClass DS.Model type
|
135
|
+
@param fixture the fixture to add
|
136
|
+
*/
|
137
|
+
pushFixture: function (modelClass, fixture) {
|
138
|
+
if (!modelClass['FIXTURES']) {
|
139
|
+
modelClass['FIXTURES'] = [];
|
140
|
+
}
|
141
|
+
modelClass['FIXTURES'].push(fixture);
|
142
|
+
return fixture;
|
143
|
+
}
|
144
|
+
})
|
@@ -0,0 +1,89 @@
|
|
1
|
+
FactoryGuyHelperMixin = Em.Mixin.create({
|
2
|
+
|
3
|
+
setup: function(app) {
|
4
|
+
this.set('container', app.__container__);
|
5
|
+
return this;
|
6
|
+
},
|
7
|
+
|
8
|
+
useFixtureAdapter: function(app) {
|
9
|
+
app.ApplicationAdapter = DS.FixtureAdapter;
|
10
|
+
this.getStore().adapterFor('application').simulateRemoteResponse = false;
|
11
|
+
},
|
12
|
+
|
13
|
+
find: function(type, id) {
|
14
|
+
return this.getStore().find(type, id);
|
15
|
+
},
|
16
|
+
|
17
|
+
make: function(name, opts) {
|
18
|
+
return this.getStore().makeFixture(name, opts);
|
19
|
+
},
|
20
|
+
|
21
|
+
getStore: function () {
|
22
|
+
return this.get('container').lookup('store:main');
|
23
|
+
},
|
24
|
+
|
25
|
+
pushPayload: function(type, hash) {
|
26
|
+
return this.getStore().pushPayload(type, hash);
|
27
|
+
},
|
28
|
+
|
29
|
+
pushRecord: function(type, hash) {
|
30
|
+
return this.getStore().push(type, hash);
|
31
|
+
},
|
32
|
+
|
33
|
+
stubEndpointForHttpRequest: function (url, json, options) {
|
34
|
+
options = options || {};
|
35
|
+
var request = {
|
36
|
+
url: url,
|
37
|
+
dataType: 'json',
|
38
|
+
responseText: json,
|
39
|
+
type: options.type || 'GET',
|
40
|
+
status: options.status || 200
|
41
|
+
}
|
42
|
+
|
43
|
+
if (options.data) {
|
44
|
+
request.data = options.data
|
45
|
+
}
|
46
|
+
|
47
|
+
$.mockjax(request);
|
48
|
+
},
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Handling ajax POST for a model
|
52
|
+
*
|
53
|
+
* @param name of the fixture ( or model ) to create
|
54
|
+
* @param opts fixture options
|
55
|
+
*/
|
56
|
+
handleCreate: function (name, opts) {
|
57
|
+
var model = FactoryGuy.lookupModelForName(name);
|
58
|
+
this.stubEndpointForHttpRequest(
|
59
|
+
"/" + Em.String.pluralize(model),
|
60
|
+
this.buildAjaxResponse(name, opts),
|
61
|
+
{type: 'POST'}
|
62
|
+
)
|
63
|
+
},
|
64
|
+
|
65
|
+
buildAjaxResponse: function (name, opts) {
|
66
|
+
var fixture = FactoryGuy.build(name, opts);
|
67
|
+
var model = FactoryGuy.lookupModelForName(name);
|
68
|
+
var hash = {};
|
69
|
+
hash[model] = fixture;
|
70
|
+
return hash;
|
71
|
+
},
|
72
|
+
|
73
|
+
handleUpdate: function (root, id) {
|
74
|
+
this.stubEndpointForHttpRequest(
|
75
|
+
"/" + Em.String.pluralize(root) + "/" + id, {}, {type: 'PUT'}
|
76
|
+
)
|
77
|
+
},
|
78
|
+
|
79
|
+
handleDelete: function (root, id) {
|
80
|
+
this.stubEndpointForHttpRequest(
|
81
|
+
"/" + Em.String.pluralize(root) + "/" + id, {}, {type: 'DELETE'}
|
82
|
+
)
|
83
|
+
},
|
84
|
+
|
85
|
+
teardown: function () {
|
86
|
+
FactoryGuy.resetModels(this.getStore());
|
87
|
+
}
|
88
|
+
|
89
|
+
})
|
data/src/has_many.js
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
(function(){
|
2
|
+
var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties;
|
3
|
+
|
4
|
+
function asyncHasMany(record, type, options, meta) {
|
5
|
+
var relationship = record._relationships[key],
|
6
|
+
promiseLabel = "DS: Async hasMany " + record + " : " + key;
|
7
|
+
|
8
|
+
if (!relationship) {
|
9
|
+
var resolver = Ember.RSVP.defer(promiseLabel);
|
10
|
+
relationship = buildRelationship(record, key, options, function(store, data) {
|
11
|
+
var link = data.links && data.links[key];
|
12
|
+
var rel;
|
13
|
+
if (link) {
|
14
|
+
rel = store.findHasMany(record, link, meta, resolver);
|
15
|
+
} else {
|
16
|
+
rel = store.findMany(record, data[key], meta.type, resolver);
|
17
|
+
}
|
18
|
+
// cache the promise so we can use it
|
19
|
+
// when we come back and don't need to rebuild
|
20
|
+
// the relationship.
|
21
|
+
set(rel, 'promise', resolver.promise);
|
22
|
+
return rel;
|
23
|
+
});
|
24
|
+
}
|
25
|
+
|
26
|
+
var promise = relationship.get('promise').then(function() {
|
27
|
+
return relationship;
|
28
|
+
}, null, "DS: Async hasMany records received");
|
29
|
+
|
30
|
+
return DS.PromiseArray.create({
|
31
|
+
promise: promise
|
32
|
+
});
|
33
|
+
}
|
34
|
+
|
35
|
+
function buildRelationship(record, key, options, callback) {
|
36
|
+
var rels = record._relationships;
|
37
|
+
|
38
|
+
if (rels[key]) { return rels[key]; }
|
39
|
+
|
40
|
+
var data = get(record, 'data'),
|
41
|
+
store = get(record, 'store');
|
42
|
+
|
43
|
+
var relationship = rels[key] = callback.call(record, store, data);
|
44
|
+
|
45
|
+
return setProperties(relationship, {
|
46
|
+
owner: record,
|
47
|
+
name: key,
|
48
|
+
isPolymorphic: options.polymorphic
|
49
|
+
});
|
50
|
+
}
|
51
|
+
|
52
|
+
function hasRelationship(type, options) {
|
53
|
+
options = options || {};
|
54
|
+
|
55
|
+
var meta = {
|
56
|
+
type: type,
|
57
|
+
isRelationship: true,
|
58
|
+
options: options,
|
59
|
+
kind: 'hasMany'
|
60
|
+
};
|
61
|
+
|
62
|
+
return Ember.computed('data', function(key) {
|
63
|
+
var adapter = this.store.adapterFor('application');
|
64
|
+
if (adapter.toString().match('Fixture')) {
|
65
|
+
var relationship = this._relationships[key],
|
66
|
+
promiseLabel = "DS: Async hasMany " + this + " : " + key;
|
67
|
+
|
68
|
+
if (!relationship) {
|
69
|
+
var resolver = Ember.RSVP.defer(promiseLabel);
|
70
|
+
relationship = buildRelationship(this, key, options, function(store, data) {
|
71
|
+
var link = data.links && data.links[key];
|
72
|
+
var rel;
|
73
|
+
if (link) {
|
74
|
+
rel = store.findHasMany(this, link, meta, resolver);
|
75
|
+
} else {
|
76
|
+
rel = store.findMany(this, data[key], meta.type, resolver);
|
77
|
+
}
|
78
|
+
// cache the promise so we can use it
|
79
|
+
// when we come back and don't need to rebuild
|
80
|
+
// the relationship.
|
81
|
+
set(rel, 'promise', resolver.promise);
|
82
|
+
return rel;
|
83
|
+
});
|
84
|
+
}
|
85
|
+
|
86
|
+
var promise = relationship.get('promise').then(function() {
|
87
|
+
return relationship;
|
88
|
+
}, null, "DS: Async hasMany records received");
|
89
|
+
|
90
|
+
return DS.PromiseArray.create({
|
91
|
+
promise: promise
|
92
|
+
});
|
93
|
+
}
|
94
|
+
|
95
|
+
return buildRelationship(this, key, options, function(store, data) {
|
96
|
+
var records = data[key];
|
97
|
+
Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", Ember.A(records).everyProperty('isEmpty', false));
|
98
|
+
return store.findMany(this, data[key], meta.type);
|
99
|
+
});
|
100
|
+
}).meta(meta).readOnly();
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
`DS.hasMany` is used to define One-To-Many and Many-To-Many
|
105
|
+
relationships on a [DS.Model](/api/data/classes/DS.Model.html).
|
106
|
+
|
107
|
+
`DS.hasMany` takes an optional hash as a second parameter, currently
|
108
|
+
supported options are:
|
109
|
+
|
110
|
+
- `async`: A boolean value used to explicitly declare this to be an async relationship.
|
111
|
+
- `inverse`: A string used to identify the inverse property on a related model.
|
112
|
+
|
113
|
+
#### One-To-Many
|
114
|
+
To declare a one-to-many relationship between two models, use
|
115
|
+
`DS.belongsTo` in combination with `DS.hasMany`, like this:
|
116
|
+
|
117
|
+
```javascript
|
118
|
+
App.Post = DS.Model.extend({
|
119
|
+
comments: DS.hasMany('comment')
|
120
|
+
});
|
121
|
+
|
122
|
+
App.Comment = DS.Model.extend({
|
123
|
+
post: DS.belongsTo('post')
|
124
|
+
});
|
125
|
+
```
|
126
|
+
|
127
|
+
#### Many-To-Many
|
128
|
+
To declare a many-to-many relationship between two models, use
|
129
|
+
`DS.hasMany`:
|
130
|
+
|
131
|
+
```javascript
|
132
|
+
App.Post = DS.Model.extend({
|
133
|
+
tags: DS.hasMany('tag')
|
134
|
+
});
|
135
|
+
|
136
|
+
App.Tag = DS.Model.extend({
|
137
|
+
posts: DS.hasMany('post')
|
138
|
+
});
|
139
|
+
```
|
140
|
+
|
141
|
+
#### Explicit Inverses
|
142
|
+
|
143
|
+
Ember Data will do its best to discover which relationships map to
|
144
|
+
one another. In the one-to-many code above, for example, Ember Data
|
145
|
+
can figure out that changing the `comments` relationship should update
|
146
|
+
the `post` relationship on the inverse because post is the only
|
147
|
+
relationship to that model.
|
148
|
+
|
149
|
+
However, sometimes you may have multiple `belongsTo`/`hasManys` for the
|
150
|
+
same type. You can specify which property on the related model is
|
151
|
+
the inverse using `DS.hasMany`'s `inverse` option:
|
152
|
+
|
153
|
+
```javascript
|
154
|
+
var belongsTo = DS.belongsTo,
|
155
|
+
hasMany = DS.hasMany;
|
156
|
+
|
157
|
+
App.Comment = DS.Model.extend({
|
158
|
+
onePost: belongsTo('post'),
|
159
|
+
twoPost: belongsTo('post'),
|
160
|
+
redPost: belongsTo('post'),
|
161
|
+
bluePost: belongsTo('post')
|
162
|
+
});
|
163
|
+
|
164
|
+
App.Post = DS.Model.extend({
|
165
|
+
comments: hasMany('comment', {
|
166
|
+
inverse: 'redPost'
|
167
|
+
})
|
168
|
+
});
|
169
|
+
```
|
170
|
+
|
171
|
+
You can also specify an inverse on a `belongsTo`, which works how
|
172
|
+
you'd expect.
|
173
|
+
|
174
|
+
@namespace
|
175
|
+
@method hasMany
|
176
|
+
@for DS
|
177
|
+
@param {String or DS.Model} type the model type of the relationship
|
178
|
+
@param {Object} options a hash of options
|
179
|
+
@return {Ember.computed} relationship
|
180
|
+
*/
|
181
|
+
DS.hasMany = function(type, options) {
|
182
|
+
if (typeof type === 'object') {
|
183
|
+
options = type;
|
184
|
+
type = undefined;
|
185
|
+
}
|
186
|
+
return hasRelationship(type, options);
|
187
|
+
}
|
188
|
+
}).call();
|