entangled 0.0.22 → 0.0.23

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a2db62264a96d90447e75a73bed98ef29fd1a86
4
- data.tar.gz: 5e2a82bd5418bb9911b54febe96721e285cee439
3
+ metadata.gz: d7c388678aa588a1f547b713feb70dbd9f5aaa44
4
+ data.tar.gz: 5c4baa78c2e13abb9fdce0aabbd132698a474af4
5
5
  SHA512:
6
- metadata.gz: a4ce644d5222adcf7a7631da195c64ea1f35c9e70419451ece36a3a46325adca6c7386d015b70e10610f3d0d484c95fca19648f624f7baf9f4d02283ade87147
7
- data.tar.gz: 31dfde1c1a38423af73029c115b38580030d2c268104b4ce5b90ea305c059458d1622e024267a81476f74880d9c7a07af2e0420e2ced2b4b33a55064b8aafe35
6
+ metadata.gz: a740b57b3679899c1c3bde3d2b969f00a5457f826af461626119fdb4083a08eba5c5115b433fc8c243736a00a6c7c074fa9b8c8673c561fd2cf6c18cef72c134
7
+ data.tar.gz: 17eb36090addf15cc12ea4a9c346dcb70411ac7273ce613ebfcf08143f9d47c15d0ee02ee8c1185a5b3200febabc2bb8fb13ac17538d205bb4a0b06daa0645e3
data/README.md CHANGED
@@ -207,6 +207,8 @@ The Entangled service comes with these functions:
207
207
  - `$update(params, callback)`
208
208
  - `$destroy(callback)`
209
209
 
210
+ They're just like class and instance methods in Active Record.
211
+
210
212
  In your controller, you could then inject that `Message` service and use it like so:
211
213
 
212
214
  ```javascript
@@ -411,14 +413,14 @@ The gem relies heavily on convention over configuration and currently only works
411
413
  ## Development Priorities
412
414
  The following features are to be implemented next:
413
415
 
414
- - Support more than one belongs_to association in back end
415
- - Support belongs_to in front end
416
- - Support deeply nested belongs_to, e.g. Parent > Child > Grandchild
417
- - Support has_one association in back end and front end
416
+ - Support more than one `belongs_to` association in back end
417
+ - Support `belongsTo` in front end
418
+ - Support deeply nested `belongs_to`, e.g. `Parent > Child > Grandchild`
419
+ - Support `has_one` association in back end and front end
418
420
  - Add offline capabilities
419
421
  - Add authentication - with JWT?
420
422
  - On Heroku, tasks are always in different order depending on which ones are checked off and not
421
- - Add $onChange function to objects - or could a simple $watch and $watchCollection suffice?
423
+ - Add `$onChange` function to objects - or could a simple $watch and $watchCollection suffice?
422
424
  - Add diagram on how it works to Readme
423
425
  - Check if Rails 4.0.0 supported too
424
426
  - GNU instead of MIT? Or something else? How to switch?
@@ -426,7 +428,8 @@ The following features are to be implemented next:
426
428
  - Handle errors gracefully (e.g. finding a non-existent id, etc, authorization error in the back end, timeouts, etc)
427
429
  - Test controllers (see https://github.com/ngauthier/tubesock/issues/41)
428
430
  - Freeze destroyed object
429
- - Set $persisted() to false on a destroyed object
431
+ - Set `$persisted()` to false on a destroyed object
432
+ - Add `.destroyAll()` function to `Resources`
430
433
 
431
434
  ## Contributing
432
435
  1. [Fork it](https://github.com/dchacke/entangled/fork) - you will notice that the repo comes with a back end and a front end part to test both parts of the gem
data/bower.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entangled",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "authors": [
5
5
  "Dennis Charles Hackethal <dennis.hackethal@gmail.com>"
6
6
  ],
data/entangled.js CHANGED
@@ -1 +1 @@
1
- angular.module("entangled",[]).factory("Entangled",function(){var e=function(e,t,r){for(var i in e)e.hasOwnProperty(i)&&(this[i]=e[i]);this.webSocketUrl=t,r&&(this[r]=function(){return new s(this.webSocketUrl+"/"+this.id+"/"+r)})};e.prototype.$save=function(e){if(this.id){var t=new WebSocket(this.webSocketUrl+"/"+this.id+"/update");t.onopen=function(){t.send(JSON.stringify(this))}.bind(this),t.onmessage=function(t){if(t.data){var r=JSON.parse(t.data);if(r.resource)for(key in r.resource)this[key]=r.resource[key]}this[this.hasMany]=new s(this.webSocketUrl+"/"+this.id+"/"+this.hasMany),e&&e(this)}.bind(this)}else{var t=new WebSocket(this.webSocketUrl+"/create");t.onopen=function(){t.send(JSON.stringify(this))}.bind(this),t.onmessage=function(t){if(t.data){var s=JSON.parse(t.data);if(s.resource)for(key in s.resource)this[key]=s.resource[key]}e&&e(this)}.bind(this)}},e.prototype.$update=function(e,t){for(var s in e)e.hasOwnProperty(s)&&(this[s]=e[s]);this.$save(t)},e.prototype.$destroy=function(e){var t=new WebSocket(this.webSocketUrl+"/"+this.id+"/destroy");t.onopen=function(){t.send(null)},t.onmessage=function(t){if(t.data){var s=JSON.parse(t.data);if(s.resource)for(key in s.resource)this[key]=s.resource[key]}e&&e(this)}.bind(this)},e.prototype.$valid=function(){return!(this.errors&&Object.keys(this.errors).length)},e.prototype.$invalid=function(){return!this.$valid()},e.prototype.$persisted=function(){return!!this.id};var t=function(t,s,r){this.all=[];for(var i=0;i<t.length;i++){var o=new e(t[i],s,r);this.all.push(o)}},s=function(e){this.webSocketUrl=e};return s.prototype.hasMany=function(e){this.hasMany=e},s.prototype["new"]=function(t){return new e(t,this.webSocketUrl,this.hasMany)},s.prototype.all=function(s){var r=new WebSocket(this.webSocketUrl);r.onmessage=function(i){if(i.data.length){var o=JSON.parse(i.data);if(o.resources)this.resources=new t(o.resources,r.url,this.hasMany);else if(o.action)if("create"===o.action)this.resources.all.push(new e(o.resource,r.url,this.hasMany));else if("update"===o.action){for(var n,a=0;a<this.resources.all.length;a++)this.resources.all[a].id===o.resource.id&&(n=a);this.resources.all[n]=new e(o.resource,r.url,this.hasMany)}else if("destroy"===o.action){for(var n,a=0;a<this.resources.all.length;a++)this.resources.all[a].id===o.resource.id&&(n=a);this.resources.all.splice(n,1)}else console.log("Something else other than CRUD happened..."),console.log(o)}s(this.resources.all)}.bind(this)},s.prototype.create=function(e,t){var s=this["new"](e);s.$save(t)},s.prototype.find=function(t,s){var r=this.webSocketUrl,i=new WebSocket(r+"/"+t);i.onmessage=function(t){if(t.data.length){var i=JSON.parse(t.data);i.resource&&!i.action?this.resource=new e(i.resource,r,this.hasMany):i.action?"update"===i.action?this.resource=new e(i.resource,r,this.hasMany):"destroy"===i.action&&(this.resource=void 0):(console.log("Something else other than CRUD happened..."),console.log(i))}s(this.resource)}.bind(this)},s});
1
+ angular.module("entangled",[]).factory("Entangled",function(){var e=function(e,t,r){for(var i in e)e.hasOwnProperty(i)&&(this[i]=e[i]);this.webSocketUrl=t,r&&(this[r]=function(){return new s(this.webSocketUrl+"/"+this.id+"/"+r)})};e.prototype.$save=function(e){if(this.id){var t=new WebSocket(this.webSocketUrl+"/"+this.id+"/update");t.onopen=function(){t.send(JSON.stringify(this))}.bind(this),t.onmessage=function(t){if(t.data){var r=JSON.parse(t.data);if(r.resource)for(key in r.resource)this[key]=r.resource[key]}this[this.hasMany]=new s(this.webSocketUrl+"/"+this.id+"/"+this.hasMany),e&&e(this)}.bind(this)}else{var t=new WebSocket(this.webSocketUrl+"/create");t.onopen=function(){t.send(JSON.stringify(this))}.bind(this),t.onmessage=function(t){if(t.data){var s=JSON.parse(t.data);if(s.resource)for(key in s.resource)this[key]=s.resource[key]}e&&e(this)}.bind(this)}},e.prototype.$update=function(e,t){for(var s in e)e.hasOwnProperty(s)&&(this[s]=e[s]);this.$save(t)},e.prototype.$destroy=function(e){var t=new WebSocket(this.webSocketUrl+"/"+this.id+"/destroy");t.onopen=function(){t.send(null)},t.onmessage=function(t){if(t.data){var s=JSON.parse(t.data);if(s.resource)for(key in s.resource)this[key]=s.resource[key]}e&&e(this)}.bind(this)},e.prototype.$valid=function(){return!(this.errors&&Object.keys(this.errors).length)},e.prototype.$invalid=function(){return!this.$valid()},e.prototype.$persisted=function(){return!!this.id};var t=function(t,s,r){this.all=[];for(var i=0;i<t.length;i++){var o=new e(t[i],s,r);this.all.push(o)}},s=function(e){this.className="Entangled",this.webSocketUrl=e};return s.prototype.hasMany=function(e){this.hasMany=e},s.prototype["new"]=function(t){return new e(t,this.webSocketUrl,this.hasMany)},s.prototype.all=function(s){var r=new WebSocket(this.webSocketUrl);r.onmessage=function(i){if(i.data.length){var o=JSON.parse(i.data);if(o.resources)this.resources=new t(o.resources,r.url,this.hasMany);else if(o.action)if("create"===o.action)this.resources.all.push(new e(o.resource,r.url,this.hasMany));else if("update"===o.action){for(var n,a=0;a<this.resources.all.length;a++)this.resources.all[a].id===o.resource.id&&(n=a);this.resources.all[n]=new e(o.resource,r.url,this.hasMany)}else if("destroy"===o.action){for(var n,a=0;a<this.resources.all.length;a++)this.resources.all[a].id===o.resource.id&&(n=a);this.resources.all.splice(n,1)}else console.log("Something else other than CRUD happened..."),console.log(o)}s(this.resources.all)}.bind(this)},s.prototype.create=function(e,t){var s=this["new"](e);s.$save(t)},s.prototype.find=function(t,s){var r=this.webSocketUrl,i=new WebSocket(r+"/"+t);i.onmessage=function(t){if(t.data.length){var i=JSON.parse(t.data);i.resource&&!i.action?this.resource=new e(i.resource,r,this.hasMany):i.action?"update"===i.action?this.resource=new e(i.resource,r,this.hasMany):"destroy"===i.action&&(this.resource=void 0):(console.log("Something else other than CRUD happened..."),console.log(i))}s(this.resource)}.bind(this)},s});
@@ -1,3 +1,3 @@
1
1
  module Entangled
2
- VERSION = "0.0.22"
2
+ VERSION = "0.0.23"
3
3
  end
@@ -19,6 +19,13 @@ class ListsController < ApplicationController
19
19
  end
20
20
  end
21
21
 
22
+ def update
23
+ broadcast do
24
+ @list = List.find(params[:id])
25
+ @list.update(list_params)
26
+ end
27
+ end
28
+
22
29
  def destroy
23
30
  broadcast do
24
31
  @list = List.find(params[:id]).destroy
@@ -3,4 +3,6 @@ class List < ActiveRecord::Base
3
3
  entangle
4
4
 
5
5
  has_many :items, dependent: :destroy
6
+
7
+ validates :name, presence: true
6
8
  end
@@ -0,0 +1,13 @@
1
+ class DropMessages < ActiveRecord::Migration
2
+ def self.up
3
+ drop_table :messages
4
+ end
5
+
6
+ def self.down
7
+ create_table "messages", force: :cascade do |t|
8
+ t.text "body"
9
+ t.datetime "created_at", null: false
10
+ t.datetime "updated_at", null: false
11
+ end
12
+ end
13
+ end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20150314055847) do
14
+ ActiveRecord::Schema.define(version: 20150316034305) do
15
15
 
16
16
  create_table "barfoos", force: :cascade do |t|
17
17
  t.text "body"
@@ -51,10 +51,4 @@ ActiveRecord::Schema.define(version: 20150314055847) do
51
51
  t.datetime "updated_at", null: false
52
52
  end
53
53
 
54
- create_table "messages", force: :cascade do |t|
55
- t.text "body"
56
- t.datetime "created_at", null: false
57
- t.datetime "updated_at", null: false
58
- end
59
-
60
54
  end
@@ -7,14 +7,6 @@ angular.module('entangledTest', [
7
7
 
8
8
  .config(function($routeProvider) {
9
9
  $routeProvider
10
- .when('/', {
11
- templateUrl: 'views/messages/index.html',
12
- controller: 'MessagesCtrl'
13
- })
14
- .when('/messages/:id', {
15
- templateUrl: 'views/messages/show.html',
16
- controller: 'MessageCtrl'
17
- })
18
10
  .when('/lists', {
19
11
  templateUrl: 'views/lists/index.html',
20
12
  controller: 'ListsCtrl'
@@ -24,6 +16,6 @@ angular.module('entangledTest', [
24
16
  controller: 'ListCtrl'
25
17
  })
26
18
  .otherwise({
27
- redirectTo: '/'
19
+ redirectTo: '/lists'
28
20
  });
29
21
  })
@@ -179,6 +179,8 @@ angular.module('entangled', [])
179
179
  // of the index action on the server where the
180
180
  // Resources can be retrieved.
181
181
  var Entangled = function(webSocketUrl) {
182
+ this.className = 'Entangled';
183
+
182
184
  // Store the root URL that sockets
183
185
  // will connect to
184
186
  this.webSocketUrl = webSocketUrl;
@@ -3,7 +3,7 @@
3
3
  angular.module('entangledTest')
4
4
 
5
5
  .factory('List', function(Entangled) {
6
- var entangled = new Entangled('ws://localhost:3000/lists');
7
- entangled.hasMany('items');
8
- return entangled;
6
+ var List = new Entangled('ws://localhost:3000/lists');
7
+ List.hasMany('items');
8
+ return List;
9
9
  });
@@ -10,11 +10,8 @@
10
10
  <script src="bower_components/angular-route/angular-route.js"></script>
11
11
  <script src="app/entangled/entangled.js"></script>
12
12
  <script src="app/app.js"></script>
13
- <script src="app/controllers/messages.js"></script>
14
- <script src="app/controllers/message.js"></script>
15
13
  <script src="app/controllers/lists.js"></script>
16
14
  <script src="app/controllers/list.js"></script>
17
- <script src="app/services/message.js"></script>
18
15
  <script src="app/services/list.js"></script>
19
16
  </body>
20
17
  </html>
@@ -0,0 +1,263 @@
1
+ 'use strict';
2
+
3
+ describe('Entangled', function() {
4
+ beforeEach(module('entangled'));
5
+
6
+ var $injector,
7
+ Entangled,
8
+ List;
9
+
10
+ beforeEach(inject(function() {
11
+ $injector = angular.injector(['entangled']);
12
+ Entangled = $injector.get('Entangled');
13
+
14
+ List = new Entangled('ws://localhost:3000/lists');
15
+ List.hasMany('items');
16
+ }));
17
+
18
+ describe('.className', function() {
19
+ it('is "Entangled"', function() {
20
+ expect(List.className).toBe('Entangled');
21
+ });
22
+ });
23
+
24
+ describe('.new', function() {
25
+ it('instantiates a new list with given values', function() {
26
+ var list = List.new({ name: 'foo' });
27
+ expect(list).toBeDefined();
28
+ expect(list.name).toBe('foo');
29
+ });
30
+ });
31
+
32
+ describe('.all', function() {
33
+ it('fetches all lists', function(done) {
34
+ List.all(function(lists) {
35
+ expect(lists).toEqual(jasmine.any(Array));
36
+ done();
37
+ });
38
+ });
39
+ });
40
+
41
+ describe('.create', function() {
42
+ it('creates a list', function(done) {
43
+ List.create({ name: 'foo' }, function(list) {
44
+ expect(list.id).toBeDefined();
45
+ expect(list.created_at).toBeDefined();
46
+ done();
47
+ });
48
+ });
49
+
50
+ it('receives validation messages', function(done) {
51
+ // Leave out name, causing model validations
52
+ // in ActiveRecord to fail
53
+ List.create({}, function(list) {
54
+ expect(list.errors.name.indexOf("can't be blank") > -1).toBeTruthy();
55
+ done();
56
+ });
57
+ });
58
+ });
59
+
60
+ describe('.find', function() {
61
+ it('finds a list', function(done) {
62
+ List.create({ name: 'foo' }, function(list) {
63
+ List.find(list.id, function(list) {
64
+ expect(list.id).toBeDefined();
65
+ expect(list.created_at).toBeDefined();
66
+ done();
67
+ });
68
+ });
69
+ });
70
+ });
71
+
72
+ describe('#$save', function() {
73
+ describe('new record', function() {
74
+ it('saves a new list', function(done) {
75
+ var list = List.new({ name: 'foo' });
76
+
77
+ list.$save(function() {
78
+ expect(list.id).toBeDefined();
79
+ expect(list.created_at).toBeDefined();
80
+ done();
81
+ });
82
+ });
83
+
84
+ it('receives validation messages', function(done) {
85
+ // Leave out name, causing model validations
86
+ // in ActiveRecord to fail
87
+ var list = List.new();
88
+
89
+ list.$save(function() {
90
+ expect(list.errors.name.indexOf("can't be blank") > -1).toBeTruthy();
91
+ done();
92
+ });
93
+ });
94
+ });
95
+
96
+ describe('existing record', function() {
97
+ it('updates an existing list', function(done) {
98
+ List.create({ name: 'foo' }, function(list) {
99
+ list.name = 'new name';
100
+ var oldUpdatedAt = list.updated_at;
101
+
102
+ list.$save(function() {
103
+ expect(list.name).toBe('new name');
104
+ expect(list.updated_at).not.toEqual(oldUpdatedAt);
105
+ done();
106
+ });
107
+ });
108
+ });
109
+
110
+ it('receives validation messages', function(done) {
111
+ List.create({ name: 'foo' }, function(list) {
112
+ // Make invalid by setting the name to an
113
+ // empty string, causing model validations
114
+ // in ActiveRecord to fail
115
+ list.name = '';
116
+ var oldUpdatedAt = list.updated_at;
117
+
118
+ list.$save(function() {
119
+ // Assert that the list was not updated
120
+ // by the server
121
+ expect(list.updated_at).toBe(oldUpdatedAt);
122
+ expect(list.errors.name.indexOf("can't be blank") > -1).toBeTruthy();
123
+ done();
124
+ });
125
+ });
126
+ });
127
+ });
128
+ });
129
+
130
+ describe('#$update', function() {
131
+ it('updates a list in place', function(done) {
132
+ List.create({ name: 'foo' }, function(list) {
133
+ var oldUpdatedAt = list.updated_at;
134
+
135
+ list.$update({ name: 'new name' }, function() {
136
+ expect(list.name).toBe('new name');
137
+ expect(list.updated_at).not.toEqual(oldUpdatedAt);
138
+ done();
139
+ });
140
+ });
141
+ });
142
+
143
+ it('receives validation messages', function(done) {
144
+ List.create({ name: 'foo' }, function(list) {
145
+ var oldUpdatedAt = list.updated_at;
146
+
147
+ // Make invalid by setting the name to an
148
+ // empty string, causing model validations
149
+ // in ActiveRecord to fail
150
+ list.$update({ name: '' }, function() {
151
+ // Assert that the list was not updated
152
+ // by the server
153
+ expect(list.updated_at).toBe(oldUpdatedAt);
154
+ expect(list.errors.name.indexOf("can't be blank") > -1).toBeTruthy();
155
+ done();
156
+ });
157
+ });
158
+ });
159
+ });
160
+
161
+ describe('#$destroy', function() {
162
+ it('destroys a list', function(done) {
163
+ List.create({ name: 'foo' }, function(list) {
164
+ list.$destroy(function() {
165
+ // If successfully destroyed, it won't be
166
+ // included in the collection of all records
167
+ // anymore
168
+ List.all(function(lists) {
169
+ expect(lists.indexOf(list)).toBe(-1);
170
+ done();
171
+ });
172
+ });
173
+ });
174
+ });
175
+ });
176
+
177
+ describe('#$valid', function() {
178
+ var list;
179
+
180
+ beforeEach(function() {
181
+ list = List.new();
182
+ });
183
+
184
+ describe('valid record', function() {
185
+ it('is true', function() {
186
+ list.errors = {};
187
+ expect(list.$valid()).toBeTruthy();
188
+ });
189
+ });
190
+
191
+ describe('invalid record', function() {
192
+ it('is false', function() {
193
+ list.errors = {
194
+ name: ["can't be blank"]
195
+ };
196
+
197
+ expect(list.$valid()).not.toBeTruthy();
198
+ });
199
+ });
200
+ });
201
+
202
+ describe('#$invalid', function() {
203
+ var list;
204
+
205
+ beforeEach(function() {
206
+ list = List.new();
207
+ });
208
+
209
+ describe('valid record', function() {
210
+ it('is false', function() {
211
+ list.errors = {};
212
+ expect(list.$invalid()).not.toBeTruthy();
213
+ });
214
+ });
215
+
216
+ describe('invalid record', function() {
217
+ it('is true', function() {
218
+ list.errors = {
219
+ name: ["can't be blank"]
220
+ };
221
+
222
+ expect(list.$invalid()).toBeTruthy();
223
+ });
224
+ });
225
+ });
226
+
227
+ describe('#$persisted', function() {
228
+ var list;
229
+
230
+ beforeEach(function() {
231
+ list = List.new();
232
+ });
233
+
234
+ describe('persisted record', function() {
235
+ it('is true', function() {
236
+ list.id = 1;
237
+ expect(list.$persisted()).toBeTruthy();
238
+ });
239
+ });
240
+
241
+ describe('unpersisted record', function() {
242
+ it('is false', function() {
243
+ expect(list.$persisted()).not.toBeTruthy();
244
+ });
245
+ });
246
+ });
247
+
248
+ describe('Associations', function() {
249
+ it('has many items', function(done) {
250
+ List.create({ name: 'foo' }, function(list) {
251
+ // Assert that relationship defined
252
+ expect(list.items).toBeDefined();
253
+
254
+ // Assert that relationship is also
255
+ // an instance of Entangled, meaning
256
+ // in turn that all class and instance
257
+ // methods are available on it
258
+ expect(list.items().className).toBe('Entangled');
259
+ done();
260
+ });
261
+ });
262
+ });
263
+ });