entangled 0.0.15 → 0.0.16
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 +4 -4
- data/README.md +11 -6
- data/lib/entangled/controller.rb +21 -2
- data/lib/entangled/model.rb +9 -0
- data/lib/entangled/version.rb +1 -1
- data/spec/dummy/app/controllers/messages_controller.rb +3 -8
- data/spec/dummy/app/models/message.rb +2 -0
- data/spec/dummy/config/environments/test.rb +5 -1
- data/spec/dummy/public/app/entangled/entangled.js +30 -0
- data/spec/dummy/public/test/controllers/messages_test.js +55 -2
- data/spec/models/message_spec.rb +14 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c1338c6837e0167bc7fab4bf7f0ca67a2e572ba
|
4
|
+
data.tar.gz: 27dc18b0cb3570ae172c603baca469a8958ead45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89baa6f21fd914f7e8f6664a5712ac6af91891bb210b28be642c0b5acd36f6bf287e70a7a39888f2cf8e17397ed0bd3d1de06b66ea8bded1982d08946c7273e9
|
7
|
+
data.tar.gz: edcafa59d08bfedb94741745f7a6fb8365ff87a68fd8a83ac2409876bdd8601e0fd0bc9a9be40730a1a3ee5f035a173c2fc1d6eb4f8b4ae82e5d919c096d0a38
|
data/README.md
CHANGED
@@ -192,7 +192,7 @@ app.factory('Message', function(Entangled) {
|
|
192
192
|
});
|
193
193
|
```
|
194
194
|
|
195
|
-
In the above example, first
|
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 route (in this case, `/messages`). Then we add helper methods to our service. 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.
|
196
196
|
|
197
197
|
In your controller, you could then inject that `Message` service and use it like so:
|
198
198
|
|
@@ -236,11 +236,16 @@ This gem is best used for Rails apps that serve as APIs only and are not concern
|
|
236
236
|
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.
|
237
237
|
|
238
238
|
## Contributing
|
239
|
-
1. Fork it
|
240
|
-
2.
|
241
|
-
3.
|
242
|
-
4.
|
243
|
-
5.
|
239
|
+
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
|
240
|
+
2. Run `$ bundle install` in the root of the repo
|
241
|
+
3. Run `$ bower install` and `$ npm install` in spec/dummy/public
|
242
|
+
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. Write your tests
|
245
|
+
7. Write your feature to make the tests pass
|
246
|
+
8. Stage and commit your changes
|
247
|
+
9. Push to a new feature branch in your repo
|
248
|
+
10. Send me a pull request!
|
244
249
|
|
245
250
|
## Credits
|
246
251
|
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/lib/entangled/controller.rb
CHANGED
@@ -144,16 +144,35 @@ module Entangled
|
|
144
144
|
tubesock.onmessage do |m|
|
145
145
|
params[resource_name.to_sym] = JSON.parse(m)
|
146
146
|
yield
|
147
|
+
|
148
|
+
# Send resource that was just created back to client. The resource
|
149
|
+
# on the client will be overridden with this one. This is important
|
150
|
+
# so that the id, created_at and updated_at and possibly other
|
151
|
+
# attributes arrive on the client
|
152
|
+
if member
|
153
|
+
tubesock.send_data({
|
154
|
+
resource: member
|
155
|
+
}.to_json)
|
156
|
+
end
|
147
157
|
end
|
148
158
|
|
149
159
|
# If the controller's action name is 'update', a record should be
|
150
160
|
# updated. Before yielding, the params hash has to be prepared
|
151
|
-
# with attributes sent to the socket
|
152
|
-
# id, created_at, and updated_at should not be included in params.
|
161
|
+
# with attributes sent to the socket
|
153
162
|
when 'update'
|
154
163
|
tubesock.onmessage do |m|
|
155
164
|
params[resource_name.to_sym] = JSON.parse(m)
|
156
165
|
yield
|
166
|
+
|
167
|
+
# Send resource that was just updated back to client. The resource
|
168
|
+
# on the client will be overridden with this one. This is important
|
169
|
+
# so that the new updated_at and possibly other attributes arrive
|
170
|
+
# on the client
|
171
|
+
if member
|
172
|
+
tubesock.send_data({
|
173
|
+
resource: member
|
174
|
+
}.to_json)
|
175
|
+
end
|
157
176
|
end
|
158
177
|
|
159
178
|
# For every other controller action, simply wrap whatever is
|
data/lib/entangled/model.rb
CHANGED
@@ -70,6 +70,15 @@ module Entangled
|
|
70
70
|
module InstanceMethods
|
71
71
|
include Entangled::Helpers
|
72
72
|
|
73
|
+
# Override the as_json method so that the
|
74
|
+
# JSON representation of the resource includes
|
75
|
+
# its errors. This is necessary so that errors
|
76
|
+
# are sent back to the client along with the
|
77
|
+
# resource on create and update
|
78
|
+
def as_json(options = {})
|
79
|
+
attributes.merge(errors: errors).as_json
|
80
|
+
end
|
81
|
+
|
73
82
|
private
|
74
83
|
|
75
84
|
# Publishes to client. Whoever is subscribed
|
data/lib/entangled/version.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# controller with all five actions
|
3
3
|
class MessagesController < ApplicationController
|
4
4
|
include Entangled::Controller
|
5
|
-
# before_action :destroy_all, only: :create, if: -> { Rails.env.test? }
|
6
5
|
|
7
6
|
def index
|
8
7
|
broadcast do
|
@@ -18,13 +17,14 @@ class MessagesController < ApplicationController
|
|
18
17
|
|
19
18
|
def create
|
20
19
|
broadcast do
|
21
|
-
Message.create(message_params)
|
20
|
+
@message = Message.create(message_params)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
25
24
|
def update
|
26
25
|
broadcast do
|
27
|
-
Message.find(params[:id])
|
26
|
+
@message = Message.find(params[:id])
|
27
|
+
@message.update(message_params)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -38,9 +38,4 @@ private
|
|
38
38
|
def message_params
|
39
39
|
params.require(:message).permit(:body)
|
40
40
|
end
|
41
|
-
|
42
|
-
# For test purposes
|
43
|
-
def destroy_all
|
44
|
-
Message.destroy_all
|
45
|
-
end
|
46
41
|
end
|
@@ -51,7 +51,11 @@ Rails.application.configure do
|
|
51
51
|
Message.destroy_all
|
52
52
|
|
53
53
|
# Create one dummy message that the JS tests depend on
|
54
|
-
Message.create body: 'foo'
|
54
|
+
Message.create! body: 'foo'
|
55
55
|
end
|
56
56
|
end
|
57
|
+
|
58
|
+
# Configure log because the server won't log anything otherwise
|
59
|
+
# config.logger = Logger.new(STDOUT)
|
60
|
+
# config.logger.level = Logger::ERROR
|
57
61
|
end
|
@@ -36,15 +36,45 @@ angular.module('entangled', [])
|
|
36
36
|
|
37
37
|
if (callback) callback();
|
38
38
|
};
|
39
|
+
|
40
|
+
// Receive updated resource from server
|
41
|
+
socket.onmessage = function(event) {
|
42
|
+
if (event.data) {
|
43
|
+
var data = JSON.parse(event.data);
|
44
|
+
|
45
|
+
// Assign/override new data (such as updated_at, etc)
|
46
|
+
if (data.resource) {
|
47
|
+
for (key in data.resource) {
|
48
|
+
that[key] = data.resource[key];
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
};
|
39
53
|
} else {
|
40
54
|
// Create
|
41
55
|
var socket = new WebSocket(that.webSocketUrl + '/create');
|
42
56
|
|
57
|
+
// Send attributes to server
|
43
58
|
socket.onopen = function() {
|
44
59
|
socket.send(JSON.stringify(that));
|
45
60
|
|
46
61
|
if (callback) callback();
|
47
62
|
};
|
63
|
+
|
64
|
+
// Receive saved resource from server
|
65
|
+
socket.onmessage = function(event) {
|
66
|
+
if (event.data) {
|
67
|
+
var data = JSON.parse(event.data);
|
68
|
+
|
69
|
+
// Assign/override new data (such as id, created_at,
|
70
|
+
// updated_at, etc)
|
71
|
+
if (data.resource) {
|
72
|
+
for (key in data.resource) {
|
73
|
+
that[key] = data.resource[key];
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
};
|
48
78
|
}
|
49
79
|
};
|
50
80
|
|
@@ -49,10 +49,26 @@ describe('MessagesCtrl', function () {
|
|
49
49
|
setTimeout(function() {
|
50
50
|
var oldLength = scope.messages.length;
|
51
51
|
|
52
|
+
// Assert it's a new message
|
53
|
+
expect(scope.message.id).not.toBeDefined();
|
54
|
+
|
55
|
+
// Be sure to pass validations in the back end
|
56
|
+
scope.message.body = 'foo';
|
57
|
+
|
58
|
+
// Save it
|
52
59
|
scope.message.$save(function() {
|
53
60
|
setTimeout(function() {
|
61
|
+
// Make sure that the message was added to the collection
|
54
62
|
expect(scope.messages.length).toBe(oldLength + 1);
|
55
|
-
|
63
|
+
|
64
|
+
// Make sure that it has been persisted and new attributes
|
65
|
+
// were sent over from the back end and updated here
|
66
|
+
setTimeout(function() {
|
67
|
+
expect(scope.message.id).toBeDefined();
|
68
|
+
expect(scope.message.created_at).toBeDefined();
|
69
|
+
expect(scope.message.updated_at).toBeDefined();
|
70
|
+
done();
|
71
|
+
}, 100);
|
56
72
|
}, 100);
|
57
73
|
});
|
58
74
|
}, 100);
|
@@ -66,12 +82,49 @@ describe('MessagesCtrl', function () {
|
|
66
82
|
// Assert that message has been persisted before
|
67
83
|
expect(message.id).toBeDefined();
|
68
84
|
|
85
|
+
// Remember old updated_at
|
86
|
+
var oldUpdatedAt = message.updated_at;
|
87
|
+
|
69
88
|
// Update it
|
70
89
|
message.body = 'new body';
|
71
90
|
message.$save(function() {
|
72
91
|
setTimeout(function() {
|
73
92
|
// Assert that message was updated across collection
|
74
|
-
expect(scope.messages[0].body).
|
93
|
+
expect(scope.messages[0].body).toBe('new body');
|
94
|
+
|
95
|
+
// Assert that message was actually updated in back end
|
96
|
+
// and attributes have been updated here
|
97
|
+
expect(scope.message.updated_at).not.toBe(oldUpdatedAt);
|
98
|
+
done();
|
99
|
+
}, 100);
|
100
|
+
});
|
101
|
+
}, 100);
|
102
|
+
});
|
103
|
+
|
104
|
+
it('gets validation errors', function(done) {
|
105
|
+
setTimeout(function() {
|
106
|
+
// Pick first message
|
107
|
+
var message = scope.messages[0];
|
108
|
+
|
109
|
+
// Assert that message has been persisted before
|
110
|
+
expect(message.id).toBeDefined();
|
111
|
+
|
112
|
+
// Update it so it won't pass validations in the back end
|
113
|
+
message.body = '';
|
114
|
+
|
115
|
+
// Remember old updated_at
|
116
|
+
var oldUpdatedAt = message.updated_at;
|
117
|
+
|
118
|
+
// Save it
|
119
|
+
message.$save(function() {
|
120
|
+
setTimeout(function() {
|
121
|
+
// Assert that message was not updated on server
|
122
|
+
expect(message.updated_at).toBe(oldUpdatedAt);
|
123
|
+
|
124
|
+
// Assert that the message has error messages attached
|
125
|
+
// to it
|
126
|
+
expect(message.errors.body).toEqual(["can't be blank"])
|
127
|
+
|
75
128
|
done();
|
76
129
|
}, 100);
|
77
130
|
});
|
data/spec/models/message_spec.rb
CHANGED
@@ -106,5 +106,19 @@ RSpec.describe Message, type: :model do
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
109
|
+
|
110
|
+
describe '#as_json' do
|
111
|
+
it 'includes errors' do
|
112
|
+
message = Message.create
|
113
|
+
expect(message.as_json["errors"][:body]).to include "can't be blank"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'Validations' do
|
119
|
+
it 'validates presence of the body' do
|
120
|
+
message = Message.create
|
121
|
+
expect(message.errors[:body]).to include "can't be blank"
|
122
|
+
end
|
109
123
|
end
|
110
124
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: entangled
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dennis Charles Hackethal
|
@@ -9831,6 +9831,7 @@ files:
|
|
9831
9831
|
- spec/dummy/public/test/controllers/messages_test.js
|
9832
9832
|
- spec/dummy/public/views/messages/index.html
|
9833
9833
|
- spec/dummy/public/views/messages/show.html
|
9834
|
+
- spec/dummy/tmp/pids/server.pid
|
9834
9835
|
- spec/models/inclusion_exclusion_spec.rb
|
9835
9836
|
- spec/models/message_spec.rb
|
9836
9837
|
- spec/routing/inclusion_exclusion_routing_spec.rb
|
@@ -19516,6 +19517,7 @@ test_files:
|
|
19516
19517
|
- spec/dummy/public/views/messages/show.html
|
19517
19518
|
- spec/dummy/Rakefile
|
19518
19519
|
- spec/dummy/README.rdoc
|
19520
|
+
- spec/dummy/tmp/pids/server.pid
|
19519
19521
|
- spec/models/inclusion_exclusion_spec.rb
|
19520
19522
|
- spec/models/message_spec.rb
|
19521
19523
|
- spec/routing/inclusion_exclusion_routing_spec.rb
|