deepstream 0.2.0 → 0.2.1

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: 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