socketclusterclient 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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