metybur 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -6
- data/README.md +19 -4
- data/lib/metybur/client.rb +36 -5
- data/lib/metybur/collection.rb +5 -1
- data/lib/metybur/version.rb +1 -1
- data/lib/metybur.rb +35 -32
- data/metybur.gemspec +1 -0
- data/spec/metybur_spec.rb +85 -3
- data/spec/mocks/websocket_mock.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ac162b165fe7dd83047c24e2f4a89be9a0fa28e
|
4
|
+
data.tar.gz: 532cc2a90d625cfab46bff3a96b281e455f3e548
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00aa652b42b18209f40cdb79ea2a6591742cc7cb8fcebf30df3acdff2e4a9eca80a9ba1f0a3d094e0f456f0598117e36447d91f01a2b68726026c86039702003
|
7
|
+
data.tar.gz: 73f236b005a50ebf862757f1bf11aca5d121c24f5a98e1463ea91e7192298d36c679c414517094a8cb5040d7379080b0e88b04269247e8aff7d8035763be24e7
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -9,11 +9,11 @@ to subscribe to collections and to receive updates on them.
|
|
9
9
|
You can also call Meteor methods from Ruby.
|
10
10
|
|
11
11
|
> ## Caution!
|
12
|
-
>
|
13
|
-
> This gem
|
14
|
-
>
|
12
|
+
>
|
13
|
+
> This gem isn't at version 1 yet and doesn't include basic features like proper error handling. Also only a subset of the DDP protocol is implemented so far. Everything described in this README should work, though.
|
14
|
+
>
|
15
15
|
> I'll keep working on it constantly, so I suppose there will be a complete and stable version soon. Stay tuned!
|
16
|
-
>
|
16
|
+
>
|
17
17
|
> Clemens
|
18
18
|
|
19
19
|
## Installation
|
@@ -86,6 +86,12 @@ meteor = Metybur.connect('http://my-meteor-app.org:80/websocket')
|
|
86
86
|
meteor.subscribe('my-chat-messages')
|
87
87
|
```
|
88
88
|
|
89
|
+
Subscription arguments can be passed right after the collection name:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
meteor.subscribe('my-chat-messages', 10, channel: 'random')
|
93
|
+
```
|
94
|
+
|
89
95
|
### Collections
|
90
96
|
|
91
97
|
Once you've subscribed, you will receive all records that are already in the record set. The record set contains records from one or more collections. You can process these records as they arrive:
|
@@ -192,6 +198,15 @@ Metybur.log_level = :debug
|
|
192
198
|
|
193
199
|
Make sure to set the log level before calling `Metybur.connect`, as it won't have any effect afterwards.
|
194
200
|
|
201
|
+
|
202
|
+
### Reconnecting
|
203
|
+
|
204
|
+
When the websocket connection is lost, Metybur will try to reconnect to your Meteor app.
|
205
|
+
After the reconnect the `login` method is called and all your subscriptions are subscribed again.
|
206
|
+
|
207
|
+
This means in particular that all documents get sent again and therefore all your
|
208
|
+
collections' `added` callbacks will get triggered again.
|
209
|
+
|
195
210
|
## Contributing
|
196
211
|
|
197
212
|
1. Fork it ( https://github.com/clemenshelm/metybur/fork )
|
data/lib/metybur/client.rb
CHANGED
@@ -3,21 +3,29 @@ require_relative 'collection'
|
|
3
3
|
require_relative 'method'
|
4
4
|
|
5
5
|
class Metybur::Client
|
6
|
-
|
7
|
-
|
6
|
+
attr_writer :websocket
|
7
|
+
|
8
|
+
def initialize(credentials)
|
9
|
+
@credentials = credentials.freeze
|
10
|
+
@subscription_messages = []
|
11
|
+
@collections = []
|
8
12
|
end
|
9
13
|
|
10
|
-
def subscribe(record_set_name)
|
14
|
+
def subscribe(record_set_name, *params)
|
11
15
|
message = {
|
12
16
|
msg: 'sub',
|
13
17
|
id: 'cde',
|
14
|
-
name: record_set_name
|
18
|
+
name: record_set_name,
|
19
|
+
params: params
|
15
20
|
}.to_json
|
21
|
+
@subscription_messages << message
|
16
22
|
@websocket.send message
|
17
23
|
end
|
18
24
|
|
19
25
|
def collection(name)
|
20
|
-
Metybur::Collection.new(name, @websocket)
|
26
|
+
Metybur::Collection.new(name, @websocket).tap do |collection|
|
27
|
+
@collections << collection
|
28
|
+
end
|
21
29
|
end
|
22
30
|
|
23
31
|
def method(name)
|
@@ -41,4 +49,27 @@ class Metybur::Client
|
|
41
49
|
end
|
42
50
|
call(method, params, &block)
|
43
51
|
end
|
52
|
+
|
53
|
+
def resubscribe
|
54
|
+
@subscription_messages.each { |message| @websocket.send(message) }
|
55
|
+
@collections.each { |collection| collection.websocket = @websocket }
|
56
|
+
end
|
57
|
+
|
58
|
+
def connect
|
59
|
+
connect_message = {
|
60
|
+
msg: 'connect',
|
61
|
+
version: '1',
|
62
|
+
support: ['1']
|
63
|
+
}
|
64
|
+
@websocket.send(connect_message.to_json)
|
65
|
+
|
66
|
+
credentials = @credentials.dup
|
67
|
+
password = credentials.delete(:password)
|
68
|
+
if password
|
69
|
+
this = self
|
70
|
+
login(user: credentials, password: password) { this.resubscribe }
|
71
|
+
else
|
72
|
+
resubscribe
|
73
|
+
end
|
74
|
+
end
|
44
75
|
end
|
data/lib/metybur/collection.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
class Metybur::Collection
|
2
2
|
def initialize(collection_name, websocket)
|
3
|
+
@collection_name = collection_name
|
3
4
|
@callbacks = {}
|
5
|
+
self.websocket = websocket
|
6
|
+
end
|
4
7
|
|
8
|
+
def websocket=(websocket)
|
5
9
|
websocket.on(:message) do |event|
|
6
10
|
attributes = JSON.parse(event.data, symbolize_names: true)
|
7
|
-
handle_message(attributes) if attributes[:collection] == collection_name
|
11
|
+
handle_message(attributes) if attributes[:collection] == @collection_name
|
8
12
|
end
|
9
13
|
end
|
10
14
|
|
data/lib/metybur/version.rb
CHANGED
data/lib/metybur.rb
CHANGED
@@ -15,43 +15,14 @@ module Metybur
|
|
15
15
|
}
|
16
16
|
|
17
17
|
def self.connect(url, credentials = {})
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
logging_middleware = Metybur::LoggingMiddleware.new
|
22
|
-
json_middleware = Metybur::JSONMiddleware.new
|
23
|
-
ping_pong_middleware = Metybur::PingPongMiddleware.new(websocket)
|
24
|
-
middleware = [logging_middleware, json_middleware, ping_pong_middleware]
|
25
|
-
|
26
|
-
websocket.on(:open) do |event|
|
27
|
-
middleware.inject(event) { |e, mw| mw.open(e) }
|
28
|
-
end
|
29
|
-
websocket.on(:message) do |event|
|
30
|
-
middleware.inject(event) { |e, mw| mw.message(e) }
|
31
|
-
end
|
32
|
-
websocket.on(:close) do |event|
|
33
|
-
middleware.inject(event) { |e, mw| mw.close(e) }
|
34
|
-
EM.stop_event_loop
|
35
|
-
end
|
36
|
-
|
37
|
-
connect_message = {
|
38
|
-
msg: 'connect',
|
39
|
-
version: '1',
|
40
|
-
support: ['1']
|
41
|
-
}
|
42
|
-
websocket.send(connect_message.to_json)
|
43
|
-
|
44
|
-
password = credentials.delete(:password)
|
45
|
-
return client unless password
|
46
|
-
client.login(user: credentials, password: password)
|
47
|
-
|
48
|
-
client
|
18
|
+
connection = Connection.new(url, credentials)
|
19
|
+
connection.connect_client
|
49
20
|
end
|
50
21
|
|
51
22
|
def self.websocket_client_class=(klass)
|
52
23
|
CONFIG[:websocket_client_class] = klass
|
53
24
|
end
|
54
|
-
|
25
|
+
|
55
26
|
def self.log_level=(level_symbol)
|
56
27
|
upcase_symbol = level_symbol.to_s.upcase.to_sym
|
57
28
|
CONFIG[:log_level] = Logger.const_get(upcase_symbol)
|
@@ -60,4 +31,36 @@ module Metybur
|
|
60
31
|
def self.log_stream=(io)
|
61
32
|
CONFIG[:log_stream] = io
|
62
33
|
end
|
34
|
+
|
35
|
+
class Connection
|
36
|
+
def initialize(url, credentials)
|
37
|
+
@url, @credentials = url, credentials
|
38
|
+
end
|
39
|
+
|
40
|
+
def connect_client(client = Metybur::Client.new(@credentials))
|
41
|
+
websocket = CONFIG[:websocket_client_class].new(@url)
|
42
|
+
client.websocket = websocket
|
43
|
+
client.connect
|
44
|
+
|
45
|
+
logging_middleware = Metybur::LoggingMiddleware.new
|
46
|
+
json_middleware = Metybur::JSONMiddleware.new
|
47
|
+
ping_pong_middleware = Metybur::PingPongMiddleware.new(websocket)
|
48
|
+
middleware = [logging_middleware, json_middleware, ping_pong_middleware]
|
49
|
+
|
50
|
+
websocket.on(:open) do |event|
|
51
|
+
middleware.inject(event) { |e, mw| mw.open(e) }
|
52
|
+
end
|
53
|
+
websocket.on(:message) do |event|
|
54
|
+
middleware.inject(event) { |e, mw| mw.message(e) }
|
55
|
+
end
|
56
|
+
websocket.on(:close) do |event|
|
57
|
+
middleware.inject(event) { |e, mw| mw.close(e) }
|
58
|
+
|
59
|
+
# Reconnect
|
60
|
+
connect_client(client)
|
61
|
+
end
|
62
|
+
|
63
|
+
client
|
64
|
+
end
|
65
|
+
end
|
63
66
|
end
|
data/metybur.gemspec
CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency "bundler", "~> 1.6"
|
29
29
|
spec.add_development_dependency "rake"
|
30
30
|
spec.add_development_dependency "rspec"
|
31
|
+
spec.add_development_dependency "rspec-collection_matchers"
|
31
32
|
spec.add_development_dependency "ffaker"
|
32
33
|
spec.add_development_dependency "pry"
|
33
34
|
end
|
data/spec/metybur_spec.rb
CHANGED
@@ -35,6 +35,20 @@ describe Metybur do
|
|
35
35
|
support: ['1']
|
36
36
|
end
|
37
37
|
|
38
|
+
it 'reconnects to a Meteor URL' do
|
39
|
+
meteor = Metybur.connect(url)
|
40
|
+
|
41
|
+
# Reconnect
|
42
|
+
new_connection = WebsocketMock.new(websocket.url)
|
43
|
+
meteor.websocket = new_connection
|
44
|
+
meteor.connect
|
45
|
+
|
46
|
+
expect(last_sent_message)
|
47
|
+
.to eq msg: 'connect',
|
48
|
+
version: '1',
|
49
|
+
support: ['1']
|
50
|
+
end
|
51
|
+
|
38
52
|
context 'logging in' do
|
39
53
|
it 'calls the login method with email and password' do
|
40
54
|
email = FFaker::Internet.email
|
@@ -83,6 +97,25 @@ describe Metybur do
|
|
83
97
|
|
84
98
|
expect(last_sent_message[:msg]).to eq 'connect'
|
85
99
|
end
|
100
|
+
|
101
|
+
it "logs in on reconnect" do
|
102
|
+
userid = FFaker::Guid.guid
|
103
|
+
password = FFaker::Internet.password
|
104
|
+
|
105
|
+
meteor = Metybur.connect(url, id: userid, password: password)
|
106
|
+
|
107
|
+
# Reconnect
|
108
|
+
new_connection = WebsocketMock.new(websocket.url)
|
109
|
+
meteor.websocket = new_connection
|
110
|
+
meteor.connect
|
111
|
+
|
112
|
+
expect(last_sent_message[:msg]).to eq 'method'
|
113
|
+
expect(last_sent_message).to have_key :id # we don't care about the value here
|
114
|
+
expect(last_sent_message[:method]).to eq 'login'
|
115
|
+
expect(last_sent_message[:params][0])
|
116
|
+
.to eq user: { id: userid },
|
117
|
+
password: password
|
118
|
+
end
|
86
119
|
end
|
87
120
|
|
88
121
|
context 'ping pong' do
|
@@ -126,7 +159,7 @@ describe Metybur do
|
|
126
159
|
Metybur.log_level = :debug
|
127
160
|
Metybur.log_stream = output
|
128
161
|
Metybur.connect(url)
|
129
|
-
|
162
|
+
|
130
163
|
websocket.receive({msg: 'logged_message'}.to_json)
|
131
164
|
|
132
165
|
expect(output.string).not_to be_empty
|
@@ -144,6 +177,31 @@ describe Metybur do
|
|
144
177
|
expect(last_sent_message).to have_key :id # we don't care about the value here
|
145
178
|
expect(last_sent_message[:name]).to eq record_set
|
146
179
|
end
|
180
|
+
|
181
|
+
it 'should allow you to pass parameters' do
|
182
|
+
record_set = FFaker::Internet.user_name
|
183
|
+
meteor = Metybur.connect(url)
|
184
|
+
meteor.subscribe(record_set, 'abc', lala: 'foo')
|
185
|
+
expect(last_sent_message[:msg]).to eq 'sub'
|
186
|
+
expect(last_sent_message).to have_key :params
|
187
|
+
expect(last_sent_message[:params]).to eq ['abc', {:lala => 'foo'}]
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'subscribes again on a new connection' do
|
191
|
+
record_set = FFaker::Internet.user_name
|
192
|
+
|
193
|
+
meteor = Metybur.connect(url)
|
194
|
+
meteor.subscribe(record_set, 'abc', lala: 'foo')
|
195
|
+
|
196
|
+
# Reconnect
|
197
|
+
new_connection = WebsocketMock.new(websocket.url)
|
198
|
+
meteor.websocket = new_connection
|
199
|
+
meteor.connect
|
200
|
+
|
201
|
+
expect(last_sent_message[:msg]).to eq 'sub'
|
202
|
+
expect(last_sent_message).to have_key :params
|
203
|
+
expect(last_sent_message[:params]).to eq ['abc', {:lala => 'foo'}]
|
204
|
+
end
|
147
205
|
end
|
148
206
|
|
149
207
|
context 'collections' do
|
@@ -238,7 +296,7 @@ describe Metybur do
|
|
238
296
|
|
239
297
|
it 'does nothing if there is no callback for an event' do
|
240
298
|
meteor.collection(collection)
|
241
|
-
|
299
|
+
|
242
300
|
message = {
|
243
301
|
msg: 'added',
|
244
302
|
collection: collection,
|
@@ -254,7 +312,7 @@ describe Metybur do
|
|
254
312
|
|
255
313
|
websocket.receive({msg: 'ping'}.to_json)
|
256
314
|
end
|
257
|
-
|
315
|
+
|
258
316
|
it "doesn't get notified of a record from another collection" do
|
259
317
|
meteor.collection('my-collection')
|
260
318
|
.on(:added) { fail('Callback got called') }
|
@@ -267,6 +325,30 @@ describe Metybur do
|
|
267
325
|
}.to_json
|
268
326
|
websocket.receive message
|
269
327
|
end
|
328
|
+
|
329
|
+
it 'still gets notified when a record is added after reconnect' do
|
330
|
+
wait_for_callback do |done|
|
331
|
+
meteor.collection(collection)
|
332
|
+
.on(:added) do |added_id, added_fields|
|
333
|
+
done.call()
|
334
|
+
expect(added_id).to eq id
|
335
|
+
expect(added_fields).to eq fields
|
336
|
+
end
|
337
|
+
|
338
|
+
# reconnect
|
339
|
+
new_connection = WebsocketMock.new(websocket.url)
|
340
|
+
meteor.websocket = new_connection
|
341
|
+
meteor.connect
|
342
|
+
|
343
|
+
message = {
|
344
|
+
msg: 'added',
|
345
|
+
collection: collection,
|
346
|
+
id: id,
|
347
|
+
fields: fields
|
348
|
+
}.to_json
|
349
|
+
new_connection.receive message
|
350
|
+
end
|
351
|
+
end
|
270
352
|
end
|
271
353
|
|
272
354
|
context 'methods' do
|
data/spec/spec_helper.rb
CHANGED
@@ -16,6 +16,8 @@
|
|
16
16
|
# users commonly want.
|
17
17
|
#
|
18
18
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
19
|
+
require 'rspec/collection_matchers'
|
20
|
+
|
19
21
|
RSpec.configure do |config|
|
20
22
|
# rspec-expectations config goes here. You can use an alternate
|
21
23
|
# assertion/expectation library such as wrong or the stdlib/minitest
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metybur
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clemens Helm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faye-websocket
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-collection_matchers
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: ffaker
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -157,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
171
|
version: '0'
|
158
172
|
requirements: []
|
159
173
|
rubyforge_project:
|
160
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.5.1
|
161
175
|
signing_key:
|
162
176
|
specification_version: 4
|
163
177
|
summary: DDP client for Ruby to connect to Meteor apps.
|