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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d88b017da86178defe787f323b432f6be6f69884
4
- data.tar.gz: ee68c9a15f92b134c61cd0b0ae2eaa59f8da52e3
3
+ metadata.gz: 2ac162b165fe7dd83047c24e2f4a89be9a0fa28e
4
+ data.tar.gz: 532cc2a90d625cfab46bff3a96b281e455f3e548
5
5
  SHA512:
6
- metadata.gz: 1ef952a06c43c9be058194c38d471188e7a67d774b9720c75cca34f74363362884ee6b9808b7556a3b07236582ad108d897dfa6d084243e29375a57480c6047c
7
- data.tar.gz: 376901b6f18c5f5f1ccaf4ab4ce26e586c699c77b18ac89da320d458d8d8f176a17a9933a0d647ca19853211a25defe62beae1a5accd7dbe5d06599034646e2a
6
+ metadata.gz: 00aa652b42b18209f40cdb79ea2a6591742cc7cb8fcebf30df3acdff2e4a9eca80a9ba1f0a3d094e0f456f0598117e36447d91f01a2b68726026c86039702003
7
+ data.tar.gz: 73f236b005a50ebf862757f1bf11aca5d121c24f5a98e1463ea91e7192298d36c679c414517094a8cb5040d7379080b0e88b04269247e8aff7d8035763be24e7
data/.travis.yml CHANGED
@@ -1,9 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.0
4
- - 2.1.0
5
- - 2.0.0
6
- - 1.9.3
7
- - jruby-19mode
3
+ - 2.3.0
4
+ - 2.2.2
8
5
  - ruby-head
9
- - jruby-head
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 is just a few weeks old so far 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
- >
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 )
@@ -3,21 +3,29 @@ require_relative 'collection'
3
3
  require_relative 'method'
4
4
 
5
5
  class Metybur::Client
6
- def initialize(websocket)
7
- @websocket = websocket
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
@@ -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
 
@@ -1,3 +1,3 @@
1
1
  module Metybur
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/metybur.rb CHANGED
@@ -15,43 +15,14 @@ module Metybur
15
15
  }
16
16
 
17
17
  def self.connect(url, credentials = {})
18
- websocket = CONFIG[:websocket_client_class].new(url)
19
- client = Metybur::Client.new(websocket)
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
@@ -6,7 +6,7 @@ class WebsocketMock
6
6
  MessageEvent = Struct.new(:data)
7
7
 
8
8
  attr_reader :url, :sent
9
-
9
+
10
10
  def initialize(url)
11
11
  WebsocketMock.instance = self
12
12
  @url = url
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.3.2
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: 2015-07-17 00:00:00.000000000 Z
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.4.5
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.