cotcube-helpers 0.2.2.3 → 0.2.4

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 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