deepstream 0.2.0 → 0.2.1

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: 6cd95fc9f274116e724233dc418e5341bb600504
4
- data.tar.gz: 2ba1ca7d05528b7221f264ed6ff133a10ca26425
3
+ metadata.gz: 7f892fdc582aa7e27a3fe12d61e0f27662fb08dd
4
+ data.tar.gz: 49ce6f01d3ce6ad8cb382eab79679ac8cbe2a9f9
5
5
  SHA512:
6
- metadata.gz: 4e8b66da27b17102bd5b52acb98c72638f95df219c767c2b4d5029ca7695f33084c74194cd453bfc2ba3e54d76b79085fc87d1c7e7f667e46c99badd449bb356
7
- data.tar.gz: 7d0196f7f8afc7be326827ef1b93496cc2066374ed4f60279ae58e01863eb6bfaa51be66403ce990d9d30a8a809145ebd8c78686f2c909cae459d2afe0c92b9c
6
+ metadata.gz: 96be6f3399854e1dfe56ed878c5aa42f6389ca11ff3e45b45a82dd74681594899ae3ff41c3af89fd6b01894ed497fb371c6bbe320113e3e7c9666043acb884cf
7
+ data.tar.gz: 8e313b9736f42b34681f0411e8cc4d13989805abe525563e0ef47e2ff82600282de8e2ae00dedcf92d1aff66f09cd08db8c13577dd3b16647cd725f7e379fa69
data/README.md CHANGED
@@ -1,43 +1,49 @@
1
1
  # deepstream-ruby
2
2
  deepstream.io ruby client
3
3
 
4
-
5
4
  ### Install
6
5
 
7
6
  ```
8
7
  gem install deepstream
9
8
  ```
10
9
 
11
-
12
10
  ### Usage
13
11
  ```ruby
14
- ds = Deepstream::Client.new('localhost')
12
+ ds = Deepstream::Client.new('localhost',
13
+ autologin: false,
14
+ verbose: true,
15
+ credentials: { username: 'John', password: 'Doe' })
16
+ # or
17
+ ds = Deepstream::Client.new('ws://localhost:6020')
18
+ # or
19
+ ds = Deepstream::Client.new('ws://localhost:6020/deepstream')
20
+
21
+ # log in to the server
22
+ ds.login
23
+ # you can use new credentials too
24
+ ds.login(username: 'John', password: 'betterDoe')
25
+
26
+ # check if the websocket connection is opened
27
+ ds.connected?
28
+
29
+ # check if the client is logged in
30
+ ds.logged_in?
15
31
 
16
32
  # Emit events
17
33
  ds.emit 'my_event'
18
34
  # or
19
35
  ds.emit 'my_event', foo: 'bar', bar: 'foo'
20
- # or
21
- ds.emit 'my_event', {foo: 'bar', bar: 'foo'}, timeout: 3
22
- # or
23
- ds.emit 'my_event', nil, timeout: 3
24
-
25
36
 
26
37
  # Subscribe to events
27
- ds.on('some_event') do |msg|
38
+ ds.on('some_event') do |event_name, msg|
28
39
  puts msg
29
40
  end
30
41
 
31
-
32
42
  # Get a record
33
43
  foo = ds.get('foo')
44
+ bar = ds.get('bar', list: 'bar_list') # get a record within a namespace (this one automatically adds it to a list)
34
45
 
35
- # Get a record with a namespace (automaticly add to a list)
36
- foo = ds.get_record('foo', list: 'bar') # record can also be accessed by ds.get('bar/foo')
37
-
38
- # Update record
39
- foo.bar = 'bar'
40
- # or
46
+ # Update a record
41
47
  foo.set('bar', 'bar')
42
48
 
43
49
  # Set the whole record
@@ -46,13 +52,15 @@ foo.set(foo: 'foo', bar: 1)
46
52
  # Get a list
47
53
  foo = ds.get_list('bar')
48
54
 
49
- # Add to list
55
+ # Add to the list
50
56
  foo.add('foo')
51
57
 
52
58
  # Remove from list
53
59
  foo.remove('foo')
54
60
 
55
61
  # Show record names on the list
62
+ foo.data
63
+ # or
56
64
  foo.keys
57
65
 
58
66
  # Access records on the list
data/deepstream.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "deepstream"
7
- spec.version = "0.2.0"
7
+ spec.version = "0.2.1"
8
8
  spec.authors = ["Currency-One S.A."]
9
9
  spec.email = ["piotr.szczudlak@currency-one.com"]
10
10
 
@@ -10,7 +10,7 @@ require 'deepstream/exceptions'
10
10
 
11
11
  module Deepstream
12
12
  class Client
13
- attr_reader :last_hearbeat, :options, :state
13
+ attr_reader :options, :state
14
14
 
15
15
  include Celluloid
16
16
  include Celluloid::Internals::Logger
@@ -19,7 +19,7 @@ module Deepstream
19
19
  execute_block_on_receiver :on, :subscribe, :listen
20
20
 
21
21
  def_delegators :@event_handler, :on, :emit, :subscribe, :unsubscribe, :listen, :resubscribe, :unlisten
22
- def_delegators :@error_handler, :error, :on_error
22
+ def_delegators :@error_handler, :error, :on_error, :on_exception
23
23
  def_delegators :@record_handler, :get, :set, :delete, :discard, :get_list
24
24
 
25
25
  def initialize(url, options = {})
@@ -34,11 +34,13 @@ module Deepstream
34
34
  @failed_reconnect_attempts = 0
35
35
  @state = CONNECTION_STATE::CLOSED
36
36
  Celluloid.logger.level = @options[:verbose] ? LOG_LEVEL::INFO : LOG_LEVEL::OFF
37
- async.connect
37
+ connect
38
38
  end
39
39
 
40
40
  def on_open
41
+ info("Websocket connection opened")
41
42
  @state = CONNECTION_STATE::AWAITING_CONNECTION
43
+ @connection_requested, @deliberate_close = false
42
44
  @failed_reconnect_attempts = 0
43
45
  end
44
46
 
@@ -51,19 +53,19 @@ module Deepstream
51
53
  when TOPIC::EVENT then @event_handler.on_message(message)
52
54
  when TOPIC::ERROR then @error_handler.on_error(message)
53
55
  when TOPIC::RECORD then @record_handler.on_message(message)
54
- when TOPIC::RPC then raise UnknownTopic('RPC is currently not implemented.')
55
- else raise UnknownTopic(message.to_s)
56
+ when TOPIC::RPC then raise(UnknownTopic, 'RPC is currently not implemented.')
57
+ else raise(UnknownTopic, message)
56
58
  end
57
59
  rescue => e
58
- @error_handler.on_exception(e)
60
+ on_exception(e)
59
61
  end
60
62
 
61
63
  def on_close(code, reason)
62
- info("Websocket connection closed: #{code.inspect}, #{reason.inspect}")
64
+ info("Websocket connection closed: code - #{code.inspect}, reason - #{reason.inspect}")
63
65
  @state = CONNECTION_STATE::CLOSED
64
66
  reconnect unless @deliberate_close
65
67
  rescue => e
66
- @error_handler.on_exception(e)
68
+ on_exception(e)
67
69
  end
68
70
 
69
71
  def login(credentials = @options[:credentials])
@@ -79,24 +81,28 @@ module Deepstream
79
81
  end
80
82
  self
81
83
  rescue => e
82
- @error_handler.on_exception(e)
84
+ on_exception(e)
83
85
  self
84
86
  end
85
87
 
86
88
  def close
87
89
  return unless connected?
90
+ @state = CONNECTION_STATE::CLOSED
88
91
  @deliberate_close = true
89
92
  @connection.close
90
93
  @connection.terminate
91
- @state = CONNECTION_STATE::CLOSED
92
94
  rescue => e
93
- @error_handler.on_exception(e)
95
+ on_exception(e)
94
96
  end
95
97
 
96
98
  def connected?
97
99
  @state != CONNECTION_STATE::CLOSED
98
100
  end
99
101
 
102
+ def reconnecting?
103
+ @state == CONNECTION_STATE::RECONNECTING
104
+ end
105
+
100
106
  def logged_in?
101
107
  @state == CONNECTION_STATE::OPEN
102
108
  end
@@ -107,19 +113,26 @@ module Deepstream
107
113
 
108
114
  def send_message(*args)
109
115
  message = Message.parse(*args)
110
- if !logged_in? && message.needs_authentication?
111
- info("Placing message #{message.inspect} in buffer, waiting for connection")
112
- @message_buffer << message
113
- else
114
- info("Sending message: #{message.inspect}")
115
- @connection.text(message.to_s)
116
- end
116
+ return unable_to_send_message(message) if !logged_in? && message.needs_authentication?
117
+ info("Sending message: #{message.inspect}")
118
+ @connection.text(message.to_s)
119
+ rescue Errno::EPIPE
120
+ unable_to_send_message(message)
117
121
  rescue => e
118
- @error_handler.on_exception(e)
122
+ on_exception(e)
119
123
  end
120
124
 
121
125
  private
122
126
 
127
+ def unable_to_send_message(message)
128
+ @state = CONNECTION_STATE::CLOSED if logged_in?
129
+ unless message.expired?
130
+ info("Placing a message #{message.inspect} in the buffer, waiting for authentication")
131
+ @message_buffer << message
132
+ end
133
+ async.reconnect if !connected? && !@connection_requested
134
+ end
135
+
123
136
  def connection_message(message)
124
137
  case message.action
125
138
  when ACTION::ACK then on_connection_ack
@@ -128,7 +141,7 @@ module Deepstream
128
141
  when ACTION::PING then on_ping
129
142
  when ACTION::REDIRECT then on_redirection(message)
130
143
  when ACTION::REJECTION then on_rejection
131
- else raise UnknownAction(message)
144
+ else raise(UnknownAction, message)
132
145
  end
133
146
  end
134
147
 
@@ -136,7 +149,7 @@ module Deepstream
136
149
  case message.action
137
150
  when ACTION::ACK then on_login
138
151
  when ACTION::ERROR then on_error(message)
139
- else raise UnknownAction(message)
152
+ else raise(UnknownAction, message)
140
153
  end
141
154
  end
142
155
 
@@ -157,8 +170,9 @@ module Deepstream
157
170
 
158
171
  def on_login
159
172
  @state = CONNECTION_STATE::OPEN
160
- @message_buffer.each { |message| send_message(message) }.clear
173
+ @message_buffer.each { |message| send_message(message) unless message.expired? }.clear
161
174
  every(@options[:heartbeat_interval]) { check_heartbeat } if @options[:heartbeat_interval]
175
+ resubscribe
162
176
  end
163
177
 
164
178
  def on_rejection
@@ -177,25 +191,38 @@ module Deepstream
177
191
  connect(message.data.last)
178
192
  end
179
193
 
180
- def connect(url = @url, reraise = false)
194
+ def connect(url = @url, reraise = false, force = false)
195
+ return if @connection_requested && !force
196
+ info("Trying to connect to #{url}.")
197
+ @connection_requested = true
181
198
  @connection = Celluloid::WebSocket::Client.new(url, Actor.current)
182
199
  rescue => e
183
- reraise ? raise : @error_handler.on_exception(e)
200
+ @connection_requested = false
201
+ reraise ? raise : on_exception(e)
184
202
  end
185
203
 
186
204
  def reconnect
187
- @state = CONNECTION_STATE::RECONNECTING
188
- if @failed_reconnect_attempts < @options[:max_reconnect_attempts]
189
- connect(@url, true)
190
- resubscribe
205
+ info("Trying to reconnect to #{@url}")
206
+ if @options[:max_reconnect_attempts].nil? || @failed_reconnect_attempts < @options[:max_reconnect_attempts]
207
+ @state = CONNECTION_STATE::RECONNECTING
208
+ @login_requested = true
209
+ connect(@url, true, true)
210
+ sleep(5)
211
+ if !logged_in?
212
+ close
213
+ reconnect
214
+ end
191
215
  else
192
216
  @state = CONNECTION_STATE::ERROR
193
217
  end
194
- rescue Errno::ECONNREFUSED
218
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET
195
219
  @failed_reconnect_attempts += 1
196
220
  on_error("Can't connect! Deepstream server unreachable on #{@url}")
221
+ info("Can't connect. Next attempt in #{reconnect_interval} seconds.")
197
222
  sleep(reconnect_interval)
198
- reconnect
223
+ retry
224
+ rescue => e
225
+ on_exception(e)
199
226
  end
200
227
 
201
228
  def reconnect_interval
@@ -18,13 +18,12 @@ module Deepstream
18
18
  else
19
19
  message
20
20
  end
21
- puts @error
21
+ puts "#{@error}\n" unless @client.options[:debug]
22
22
  end
23
23
 
24
24
  def on_exception(exception)
25
25
  raise exception if @client.options[:debug]
26
- puts exception.message
27
- puts exception.backtrace
26
+ puts "\n#{exception.message}\n#{exception.backtrace}\n"
28
27
  end
29
28
  end
30
29
  end
@@ -17,6 +17,8 @@ module Deepstream
17
17
  @ack_timeout_registry.add(event, "No ACK message received in time for #{event}")
18
18
  end
19
19
  @callbacks[event] = block
20
+ rescue => e
21
+ @client.on_exception(e)
20
22
  end
21
23
  alias subscribe on
22
24
 
@@ -25,12 +27,16 @@ module Deepstream
25
27
  @listeners[pattern] = block
26
28
  @client.send_message(TOPIC::EVENT, ACTION::LISTEN, pattern)
27
29
  @ack_timeout_registry.add(pattern, "No ACK message received in time for #{pattern}")
30
+ rescue => e
31
+ @client.on_exception(e)
28
32
  end
29
33
 
30
34
  def unlisten(pattern)
31
35
  pattern = pattern.is_a?(Regexp) ? pattern.source : pattern
32
36
  @listeners.delete(pattern)
33
37
  @client.send_message(TOPIC::EVENT, ACTION::UNLISTEN, pattern)
38
+ rescue => e
39
+ @client.on_exception(e)
34
40
  end
35
41
 
36
42
  def on_message(message)
@@ -43,18 +49,24 @@ module Deepstream
43
49
  end
44
50
  end
45
51
 
46
- def emit(event, data = nil)
47
- @client.send_message(TOPIC::EVENT, ACTION::EVENT, event, Helpers.to_deepstream_type(data))
52
+ def emit(event, data = nil, timeout: @client.options[:emit_timeout])
53
+ @client.send_message(TOPIC::EVENT, ACTION::EVENT, event, Helpers.to_deepstream_type(data), timeout: timeout)
54
+ rescue => e
55
+ @client.on_exception(e)
48
56
  end
49
57
 
50
58
  def unsubscribe(event)
51
59
  @callbacks.delete(event)
52
60
  @client.send_message(TOPIC::EVENT, ACTION::UNSUBSCRIBE, event)
61
+ rescue => e
62
+ @client.on_exception(e)
53
63
  end
54
64
 
55
65
  def resubscribe
56
66
  @callbacks.keys.each { |event| @client.send_message(TOPIC::EVENT, ACTION::SUBSCRIBE, event) }
57
67
  @listeners.keys.each { |pattern| @client.send_message(TOPIC::EVENT, ACTION::LISTEN, pattern) }
68
+ rescue => e
69
+ @client.on_exception(e)
58
70
  end
59
71
 
60
72
  private
@@ -36,9 +36,10 @@ module Deepstream
36
36
  autologin: true,
37
37
  credentials: {},
38
38
  heartbeat_interval: nil,
39
- max_reconnect_attempts: 5,
39
+ max_reconnect_attempts: nil,
40
40
  max_reconnect_interval: 30,
41
41
  reconnect_interval: 1,
42
+ emit_timeout: 0,
42
43
  verbose: false,
43
44
  debug: false
44
45
  }
@@ -24,6 +24,10 @@ module Deepstream
24
24
  set(@data) if @data.delete(record_name)
25
25
  end
26
26
 
27
+ def keys
28
+ @data
29
+ end
30
+
27
31
  def all
28
32
  @data.map { |record_name| @client.get(record_name) }
29
33
  end
@@ -3,16 +3,17 @@ require 'deepstream/constants'
3
3
 
4
4
  module Deepstream
5
5
  class Message
6
- attr_reader :topic, :action, :data
6
+ attr_reader :action, :data, :topic, :sending_deadline
7
7
 
8
8
  def self.parse(*args)
9
9
  args.first.is_a?(self) ? args.first : new(*args)
10
10
  end
11
11
 
12
- def initialize(*args)
12
+ def initialize(*args, timeout: nil)
13
13
  if args.one?
14
14
  args = args.first.delete(MESSAGE_SEPARATOR).split(MESSAGE_PART_SEPARATOR)
15
15
  end
16
+ @sending_deadline = Time.now + timeout if timeout
16
17
  @topic, @action = args.take(2).map(&:to_sym)
17
18
  @data = args.drop(2)
18
19
  end
@@ -30,5 +31,9 @@ module Deepstream
30
31
  def needs_authentication?
31
32
  ![TOPIC::CONNECTION, TOPIC::AUTH].include?(@topic)
32
33
  end
34
+
35
+ def expired?
36
+ @sending_deadline && @sending_deadline < Time.now
37
+ end
33
38
  end
34
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deepstream
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Currency-One S.A.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-06 00:00:00.000000000 Z
11
+ date: 2017-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: celluloid-websocket-client