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.
@@ -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();