ember-data-factory-guy 0.1

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