socketclusterclient 0.1.0

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.
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'socketclusterclient'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,22 @@
1
+ require 'socketclusterclient'
2
+
3
+ on_connect = -> { puts 'on connect got called' }
4
+
5
+ on_disconnect = -> { puts 'on disconnect got called' }
6
+
7
+ on_connect_error = -> { puts 'on connect error got called' }
8
+
9
+ on_set_authentication = lambda do |socket, token|
10
+ puts "Token received #{token}"
11
+ socket.set_auth_token(token)
12
+ end
13
+
14
+ on_authentication = lambda do |socket, is_authenticated|
15
+ puts "Authenticated is #{is_authenticated}"
16
+ socket.disconnect
17
+ end
18
+
19
+ socket = ScClient.new('ws://localhost:8000/socketcluster/')
20
+ socket.set_basic_listener(on_connect, on_disconnect, on_connect_error)
21
+ socket.set_authentication_listener(on_set_authentication, on_authentication)
22
+ socket.connect
@@ -0,0 +1,49 @@
1
+ require 'socketclusterclient'
2
+
3
+ on_connect = -> { puts 'on connect got called' }
4
+
5
+ on_disconnect = -> { puts 'on disconnect got called' }
6
+
7
+ on_connect_error = -> { puts 'on connect error got called' }
8
+
9
+ ack_subscribe = lambda do |channel, error, _object|
10
+ puts "Subscribed successfully to channel => #{channel}" if error == ''
11
+ end
12
+
13
+ ack_publish = lambda do |channel, error, _object|
14
+ puts "Publish sent successfully to channel => #{channel}" if error == ''
15
+ end
16
+
17
+ channel_message = lambda do |key, object|
18
+ puts "Got data => #{object} from key => #{key}"
19
+ end
20
+
21
+ ack_unsubscribe = lambda do |channel, error, _object|
22
+ puts "Unsubscribed to channel => #{channel}" if error == ''
23
+ end
24
+
25
+ on_set_authentication = lambda do |socket, token|
26
+ puts "Token received #{token}"
27
+ socket.set_auth_token(token)
28
+ end
29
+
30
+ on_authentication = lambda do |socket, is_authenticated|
31
+ puts "Authenticated is #{is_authenticated}"
32
+
33
+ # channel communication
34
+ socket.subscribe('yell')
35
+ socket.publish('yell', 'Hi')
36
+ socket.onchannel('yell', channel_message)
37
+ socket.unsubscribe('yell')
38
+
39
+ # channel communication with acknowledgment
40
+ socket.subscribeack('yell', ack_subscribe)
41
+ socket.publishack('yell', 'Hi dudies', ack_publish)
42
+ socket.onchannel('yell', channel_message)
43
+ socket.unsubscribeack('yell', ack_unsubscribe)
44
+ end
45
+
46
+ socket = ScClient.new('ws://localhost:8000/socketcluster/')
47
+ socket.set_basic_listener(on_connect, on_disconnect, on_connect_error)
48
+ socket.set_authentication_listener(on_set_authentication, on_authentication)
49
+ socket.connect
@@ -0,0 +1,31 @@
1
+ require 'socketclusterclient'
2
+
3
+ on_connect = -> { puts 'on connect got called' }
4
+
5
+ on_disconnect = -> { puts 'on disconnect got called' }
6
+
7
+ on_connect_error = -> { puts 'on connect error got called' }
8
+
9
+ ack_emit = lambda do |key, error, object|
10
+ puts "Got ack data => #{object} and error => #{error} and key => #{key}"
11
+ end
12
+
13
+ on_set_authentication = lambda do |socket, token|
14
+ puts "Token received #{token}"
15
+ socket.set_auth_token(token)
16
+ end
17
+
18
+ on_authentication = lambda do |socket, is_authenticated|
19
+ puts "Authenticated is #{is_authenticated}"
20
+
21
+ # emit event chat and message Hi to ScServer
22
+ socket.emit('chat', 'Hi')
23
+
24
+ # emit event chat and message Hi with acknowledgment to ScServer
25
+ socket.emitack('chat', 'Hello', ack_emit)
26
+ end
27
+
28
+ socket = ScClient.new('ws://localhost:8000/socketcluster/')
29
+ socket.set_basic_listener(on_connect, on_disconnect, on_connect_error)
30
+ socket.set_authentication_listener(on_set_authentication, on_authentication)
31
+ socket.connect
@@ -0,0 +1,36 @@
1
+ require 'socketclusterclient'
2
+
3
+ on_connect = -> { puts 'on connect got called' }
4
+
5
+ on_disconnect = -> { puts 'on disconnect got called' }
6
+
7
+ on_connect_error = -> { puts 'on connect error got called' }
8
+
9
+ message = lambda do |key, object|
10
+ puts "Got data => #{object} from key => #{key}"
11
+ end
12
+
13
+ ack_message = lambda do |key, object, block_ack_message|
14
+ puts "Got ack data => #{object} from key => #{key}"
15
+ block_ack_message.call('error lorem', 'data ipsum')
16
+ end
17
+
18
+ on_set_authentication = lambda do |socket, token|
19
+ puts "Token received #{token}"
20
+ socket.set_auth_token(token)
21
+ end
22
+
23
+ on_authentication = lambda do |socket, is_authenticated|
24
+ puts "Authenticated is #{is_authenticated}"
25
+
26
+ # bind ping event on ScServer
27
+ socket.on('yell', message)
28
+
29
+ # bind ping event on ScServer with acknowledgment
30
+ socket.onack('ping', ack_message)
31
+ end
32
+
33
+ socket = ScClient.new('ws://localhost:8000/socketcluster/')
34
+ socket.set_basic_listener(on_connect, on_disconnect, on_connect_error)
35
+ socket.set_authentication_listener(on_set_authentication, on_authentication)
36
+ socket.connect
@@ -0,0 +1,25 @@
1
+ require 'socketclusterclient'
2
+
3
+ on_connect = -> { puts 'on connect got called' }
4
+
5
+ on_disconnect = -> { puts 'on disconnect got called' }
6
+
7
+ on_connect_error = -> { puts 'on connect error got called' }
8
+
9
+ on_set_authentication = lambda do |socket, token|
10
+ puts "Token received #{token}"
11
+ socket.set_auth_token(token)
12
+ end
13
+
14
+ on_authentication = lambda do |_socket, is_authenticated|
15
+ puts "Authenticated is #{is_authenticated}"
16
+ end
17
+
18
+ socket = ScClient.new('ws://localhost:8000/socketcluster/')
19
+ socket.set_basic_listener(on_connect, on_disconnect, on_connect_error)
20
+ socket.set_authentication_listener(on_set_authentication, on_authentication)
21
+ socket.set_reconnection_listener(3000, 30_000) # (reconnect_inverval, max_reconnect_interval)
22
+ socket.set_reconnection_listener(3000, 30_000, 10) # (reconnect_inverval, max_reconnect_interval, max_attempts)
23
+ socket.set_reconnection(true) # to allow reconnection, default reconnection is disabled
24
+ socket.max_attempts = 5 # set maximum attempts allowed
25
+ socket.connect
@@ -0,0 +1,12 @@
1
+ require 'socketclusterclient'
2
+
3
+ on_connect = -> { puts 'on connect got called' }
4
+
5
+ on_disconnect = -> { puts 'on disconnect got called' }
6
+
7
+ on_connect_error = -> { puts 'on connect error got called' }
8
+
9
+ # connection to ssl server at websocket.org
10
+ socket = ScClient.new('wss://echo.websocket.org/')
11
+ socket.set_basic_listener(on_connect, on_disconnect, on_connect_error)
12
+ socket.connect
data/lib/sc_client.rb ADDED
@@ -0,0 +1,365 @@
1
+ require 'websocket-eventmachine-client'
2
+ require 'json'
3
+ require 'logger'
4
+
5
+ require_relative './socketclusterclient/emitter'
6
+ require_relative './socketclusterclient/parser'
7
+ require_relative './socketclusterclient/data_models'
8
+ require_relative './socketclusterclient/reconnect'
9
+ require_relative './socketclusterclient/log'
10
+
11
+ #
12
+ # Class ScClient provides an interface to connect to the socketcluster server
13
+ #
14
+ # @author Maanav Shah <shahmaanav07@gmail.com>
15
+ #
16
+ class ScClient
17
+ include Emitter
18
+ include DataModels
19
+ include Reconnect
20
+ include Log
21
+
22
+ attr_accessor :reconnect_interval, :max_reconnect_interval, :max_attempts
23
+
24
+ #
25
+ # Initializes instance variables in socketcluster client
26
+ #
27
+ # @param [String] url ScServer connection URL
28
+ #
29
+ def initialize(url)
30
+ @id = ''
31
+ @cnt = 0
32
+ @url = url
33
+ @acks = {}
34
+ @channels = []
35
+ @enable_reconnection = false
36
+ @delay = 3
37
+ initialize_emitter
38
+ initialize_reconnect
39
+ initialize_logger
40
+ end
41
+
42
+ #
43
+ # Adds handler for connect, disconnect and error
44
+ #
45
+ # @param [Lambda] on_connected Block to execute on connected
46
+ # @param [Lambda] on_disconnected Block to execute on disconnected
47
+ # @param [Lambda] on_connect_error Block to execute on connection error
48
+ #
49
+ #
50
+ #
51
+ def set_basic_listener(on_connected, on_disconnected, on_connect_error)
52
+ @on_connected = on_connected
53
+ @on_disconnected = on_disconnected
54
+ @on_connect_error = on_connect_error
55
+ end
56
+
57
+ #
58
+ # Adds handler for authentication
59
+ #
60
+ # @param [Lambda] on_set_authentication Block to set authentication token
61
+ # @param [Lambda] on_authentication Block to execute on authentication
62
+ #
63
+ #
64
+ #
65
+ def set_authentication_listener(on_set_authentication, on_authentication)
66
+ @on_set_authentication = on_set_authentication
67
+ @on_authentication = on_authentication
68
+ end
69
+
70
+ #
71
+ # Method to set the authentication token
72
+ #
73
+ # @param [String] token Authentication token provided by ScServer
74
+ #
75
+ #
76
+ #
77
+ def set_auth_token(token)
78
+ @auth_token = token.to_s
79
+ end
80
+
81
+ #
82
+ # Get the list of all subscribed channels
83
+ #
84
+ #
85
+ #
86
+ #
87
+ def subscribed_channels
88
+ @channels
89
+ end
90
+
91
+ #
92
+ # Subscribe all the channels available
93
+ #
94
+ #
95
+ #
96
+ #
97
+ def subscribe_channels
98
+ @channels.each { |channel| subscribe(channel) }
99
+ end
100
+
101
+ #
102
+ # Acknowledment block to be executed on event
103
+ #
104
+ # @param [String] cid @cnt id received from ScServer
105
+ #
106
+ # @return [Lambda] Acknowledgment to be sent to ScServer
107
+ #
108
+ def ack_block(cid)
109
+ ws = @ws
110
+ lambda do |error, data|
111
+ ws.send(get_ack_object(error, data, cid))
112
+ end
113
+ end
114
+
115
+ #
116
+ # Connects to the ScServer
117
+ #
118
+ #
119
+ #
120
+ #
121
+ def connect
122
+ EM.epoll
123
+
124
+ EM.run do
125
+ trap('TERM') do
126
+ @enable_reconnection = false
127
+ disconnect
128
+ end
129
+
130
+ trap('INT') do
131
+ @enable_reconnection = false
132
+ disconnect
133
+ end
134
+
135
+ @ws = WebSocket::EventMachine::Client.connect(uri: @url)
136
+
137
+ @ws.onopen do
138
+ reset_value
139
+ @ws.send(get_handshake_object(increment_cnt).to_json)
140
+ @on_connected.call if @on_connected
141
+ end
142
+
143
+ @ws.onmessage do |message, _type|
144
+ @logger.info("Message received : #{message}") if @logger
145
+ if message == '#1'
146
+ @ws.send('#2')
147
+ else
148
+ main_object = JSON.parse(message)
149
+ data_object = main_object['data']
150
+ rid = main_object['rid']
151
+ cid = main_object['cid']
152
+ event = main_object['event']
153
+ result = Parser.parse(event, rid)
154
+ if result == Parser::CHECK_AUTHENTICATION
155
+ if @on_authentication
156
+ @id = data_object['id']
157
+ @on_authentication.call(self, data_object['isAuthenticated'])
158
+ end
159
+ subscribe_channels
160
+ elsif result == Parser::PUBLISH
161
+ execute(data_object['channel'], data_object['data'])
162
+ elsif result == Parser::REMOVE_AUTHENTICATION
163
+ @auth_token = nil
164
+ elsif result == Parser::SET_AUTHENTICATION
165
+ if @on_set_authentication
166
+ @on_set_authentication.call(self, data_object['token'])
167
+ end
168
+ elsif result == Parser::EVENT
169
+ if haseventack(event)
170
+ executeack(event, data_object, ack_block(cid))
171
+ else
172
+ execute(event, data_object)
173
+ end
174
+ else # Parser::ACKNOWLEDGEMENT
175
+ tuple = @acks[rid] if @acks.include?(rid)
176
+ if tuple
177
+ ack = tuple[1]
178
+ ack.call(tuple[0], String(main_object['error']), String(main_object['data']))
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ @ws.onerror do |error|
185
+ @on_connect_error.call(error) if @on_connect_error
186
+ end
187
+
188
+ @ws.onclose do
189
+ if should_reconnect
190
+ @reconnect_interval = @max_reconnect_interval if @reconnect_interval > @max_reconnect_interval
191
+ sleep(@reconnect_interval / 1000)
192
+ @attempts_made += 1
193
+ @logger.info("Attempting to reconnect : #{@attempts_made}") if @logger
194
+ connect
195
+ else
196
+ stop
197
+ end
198
+ end
199
+
200
+ def stop
201
+ EventMachine.stop
202
+ end
203
+ end
204
+ end
205
+
206
+ #
207
+ # Set the delay for reconnection to ScServer
208
+ #
209
+ # @param [Integer] delay Delay in seconds to reconnect
210
+ #
211
+ #
212
+ #
213
+ def set_delay(delay)
214
+ @delay = delay
215
+ end
216
+
217
+ #
218
+ # Allows to reconnect to ScServer
219
+ #
220
+ # @param [Boolean] enable True to reconnect to server, False to disconnect
221
+ #
222
+ #
223
+ #
224
+ def set_reconnection(enable)
225
+ @enable_reconnection = enable
226
+ end
227
+
228
+ #
229
+ # Disconnects the connection with ScServer
230
+ #
231
+ #
232
+ #
233
+ #
234
+ def disconnect
235
+ @on_disconnected.call if @on_disconnected
236
+ @enable_reconnection = false
237
+ @ws.close
238
+ end
239
+
240
+ #
241
+ # Emits the specified event on the corresponding server-side socket
242
+ #
243
+ # @param [String] event Event
244
+ # @param [String] object Data for the specified event
245
+ #
246
+ #
247
+ #
248
+ def emit(event, object)
249
+ @ws.send(get_emit_object(event, object).to_json)
250
+ end
251
+
252
+ #
253
+ # Emits the specified event on the corresponding server-side socket with acknowledgment
254
+ #
255
+ # @param [String] event Event
256
+ # @param [String] object Data for the specified event
257
+ # @param [Lambda] ack Block to execute on event acknowledgment
258
+ #
259
+ # @return [<type>] <description>
260
+ #
261
+ def emitack(event, object, ack)
262
+ @ws.send(get_emit_ack_object(event, object, increment_cnt).to_json)
263
+ @acks[@cnt] = [event, ack]
264
+ end
265
+
266
+ #
267
+ # Subscribes to a particular channel
268
+ #
269
+ # @param [String] channel A channel name
270
+ #
271
+ #
272
+ #
273
+ def subscribe(channel)
274
+ @ws.send(get_subscribe_object(channel, increment_cnt).to_json)
275
+ @channels << channel unless @channels.include?(channel)
276
+ end
277
+
278
+ #
279
+ # Subscribes to a particular channel with acknowledgment
280
+ #
281
+ # @param [String] channel A channel name
282
+ # @param [Lambda] ack Block to execute on subscribe acknowledgment
283
+ #
284
+ #
285
+ #
286
+ def subscribeack(channel, ack)
287
+ @ws.send(get_subscribe_object(channel, increment_cnt).to_json)
288
+ @channels << channel
289
+ @acks[@cnt] = [channel, ack]
290
+ end
291
+
292
+ #
293
+ # Unsubscribes to a particular channel
294
+ #
295
+ # @param [String] channel A channel name
296
+ #
297
+ #
298
+ #
299
+ def unsubscribe(channel)
300
+ @ws.send(get_unsubscribe_object(channel, increment_cnt).to_json)
301
+ @channels.delete(channel)
302
+ end
303
+
304
+ #
305
+ # Unsubscribes to a particular channel with acknowledgment
306
+ #
307
+ # @param [String] channel A channel name
308
+ # @param [Lambda] ack Block to execute on unsubscribe acknowledgment
309
+ #
310
+ #
311
+ #
312
+ def unsubscribeack(channel, ack)
313
+ @ws.send(get_unsubscribe_object(channel, increment_cnt).to_json)
314
+ @channels.delete(channel)
315
+ @acks[@cnt] = [channel, ack]
316
+ end
317
+
318
+ #
319
+ # Publish data to the specified channel name
320
+ #
321
+ # @param [String] channel A channel name
322
+ # @param [String] data Data to be published on the channel
323
+ #
324
+ #
325
+ #
326
+ def publish(channel, data)
327
+ @ws.send(get_publish_object(channel, data, increment_cnt).to_json)
328
+ end
329
+
330
+ #
331
+ # Publish data to the specified channel name
332
+ #
333
+ # @param [String] channel A channel name
334
+ # @param [String] data Data to be published on the channel
335
+ # @param [Lambda] ack Block to execute on publish acknowledgment
336
+ #
337
+ #
338
+ #
339
+ def publishack(channel, data, ack)
340
+ @ws.send(get_publish_object(channel, data, increment_cnt).to_json)
341
+ @acks[@cnt] = [channel, ack]
342
+ end
343
+
344
+ private
345
+
346
+ #
347
+ # Resets the value of @cnt
348
+ #
349
+ #
350
+ #
351
+ #
352
+ def reset_value
353
+ @cnt = 0
354
+ end
355
+
356
+ #
357
+ # Increments the value of @cnt
358
+ #
359
+ #
360
+ #
361
+ #
362
+ def increment_cnt
363
+ @cnt += 1
364
+ end
365
+ end