cotcube-helpers 0.2.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54c97a5746e0e03b971b9421be0636eb2c259beb90840ff34404f8049114019b
4
- data.tar.gz: 6d565f00ed620b2458122ad686b1b6c60bb26e815f248474f746b38c10902dc6
3
+ metadata.gz: 569d3d98802f31608b3525e0b0692a19a67b65a05036f7b9c04cd4d8985017d3
4
+ data.tar.gz: ad92e036d65e317ab94f179020b4d131ebb085e8ff87395f17ac5c65b0ec02a2
5
5
  SHA512:
6
- metadata.gz: f9a71ad40a61b1caf44162851d2c323c1b762501705459fbbca85d38c8a6bf66e261eb920093fa01f3ea6ddd07b48dbe7aa4f46e5010a39e0e5bb3045c9941d1
7
- data.tar.gz: a862cb81820c8459a015788d273c1cd159078f11705f2c5f8db2847e355a6e5f6cf54b4532a43efef246804a1679053ca807b76c79c580881a46e3ea09fb0f01
6
+ metadata.gz: ca30750b5450a285efc24f7d8498919b329fddc848e8eaee24990632701e529ab967aaf4c94d6718858f921297674adaaee36025a2fce2664d11e370e0003a86
7
+ data.tar.gz: 7b85764c5630f909f87c0987e662b6801e20e22de850945342dddc839a67c7636abe284463f4e5fba1c952aec80e51dce25f24a1795e9c9a6cdb96e8b29907e6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 0.2.4 (January 13, 2022)
2
+ - minor changes to init and bin/uncommitted
3
+ - datetime_ext: added #seconds_until_next_minute
4
+ - hash: added #deep_dup and #reduce_group; array: added #deep_dup
5
+ - bin/gitlog: pretty formatted git log
6
+ - string_ext: escape_regex escapes characters that otherwise have functional meanings in a regex
7
+ - array_ext: elem_raises? checks for each elem if raises with block, return raising elems or false
8
+ - Merge branch 'main' of github.com:donkeybridge/cotcube-helpers into main
9
+ - merging conflict
10
+
11
+ ## 0.2.3 (December 30, 2021)
12
+ - merging conflict
13
+ - added bare josch_ and order_client s
14
+ - added bare js_ and order_client s
15
+
16
+ ## 0.2.2.5 (December 23, 2021)
17
+ - hash_ext: reworked keys_to_sym!
18
+ - minor changes
19
+ - deep_decode_datetime: added helper for conversion of timestrings in nested arrays and hashed
20
+ - cache_client: rewritten as class
21
+ - gemspec: raised activesupport to version 7
22
+
23
+ ## 0.2.2.4 (December 07, 2021)
24
+ - introduced cache_client as client to readcache
25
+
1
26
  ## 0.2.2.3 (November 28, 2021)
2
27
  - data_client: fixing troublesome output in .command
3
28
 
data/Gemfile CHANGED
@@ -6,3 +6,4 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
  gem 'parallel'
8
8
  gem 'bunny'
9
+ gem 'httparty'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.2.3
1
+ 0.2.4
data/bin/gitlog ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ GIT=`which git`
3
+ ${GIT} log --graph --pretty=format:'%N%h %<(100)%s (%cr) %d%n%b' --abbrev-commit | egrep '\\|/|\w+' | sed 's/^|/ /'
4
+
data/bin/uncommitted ADDED
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+ ROOT=${1:-$HOME/GEMS}
3
+
4
+
5
+ check_dir () {
6
+ CYAN="\033[1;36m"
7
+ RESET="\033[0m"
8
+ VGREP="grep --color=always -v"
9
+ dir=$1
10
+ if [ -d "${dir}/.git" ]; then
11
+ echo -e ${CYAN}${dir}${RESET}
12
+ pushd ${dir} 2>&1 >/dev/null
13
+ git status 2>&1 | $VGREP 'no changes added to commit' |\
14
+ $VGREP '^$' |\
15
+ $VGREP 'to include in what will' |\
16
+ $VGREP '(use "git ' |\
17
+ $VGREP 'On branch main' |\
18
+ $VGREP 'On branch master' |\
19
+ $VGREP 'nothing to commit' |\
20
+ $VGREP 'Your branch is up to date'
21
+ popd 2>&1 >/dev/null
22
+ fi
23
+ }
24
+
25
+
26
+ export -f check_dir
27
+
28
+ find $ROOT -type d -maxdepth 1 2>/dev/null | xargs -n 1 bash -c 'check_dir "$1"' _
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = ['lib']
28
28
 
29
- spec.add_dependency 'activesupport', '~> 6'
29
+ spec.add_dependency 'activesupport', '~> 7'
30
30
  spec.add_dependency 'colorize', '~> 0.8'
31
31
 
32
32
 
@@ -64,38 +64,63 @@ class Array
64
64
  def select_within(ranges:, attr: nil, &block)
65
65
  unless attr.nil? || first[attr]
66
66
  raise ArgumentError,
67
- "At least first element of Array '#{first}' does not contain attr '#{attr}'!"
68
- end
69
- raise ArgumentError, 'Ranges should be an Array or, more precisely, respond_to :map' unless ranges.respond_to? :map
70
- raise ArgumentError, 'Each range in :ranges should respond to .include!' unless ranges.map do |x|
71
- x.respond_to? :include?
72
- end.reduce(:&)
73
-
74
- select do |el|
75
- value = attr.nil? ? el : el[attr]
76
- ranges.map do |range|
77
- range.include?(block.nil? ? value : block.call(value))
78
- end.reduce(:|)
67
+ "At least first element of Array '#{first}' does not contain attr '#{attr}'!"
68
+ end
69
+ raise ArgumentError, 'Ranges should be an Array or, more precisely, respond_to :map' unless ranges.respond_to? :map
70
+ raise ArgumentError, 'Each range in :ranges should respond to .include!' unless ranges.map do |x|
71
+ x.respond_to? :include?
72
+ end.reduce(:&)
73
+
74
+ select do |el|
75
+ value = attr.nil? ? el : el[attr]
76
+ ranges.map do |range|
77
+ range.include?(block.nil? ? value : block.call(value))
78
+ end.reduce(:|)
79
+ end
79
80
  end
80
- end
81
81
 
82
- def select_right_by(inclusive: false, exclusive: false, initial: [], &block)
83
- # unless range.is_a? Range and
84
- # (range.begin.nil? or range.begin.is_a?(Integer)) and
85
- # (range.end.nil? or range.end.is_a?(Integer))
86
- # raise ArgumentError, ":range, if given, must be a range of ( nil|Integer..nil|Integer), got '#{range}'"
87
- # end
82
+ def select_right_by(inclusive: false, exclusive: false, initial: [], &block)
83
+ # unless range.is_a? Range and
84
+ # (range.begin.nil? or range.begin.is_a?(Integer)) and
85
+ # (range.end.nil? or range.end.is_a?(Integer))
86
+ # raise ArgumentError, ":range, if given, must be a range of ( nil|Integer..nil|Integer), got '#{range}'"
87
+ # end
88
+
89
+ raise ArgumentError, 'No block given.' unless block.is_a? Proc
90
+
91
+ inclusive = true unless exclusive
92
+ if inclusive && exclusive
93
+ raise ArgumentError,
94
+ "Either :inclusive or :exclusive must remain falsey, got '#{inclusive}' and '#{exclusive}'"
95
+ end
96
+
97
+ index = find_index { |obj| block.call(obj) }
88
98
 
89
- raise ArgumentError, 'No block given.' unless block.is_a? Proc
99
+ self[((inclusive ? index : index + 1)..)]
100
+ end
90
101
 
91
- inclusive = true unless exclusive
92
- if inclusive && exclusive
93
- raise ArgumentError,
94
- "Either :inclusive or :exclusive must remain falsey, got '#{inclusive}' and '#{exclusive}'"
102
+ def elem_raises?(&block)
103
+ raise ArgumentError, "Must provide a block." unless block_given?
104
+ raise ArgumentError, "Block must have arity of 1." unless block.arity == 1
105
+ map do |elem|
106
+ begin
107
+ block.call(elem)
108
+ false
109
+ rescue
110
+ elem
111
+ end
112
+ end.reject{|z| z.is_a? FalseClass }.tap{|z| z.empty? ? (return false) : (return z)}
95
113
  end
96
114
 
97
- index = find_index { |obj| block.call(obj) }
115
+ def deep_dup
116
+ map do |el|
117
+ case el
118
+ when Hash, Array
119
+ el.deep_dup
120
+ else
121
+ el.dup
122
+ end
123
+ end
124
+ end
98
125
 
99
- self[((inclusive ? index : index + 1)..)]
100
- end
101
126
  end
@@ -0,0 +1,52 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module Cotcube
5
+ module Helpers
6
+ class CacheClient
7
+
8
+ def initialize(query='keys', timezone: Cotcube::Helpers::CHICAGO, debug: false, deflate: false, update: false)
9
+ raise ArgumentError, "Query must not be empty." if [ nil, '' ].include? query
10
+ raise ArgumentError, "Query '#{query}' is garbage." if query.split('/').size > 2 or not query.match? /\A[a-zA-Z0-9?=\/]+\Z/
11
+ @update = update ? '?update=true' : ''
12
+ @request_headers = {}
13
+ @request_headers['Accept-Encoding'] = 'deflate' if deflate
14
+ @query = query
15
+ @result = JSON.parse(HTTParty.get("http://100.100.0.14:8081/#{query}#{@update}").body, headers: @request_headers, symbolize_names: true) rescue { error: 1, msg: "Could not parse response for query '#{query}'." }
16
+ retry_once if has_errors?
17
+ end
18
+
19
+ def retry_once
20
+ sleep 2
21
+ raw = HTTParty.get("http://100.100.0.14:8081/#{query}#{update}")
22
+ @result = JSON.parse(raw.body, symbolize_names: true) rescue { error: 1, msg: "Could not parse response for query '#{query}'." }
23
+ if has_errors?
24
+ puts "ERROR in parsing response: #{raw[..300]}"
25
+ end
26
+ end
27
+
28
+ def has_errors?
29
+ result[:error].nil? or result[:error] > 0
30
+ end
31
+
32
+ def warnings
33
+ result[:warnings]
34
+ end
35
+
36
+ def payload
37
+ has_errors? ? false : @result[:payload]
38
+ end
39
+
40
+ def entity
41
+ query.split('/').first
42
+ end
43
+
44
+ def asset
45
+ entity, asset = query.split('/')
46
+ asset
47
+ end
48
+
49
+ attr_reader :query, :result, :update
50
+ end
51
+ end
52
+ end
@@ -30,6 +30,8 @@ module Cotcube
30
30
 
31
31
 
32
32
  CHICAGO = Time.find_zone('America/Chicago')
33
+ NEW_YORK = Time.find_zone('America/New_York')
34
+ BERLIN = Time.find_zone('Europe/Berlin')
33
35
 
34
36
  DATE_FMT = '%Y-%m-%d'
35
37
 
@@ -44,7 +44,7 @@ module Cotcube
44
44
  # otherwise times out --- the counterpart here is the subscription within
45
45
  # setup_reply_queue
46
46
  #
47
- def command(command, timeout: 5)
47
+ def command(command, timeout: 10)
48
48
  command = { command: command.to_s } unless command.is_a? Hash
49
49
  command[:timestamp] ||= (Time.now.to_f * 1000).to_i
50
50
  request_id = Digest::SHA256.hexdigest(command.to_json)[..6]
@@ -227,4 +227,4 @@ begin
227
227
  }
228
228
  ensure
229
229
  client.stop
230
- e,nd
230
+ end
@@ -12,6 +12,14 @@ class DateTime
12
12
  end
13
13
 
14
14
  alias to_sssm to_seconds_since_sunday_morning
15
+
16
+ def seconds_until_next_minute(offset: 60)
17
+ offset = offset % 60
18
+ offset = 60 if offset.zero?
19
+ seconds = (self + offset - (self.to_f % 60).round(3) - self).to_f
20
+ seconds + (seconds.negative? ? 60 : 0)
21
+ end
22
+
15
23
  end
16
24
 
17
25
  class Date
@@ -21,12 +29,12 @@ class Date
21
29
  form = '%Y %W %w'
22
30
  build_range = lambda {|w|
23
31
  begin
24
- ( DateTime.strptime("#{year} #{w} 1", form).to_date..
25
- DateTime.strptime("#{year} #{w} 0", form).to_date)
32
+ ( DateTime.strptime("#{year} #{w} 1", form).to_date..
33
+ DateTime.strptime("#{year} #{w} 0", form).to_date)
26
34
  rescue
27
- # beyond Dec 31st #strptime must be called with cw:0 to keep it working
28
- ( DateTime.strptime("#{year} #{w} 1", form).to_date..
29
- DateTime.strptime("#{year+1} 0 0", form).to_date)
35
+ # beyond Dec 31st #strptime must be called with cw:0 to keep it working
36
+ ( DateTime.strptime("#{year} #{w} 1", form).to_date..
37
+ DateTime.strptime("#{year+1} 0 0", form).to_date)
30
38
  end
31
39
  }
32
40
  case week
@@ -0,0 +1,25 @@
1
+ module Cotcube
2
+ module Helpers
3
+ VALID_DATETIME_STRING = lambda {|str| str.is_a?(String) and [10,25,29].include?(str.length) and str.count("^0-9:TZ+-= ").zero? }
4
+
5
+ def deep_decode_datetime(data, zone: DateTime)
6
+ case data
7
+ when nil; nil
8
+ when VALID_DATETIME_STRING
9
+ res = nil
10
+ begin
11
+ res = zone.parse(data)
12
+ rescue ArgumentError
13
+ data
14
+ end
15
+ [ DateTime, ActiveSupport::TimeWithZone ].include?(res.class) ? res : data
16
+ when Array; data.map! { |d| deep_decode_datetime(d, zone: zone) }
17
+ when Hash; data.transform_values! { |v| deep_decode_datetime(v, zone: zone) }
18
+ else; data
19
+ end
20
+ end
21
+
22
+ module_function :deep_decode_datetime
23
+ end
24
+ end
25
+
@@ -2,16 +2,42 @@
2
2
 
3
3
  # Monkey patching the Ruby Core class Hash
4
4
  class Hash
5
- def keys_to_sym
6
- each_key do |key|
5
+ def keys_to_sym!
6
+ self.keys.each do |key|
7
7
  case self[key].class.to_s
8
8
  when 'Hash'
9
- self[key].keys_to_sym
9
+ self[key].keys_to_sym!
10
10
  when 'Array'
11
- self[key].map { |el| el.is_a?(Hash) ? el.keys_to_sym : el }
11
+ self[key].map { |el| el.is_a?(Hash) ? el.keys_to_sym! : el }
12
12
  end
13
- self[key.to_sym] = delete(key)
13
+ self["#{key}".to_sym] = delete(key)
14
14
  end
15
15
  self
16
16
  end
17
+
18
+ # a group_hash was created from an array by running group_by
19
+ # to reduce a group_hash, the given block is applied to each array of the hash
20
+ # if its not an array value, the block will auto-yield nil
21
+ def reduce_group(&block)
22
+ raise ArgumentError, 'No block given' unless block_given?
23
+ map do |key,value|
24
+ case value
25
+ when Array
26
+ [key, (block.call(value) rescue nil) ]
27
+ else
28
+ [key, nil]
29
+ end
30
+ end.to_h
31
+ end
32
+
33
+ def deep_dup
34
+ map do |k,v|
35
+ case v
36
+ when Hash, Array
37
+ [k, v.deep_dup]
38
+ else
39
+ [k, v.dup]
40
+ end
41
+ end.to_h
42
+ end
17
43
  end
@@ -22,9 +22,10 @@ module Cotcube
22
22
  def init(config_file_name: nil,
23
23
  gem_name: nil,
24
24
  debug: false)
25
- gem_name ||= self.ancestors.first.to_s
26
- config_file_name = "#{gem_name.downcase.split('::').last}.yml"
27
- config_file = config_path + "/#{config_file_name}"
25
+ gem_name ||= self.ancestors.first.to_s
26
+ name = gem_name.split('::').last.downcase
27
+ config_file_name = "#{name}.yml"
28
+ config_file = config_path + "/#{config_file_name}"
28
29
 
29
30
  if File.exist?(config_file)
30
31
  require 'yaml'
@@ -35,6 +36,7 @@ module Cotcube
35
36
 
36
37
  defaults = {
37
38
  data_path: '/var/cotcube/' + name,
39
+ pid_file: "/var/run/cotcube/#{name}.pid"
38
40
  }
39
41
 
40
42
  config = defaults.merge(config)
@@ -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
@@ -10,5 +10,9 @@ class String
10
10
  false
11
11
  end
12
12
 
13
+ def escape_regex
14
+ chars.map{|z| %w[ . | ( ) [ ] { } \ ^ $ + * ? ].include?(z) ? "\\#{z}" : z }.join
15
+ end
16
+
13
17
  alias is_valid_json? valid_json?
14
18
  end
@@ -21,6 +21,7 @@ require_relative 'cotcube-helpers/subpattern'
21
21
  require_relative 'cotcube-helpers/parallelize'
22
22
  require_relative 'cotcube-helpers/simple_output'
23
23
  require_relative 'cotcube-helpers/simple_series_stats'
24
+ require_relative 'cotcube-helpers/deep_decode_datetime'
24
25
  require_relative 'cotcube-helpers/constants'
25
26
  require_relative 'cotcube-helpers/input'
26
27
  require_relative 'cotcube-helpers/output'
@@ -52,4 +53,6 @@ module Cotcube
52
53
  end
53
54
  end
54
55
 
55
- require_relative 'cotcube-helpers/data_client'
56
+ %w[ data cache order josch ].each do |part|
57
+ require_relative "cotcube-helpers/#{part}_client"
58
+ end
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
 
3
- export rubyenv=/home/pepe/.rvm/environments/default
3
+ export rubyenv=/home/pepe/.rvm/environments/ruby-2.7.5
4
4
 
5
5
  . $rubyenv
6
6
  cd /home/pepe/GEMS/${1}
@@ -10,6 +10,7 @@ ruby ${2} ${3} ${4} ${5} ${6}
10
10
 
11
11
 
12
12
  exit
13
+
13
14
  for testing run
14
15
  env - `cat /home/pepe/bin/cron_ruby_wrapper.sh | tail -n 6` /bin/bash
15
16
 
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.2.3
4
+ version: 0.2.4
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-11-28 00:00:00.000000000 Z
11
+ date: 2022-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '6'
19
+ version: '7'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '6'
26
+ version: '7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: colorize
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -93,12 +93,16 @@ files:
93
93
  - LICENSE.txt
94
94
  - README.md
95
95
  - VERSION
96
+ - bin/gitlog
97
+ - bin/uncommitted
96
98
  - cotcube-helpers.gemspec
97
99
  - lib/cotcube-helpers.rb
98
100
  - lib/cotcube-helpers/array_ext.rb
101
+ - lib/cotcube-helpers/cache_client.rb
99
102
  - lib/cotcube-helpers/constants.rb
100
103
  - lib/cotcube-helpers/data_client.rb
101
104
  - lib/cotcube-helpers/datetime_ext.rb
105
+ - lib/cotcube-helpers/deep_decode_datetime.rb
102
106
  - lib/cotcube-helpers/enum_ext.rb
103
107
  - lib/cotcube-helpers/expiration.rb
104
108
  - lib/cotcube-helpers/get_id_set.rb
@@ -106,7 +110,9 @@ files:
106
110
  - lib/cotcube-helpers/ib_contracts.rb
107
111
  - lib/cotcube-helpers/init.rb
108
112
  - lib/cotcube-helpers/input.rb
113
+ - lib/cotcube-helpers/josch_client.rb
109
114
  - lib/cotcube-helpers/numeric_ext.rb
115
+ - lib/cotcube-helpers/order_client.rb
110
116
  - lib/cotcube-helpers/orderclient.rb
111
117
  - lib/cotcube-helpers/output.rb
112
118
  - lib/cotcube-helpers/parallelize.rb