cotcube-helpers 0.1.3 → 0.1.6
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/.irbrc.rb +3 -1
- data/CHANGELOG.md +25 -0
- data/Gemfile +3 -2
- data/README.md +22 -16
- data/VERSION +1 -1
- data/cotcube-helpers.gemspec +2 -3
- data/lib/cotcube-helpers.rb +8 -10
- data/lib/cotcube-helpers/array_ext.rb +54 -10
- data/lib/cotcube-helpers/datetime_ext.rb +12 -0
- data/lib/cotcube-helpers/enum_ext.rb +5 -3
- data/lib/cotcube-helpers/hash_ext.rb +8 -6
- data/lib/cotcube-helpers/input.rb +19 -15
- data/lib/cotcube-helpers/parallelize.rb +20 -22
- data/lib/cotcube-helpers/range_ext.rb +85 -41
- data/lib/cotcube-helpers/reduce.rb +44 -32
- data/lib/cotcube-helpers/string_ext.rb +11 -6
- data/lib/cotcube-helpers/subpattern.rb +3 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0417adfcb0f782bfa9e783dbd1c673a489fca39ce9a0f14fa965eb47aaa64b11
|
4
|
+
data.tar.gz: 1b14e149cc6b1bd437d1413dfa49b461c798868959cf97b164a0b3a2e1f72889
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2babf733929f4ca0705ac2018b075e3b62df051c4918d01fcc6c11b49a7cad2ee68fd8ef26504edc5678c3057a97092de252a34cec2a953f638cd9262c2e65d
|
7
|
+
data.tar.gz: 1d9e367613f5b373c73cada786dee68d4e5ccc965573373b39d6419433a5f8131bc553773279d7ba58f2706a63be474448e15cc1e7bce1da63278ad384abeee0
|
data/.irbrc.rb
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
## 0.1.6 (January 15, 2021)
|
2
|
+
- removing :ranges from Range#select_within
|
3
|
+
- Added Array#select_right_by
|
4
|
+
|
5
|
+
## 0.1.5.4 (January 02, 2021)
|
6
|
+
|
7
|
+
|
8
|
+
## 1.5.1.3 (January 02, 2021)
|
9
|
+
- hotfixing the hotfix (hello CI tools, c ya coming)
|
10
|
+
|
11
|
+
## 1.5.1.2 (January 02, 2021)
|
12
|
+
- hotfix problem in Range.to_time_intervals
|
13
|
+
|
14
|
+
## 0.1.5.1 (January 02, 2021)
|
15
|
+
- Hotfixing parallelize
|
16
|
+
|
17
|
+
## 0.1.5 (January 02, 2021)
|
18
|
+
- applied new datetime helper to Range#to_time_intervals
|
19
|
+
- added new DateTime extension, containing 'to_seconds_since_sunday_morning'
|
20
|
+
- added #select_within to array_ext
|
21
|
+
|
22
|
+
## 0.1.4 (December 27, 2020)
|
23
|
+
- applied cops
|
24
|
+
- added README for reduce; minor changes
|
25
|
+
|
1
26
|
## 0.1.3 (December 22, 2020)
|
2
27
|
- added .reduce(bars: , to: ,*args, &block) to reduce a series of bars to a higher timeframe (though only 1hour and 1day are supported yet)
|
3
28
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Returns the array compacted--or just nil, when the result is empty?
|
|
12
12
|
|
13
13
|
This is a rather old implementation. Most probably something I developed before I met Array#group\_by.
|
14
14
|
|
15
|
-
##### pairwise(&block) /
|
15
|
+
##### pairwise(&block) / one_by_one(&block)
|
16
16
|
|
17
17
|
Yields block on each consecutive pair of the array, hence returning array.size-1 results.
|
18
18
|
|
@@ -41,17 +41,18 @@ Uses the range of *date alikes* delivered to create an Array of time periods of
|
|
41
41
|
|
42
42
|
When the step is sub-day, the periods are cleared for DST-changes.
|
43
43
|
|
44
|
-
When ranges are nil, only periods are returned that are within the full trading hours.
|
45
|
-
|
44
|
+
When ranges are nil, only periods are returned that are within the full trading hours.
|
45
|
+
|
46
|
+
This is implemented with DateTime's propery, that Sunday is wday==0. If you want to use ranges, just send a an array
|
46
47
|
of ranges, providing seconds starting at Sunday morning midnight. Here is the default as example:
|
47
48
|
|
48
49
|
```
|
49
50
|
ranges ||= [
|
50
|
-
61200
|
51
|
-
147600
|
52
|
-
234000
|
53
|
-
320400
|
54
|
-
406800
|
51
|
+
61200...144000, # Sun 5pm .. Mon 4pm
|
52
|
+
147600...230400, # Mon 5pm .. Tue 4pm
|
53
|
+
234000...316800, # ...
|
54
|
+
320400...403300,
|
55
|
+
406800...489600
|
55
56
|
]
|
56
57
|
```
|
57
58
|
|
@@ -63,18 +64,19 @@ of ranges, providing seconds starting at Sunday morning midnight. Here is the de
|
|
63
64
|
|
64
65
|
##### sub(minimum: 1) { [:hyper, :mega] }
|
65
66
|
|
66
|
-
sub (should be 'subpattern', but too long) is for use in case / when statements
|
67
|
-
|
68
|
-
based on the the giving minimum. E.g. 'a', 'ab' .. 'abcd' will match sub(1){'abcd'}
|
67
|
+
sub (should be 'subpattern', but too long) is for use in `case / when` statements.
|
68
|
+
It returns a lambda, that checks the *case'd* expression for matching subpatterns
|
69
|
+
based on the the giving minimum. E.g. 'a', 'ab' .. 'abcd' .. 'abcd<any_garbage> will match sub(1){'abcd'}
|
69
70
|
but only 'abc' and 'abcd' will match sub(3){'abcd'}
|
70
71
|
|
71
|
-
|
72
|
+
It is developed for evaluating user input, where abbreviation of incoming commands
|
72
73
|
is desirable (h for hoover and hyper, what will translate to sub(2){'hoover'} and sub(2){hyper})
|
73
74
|
|
74
|
-
To extend functionality even more, it is possible to send
|
75
|
-
sub(2){[:hyper,:mega]}, what will respond truthy to "hy" and "meg" but not to "m" or "hypo"
|
75
|
+
To extend functionality even more, it is possible to send array of patterns like
|
76
|
+
sub(2){[:hyper,:mega]}, what will respond truthy to "hy" and "meg" but not to "m" or "hypo" (sadly, you can
|
77
|
+
set `:minimum` only once).
|
76
78
|
|
77
|
-
*
|
79
|
+
*Paired with keystroke() it allows an easy build of an inputhandler.*
|
78
80
|
|
79
81
|
#### SimpleOutput
|
80
82
|
|
@@ -90,7 +92,7 @@ flow like logs, to pause and continue output.
|
|
90
92
|
A version of STDIN.gets, that does not wait for pressing 'enter' but instantly returns the content
|
91
93
|
of the keystroke.
|
92
94
|
|
93
|
-
*
|
95
|
+
*Paired with subpattern it allows an easy build of an InputHandler.*
|
94
96
|
|
95
97
|
#### Parallelize
|
96
98
|
|
@@ -98,4 +100,8 @@ of the keystroke.
|
|
98
100
|
|
99
101
|
Based on https://github.com/grosser/parallel, it is a quite convenient way to parallelize tasks.
|
100
102
|
|
103
|
+
#### Reduce
|
104
|
+
|
105
|
+
##### reduce(bars: , to: nil, datelike: :datetime, &block)
|
101
106
|
|
107
|
+
Given a series resp. an array of bars (a bar is a set of contract/symbol, datelike, OHLC, volume) respresenting periods of e.g. 1.minute or 15.minutes, these are 'reduced' to an new series of a higher timeframe like hours or days. More target periods might be added in the futures.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.6
|
data/cotcube-helpers.gemspec
CHANGED
@@ -9,13 +9,13 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.summary = 'Some helpers and core extensions as part of the Cotcube Suite.'
|
10
10
|
spec.description = 'Some helpers and core extensions as part of the Cotcube Suite...'
|
11
11
|
|
12
|
-
spec.homepage =
|
12
|
+
spec.homepage = "https://github.com/donkeybridge/#{spec.name}"
|
13
13
|
spec.license = 'BSD-4-Clause'
|
14
14
|
spec.required_ruby_version = Gem::Requirement.new('~> 2.7')
|
15
15
|
|
16
16
|
spec.metadata['homepage_uri'] = spec.homepage
|
17
17
|
spec.metadata['source_code_uri'] = spec.homepage
|
18
|
-
spec.metadata['changelog_uri'] = spec.homepage
|
18
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/CHANGELOG.md"
|
19
19
|
|
20
20
|
# Specify which files should be added to the gem when it is released.
|
21
21
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -28,7 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
spec.add_dependency 'activesupport'
|
30
30
|
|
31
|
-
|
32
31
|
spec.add_development_dependency 'rake'
|
33
32
|
spec.add_development_dependency 'rspec', '~>3.6'
|
34
33
|
spec.add_development_dependency 'yard', '~>0.9'
|
data/lib/cotcube-helpers.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# rubocop:disable Naming/FileName
|
2
|
+
# rubocop:enable Naming/FileName
|
1
3
|
# frozen_string_literal: true
|
2
4
|
|
3
5
|
require 'active_support'
|
@@ -10,24 +12,20 @@ require_relative 'cotcube-helpers/enum_ext'
|
|
10
12
|
require_relative 'cotcube-helpers/hash_ext'
|
11
13
|
require_relative 'cotcube-helpers/range_ext'
|
12
14
|
require_relative 'cotcube-helpers/string_ext'
|
13
|
-
require_relative 'cotcube-helpers/
|
15
|
+
require_relative 'cotcube-helpers/datetime_ext'
|
16
|
+
require_relative 'cotcube-helpers/subpattern'
|
14
17
|
require_relative 'cotcube-helpers/parallelize'
|
15
18
|
require_relative 'cotcube-helpers/simple_output'
|
16
19
|
require_relative 'cotcube-helpers/input'
|
17
20
|
require_relative 'cotcube-helpers/reduce'
|
18
21
|
|
19
|
-
|
20
22
|
module Cotcube
|
21
23
|
module Helpers
|
24
|
+
module_function :sub,
|
25
|
+
:parallelize,
|
26
|
+
:reduce,
|
27
|
+
:keystroke
|
22
28
|
|
23
|
-
module_function :sub,
|
24
|
-
:parallelize,
|
25
|
-
:reduce,
|
26
|
-
:keystroke
|
27
|
-
|
28
|
-
|
29
|
-
|
30
29
|
# please not that module_functions of source provided in private files must be published there
|
31
30
|
end
|
32
31
|
end
|
33
|
-
|
@@ -1,22 +1,25 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Monkey patching the Ruby Core class Array
|
4
|
+
class Array
|
3
5
|
# returns nil if the compacted array is empty, otherwise returns the compacted array
|
4
|
-
def compact_or_nil(*
|
5
|
-
return nil if
|
6
|
-
|
6
|
+
def compact_or_nil(*_args)
|
7
|
+
return nil if compact == []
|
8
|
+
|
9
|
+
yield compact
|
7
10
|
end
|
8
11
|
|
9
12
|
# sorts by a given attribute and then returns groups of where this attribute is equal
|
10
13
|
# .... seems like some_array.group_by(&attr).values
|
11
14
|
def split_by(attrib)
|
12
15
|
res = []
|
13
|
-
sub = []
|
14
|
-
|
15
|
-
if sub.empty?
|
16
|
+
sub = []
|
17
|
+
sort_by(&attrib).each do |elem|
|
18
|
+
if sub.empty? || (sub.last[attrib] == elem[attrib])
|
16
19
|
sub << elem
|
17
20
|
else
|
18
21
|
res << sub
|
19
|
-
sub = [
|
22
|
+
sub = [elem]
|
20
23
|
end
|
21
24
|
end
|
22
25
|
res << sub
|
@@ -37,7 +40,7 @@ class Array
|
|
37
40
|
end.compact
|
38
41
|
end
|
39
42
|
|
40
|
-
|
43
|
+
alias one_by_one pairwise
|
41
44
|
|
42
45
|
# same as pairwise, but with arity of three
|
43
46
|
def triplewise(&block)
|
@@ -47,7 +50,48 @@ class Array
|
|
47
50
|
each_with_index.map do |_, i|
|
48
51
|
next if i < 2
|
49
52
|
|
50
|
-
block.call(self[i - 2], self[i-1], self[i])
|
53
|
+
block.call(self[i - 2], self[i - 1], self[i])
|
51
54
|
end.compact
|
52
55
|
end
|
56
|
+
|
57
|
+
# selects all elements from array that fit in given ranges.
|
58
|
+
# if :attr is given, selects all elements, where elem[:attr] fit
|
59
|
+
# raises if elem.first[attr].nil?
|
60
|
+
def select_within(ranges:, attr: nil, &block)
|
61
|
+
unless attr.nil? || first[attr]
|
62
|
+
raise ArgumentError,
|
63
|
+
"At least first element of Array '#{first}' does not contain attr '#{attr}'!"
|
64
|
+
end
|
65
|
+
raise ArgumentError, 'Ranges should be an Array or, more precisely, respond_to :map' unless ranges.respond_to? :map
|
66
|
+
raise ArgumentError, 'Each range in :ranges should respond to .include!' unless ranges.map do |x|
|
67
|
+
x.respond_to? :include?
|
68
|
+
end.reduce(:&)
|
69
|
+
|
70
|
+
select do |el|
|
71
|
+
value = attr.nil? ? el : el[attr]
|
72
|
+
ranges.map do |range|
|
73
|
+
range.include?(block.nil? ? value : block.call(value))
|
74
|
+
end.reduce(:|)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def select_right_by(inclusive: false, exclusive: false, initial: [], &block)
|
79
|
+
# unless range.is_a? Range and
|
80
|
+
# (range.begin.nil? or range.begin.is_a?(Integer)) and
|
81
|
+
# (range.end.nil? or range.end.is_a?(Integer))
|
82
|
+
# raise ArgumentError, ":range, if given, must be a range of ( nil|Integer..nil|Integer), got '#{range}'"
|
83
|
+
# end
|
84
|
+
|
85
|
+
raise ArgumentError, 'No block given.' unless block.is_a? Proc
|
86
|
+
|
87
|
+
inclusive = true unless exclusive
|
88
|
+
if inclusive && exclusive
|
89
|
+
raise ArgumentError,
|
90
|
+
"Either :inclusive or :exclusive must remain falsey, got '#{inclusive}' and '#{exclusive}'"
|
91
|
+
end
|
92
|
+
|
93
|
+
index = find_index { |obj| block.call(obj) }
|
94
|
+
|
95
|
+
self[((inclusive ? index : index + 1)..)]
|
96
|
+
end
|
53
97
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patching the Ruby class DateTime
|
4
|
+
class DateTime
|
5
|
+
# based on the fact that sunday is 'wday 0' plus that trading week starts
|
6
|
+
# sunday 0:00 (as trading starts sunday 5pm CT to fit tokyo monday morning)
|
7
|
+
def to_seconds_since_sunday_morning
|
8
|
+
wday * 86_400 + hour * 3600 + min * 60 + sec
|
9
|
+
end
|
10
|
+
|
11
|
+
alias to_sssm to_seconds_since_sunday_morning
|
12
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Monkey patching the Ruby Core class Hash
|
4
|
+
class Hash
|
3
5
|
def keys_to_sym
|
4
|
-
|
6
|
+
each_key do |key|
|
5
7
|
case self[key].class.to_s
|
6
|
-
when
|
8
|
+
when 'Hash'
|
7
9
|
self[key].keys_to_sym
|
8
|
-
when
|
9
|
-
self[key].map {|el| el.is_a?(Hash) ? el.keys_to_sym : el}
|
10
|
+
when 'Array'
|
11
|
+
self[key].map { |el| el.is_a?(Hash) ? el.keys_to_sym : el }
|
10
12
|
end
|
11
|
-
self[key.to_sym] =
|
13
|
+
self[key.to_sym] = delete(key)
|
12
14
|
end
|
13
15
|
self
|
14
16
|
end
|
@@ -1,40 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Top level comment missing!
|
1
4
|
module Cotcube
|
5
|
+
# noinspection ALL
|
2
6
|
module Helpers
|
3
7
|
def keystroke(quit: false)
|
4
8
|
begin
|
5
9
|
# save previous state of stty
|
6
10
|
old_state = `stty -g`
|
7
11
|
# disable echoing and enable raw (not having to press enter)
|
8
|
-
system
|
9
|
-
c =
|
12
|
+
system 'stty raw -echo'
|
13
|
+
c = $stdin.getc.chr rescue '_' # rubocop:disable Style/RescueModifier
|
10
14
|
# gather next two characters of special keys
|
11
|
-
if
|
12
|
-
extra_thread = Thread.new
|
13
|
-
c
|
14
|
-
c
|
15
|
-
|
15
|
+
if c == "\e"
|
16
|
+
extra_thread = Thread.new do
|
17
|
+
c += $stdin.getc.chr
|
18
|
+
c += $stdin.getc.chr
|
19
|
+
end
|
16
20
|
# wait just long enough for special keys to get swallowed
|
17
21
|
extra_thread.join(0.00001)
|
18
22
|
# kill thread so not-so-long special keys don't wait on getc
|
19
23
|
extra_thread.kill
|
20
24
|
end
|
21
|
-
rescue =>
|
22
|
-
puts "#{
|
23
|
-
puts
|
25
|
+
rescue StandardError => e
|
26
|
+
puts "#{e.class}: #{e.message}"
|
27
|
+
puts e.backtrace
|
24
28
|
ensure
|
25
29
|
# restore previous state of stty
|
26
30
|
system "stty #{old_state}"
|
27
31
|
end
|
28
|
-
c.each_byte do |x|
|
32
|
+
c.each_byte do |x| # rubocop:disable Lint/UnreachableLoop
|
29
33
|
case x
|
30
34
|
when 3
|
31
|
-
puts
|
35
|
+
puts 'Strg-C captured, exiting...'
|
32
36
|
quit ? exit : (return true)
|
33
37
|
when 13
|
34
|
-
return
|
38
|
+
return '_return_'
|
35
39
|
when 27
|
36
|
-
puts
|
37
|
-
return
|
40
|
+
puts 'ESCAPE gathered'
|
41
|
+
return '_esc_'
|
38
42
|
else
|
39
43
|
return c
|
40
44
|
end
|
@@ -1,30 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TODO: Missing top level documentation!
|
1
4
|
module Cotcube
|
5
|
+
# TODO: Missing top level documentation!
|
2
6
|
module Helpers
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
result = Parallel.map(ary, in_processes: processes) {|u| v = yield(u)}
|
7
|
+
def parallelize(ary, processes: 1, threads: 1, progress: '', &block)
|
8
|
+
chunks = []
|
9
|
+
if [0, 1].include? processes
|
10
|
+
result = Parallel.map(ary, in_threads: threads, &block)
|
11
|
+
elsif [0, 1].include? threads
|
12
|
+
result = Parallel.map(ary, in_processes: processes, &block)
|
10
13
|
else
|
11
|
-
ary.each_slice(threads) {|chunk| chunks << chunk }
|
12
|
-
if progress ==
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
yield(unit)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
14
|
+
ary.each_slice(threads) { |chunk| chunks << chunk }
|
15
|
+
result = if progress == ''
|
16
|
+
Parallel.map(chunks, in_processes: processes) do |chunk|
|
17
|
+
Parallel.map(chunk, in_threads: threads, &block)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
Parallel.map(chunks, progress: progress, in_processes: processes) do |chunk|
|
21
|
+
Parallel.map(chunk, in_threads: threads, &block)
|
22
|
+
end
|
23
|
+
end
|
25
24
|
end
|
26
25
|
result
|
27
26
|
end
|
28
|
-
|
29
27
|
end
|
30
28
|
end
|
@@ -1,52 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patching the Ruby Core class Range
|
1
4
|
class Range
|
2
|
-
def to_time_intervals(
|
5
|
+
def to_time_intervals(step:,
|
6
|
+
timezone: Time.find_zone('America/Chicago'),
|
7
|
+
# ranges: nil,
|
8
|
+
debug: false)
|
9
|
+
unless step.is_a? ActiveSupport::Duration
|
10
|
+
raise ArgumentError,
|
11
|
+
":step must be a 'ActiveSupport::Duration', like '15.minutes', but '#{step}' is a '#{step.class}'"
|
12
|
+
end
|
13
|
+
unless [15.minutes, 60.minutes, 1.hour, 1.day].include? step
|
14
|
+
raise ArgumentError, 'Sorry, currently supporting only 15.minutes, 1.hour, 1.day as :step'
|
15
|
+
end
|
3
16
|
|
4
|
-
|
17
|
+
valid_classes = [ActiveSupport::TimeWithZone, Time, Date, DateTime]
|
18
|
+
unless timezone.is_a? ActiveSupport::TimeZone
|
19
|
+
raise "Expecting 'ActiveSupport::TimeZone' for :timezone, got '#{timezone.class}"
|
20
|
+
end
|
5
21
|
|
6
|
-
valid_classes = [ ActiveSupport::TimeWithZone, Time, Date, DateTime ]
|
7
|
-
raise "Expecting 'ActiveSupport::TimeZone' for :timezone, got '#{timezone.class}" unless timezone.is_a? ActiveSupport::TimeZone
|
8
22
|
starting = self.begin
|
9
23
|
ending = self.end
|
10
24
|
starting = timezone.parse(starting) if starting.is_a? String
|
11
25
|
ending = timezone.parse(ending) if ending.is_a? String
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
26
|
+
unless valid_classes.include? starting.class
|
27
|
+
raise ArgumentError,
|
28
|
+
":self.begin seems not to be proper time value: #{starting} is a #{starting.class}"
|
29
|
+
end
|
30
|
+
unless valid_classes.include? ending.class
|
31
|
+
raise ArgumentError,
|
32
|
+
":self.end seems not to be proper time value: #{ending} is a #{ending.class}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# here sub-day and super-day need to be distinguished, as they react differently to daylight time
|
36
|
+
# for super-day, just return an array containing all calendar days
|
37
|
+
if step.to_i >= 1.day
|
38
|
+
(starting.to_date..ending.to_date).to_a.map(&:to_datetime)
|
39
|
+
else
|
40
|
+
|
41
|
+
# sub-day is checked for DST
|
42
|
+
# noinspection RubyNilAnalysis
|
43
|
+
actual_starting = starting.to_time.to_i
|
44
|
+
actual_ending = ending.to_time.to_i
|
45
|
+
actual_ending -= 3600 if starting.dst? && (not ending.dst?)
|
46
|
+
actual_ending += 3600 if ending.dst? && (not starting.dst?)
|
47
|
+
|
48
|
+
##### The following is the actual big magic line, as it creates the raw target array:
|
49
|
+
#
|
50
|
+
result = (actual_starting..actual_ending).step(step).to_a.map { |x| timezone.at(x) }
|
51
|
+
#
|
52
|
+
# ###################<3##
|
53
|
+
|
54
|
+
# It should probably relocated to Cotcube::Bardata
|
55
|
+
# NOTE: In this current version 12 belongs to it succeeding hour
|
56
|
+
# i.e. 12am is right before 1am and 12pm right before 1pm
|
57
|
+
convert_to_sec_since = lambda do |clocking|
|
58
|
+
from_src, to_src = clocking.split(' - ')
|
59
|
+
regex = /^(?<hour>\d+):(?<minute>\d+)(?<morning>[pa]).?m.*/
|
60
|
+
|
61
|
+
from = from_src.match(regex)
|
62
|
+
to = to_src.match(regex)
|
63
|
+
|
64
|
+
from_i = from[:hour].to_i * 3600 + from[:minute].to_i * 60 + (from[:morning] == 'a' ? 2 : 1) * 12 * 3600
|
65
|
+
to_i = to[:hour].to_i * 3600 + to[:minute].to_i * 60 + (to[:morning] == 'a' ? 2 : 3) * 12 * 3600
|
66
|
+
|
67
|
+
(0...5).to_a.map { |i| [from_i + i * 24 * 3600, to_i + i * 24 * 3600] }
|
47
68
|
end
|
69
|
+
convert_to_sec_since.call('9:00a.m - 5:00p.m.')
|
70
|
+
|
71
|
+
# ranges ||= [
|
72
|
+
# 61_200...144_000, # Sun 5pm .. Mon 4pm
|
73
|
+
# 147_600...230_400, # Mon 5pm .. Tue 4pm
|
74
|
+
# 234_000...316_800, # ...
|
75
|
+
# 320_400...403_200,
|
76
|
+
# 406_800...489_600
|
77
|
+
# ]
|
78
|
+
|
79
|
+
# if there was a change towards daylight saving time, subtract 1 hour, otherwise add 1 hour
|
80
|
+
result.map! do |time|
|
81
|
+
print "#{time}\t" if debug
|
82
|
+
if (not starting.dst?) && time.dst?
|
83
|
+
time -= 3600
|
84
|
+
print "Time reduced (not starting_DST, but current\t" if debug
|
85
|
+
elsif starting.dst? && (not time.dst?)
|
86
|
+
time += 3600
|
87
|
+
print "Time extended (starting DST, but not current\t" if debug
|
88
|
+
end
|
89
|
+
puts "#{time} " if debug
|
90
|
+
time
|
91
|
+
end
|
92
|
+
|
93
|
+
result # .select_within(ranges: ranges) { |x| x.to_datetime.to_seconds_since_sunday_morning }
|
48
94
|
end
|
49
|
-
return result if ranges.empty?
|
50
|
-
result.select{|x| ranges.map{|r| r.include? seconds_since_sunday_morning.call(x)}.reduce(:|) }
|
51
95
|
end
|
52
96
|
end
|
@@ -1,39 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Cotcube
|
4
|
+
# Missing top level documentation
|
2
5
|
module Helpers
|
3
|
-
def reduce(bars
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
6
|
+
def reduce(bars:, to: nil, date_alike: :datetime, &block)
|
7
|
+
case to
|
8
|
+
when :days
|
9
|
+
terminators = %i[last daily beginning_of_day]
|
10
|
+
block = proc { |c, b| c[:day] == b[:day] } unless block_given?
|
11
|
+
when :hours
|
12
|
+
terminators = %i[first hours beginning_of_hour]
|
13
|
+
block = proc { |c, b| c[:day] == b[:day] and c[:datetime].hour == b[:datetime].hour } unless block_given?
|
14
|
+
when :weeks
|
15
|
+
terminators = %i[first weeks beginning_of_week]
|
16
|
+
block = proc { |a, b| a[:datetime].to_datetime.cweek == b[:datetime].to_datetime.cweek } unless block_given?
|
17
|
+
when :months
|
18
|
+
terminators = %i[first months beginning_of_month]
|
19
|
+
block = proc { |a, b| a[:datetime].to_datetime.month == b[:datetime].to_datetime.month } unless block_given?
|
20
|
+
else
|
21
|
+
raise ArgumentError, 'Currently supported are reductions to :hours, :days, :weeks, :months '
|
22
|
+
end
|
23
|
+
determine_date_alike = ->(ary) { ary.send(terminators.first)[date_alike].send(terminators.last) }
|
24
|
+
make_new_bar = lambda do |ary, _date = nil|
|
25
|
+
result = {
|
26
|
+
contract: ary.first[:contract],
|
27
|
+
symbol: ary.first[:symbol],
|
28
|
+
datetime: determine_date_alike.call(ary),
|
29
|
+
day: ary.first[:day],
|
30
|
+
open: ary.first[:open],
|
31
|
+
high: ary.map { |x| x[:high] }.max,
|
32
|
+
low: ary.map { |x| x[:low] }.min,
|
33
|
+
close: ary.last[:close],
|
34
|
+
volume: ary.map { |x| x[:volume] }.reduce(:+),
|
35
|
+
type: terminators[1]
|
36
|
+
}
|
37
|
+
result.map { |k, v| result.delete(k) if v.nil? }
|
38
|
+
result
|
26
39
|
end
|
27
|
-
collector = [
|
28
|
-
final = [
|
40
|
+
collector = []
|
41
|
+
final = []
|
29
42
|
bars.each do |bar|
|
30
|
-
if collector.empty?
|
31
|
-
|
32
|
-
|
43
|
+
if collector.empty? || block.call(collector.last, bar)
|
44
|
+
collector << bar
|
45
|
+
else
|
33
46
|
new_bar = make_new_bar.call(collector)
|
34
|
-
|
35
|
-
|
36
|
-
|
47
|
+
final << new_bar
|
48
|
+
collector = [bar]
|
49
|
+
end
|
37
50
|
end
|
38
51
|
new_bar = make_new_bar.call(collector)
|
39
52
|
final << new_bar
|
@@ -41,4 +54,3 @@ module Cotcube
|
|
41
54
|
end
|
42
55
|
end
|
43
56
|
end
|
44
|
-
|
@@ -1,9 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patching the Ruby Core class String
|
1
4
|
class String
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
# ...
|
6
|
+
def valid_json?
|
7
|
+
JSON.parse(self)
|
8
|
+
true
|
9
|
+
rescue JSON::ParserError
|
10
|
+
false
|
7
11
|
end
|
8
|
-
end
|
9
12
|
|
13
|
+
alias is_valid_json? valid_json?
|
14
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# ...
|
3
4
|
module Cotcube
|
5
|
+
# ...
|
4
6
|
module Helpers
|
5
7
|
# sub (should be 'subpattern', but too long) is for use in case / when statements
|
6
8
|
# it returns a lambda, that checks the case'd expression for matching subpattern
|
7
9
|
# based on the the giving minimum. E.g. 'a', 'ab' .. 'abcd' will match sub(1){'abcd'}
|
8
|
-
# but only 'abc' and 'abcd' will match sub(3){'abcd'}
|
10
|
+
# but only 'abc' and 'abcd' will match sub(3){'abcd'}.:
|
9
11
|
#
|
10
12
|
# The recommended use within evaluating user input, where abbreviation of incoming commands
|
11
13
|
# is desirable (h for hoover and hyper, what will translate to sub(2){'hoover'} and sub(2){hyper})
|
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.1.
|
4
|
+
version: 0.1.6
|
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:
|
11
|
+
date: 2021-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -82,6 +82,7 @@ files:
|
|
82
82
|
- cotcube-helpers.gemspec
|
83
83
|
- lib/cotcube-helpers.rb
|
84
84
|
- lib/cotcube-helpers/array_ext.rb
|
85
|
+
- lib/cotcube-helpers/datetime_ext.rb
|
85
86
|
- lib/cotcube-helpers/enum_ext.rb
|
86
87
|
- lib/cotcube-helpers/hash_ext.rb
|
87
88
|
- lib/cotcube-helpers/input.rb
|