entangled 0.0.22 → 0.0.23

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.
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
+ });