ewelink 2.3.0 → 3.0.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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/ewelink.gemspec +0 -1
- data/lib/ewelink.rb +0 -4
- data/lib/ewelink/api.rb +39 -294
- metadata +2 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 558b7f50525eabfc0e0ac3cd32fd5a0ff5c0d4b6cc7658024dba4c6c0336f96a
|
4
|
+
data.tar.gz: cda09a44af6247436a22af9443a1dd3c078e8b86ab228f3ce12761baca606a7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9607d6170f6e3f0cb00d599fa7a8258b25367423e6a87b2238079d336d6ee2a58389f78ffdcbc3eaf9726c1685ff3cb8b95fb0387d655ae6b81614d50d990c01
|
7
|
+
data.tar.gz: 4ad312d700e40c50f20cd47f5d66dec54937633e1162e21b3d131556752dc937e41c70d128e210d344d19a8bd0cb0fb4de1606f081cba50ad1189c84655440b6
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.0.0
|
data/ewelink.gemspec
CHANGED
@@ -16,7 +16,6 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.required_ruby_version = '>= 2.0.0'
|
17
17
|
|
18
18
|
s.add_dependency 'activesupport', '>= 6.0.0', '< 7.0.0'
|
19
|
-
s.add_dependency 'faye-websocket', '>= 0.11.0', '< 0.12.0'
|
20
19
|
s.add_dependency 'httparty', '>= 0.18.0', '< 0.19.0'
|
21
20
|
|
22
21
|
s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
|
data/lib/ewelink.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_support/core_ext'
|
3
3
|
require 'byebug' if ENV['DEBUGGER']
|
4
|
-
require 'eventmachine'
|
5
|
-
require 'faye/websocket'
|
6
4
|
require 'httparty'
|
7
5
|
require 'io/console'
|
8
6
|
require 'json'
|
9
7
|
require 'logger'
|
10
8
|
require 'openssl'
|
11
9
|
require 'optparse'
|
12
|
-
require 'set'
|
13
|
-
require 'timeout'
|
14
10
|
|
15
11
|
module Ewelink
|
16
12
|
|
data/lib/ewelink/api.rb
CHANGED
@@ -5,16 +5,12 @@ module Ewelink
|
|
5
5
|
APP_ID = 'oeVkj2lYFGnJu5XUtWisfW4utiN4u9Mq'
|
6
6
|
APP_SECRET = '6Nz4n0xA8s8qdxQf2GqurZj2Fs55FUvM'
|
7
7
|
DEFAULT_REGION = 'us'
|
8
|
-
REQUEST_TIMEOUT = 10.seconds
|
9
8
|
RF_BRIDGE_DEVICE_UIID = 28
|
10
9
|
SWITCH_DEVICES_UIIDS = [1, 5, 6, 24]
|
10
|
+
TIMEOUT = 10
|
11
11
|
URL = 'https://#{region}-api.coolkit.cc:8080'
|
12
12
|
UUID_NAMESPACE = 'e25750fb-3710-41af-b831-23224f4dd609';
|
13
13
|
VERSION = 8
|
14
|
-
WEB_SOCKET_CHECK_AUTHENTICATION_TIMEOUT = 30.seconds
|
15
|
-
WEB_SOCKET_PING_TOLERANCE_FACTOR = 1.5
|
16
|
-
SWITCH_STATUS_CHANGE_CHECK_TIMEOUT = 2.seconds
|
17
|
-
WEB_SOCKET_WAIT_INTERVAL = 0.2.seconds
|
18
14
|
|
19
15
|
attr_reader :email, :password, :phone_number
|
20
16
|
|
@@ -23,74 +19,31 @@ module Ewelink
|
|
23
19
|
@mutexs = {}
|
24
20
|
@password = password.presence || raise(Error.new(":password must be specified"))
|
25
21
|
@phone_number = phone_number.presence.try(:strip)
|
26
|
-
|
27
|
-
@web_socket_switches_statuses = {}
|
28
|
-
|
29
|
-
raise(Error.new(':email or :phone_number must be specified')) if email.blank? && phone_number.blank?
|
30
|
-
|
31
|
-
start_web_socket_authentication_check_thread
|
22
|
+
raise(Error.new(":email or :phone_number must be specified")) if email.blank? && phone_number.blank?
|
32
23
|
end
|
33
24
|
|
34
25
|
def press_rf_bridge_button!(uuid)
|
35
26
|
synchronize(:press_rf_bridge_button) do
|
36
27
|
button = find_rf_bridge_button!(uuid)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
'
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
Ewelink.logger.debug(self.class.name) { "Pressing RF bridge button #{button[:uuid].inspect}" }
|
51
|
-
send_to_web_socket(JSON.generate(params))
|
52
|
-
true
|
53
|
-
end
|
28
|
+
params = {
|
29
|
+
'appid' => APP_ID,
|
30
|
+
'deviceid' => button[:device_id],
|
31
|
+
'nonce' => nonce,
|
32
|
+
'params' => {
|
33
|
+
'cmd' => 'transmit',
|
34
|
+
'rfChl' => button[:channel],
|
35
|
+
},
|
36
|
+
'ts' => Time.now.to_i,
|
37
|
+
'version' => VERSION,
|
38
|
+
}
|
39
|
+
rest_request(:post, '/api/user/device/status', body: JSON.generate(params), headers: authentication_headers)
|
40
|
+
true
|
54
41
|
end
|
55
42
|
end
|
56
43
|
|
57
44
|
def reload
|
58
|
-
Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, devices
|
59
|
-
|
60
|
-
@web_socket_authenticated_api_keys.clear
|
61
|
-
@web_socket_switches_statuses.clear
|
62
|
-
|
63
|
-
[@web_socket_ping_thread, @web_socket_thread].each do |thread|
|
64
|
-
next unless thread
|
65
|
-
if Thread.current == thread
|
66
|
-
thread[:stop] = true
|
67
|
-
else
|
68
|
-
thread.kill
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
if @web_socket.present?
|
73
|
-
begin
|
74
|
-
@web_socket.close if @web_socket.open?
|
75
|
-
rescue
|
76
|
-
# Ignoring close errors
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
[
|
81
|
-
:@api_keys,
|
82
|
-
:@authentication_token,
|
83
|
-
:@devices,
|
84
|
-
:@last_web_socket_pong_at,
|
85
|
-
:@region,
|
86
|
-
:@rf_bridge_buttons,
|
87
|
-
:@switches,
|
88
|
-
:@web_socket_ping_interval,
|
89
|
-
:@web_socket_ping_thread,
|
90
|
-
:@web_socket_thread,
|
91
|
-
:@web_socket_url,
|
92
|
-
:@web_socket,
|
93
|
-
].each do |variable|
|
45
|
+
Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, devices & region cache)' }
|
46
|
+
[:@authentication_token, :@devices, :@rf_bridge_buttons, :@region, :@switches].each do |variable|
|
94
47
|
remove_instance_variable(variable) if instance_variable_defined?(variable)
|
95
48
|
end
|
96
49
|
self
|
@@ -103,12 +56,10 @@ module Ewelink
|
|
103
56
|
Ewelink.logger.debug(self.class.name) { "Found #{devices.size} RF 433MHz bridge device(s)" }
|
104
57
|
end
|
105
58
|
rf_bridge_devices.each do |device|
|
106
|
-
api_key = device['apikey'].presence || next
|
107
59
|
device_id = device['deviceid'].presence || next
|
108
60
|
device_name = device['name'].presence || next
|
109
61
|
buttons = device['params']['rfList'].each do |rf|
|
110
62
|
button = {
|
111
|
-
api_key: api_key,
|
112
63
|
channel: rf['rfChl'],
|
113
64
|
device_id: device_id,
|
114
65
|
device_name: device_name,
|
@@ -132,23 +83,15 @@ module Ewelink
|
|
132
83
|
|
133
84
|
def switch_on?(uuid)
|
134
85
|
switch = find_switch!(uuid)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
'userAgent' => 'app',
|
145
|
-
}
|
146
|
-
send_to_web_socket(JSON.generate(params))
|
147
|
-
end
|
148
|
-
end
|
149
|
-
web_socket_wait_for(-> { !@web_socket_switches_statuses[switch[:uuid]].nil? }, initialize_web_socket: true) do
|
150
|
-
@web_socket_switches_statuses[switch[:uuid]] == 'on'
|
151
|
-
end
|
86
|
+
params = {
|
87
|
+
'appid' => APP_ID,
|
88
|
+
'deviceid' => switch[:device_id],
|
89
|
+
'nonce' => nonce,
|
90
|
+
'ts' => Time.now.to_i,
|
91
|
+
'version' => VERSION,
|
92
|
+
}
|
93
|
+
response = rest_request(:get, '/api/user/device/status', headers: authentication_headers, query: params)
|
94
|
+
response['params']['switch'] == 'on'
|
152
95
|
end
|
153
96
|
|
154
97
|
def switches
|
@@ -156,11 +99,9 @@ module Ewelink
|
|
156
99
|
@switches ||= [].tap do |switches|
|
157
100
|
switch_devices = devices.select { |device| SWITCH_DEVICES_UIIDS.include?(device['uiid']) }
|
158
101
|
switch_devices.each do |device|
|
159
|
-
api_key = device['apikey'].presence || next
|
160
102
|
device_id = device['deviceid'].presence || next
|
161
103
|
name = device['name'].presence || next
|
162
104
|
switch = {
|
163
|
-
api_key: api_key,
|
164
105
|
device_id: device_id,
|
165
106
|
name: name,
|
166
107
|
}
|
@@ -178,53 +119,22 @@ module Ewelink
|
|
178
119
|
on = false
|
179
120
|
end
|
180
121
|
switch = find_switch!(uuid)
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
'
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
'userAgent' => 'app',
|
193
|
-
}
|
194
|
-
Ewelink.logger.debug(self.class.name) { "Turning switch #{switch[:uuid].inspect} #{on ? 'on' : 'off'}" }
|
195
|
-
send_to_web_socket(JSON.generate(params))
|
196
|
-
end
|
197
|
-
sleep(SWITCH_STATUS_CHANGE_CHECK_TIMEOUT)
|
198
|
-
switch_on?(switch[:uuid]) # Waiting for switch status update
|
122
|
+
params = {
|
123
|
+
'appid' => APP_ID,
|
124
|
+
'deviceid' => switch[:device_id],
|
125
|
+
'nonce' => nonce,
|
126
|
+
'params' => {
|
127
|
+
'switch' => on ? 'on' : 'off',
|
128
|
+
},
|
129
|
+
'ts' => Time.now.to_i,
|
130
|
+
'version' => VERSION,
|
131
|
+
}
|
132
|
+
rest_request(:post, '/api/user/device/status', body: JSON.generate(params), headers: authentication_headers)
|
199
133
|
true
|
200
134
|
end
|
201
135
|
|
202
136
|
private
|
203
137
|
|
204
|
-
def api_keys
|
205
|
-
synchronize(:api_keys) do
|
206
|
-
@api_keys ||= Set.new(devices.map { |device| device['apikey'] })
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def authenticate_web_socket_api_keys
|
211
|
-
api_keys.each do |api_key|
|
212
|
-
params = {
|
213
|
-
'action' => 'userOnline',
|
214
|
-
'apikey' => api_key,
|
215
|
-
'appid' => APP_ID,
|
216
|
-
'at' => authentication_token,
|
217
|
-
'nonce' => nonce,
|
218
|
-
'sequence' => web_socket_sequence,
|
219
|
-
'ts' => Time.now.to_i,
|
220
|
-
'userAgent' => 'app',
|
221
|
-
'version' => VERSION,
|
222
|
-
}
|
223
|
-
Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.truncate(16).inspect}" }
|
224
|
-
send_to_web_socket(JSON.generate(params))
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
138
|
def authentication_headers
|
229
139
|
{ 'Authorization' => "Bearer #{authentication_token}" }
|
230
140
|
end
|
@@ -290,7 +200,7 @@ module Ewelink
|
|
290
200
|
method = method.to_s.upcase
|
291
201
|
headers = (options[:headers] || {}).reverse_merge('Content-Type' => 'application/json')
|
292
202
|
Ewelink.logger.debug(self.class.name) { "#{method} #{url}" }
|
293
|
-
response = HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout:
|
203
|
+
response = HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout: TIMEOUT))
|
294
204
|
raise(Error.new("#{method} #{url}: #{response.code}")) unless response.success?
|
295
205
|
if response['error'] == 301 && response['region'].present?
|
296
206
|
@region = response['region']
|
@@ -299,180 +209,15 @@ module Ewelink
|
|
299
209
|
end
|
300
210
|
remove_instance_variable(:@authentication_token) if instance_variable_defined?(:@authentication_token) && [401, 403].include?(response['error'])
|
301
211
|
raise(Error.new("#{method} #{url}: #{response['error']} #{response['msg']}".strip)) if response['error'].present? && response['error'] != 0
|
302
|
-
response
|
212
|
+
response
|
303
213
|
rescue Errno::ECONNREFUSED, OpenSSL::OpenSSLError, SocketError, Timeout::Error => e
|
304
214
|
raise Error.new(e)
|
305
215
|
end
|
306
216
|
|
307
|
-
def send_to_web_socket(message)
|
308
|
-
web_socket.send(message)
|
309
|
-
rescue => e
|
310
|
-
reload
|
311
|
-
raise Error.new(e)
|
312
|
-
end
|
313
|
-
|
314
|
-
def start_web_socket_authentication_check_thread
|
315
|
-
raise Error.new('WebSocket authentication check must only be started once') if @web_socket_authentication_check_thread.present?
|
316
|
-
|
317
|
-
@web_socket_authentication_check_thread = Thread.new do
|
318
|
-
loop do
|
319
|
-
Ewelink.logger.debug(self.class.name) { 'Checking if WebSocket is authenticated' }
|
320
|
-
begin
|
321
|
-
web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
|
322
|
-
Ewelink.logger.debug(self.class.name) { 'WebSocket is authenticated' }
|
323
|
-
end
|
324
|
-
rescue => e
|
325
|
-
Ewelink.logger.error(self.class.name) { e }
|
326
|
-
end
|
327
|
-
sleep(WEB_SOCKET_CHECK_AUTHENTICATION_TIMEOUT)
|
328
|
-
end
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
def start_web_socket_ping_thread(interval)
|
333
|
-
@last_web_socket_pong_at = Time.now
|
334
|
-
@web_socket_ping_interval = interval
|
335
|
-
Ewelink.logger.debug(self.class.name) { "Creating thread for WebSocket ping every #{@web_socket_ping_interval} seconds" }
|
336
|
-
@web_socket_ping_thread = Thread.new do
|
337
|
-
loop do
|
338
|
-
break if Thread.current[:stop]
|
339
|
-
sleep(@web_socket_ping_interval)
|
340
|
-
Ewelink.logger.debug(self.class.name) { 'Sending WebSocket ping' }
|
341
|
-
send_to_web_socket('ping')
|
342
|
-
end
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
217
|
def synchronize(name, &block)
|
347
218
|
(@mutexs[name] ||= Mutex.new).synchronize(&block)
|
348
219
|
end
|
349
220
|
|
350
|
-
def web_socket
|
351
|
-
if web_socket_outdated_ping?
|
352
|
-
Ewelink.logger.warn(self.class.name) { 'WebSocket ping is outdated' }
|
353
|
-
reload
|
354
|
-
end
|
355
|
-
|
356
|
-
synchronize(:web_socket) do
|
357
|
-
next @web_socket if @web_socket
|
358
|
-
|
359
|
-
# Initializes caches before opening WebSocket: important in order to
|
360
|
-
# NOT cumulate requests Timeouts from #web_socket_wait_for.
|
361
|
-
api_keys
|
362
|
-
web_socket_url
|
363
|
-
|
364
|
-
Ewelink.logger.debug(self.class.name) { "Opening WebSocket to #{web_socket_url.inspect}" }
|
365
|
-
|
366
|
-
@web_socket_thread = Thread.new do
|
367
|
-
EventMachine.run do
|
368
|
-
@web_socket = Faye::WebSocket::Client.new(web_socket_url)
|
369
|
-
|
370
|
-
@web_socket.on(:close) do |event|
|
371
|
-
Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
|
372
|
-
reload
|
373
|
-
end
|
374
|
-
|
375
|
-
@web_socket.on(:open) do |event|
|
376
|
-
Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
|
377
|
-
@last_web_socket_pong_at = Time.now
|
378
|
-
authenticate_web_socket_api_keys
|
379
|
-
end
|
380
|
-
|
381
|
-
@web_socket.on(:message) do |event|
|
382
|
-
message = event.data
|
383
|
-
|
384
|
-
if message == 'pong'
|
385
|
-
Ewelink.logger.debug(self.class.name) { "Received WebSocket #{message.inspect} message" }
|
386
|
-
@last_web_socket_pong_at = Time.now
|
387
|
-
next
|
388
|
-
end
|
389
|
-
|
390
|
-
begin
|
391
|
-
json = JSON.parse(message)
|
392
|
-
rescue => e
|
393
|
-
Ewelink.logger.error(self.class.name) { 'WebSocket JSON parse error' }
|
394
|
-
reload
|
395
|
-
next
|
396
|
-
end
|
397
|
-
|
398
|
-
if json.key?('error') && json['error'] != 0
|
399
|
-
Ewelink.logger.error(self.class.name) { "WebSocket message error: #{message.inspect}" }
|
400
|
-
reload
|
401
|
-
next
|
402
|
-
end
|
403
|
-
|
404
|
-
if !@web_socket_ping_thread && json.key?('config') && json['config']['hb'] == 1 && json['config']['hbInterval'].present?
|
405
|
-
start_web_socket_ping_thread(json['config']['hbInterval'] + 7)
|
406
|
-
end
|
407
|
-
|
408
|
-
if json['apikey'].present? && !@web_socket_authenticated_api_keys.include?(json['apikey'])
|
409
|
-
@web_socket_authenticated_api_keys << json['apikey']
|
410
|
-
Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{json['apikey'].truncate(16).inspect}" }
|
411
|
-
end
|
412
|
-
|
413
|
-
if json['deviceid'].present? && json['params'].is_a?(Hash) && json['params']['switch'].present?
|
414
|
-
switch = switches.find { |switch| switch[:device_id] == json['deviceid'] }
|
415
|
-
if switch.present?
|
416
|
-
@web_socket_switches_statuses[switch[:uuid]] = json['params']['switch']
|
417
|
-
Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
web_socket_wait_for(-> { @web_socket.present? }) do
|
425
|
-
@web_socket
|
426
|
-
end
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
def web_socket_authenticated?
|
431
|
-
api_keys == @web_socket_authenticated_api_keys
|
432
|
-
end
|
433
|
-
|
434
|
-
def web_socket_outdated_ping?
|
435
|
-
@last_web_socket_pong_at.present? && @web_socket_ping_interval.present? && @last_web_socket_pong_at < (@web_socket_ping_interval * WEB_SOCKET_PING_TOLERANCE_FACTOR).seconds.ago
|
436
|
-
end
|
437
|
-
|
438
|
-
def web_socket_sequence
|
439
|
-
(Time.now.to_f * 1000).round.to_s
|
440
|
-
end
|
441
|
-
|
442
|
-
def web_socket_url
|
443
|
-
synchronize(:web_socket_url) do
|
444
|
-
@web_socket_url ||= begin
|
445
|
-
params = {
|
446
|
-
'accept' => 'ws',
|
447
|
-
'appid' => APP_ID,
|
448
|
-
'nonce' => nonce,
|
449
|
-
'ts' => Time.now.to_i,
|
450
|
-
'version' => VERSION,
|
451
|
-
}
|
452
|
-
response = rest_request(:post, '/dispatch/app', body: JSON.generate(params), headers: authentication_headers)
|
453
|
-
raise('Error while getting WebSocket URL') unless response['error'] == 0
|
454
|
-
domain = response['domain'].presence || raise("Can't get WebSocket server domain")
|
455
|
-
port = response['port'].presence || raise("Can't get WebSocket server port")
|
456
|
-
"wss://#{domain}:#{port}/api/ws".tap { |url| Ewelink.logger.debug(self.class.name) { "WebSocket URL is: #{url.inspect}" } }
|
457
|
-
end
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
|
-
def web_socket_wait_for(condition, initialize_web_socket: false, &block)
|
462
|
-
web_socket if initialize_web_socket
|
463
|
-
begin
|
464
|
-
Timeout.timeout(REQUEST_TIMEOUT) do
|
465
|
-
while !condition.call
|
466
|
-
sleep(WEB_SOCKET_WAIT_INTERVAL)
|
467
|
-
end
|
468
|
-
block_given? ? yield : true
|
469
|
-
end
|
470
|
-
rescue => e
|
471
|
-
reload
|
472
|
-
raise Error.new(e)
|
473
|
-
end
|
474
|
-
end
|
475
|
-
|
476
221
|
end
|
477
222
|
|
478
223
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ewelink
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexis Toulotte
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,26 +30,6 @@ dependencies:
|
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 7.0.0
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: faye-websocket
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
36
|
-
requirements:
|
37
|
-
- - ">="
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: 0.11.0
|
40
|
-
- - "<"
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: 0.12.0
|
43
|
-
type: :runtime
|
44
|
-
prerelease: false
|
45
|
-
version_requirements: !ruby/object:Gem::Requirement
|
46
|
-
requirements:
|
47
|
-
- - ">="
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: 0.11.0
|
50
|
-
- - "<"
|
51
|
-
- !ruby/object:Gem::Version
|
52
|
-
version: 0.12.0
|
53
33
|
- !ruby/object:Gem::Dependency
|
54
34
|
name: httparty
|
55
35
|
requirement: !ruby/object:Gem::Requirement
|