entangled 0.0.16 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +110 -40
- data/bower.json +1 -1
- data/entangled.gemspec +1 -1
- data/entangled.js +80 -26
- data/lib/entangled/helpers.rb +2 -2
- data/lib/entangled/version.rb +1 -1
- data/spec/dummy/public/app/entangled/entangled.js +52 -4
- data/spec/dummy/public/app/services/message.js +1 -15
- data/spec/dummy/public/test/controllers/messages_test.js +97 -12
- data/spec/helpers/redis_spec.rb +74 -0
- metadata +12 -19216
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 620c93edc4a4892c41fe62cfd60cd448a245621e
|
4
|
+
data.tar.gz: d0cde74b9d171662e6bb716349a80eedcf943441
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24ee4a89da4c1ff4dd462ea2b34f335cb9c05aea74488a6682e6ab6959e0388b3894262ff4adde9f15896c039f8de9a9015d0cce61e794e7a91fa322771493d0
|
7
|
+
data.tar.gz: 34d56b6e058a103daa410ca346e01165ff5de40eccab0c04c61c8ee5ec13029498f991106dc8b97d012fa1712123a3d93376bf0b73ba949703818143edb86747
|
data/README.md
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
[![Codeship Status for dchacke/entangled](https://codeship.com/projects/9fe9a790-9df7-0132-5fb8-6e77ea26735b/status?branch=master)](https://codeship.com/projects/64679)
|
4
4
|
|
5
|
-
Services like Firebase are great because they provide real time data binding between client and server. But they come at a price: You give up control over your
|
5
|
+
Services like Firebase are great because they provide real time data binding between client and server. But they come at a price: You give up control over your back end. Wouldn't it be great to have real time functionality but still keep your beloved Rails back end? That's where Entangled comes in.
|
6
6
|
|
7
|
-
Entangled is a layer behind your controllers and models that pushes updates to
|
7
|
+
Entangled stores and syncs data instantly across every device. It is a layer behind your controllers and models that pushes updates to all connected clients in real time. It is cross-browser compatible and even offers real time validations.
|
8
8
|
|
9
|
-
|
9
|
+
Real time data binding should be the default, not an add-on. Entangled aims at making real time features as easy to implement as possible, while at the same time making your restful controllers thinner. All this without having to give up control over your back end.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
Add this line to your application's Gemfile:
|
@@ -104,13 +104,14 @@ class MessagesController < ApplicationController
|
|
104
104
|
|
105
105
|
def create
|
106
106
|
broadcast do
|
107
|
-
Message.create(message_params)
|
107
|
+
@message = Message.create(message_params)
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
111
|
def update
|
112
112
|
broadcast do
|
113
|
-
Message.find(params[:id])
|
113
|
+
@message = Message.find(params[:id])
|
114
|
+
@message.update(message_params)
|
114
115
|
end
|
115
116
|
end
|
116
117
|
|
@@ -131,8 +132,7 @@ Note the following:
|
|
131
132
|
|
132
133
|
- All methods are wrapped in a new `broadcast` block needed to send messages to connected clients
|
133
134
|
- The `index` method will expect an instance variable with the same name as your controller in the plural form (e.g. `@messages` in a `MessagesController`)
|
134
|
-
- The `show`
|
135
|
-
- Instance variables only need to be assigned in `index` and `show` since these are the only methods that should be concerned with sending data to clients. All other methods only publish updates to the data clients are subscribed to through the callbacks added to the model, so no instance variables are needed
|
135
|
+
- The `show`, `create` and `update` methods will expect an instance variable with the singular name of your controller (e.g. `@message` in a `MessagesController`)
|
136
136
|
- Data sent to clients arrives as stringified JSON
|
137
137
|
- Strong parameters are expected
|
138
138
|
|
@@ -146,13 +146,13 @@ $ redis-server
|
|
146
146
|
|
147
147
|
Otherwise the channels won't work.
|
148
148
|
|
149
|
-
If you store your Redis instance in `$redis` or `REDIS` (e.g. in an initializer), Entangled will use that assigned instance so that you can configure Redis just like you're used to. Otherwise, Entangled will instantiate Redis itself and use its
|
149
|
+
If you store your Redis instance in `$redis` or `REDIS` (e.g. in an initializer), Entangled will use that assigned instance so that you can configure Redis just like you're used to. Otherwise, Entangled will instantiate Redis itself and use its default settings.
|
150
150
|
|
151
151
|
### Database
|
152
152
|
Depending on your app's settings, you might have to increase the pool size in your database.yml configuration file, since every new socket will open a new connection to your database.
|
153
153
|
|
154
154
|
## The Client
|
155
|
-
You will need to configure your client to create Websockets and understand incoming requests on those sockets. If you use Angular for your
|
155
|
+
You will need to configure your client to create Websockets and understand incoming requests on those sockets. If you use Angular for your front end, you can use the Angular library from this repository. The use of Angular as counterpart of this gem is highly recommended, since its inherent two way data binding complements the real time functionality of this gem nicely.
|
156
156
|
|
157
157
|
### Installation
|
158
158
|
You can either download or reference the file `entangled.js` from this repository, or simply install it with Bower:
|
@@ -174,25 +174,24 @@ Entangled is best used within Angular services. For example, consider a `Message
|
|
174
174
|
|
175
175
|
```javascript
|
176
176
|
app.factory('Message', function(Entangled) {
|
177
|
-
|
178
|
-
|
179
|
-
var Message = {
|
180
|
-
new: function(params) {
|
181
|
-
return entangled.new(params);
|
182
|
-
},
|
183
|
-
all: function(callback) {
|
184
|
-
return entangled.all(callback);
|
185
|
-
},
|
186
|
-
find: function(id, callback) {
|
187
|
-
return entangled.find(id, callback);
|
188
|
-
}
|
189
|
-
};
|
190
|
-
|
191
|
-
return Message;
|
177
|
+
return new Entangled('ws://localhost:3000/messages');
|
192
178
|
});
|
193
179
|
```
|
194
180
|
|
195
|
-
In the above example, first we inject Entangled into our service, then instantiate a new Entangled object. The Entangled object takes one argument when instantiated: the URL of your resource's index
|
181
|
+
In the above example, first we inject Entangled into our service, then instantiate a new Entangled object and return it. The Entangled object takes one argument when instantiated: the URL of your resource's index action (in this case, `/messages`). Note that the socket URL looks just like a standard restful URL with http, except that the protocol part has been switched with `ws` to use the websocket protocol. Also note that you need to use `wss` instead if you want to use SSL.
|
182
|
+
|
183
|
+
The Entangled service come with these functions:
|
184
|
+
|
185
|
+
- `new(params)`
|
186
|
+
- `create(params, callback)`
|
187
|
+
- `find(id, callback)`
|
188
|
+
- `all(callback)`
|
189
|
+
|
190
|
+
...and the following functions on returned objects:
|
191
|
+
|
192
|
+
- `$save(callback)`
|
193
|
+
- `$update(params, callback)`
|
194
|
+
- `$destroy(callback)`
|
196
195
|
|
197
196
|
In your controller, you could then inject that `Message` service and use it like so:
|
198
197
|
|
@@ -202,20 +201,21 @@ In your controller, you could then inject that `Message` service and use it like
|
|
202
201
|
// set some default values
|
203
202
|
$scope.message = Message.new();
|
204
203
|
|
204
|
+
// To instantiate and save a message in one go
|
205
|
+
Message.create({ body: 'text' }, function(message) {
|
206
|
+
$scope.$apply(function() {
|
207
|
+
$scope.message = message;
|
208
|
+
});
|
209
|
+
});
|
210
|
+
|
205
211
|
// To retrieve a specific message from the server
|
206
212
|
// with id 1 and subscribe to its channel
|
207
|
-
Message.find(1, function() {
|
213
|
+
Message.find(1, function(message) {
|
208
214
|
$scope.$apply(function() {
|
209
215
|
$scope.message = message;
|
210
216
|
});
|
211
217
|
});
|
212
218
|
|
213
|
-
// To create a new or update an existing message
|
214
|
-
$scope.message.$save();
|
215
|
-
|
216
|
-
// To destroy a message
|
217
|
-
$scope.message.$destroy();
|
218
|
-
|
219
219
|
// To retrieve all messages from the server and
|
220
220
|
// subscribe to the collection's channel
|
221
221
|
Message.all(function(messages) {
|
@@ -223,14 +223,83 @@ Message.all(function(messages) {
|
|
223
223
|
$scope.messages = messages;
|
224
224
|
});
|
225
225
|
});
|
226
|
+
|
227
|
+
// To store a newly instantiated or update an existing message.
|
228
|
+
// If saved successfully, $scope.message is updated in place
|
229
|
+
// with the attributes id, created_at and updated_at
|
230
|
+
$scope.message.body = 'new body';
|
231
|
+
$scope.message.$save(function() {
|
232
|
+
// Do stuff after save
|
233
|
+
});
|
234
|
+
|
235
|
+
// To update a newly instantiated or existing message in place.
|
236
|
+
// If updated successfully, $scope.message is updated in place
|
237
|
+
// with the attributes id, created_at and updated_at
|
238
|
+
$scope.message.$update({ body: 'new body' }, function() {
|
239
|
+
// Do stuff after update
|
240
|
+
});
|
241
|
+
|
242
|
+
// To destroy a message
|
243
|
+
$scope.message.$destroy(function() {
|
244
|
+
// Do stuff after destroy
|
245
|
+
});
|
226
246
|
```
|
227
247
|
|
228
|
-
|
248
|
+
All functions above will interact with your server's controllers in real time.
|
229
249
|
|
230
250
|
If data in your server's database changes, so will your scope variables - in real time, for all connected clients.
|
231
251
|
|
252
|
+
### Available Functions
|
253
|
+
A number of functions is attached to Entangled JavaScript objects. They basically mimic ActiveRecord's behavior in the back end to make the database more accessible in the front end.
|
254
|
+
|
255
|
+
#### Validations
|
256
|
+
Objects from the Entangled service automatically receive ActiveRecord's error messages from your model when you `$save()`. An additional property called `errors` containing the error messages is available, formatted the same way you're used to from calling `.errors` on a model in Rails.
|
257
|
+
|
258
|
+
For example, consider the following scenario:
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
# Message model (Rails)
|
262
|
+
validates :body, presence: true
|
263
|
+
```
|
264
|
+
|
265
|
+
```javascript
|
266
|
+
// Controller (Angular)
|
267
|
+
$scope.message.$save(function() {
|
268
|
+
console.log($scope.message.errors);
|
269
|
+
// => { body: ["can't be blank"] }
|
270
|
+
});
|
271
|
+
```
|
272
|
+
|
273
|
+
You could then display these error messages to your users.
|
274
|
+
|
275
|
+
To check if a resource is valid, you can use `$valid()` and `$invalid()`. Both functions return booleans. For example:
|
276
|
+
|
277
|
+
```javascript
|
278
|
+
$scope.message.$save(function() {
|
279
|
+
// Check if record has no errors
|
280
|
+
if ($scope.message.$valid()) { // similar to ActiveRecord's .valid?
|
281
|
+
alert('Yay!');
|
282
|
+
}
|
283
|
+
|
284
|
+
// Check if record errors
|
285
|
+
if ($scope.message.$invalid()) { // similar to ActiveRecord's .invalid?
|
286
|
+
alert('Nay!');
|
287
|
+
}
|
288
|
+
});
|
289
|
+
```
|
290
|
+
|
291
|
+
Note that `$valid()` and `$invalid()` should only be used after $saving a resource, i.e. in the callback of `$save`, since they don't actually invoke server side validations. They only check if a resource contains errors.
|
292
|
+
|
293
|
+
#### Persistence
|
294
|
+
Just like with ActiveRecord's `persisted?` method, you can use `$persisted()` on an object to check if it was successfully stored in the database.
|
295
|
+
|
296
|
+
```javascript
|
297
|
+
$scope.message.$persisted();
|
298
|
+
// => true or false
|
299
|
+
```
|
300
|
+
|
232
301
|
## Planning Your Infrastructure
|
233
|
-
This gem is best used for Rails apps that serve as APIs only and are not concerned with rendering views. A
|
302
|
+
This gem is best used for Rails apps that serve as APIs only and are not concerned with rendering views, since Entangled controllers cannot render views. A front end separate from your Rails app is recommended, either in your Rails app's public directory, or a separate front end app altogether.
|
234
303
|
|
235
304
|
## Limitations
|
236
305
|
The gem rely's heavily on convention over configuration and currently only works with restful style controllers as shown above. More customization will be available soon.
|
@@ -240,12 +309,13 @@ The gem rely's heavily on convention over configuration and currently only works
|
|
240
309
|
2. Run `$ bundle install` in the root of the repo
|
241
310
|
3. Run `$ bower install` and `$ npm install` in spec/dummy/public
|
242
311
|
4. The back end example app resides in spec/dummy; you can run `rails` and `rake` commands in there if you prefix them with `bin/`, i.e. `$ bin/rails s` or `$ bin/rake db:schema:load`; run your tests in the root of the repo by running `$ rspec`
|
243
|
-
5. The front end example app resides in spec/dummy/public. To look at it in your browser, cd into spec/dummy/public and run `$ bin/rails s`. Tests for this part of the app can be located under spec/dummy/public/test and are written with Jasmine. To run the tests, first run `$ bin/rails -e test` to start up the server in test mode, and then run `$ grunt test` in a new terminal tab. It's important to remember that changes you make to the server will not take effect until you restart the server since you're running it in the test environment!
|
244
|
-
6.
|
245
|
-
7. Write your
|
246
|
-
8.
|
247
|
-
9.
|
248
|
-
10.
|
312
|
+
5. The front end example app resides in spec/dummy/public. To look at it in your browser, cd into spec/dummy/public and run `$ bin/rails s`. Tests for this part of the app can be located under spec/dummy/public/test and are written with Jasmine. To run the tests, first run `$ bin/rails -e test` to start up the server in test mode, and then run `$ grunt test` in a new terminal tab. It's important to remember that changes you make to the server will not take effect until you restart the server since you're running it in the test environment! Also remember to prepare the test database by running `$ bin/rake db:test:prepare`
|
313
|
+
6. The Entangled Angular service resides in spec/dummy/public/app/entangled/entangled.js. This is where you can make changes to the service; a copy of it, living in /entangled.js at the root of the repo, should be kept in sync for it to be available with Bower, so it's best if you replace this file with the one from the dummy app should have made any changes to the latter
|
314
|
+
7. Write your tests
|
315
|
+
8. Write your feature to make the tests pass
|
316
|
+
9. Stage and commit your changes
|
317
|
+
10. Push to a new feature branch in your repo
|
318
|
+
11. Send me a pull request!
|
249
319
|
|
250
320
|
## Credits
|
251
321
|
Thanks to [Ilias Tsangaris](https://github.com/iliastsangaris) for inspiring the name "Entanglement" based on [Quantum Entanglement](http://en.wikipedia.org/wiki/Quantum_entanglement) where pairs or groups of particles always react to changes as a whole, i.e. changes to one particle will result in immediate change of all particles in the group.
|
data/bower.json
CHANGED
data/entangled.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
|
16
16
|
s.files = `git ls-files -z`.split("\x0")
|
17
17
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
s.test_files
|
18
|
+
s.test_files = `git ls-files -z spec`.split("\x0")
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
s.add_development_dependency 'bundler', '~> 1.7'
|
data/entangled.js
CHANGED
@@ -26,43 +26,83 @@ angular.module('entangled', [])
|
|
26
26
|
// an existing, depending on whether or not
|
27
27
|
// an id is present.
|
28
28
|
Resource.prototype.$save = function(callback) {
|
29
|
-
console.log('Saving...');
|
30
|
-
|
31
29
|
var that = this;
|
32
|
-
console.log(that);
|
33
30
|
|
34
31
|
if (this.id) {
|
35
32
|
// Update
|
36
|
-
console.log('Updating...');
|
37
33
|
var socket = new WebSocket(that.webSocketUrl + '/' + that.id + '/update');
|
38
34
|
socket.onopen = function() {
|
39
35
|
socket.send(JSON.stringify(that));
|
36
|
+
};
|
40
37
|
|
41
|
-
|
38
|
+
// Receive updated resource from server
|
39
|
+
socket.onmessage = function(event) {
|
40
|
+
if (event.data) {
|
41
|
+
var data = JSON.parse(event.data);
|
42
42
|
|
43
|
-
|
43
|
+
// Assign/override new data (such as updated_at, etc)
|
44
|
+
if (data.resource) {
|
45
|
+
for (key in data.resource) {
|
46
|
+
that[key] = data.resource[key];
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
// Pass 'that' to callback for create
|
52
|
+
// function so that the create function
|
53
|
+
// can pass the created resource to its
|
54
|
+
// own callback; not needed for $save per se
|
55
|
+
if (callback) callback(that);
|
44
56
|
};
|
45
57
|
} else {
|
46
58
|
// Create
|
47
|
-
console.log('Creating...');
|
48
59
|
var socket = new WebSocket(that.webSocketUrl + '/create');
|
49
60
|
|
61
|
+
// Send attributes to server
|
50
62
|
socket.onopen = function() {
|
51
|
-
console.log(socket);
|
52
63
|
socket.send(JSON.stringify(that));
|
64
|
+
};
|
53
65
|
|
54
|
-
|
66
|
+
// Receive saved resource from server
|
67
|
+
socket.onmessage = function(event) {
|
68
|
+
if (event.data) {
|
69
|
+
var data = JSON.parse(event.data);
|
55
70
|
|
56
|
-
|
71
|
+
// Assign/override new data (such as id, created_at,
|
72
|
+
// updated_at, etc)
|
73
|
+
if (data.resource) {
|
74
|
+
for (key in data.resource) {
|
75
|
+
that[key] = data.resource[key];
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
// Pass 'that' to callback for create
|
81
|
+
// function so that the create function
|
82
|
+
// can pass the created resource to its
|
83
|
+
// own callback; not needed for $save per se
|
84
|
+
if (callback) callback(that);
|
57
85
|
};
|
58
86
|
}
|
59
87
|
};
|
60
88
|
|
89
|
+
// $update() updates a record in place
|
90
|
+
Resource.prototype.$update = function(params, callback) {
|
91
|
+
// Update object in memory
|
92
|
+
for (var key in params) {
|
93
|
+
// Skip inherent object properties
|
94
|
+
if (params.hasOwnProperty(key)) {
|
95
|
+
this[key] = params[key];
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
// Save object
|
100
|
+
this.$save(callback);
|
101
|
+
};
|
102
|
+
|
61
103
|
// $destroy() will send a request to the server to
|
62
104
|
// destroy an existing record.
|
63
105
|
Resource.prototype.$destroy = function(callback) {
|
64
|
-
console.log('From $destroy: ', this);
|
65
|
-
|
66
106
|
var socket = new WebSocket(this.webSocketUrl + '/' + this.id + '/destroy');
|
67
107
|
socket.onopen = function() {
|
68
108
|
// It's fine to send an empty message since the
|
@@ -74,6 +114,26 @@ angular.module('entangled', [])
|
|
74
114
|
};
|
75
115
|
};
|
76
116
|
|
117
|
+
// $valid() checks if any errors are attached to the object
|
118
|
+
// and return false if so, false otherwise. This doesn't actually
|
119
|
+
// invoke server side validations, so it should only be used
|
120
|
+
// after calling $save() to check if the record was successfully
|
121
|
+
// stored in the database
|
122
|
+
Resource.prototype.$valid = function() {
|
123
|
+
return !(this.errors && Object.keys(this.errors).length);
|
124
|
+
};
|
125
|
+
|
126
|
+
// $invalid() returns the opposite of $valid()
|
127
|
+
Resource.prototype.$invalid = function() {
|
128
|
+
return !this.$valid();
|
129
|
+
};
|
130
|
+
|
131
|
+
// $persisted() checks if the record was successfully stored
|
132
|
+
// in the back end's database
|
133
|
+
Resource.prototype.$persisted = function() {
|
134
|
+
return !!this.id;
|
135
|
+
};
|
136
|
+
|
77
137
|
// Resources wraps all individual Resource objects
|
78
138
|
// in a collection.
|
79
139
|
var Resources = function(resources, webSocketUrl) {
|
@@ -119,22 +179,16 @@ angular.module('entangled', [])
|
|
119
179
|
|
120
180
|
// If the collection of Resources was sent
|
121
181
|
if (data.resources) {
|
122
|
-
console.log('Index');
|
123
|
-
|
124
182
|
// Store retrieved Resources in property
|
125
183
|
this.resources = new Resources(data.resources, socket.url);
|
126
184
|
} else if (data.action) {
|
127
185
|
if (data.action === 'create') {
|
128
186
|
// If new Resource was created, add it to the
|
129
187
|
// collection
|
130
|
-
console.log('Created');
|
131
|
-
console.log(this.resources);
|
132
|
-
|
133
188
|
this.resources.all.push(new Resource(data.resource, socket.url));
|
134
189
|
} else if (data.action === 'update') {
|
135
190
|
// If an existing Resource was updated,
|
136
191
|
// update it in the collection as well
|
137
|
-
console.log('Updated');
|
138
192
|
var index;
|
139
193
|
|
140
194
|
for (var i = 0; i < this.resources.all.length; i++) {
|
@@ -147,7 +201,6 @@ angular.module('entangled', [])
|
|
147
201
|
} else if (data.action === 'destroy') {
|
148
202
|
// If a Resource was destroyed, remove it
|
149
203
|
// from Resources as well
|
150
|
-
console.log('Destroyed');
|
151
204
|
var index;
|
152
205
|
|
153
206
|
for (var i = 0; i < this.resources.all.length; i++) {
|
@@ -158,7 +211,7 @@ angular.module('entangled', [])
|
|
158
211
|
|
159
212
|
this.resources.all.splice(index, 1);
|
160
213
|
} else {
|
161
|
-
console.log('Something else happened...');
|
214
|
+
console.log('Something else other than CRUD happened...');
|
162
215
|
console.log(data);
|
163
216
|
}
|
164
217
|
}
|
@@ -170,6 +223,12 @@ angular.module('entangled', [])
|
|
170
223
|
};
|
171
224
|
};
|
172
225
|
|
226
|
+
// Instantiate and persist a record in one go
|
227
|
+
Entangled.prototype.create = function(params, callback) {
|
228
|
+
var resource = this.new(params);
|
229
|
+
resource.$save(callback);
|
230
|
+
};
|
231
|
+
|
173
232
|
// Find an individual Resource on the server
|
174
233
|
Entangled.prototype.find = function(id, callback) {
|
175
234
|
var webSocketUrl = this.webSocketUrl;
|
@@ -180,7 +239,6 @@ angular.module('entangled', [])
|
|
180
239
|
if (event.data.length) {
|
181
240
|
// Parse message and convert to JSON
|
182
241
|
var data = JSON.parse(event.data);
|
183
|
-
console.log('Show');
|
184
242
|
|
185
243
|
if (data.resource && !data.action) {
|
186
244
|
// If the Resource was sent from the server,
|
@@ -190,18 +248,14 @@ angular.module('entangled', [])
|
|
190
248
|
if (data.action === 'update') {
|
191
249
|
// If the stored Resource was updated,
|
192
250
|
// update it here as well
|
193
|
-
console.log('updated!');
|
194
|
-
|
195
251
|
this.resource = new Resource(data.resource, webSocketUrl);
|
196
252
|
} else if (data.action === 'destroy') {
|
197
253
|
// If the stored Resource was destroyed,
|
198
254
|
// remove it from here as well
|
199
|
-
console.log('destroyed!');
|
200
|
-
|
201
255
|
this.resource = undefined;
|
202
256
|
}
|
203
257
|
} else {
|
204
|
-
console.log('
|
258
|
+
console.log('Something else other than CRUD happened...');
|
205
259
|
console.log(data);
|
206
260
|
}
|
207
261
|
}
|