grumlin 0.6.2 → 0.7.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/Gemfile.lock +1 -1
- data/lib/async/channel.rb +1 -1
- data/lib/grumlin/client.rb +36 -17
- data/lib/grumlin/request_dispatcher.rb +6 -0
- data/lib/grumlin/transport.rb +50 -35
- data/lib/grumlin/version.rb +1 -1
- data/lib/grumlin.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4260297c3d75ec7bc1f987d8c7bad4efd75c81627814fdf984a0aaac0045332b
|
4
|
+
data.tar.gz: 1cb521906a08cf5390f058a28f380492a2999e55ef023c473fc6bae64533b53b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 992d2192402f3fe282203a3fd0eff1e6c886470899cb37f00ca2abf1392e4d946c7391be0eb7d6560c9941f7a17a3552682a8a47f56273f4299c06551c1e9ffc
|
7
|
+
data.tar.gz: d8695e2485915cb7b4d37a755e42d20b6d7f575d906ae5a0aca5f508c01a104c08f7bb51a2285c9dac48a0091771882afeb405fd26e7802c4d48513274e2895f
|
data/Gemfile.lock
CHANGED
data/lib/async/channel.rb
CHANGED
data/lib/grumlin/client.rb
CHANGED
@@ -13,6 +13,7 @@ module Grumlin
|
|
13
13
|
def initialize(url, client_factory:, concurrency: 1, parent: Async::Task.current)
|
14
14
|
super(concurrency)
|
15
15
|
@client = client_factory.call(url, parent).tap(&:connect)
|
16
|
+
@parent = parent
|
16
17
|
end
|
17
18
|
|
18
19
|
def closed?
|
@@ -25,6 +26,8 @@ module Grumlin
|
|
25
26
|
|
26
27
|
def write(*args)
|
27
28
|
@client.write(*args)
|
29
|
+
ensure
|
30
|
+
@count += 1
|
28
31
|
end
|
29
32
|
|
30
33
|
def viable?
|
@@ -36,33 +39,54 @@ module Grumlin
|
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
42
|
+
include Console
|
43
|
+
|
44
|
+
# Client is not reusable. Once closed should be recreated.
|
39
45
|
def initialize(url, parent: Async::Task.current, **client_options)
|
40
46
|
@url = url
|
41
47
|
@client_options = client_options
|
42
48
|
@parent = parent
|
43
|
-
|
49
|
+
@request_dispatcher = nil
|
50
|
+
@transport = nil
|
44
51
|
end
|
45
52
|
|
46
53
|
def connect
|
54
|
+
raise "ClientClosed" if @closed
|
55
|
+
|
47
56
|
@transport = build_transport
|
48
57
|
response_channel = @transport.connect
|
49
58
|
@request_dispatcher = RequestDispatcher.new
|
50
|
-
@parent.async do
|
59
|
+
@response_task = @parent.async do
|
51
60
|
response_channel.each do |response|
|
52
61
|
@request_dispatcher.add_response(response)
|
53
62
|
end
|
54
|
-
rescue StandardError
|
55
|
-
close
|
63
|
+
rescue Async::Stop, Async::TimeoutError, StandardError
|
64
|
+
close(check_requests: false)
|
56
65
|
end
|
66
|
+
logger.debug(self, "Connected")
|
57
67
|
end
|
58
68
|
|
59
|
-
|
69
|
+
# Before calling close the user must ensure that:
|
70
|
+
# 1) There are no ongoing requests
|
71
|
+
# 2) There will be no new writes after
|
72
|
+
def close(check_requests: true) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
73
|
+
return if @closed
|
74
|
+
|
75
|
+
@closed = true
|
76
|
+
|
60
77
|
@transport&.close
|
61
|
-
|
62
|
-
raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}"
|
63
|
-
end
|
78
|
+
@transport&.wait
|
64
79
|
|
65
|
-
|
80
|
+
@response_task&.stop
|
81
|
+
@response_task&.wait
|
82
|
+
|
83
|
+
return if @request_dispatcher&.requests&.empty?
|
84
|
+
|
85
|
+
@request_dispatcher.clear unless check_requests
|
86
|
+
|
87
|
+
raise ResourceLeakError, "Request list is not empty: #{@request_dispatcher.requests}" if check_requests
|
88
|
+
ensure
|
89
|
+
logger.debug(self, "Closed")
|
66
90
|
end
|
67
91
|
|
68
92
|
def connected?
|
@@ -80,9 +104,9 @@ module Grumlin
|
|
80
104
|
|
81
105
|
begin
|
82
106
|
channel.dequeue.flat_map { |item| Typing.cast(item) }
|
83
|
-
rescue Async::Stop
|
84
|
-
|
85
|
-
raise
|
107
|
+
rescue Async::Stop, Async::TimeoutError
|
108
|
+
close(check_requests: false)
|
109
|
+
raise
|
86
110
|
end
|
87
111
|
end
|
88
112
|
|
@@ -106,11 +130,6 @@ module Grumlin
|
|
106
130
|
}
|
107
131
|
end
|
108
132
|
|
109
|
-
def reset!
|
110
|
-
@request_dispatcher = nil
|
111
|
-
@transport = nil
|
112
|
-
end
|
113
|
-
|
114
133
|
def build_transport
|
115
134
|
Transport.new(@url, parent: @parent, **@client_options)
|
116
135
|
end
|
@@ -22,6 +22,8 @@ module Grumlin
|
|
22
22
|
498 => ClientSideError
|
23
23
|
}.freeze
|
24
24
|
|
25
|
+
include Console
|
26
|
+
|
25
27
|
def initialize
|
26
28
|
@requests = {}
|
27
29
|
end
|
@@ -69,6 +71,10 @@ module Grumlin
|
|
69
71
|
@requests.key?(request_id)
|
70
72
|
end
|
71
73
|
|
74
|
+
def clear
|
75
|
+
@requests.clear
|
76
|
+
end
|
77
|
+
|
72
78
|
private
|
73
79
|
|
74
80
|
def check_errors!(status)
|
data/lib/grumlin/transport.rb
CHANGED
@@ -5,47 +5,33 @@ module Grumlin
|
|
5
5
|
# A transport based on https://github.com/socketry/async
|
6
6
|
# and https://github.com/socketry/async-websocket
|
7
7
|
|
8
|
+
include Console
|
9
|
+
|
8
10
|
attr_reader :url
|
9
11
|
|
12
|
+
# Transport is not reusable. Once closed should be recreated.
|
10
13
|
def initialize(url, parent: Async::Task.current, **client_options)
|
11
14
|
@url = url
|
12
15
|
@parent = parent
|
13
16
|
@client_options = client_options
|
14
17
|
@request_channel = Async::Channel.new
|
15
18
|
@response_channel = Async::Channel.new
|
16
|
-
reset!
|
17
19
|
end
|
18
20
|
|
19
21
|
def connected?
|
20
|
-
|
22
|
+
!@connection.nil?
|
21
23
|
end
|
22
24
|
|
23
|
-
def connect
|
25
|
+
def connect
|
26
|
+
raise "ClientClosed" if @closed
|
24
27
|
raise AlreadyConnectedError if connected?
|
25
28
|
|
26
29
|
@connection = Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(@url), **@client_options)
|
30
|
+
logger.debug(self) { "Connected to #{@url}." }
|
27
31
|
|
28
|
-
@response_task = @parent.async
|
29
|
-
loop do
|
30
|
-
data = @connection.read
|
31
|
-
@response_channel << data
|
32
|
-
end
|
33
|
-
rescue Async::Stop
|
34
|
-
@response_channel.close
|
35
|
-
rescue StandardError => e
|
36
|
-
@response_channel.exception(e)
|
37
|
-
end
|
32
|
+
@response_task = @parent.async { run_response_task }
|
38
33
|
|
39
|
-
@request_task = @parent.async
|
40
|
-
@request_channel.each do |message|
|
41
|
-
@connection.write(message)
|
42
|
-
@connection.flush
|
43
|
-
end
|
44
|
-
rescue StandardError => e
|
45
|
-
@response_channel.exception(e)
|
46
|
-
end
|
47
|
-
|
48
|
-
@connected = true
|
34
|
+
@request_task = @parent.async { run_request_task }
|
49
35
|
|
50
36
|
@response_channel
|
51
37
|
end
|
@@ -57,30 +43,59 @@ module Grumlin
|
|
57
43
|
end
|
58
44
|
|
59
45
|
def close
|
60
|
-
return
|
46
|
+
return if @closed
|
61
47
|
|
62
|
-
@
|
63
|
-
@request_task.wait
|
48
|
+
@closed = true
|
64
49
|
|
65
|
-
@
|
66
|
-
@
|
50
|
+
@request_channel.close
|
51
|
+
@response_channel.close
|
67
52
|
|
68
53
|
begin
|
69
54
|
@connection.close
|
70
|
-
rescue
|
55
|
+
rescue StandardError
|
71
56
|
nil
|
72
57
|
end
|
58
|
+
@connection = nil
|
59
|
+
|
60
|
+
@request_task&.stop(true)
|
61
|
+
@response_task&.stop(true)
|
62
|
+
end
|
73
63
|
|
74
|
-
|
64
|
+
def wait
|
65
|
+
@request_task.wait
|
66
|
+
@response_task.wait
|
75
67
|
end
|
76
68
|
|
77
69
|
private
|
78
70
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
71
|
+
def run_response_task
|
72
|
+
with_guard do
|
73
|
+
loop do
|
74
|
+
data = @connection.read
|
75
|
+
@response_channel << data
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def run_request_task
|
81
|
+
with_guard do
|
82
|
+
@request_channel.each do |message|
|
83
|
+
@connection.write(message)
|
84
|
+
@connection.flush
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def with_guard
|
90
|
+
yield
|
91
|
+
rescue Async::Stop, Async::TimeoutError, StandardError => e
|
92
|
+
logger.debug(self) { "Guard error, closing." }
|
93
|
+
begin
|
94
|
+
@response_channel.exception(e)
|
95
|
+
rescue Async::Channel::ChannelClosedError
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
close
|
84
99
|
end
|
85
100
|
end
|
86
101
|
end
|
data/lib/grumlin/version.rb
CHANGED
data/lib/grumlin.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grumlin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gleb Sinyavskiy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-pool
|