cotcube-helpers 0.2.2.5 → 0.2.3
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/CHANGELOG.md +5 -0
- data/VERSION +1 -1
- data/lib/cotcube-helpers/data_client.rb +1 -1
- data/lib/cotcube-helpers/josch_client.rb +115 -0
- data/lib/cotcube-helpers/order_client.rb +131 -0
- data/lib/cotcube-helpers.rb +3 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d08ed5fa831283b0934d2ce7f86f9da0f1be5430d0d3164ee503b0aa7bac74f
|
4
|
+
data.tar.gz: ac59b5558432299e6906c8a93b4ddab8b7a57a7cac4b2c11239a8f41a8c05f4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b3ce7871cbc2068523994caa155a2291512c0cce8c72398f51de275e56c6e1a75a94402327118f27ae0376ccbf70b148be3fb17d0f66326e019d21a639ae485
|
7
|
+
data.tar.gz: e17fe32e700a3441e5e54a4b521c0c3d20acedbfb45606cd8f3da6edb5041a7cbcab522729da6d9d0e784f739730972af1ad1210b5542e06cd879f70587d7b1e
|
data/CHANGELOG.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.3
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bunny'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Cotcube
|
8
|
+
module Helpers
|
9
|
+
class JoSchClient
|
10
|
+
SECRETS_DEFAULT = {
|
11
|
+
'josch_mq_proto' => 'http',
|
12
|
+
'josch_mq_user' => 'guest',
|
13
|
+
'josch_mq_password' => 'guest',
|
14
|
+
'josch_mq_host' => 'localhost',
|
15
|
+
'josch_mq_port' => '15672',
|
16
|
+
'josch_mq_vhost' => '%2F'
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
SECRETS = SECRETS_DEFAULT.merge(
|
20
|
+
lambda {
|
21
|
+
begin
|
22
|
+
YAML.safe_load(File.read(Cotcube::Helpers.init[:secrets_file]))
|
23
|
+
rescue StandardError
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
}.call
|
27
|
+
)
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@connection = Bunny.new(user: SECRETS['josch_mq_user'],
|
31
|
+
password: SECRETS['josch_mq_password'],
|
32
|
+
vhost: SECRETS['josch_mq_vhost'])
|
33
|
+
@connection.start
|
34
|
+
|
35
|
+
@commands = connection.create_channel
|
36
|
+
@exchange = commands.direct('josch_commands')
|
37
|
+
@requests = {}
|
38
|
+
@debug = false
|
39
|
+
setup_reply_queue
|
40
|
+
end
|
41
|
+
|
42
|
+
# command acts a synchronizer: it sends the command and waits for the response
|
43
|
+
# otherwise times out --- the counterpart here is the subscription within
|
44
|
+
# setup_reply_queue
|
45
|
+
#
|
46
|
+
def command(command, timeout: 10)
|
47
|
+
command = { command: command.to_s } unless command.is_a? Hash
|
48
|
+
command[:timestamp] ||= (Time.now.to_f * 1000).to_i
|
49
|
+
request_id = Digest::SHA256.hexdigest(command.to_json)[..6]
|
50
|
+
requests[request_id] = {
|
51
|
+
request: command,
|
52
|
+
id: request_id,
|
53
|
+
lock: Mutex.new,
|
54
|
+
condition: ConditionVariable.new
|
55
|
+
}
|
56
|
+
|
57
|
+
exchange.publish(command.to_json,
|
58
|
+
routing_key: 'josch_commands',
|
59
|
+
correlation_id: request_id,
|
60
|
+
reply_to: reply_queue.name)
|
61
|
+
|
62
|
+
# wait for the signal to continue the execution
|
63
|
+
#
|
64
|
+
requests[request_id][:lock].synchronize do
|
65
|
+
requests[request_id][:condition].wait(requests[request_id][:lock], timeout)
|
66
|
+
end
|
67
|
+
|
68
|
+
# if we reached timeout, we will return nil, just for explicity
|
69
|
+
response = requests[request_id][:response].dup
|
70
|
+
requests.delete(request_id)
|
71
|
+
response
|
72
|
+
end
|
73
|
+
|
74
|
+
alias_method :send_command, :command
|
75
|
+
|
76
|
+
attr_accessor :response
|
77
|
+
attr_reader :lock, :condition
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
attr_reader :call_id, :connection, :requests, :persistent,
|
82
|
+
:commands, :server_queue_name, :reply_queue, :exchange
|
83
|
+
|
84
|
+
def setup_reply_queue
|
85
|
+
@reply_queue = commands.queue('', exclusive: true, auto_delete: true)
|
86
|
+
@reply_queue.bind(commands.exchange('josch_replies'), routing_key: @reply_queue.name)
|
87
|
+
|
88
|
+
reply_queue.subscribe do |delivery_info, properties, payload|
|
89
|
+
__id__ = properties[:correlation_id]
|
90
|
+
|
91
|
+
if __id__.nil?
|
92
|
+
puts "Received without __id__: #{delivery_info.map { |k, v| "#{k}\t#{v}" }.join("\n")
|
93
|
+
}\n\n#{properties.map { |k, v| "#{k}\t#{v}" }.join("\n")
|
94
|
+
}\n\n#{JSON.parse(payload).map { |k, v| "#{k}\t#{v}" }.join("\n")}" if @debug
|
95
|
+
|
96
|
+
elsif requests[__id__].nil?
|
97
|
+
puts "Received non-matching response, maybe previously timed out: \n\n#{delivery_info}\n\n#{properties}\n\n#{payload}\n."[..620].scan(/.{1,120}/).join(' '*30 + "\n") if @debug
|
98
|
+
else
|
99
|
+
# save the payload and send the signal to continue the execution of #command
|
100
|
+
# need to rescue the rare case, where lock and condition are destroyed right in parallel by timeout
|
101
|
+
begin
|
102
|
+
puts "Received result for #{__id__}" if @debug
|
103
|
+
requests[__id__][:response] = payload
|
104
|
+
requests[__id__][:lock].synchronize { requests[__id__][:condition].signal }
|
105
|
+
rescue nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
JoschClient = JoSchClient
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
__END__
|
@@ -0,0 +1,131 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bunny'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Cotcube
|
8
|
+
module Helpers
|
9
|
+
class OrderClient
|
10
|
+
SECRETS_DEFAULT = {
|
11
|
+
'orderproxy_mq_proto' => 'http',
|
12
|
+
'orderproxy_mq_user' => 'guest',
|
13
|
+
'orderproxy_mq_password' => 'guest',
|
14
|
+
'orderproxy_mq_host' => 'localhost',
|
15
|
+
'orderproxy_mq_port' => '15672',
|
16
|
+
'orderproxy_mq_vhost' => '%2F'
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
SECRETS = SECRETS_DEFAULT.merge(
|
20
|
+
lambda {
|
21
|
+
begin
|
22
|
+
YAML.safe_load(File.read(Cotcube::Helpers.init[:secrets_file]))
|
23
|
+
rescue StandardError
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
}.call
|
27
|
+
)
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@connection = Bunny.new(user: SECRETS['orderproxy_mq_user'],
|
31
|
+
password: SECRETS['orderproxy_mq_password'],
|
32
|
+
vhost: SECRETS['orderproxy_mq_vhost'])
|
33
|
+
@connection.start
|
34
|
+
|
35
|
+
@commands = connection.create_channel
|
36
|
+
@exchange = commands.direct('orderproxy_commands')
|
37
|
+
@requests = {}
|
38
|
+
@persistent = { depth: {}, realtimebars: {}, ticks: {} }
|
39
|
+
@debug = false
|
40
|
+
setup_reply_queue
|
41
|
+
end
|
42
|
+
|
43
|
+
# command acts a synchronizer: it sends the command and waits for the response
|
44
|
+
# otherwise times out --- the counterpart here is the subscription within
|
45
|
+
# setup_reply_queue
|
46
|
+
#
|
47
|
+
def command(command, timeout: 10)
|
48
|
+
command = { command: command.to_s } unless command.is_a? Hash
|
49
|
+
command[:timestamp] ||= (Time.now.to_f * 1000).to_i
|
50
|
+
request_id = Digest::SHA256.hexdigest(command.to_json)[..6]
|
51
|
+
requests[request_id] = {
|
52
|
+
request: command,
|
53
|
+
id: request_id,
|
54
|
+
lock: Mutex.new,
|
55
|
+
condition: ConditionVariable.new
|
56
|
+
}
|
57
|
+
|
58
|
+
exchange.publish(command.to_json,
|
59
|
+
routing_key: 'orderproxy_commands',
|
60
|
+
correlation_id: request_id,
|
61
|
+
reply_to: reply_queue.name)
|
62
|
+
|
63
|
+
# wait for the signal to continue the execution
|
64
|
+
#
|
65
|
+
requests[request_id][:lock].synchronize do
|
66
|
+
requests[request_id][:condition].wait(requests[request_id][:lock], timeout)
|
67
|
+
end
|
68
|
+
|
69
|
+
# if we reached timeout, we will return nil, just for explicity
|
70
|
+
response = requests[request_id][:response].dup
|
71
|
+
requests.delete(request_id)
|
72
|
+
response
|
73
|
+
end
|
74
|
+
|
75
|
+
alias_method :send_command, :command
|
76
|
+
|
77
|
+
def stop
|
78
|
+
commands.close
|
79
|
+
connection.close
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_contracts(symbol:)
|
83
|
+
send_command({ command: :get_contracts, symbol: symbol })
|
84
|
+
end
|
85
|
+
|
86
|
+
attr_accessor :response
|
87
|
+
attr_reader :lock, :condition
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
attr_reader :call_id, :connection, :requests, :persistent,
|
92
|
+
:commands, :server_queue_name, :reply_queue, :exchange
|
93
|
+
|
94
|
+
def setup_reply_queue
|
95
|
+
@reply_queue = commands.queue('', exclusive: true, auto_delete: true)
|
96
|
+
@reply_queue.bind(commands.exchange('orderproxy_replies'), routing_key: @reply_queue.name)
|
97
|
+
|
98
|
+
reply_queue.subscribe do |delivery_info, properties, payload|
|
99
|
+
__id__ = properties[:correlation_id]
|
100
|
+
|
101
|
+
if __id__.nil?
|
102
|
+
puts "Received without __id__: #{delivery_info.map { |k, v| "#{k}\t#{v}" }.join("\n")
|
103
|
+
}\n\n#{properties.map { |k, v| "#{k}\t#{v}" }.join("\n")
|
104
|
+
}\n\n#{JSON.parse(payload).map { |k, v| "#{k}\t#{v}" }.join("\n")}" if @debug
|
105
|
+
|
106
|
+
elsif requests[__id__].nil?
|
107
|
+
puts "Received non-matching response, maybe previously timed out: \n\n#{delivery_info}\n\n#{properties}\n\n#{payload}\n."[..620].scan(/.{1,120}/).join(' '*30 + "\n") if @debug
|
108
|
+
else
|
109
|
+
# save the payload and send the signal to continue the execution of #command
|
110
|
+
# need to rescue the rare case, where lock and condition are destroyed right in parallel by timeout
|
111
|
+
begin
|
112
|
+
puts "Received result for #{__id__}" if @debug
|
113
|
+
requests[__id__][:response] = payload
|
114
|
+
requests[__id__][:lock].synchronize { requests[__id__][:condition].signal }
|
115
|
+
rescue nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
__END__
|
125
|
+
begin
|
126
|
+
client = OrderClient.new
|
127
|
+
reply = client.send_command( { command: 'ping' } )
|
128
|
+
puts reply.nil? ? 'nil' : JSON.parse(reply)
|
129
|
+
ensure
|
130
|
+
client.stop
|
131
|
+
end
|
data/lib/cotcube-helpers.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cotcube-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin L. Tischendorf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -108,7 +108,9 @@ files:
|
|
108
108
|
- lib/cotcube-helpers/ib_contracts.rb
|
109
109
|
- lib/cotcube-helpers/init.rb
|
110
110
|
- lib/cotcube-helpers/input.rb
|
111
|
+
- lib/cotcube-helpers/josch_client.rb
|
111
112
|
- lib/cotcube-helpers/numeric_ext.rb
|
113
|
+
- lib/cotcube-helpers/order_client.rb
|
112
114
|
- lib/cotcube-helpers/orderclient.rb
|
113
115
|
- lib/cotcube-helpers/output.rb
|
114
116
|
- lib/cotcube-helpers/parallelize.rb
|