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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cec4f9d660486e17a4ff9ef38f53ffd1a5089de00ecbcdadb016b80dbc4231f9
4
- data.tar.gz: 90943be69de4e059082d49070741b9689cd714a5a88a4b8ead2dea74ab4f3017
3
+ metadata.gz: 0417adfcb0f782bfa9e783dbd1c673a489fca39ce9a0f14fa965eb47aaa64b11
4
+ data.tar.gz: 1b14e149cc6b1bd437d1413dfa49b461c798868959cf97b164a0b3a2e1f72889
5
5
  SHA512:
6
- metadata.gz: bf6306e1baaadfe9fa6f6b4533d2bfb0c31fc464ffa288368d3e47a22f76c1db7a1c48470aa83970e52752b4a911a7b60f759ee75495df43f093661274c13f0c
7
- data.tar.gz: 6ea7ccc812fd160479f9519532669db81fbf4311fcb6335b841d738bcd81c86e922c994bb005e56c543b4cf0cd3f1d7a4a92db1ad7b52e5ac4d770c6932139cb
6
+ metadata.gz: c2babf733929f4ca0705ac2018b075e3b62df051c4918d01fcc6c11b49a7cad2ee68fd8ef26504edc5678c3057a97092de252a34cec2a953f638cd9262c2e65d
7
+ data.tar.gz: 1d9e367613f5b373c73cada786dee68d4e5ccc965573373b39d6419433a5f8131bc553773279d7ba58f2706a63be474448e15cc1e7bce1da63278ad384abeee0
data/.irbrc.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  def verbose_toggle
2
- irb_context.echo ? irb_context.echo = false : irb_context.echo = true
4
+ irb_context.echo = (irb_context.echo ? false : true)
3
5
  end
4
6
 
5
7
  alias vt verbose_toggle
@@ -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
@@ -1,5 +1,6 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in bitangent.gemspec
4
6
  gemspec
5
-
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) / triplewise(&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. This is
45
- accomplished by the fact, that Sunday is wday==0. If you want to use ranges, just send a an array
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..143999, # Sun 5pm .. Mon 4pm
51
- 147600..230399, # Mon 5pm .. Tue 4pm
52
- 234000..316799, # ...
53
- 320400..403199,
54
- 406800..489599
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
- it returns a lambda, that checks the case'd expression for matching subpattern
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
- The recommended use within evaluating user input, where abbreviation of incoming commands
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 a group of patterns to, like
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
- *paired with keystroke() it allows an easy build of an inputhandler*
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
- *paired with subpattern it allows an easy build of an inputhandler*
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.3
1
+ 0.1.6
@@ -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 = 'https://github.com/donkeybridge/'+ spec.name
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 + '/CHANGELOG.md'
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'
@@ -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/subpattern.rb'
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
- class Array
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(*args, &block)
5
- return nil if self.compact == []
6
- yield self.compact
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
- self.sort_by(&attrib).each do |elem|
15
- if sub.empty? or sub.last[attrib] == elem[attrib]
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 = [ elem ]
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
- alias_method :one_by_one, :pairwise
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,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Monkey patching the Ruby Core class Enumerator
1
4
  class Enumerator
2
5
  def shy_peek
3
6
  begin
4
- ret = self.peek
5
- rescue
7
+ ret = peek
8
+ rescue StandardError
6
9
  ret = nil
7
10
  end
8
11
  ret
9
12
  end
10
13
  end
11
-
@@ -1,14 +1,16 @@
1
- class Hash
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
- self.keys.each do |key|
6
+ each_key do |key|
5
7
  case self[key].class.to_s
6
- when "Hash"
8
+ when 'Hash'
7
9
  self[key].keys_to_sym
8
- when "Array"
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] = self.delete(key)
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 "stty raw -echo"
9
- c = STDIN.getc.chr
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(c=="\e")
12
- extra_thread = Thread.new{
13
- c = c + STDIN.getc.chr
14
- c = c + STDIN.getc.chr
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 => ex
22
- puts "#{ex.class}: #{ex.message}"
23
- puts ex.backtrace
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 "Strg-C captured, exiting..."
35
+ puts 'Strg-C captured, exiting...'
32
36
  quit ? exit : (return true)
33
37
  when 13
34
- return "_return_"
38
+ return '_return_'
35
39
  when 27
36
- puts "ESCAPE gathered"
37
- return "_esc_"
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
- def parallelize(ary, processes: 1, threads: 1, progress: "", &block)
5
- chunks = []
6
- if [0,1].include? processes
7
- result = Parallel.map(ary, in_threads: threads) {|u| v = yield(u); v}
8
- elsif [0,1].include?(threads)
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
- result = Parallel.map(chunks, :in_processes => processes) do |chunk|
14
- Parallel.map(chunk, in_threads: threads) do |unit|
15
- yield(unit)
16
- end
17
- end
18
- else
19
- result = Parallel.map(chunks, :progress => progress, :in_processes => processes) do |chunk|
20
- Parallel.map(chunk, in_threads: threads) do |unit|
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(timezone: Time.find_zone('America/Chicago'), step:, ranges: nil)
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
- raise ArgumentError, ":step must be a 'ActiveSupport::Duration', like '15.minutes', but '#{step}' is a '#{step.class}'" unless step.is_a? ActiveSupport::Duration
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
- raise ArgumentError, ":self.begin seems not to be proper time value: #{starting} is a #{starting.class}" unless valid_classes.include? starting.class
13
- raise ArgumentError, ":self.end seems not to be proper time value: #{ending} is a #{ending.class}" unless valid_classes.include? ending.class
14
-
15
- ##### The following is the actual big magic line:
16
- #
17
- result = (starting.to_time.to_i..ending.to_time.to_i).step(step).to_a.map{|x| timezone.at(x)}
18
- #
19
- ####################<3
20
-
21
-
22
- # with step.to_i >= 86400 we are risking stuff like 25.hours to return bogus
23
- # also notice: When using this with swaps, you will loose 1 hour (#f**k_it)
24
- #
25
- # eventually, for dailies and above, return M-F default, return S-S when forced by empty ranges
26
- return result.select{|x| (not ranges.nil? and ranges.empty?) ? true : (not [6,0].include?(x.wday)) } if step.to_i >= 86400
27
-
28
- # sub-day is checked for DST and filtered along provided ranges
29
- starting_with_dst = result.first.dst?
30
- seconds_since_sunday_morning = lambda {|x| x.wday * 86400 + x.hour * 3600 + x.min * 60 + x.sec}
31
- ranges ||= [
32
- 61200..143999, # Sun 5pm .. Mon 4pm
33
- 147600..230399, # Mon 5pm .. Tue 4pm
34
- 234000..316799, # ...
35
- 320400..403199,
36
- 406800..489599
37
- ]
38
-
39
- # if there was a change towards daylight saving time, substract 1 hour, otherwise add 1 hour
40
- result.map! do |time|
41
- if not starting_with_dst and time.dst?
42
- time - 3600
43
- elsif starting_with_dst and not time.dst?
44
- time + 3600
45
- else
46
- time
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: , to: nil, datelike: :datetime, &block)
4
- terminators = case to
5
- when 1.day
6
- [:last, :beginning_of_day]
7
- when 1.hour
8
- [:first, :beginning_of_hour]
9
- else
10
- raise ArgumentError, "Currently supported are reductions to '1.hour' and '1.day'"
11
- end
12
- determine_datelike = lambda {|ary| ary.send(terminators.first)[datelike].send(terminators.last) }
13
- make_new_bar = lambda do |ary, date = nil|
14
- result = {
15
- symbol: ary.first[:symbol],
16
- datetime: determine_datelike.call(ary),
17
- day: ary.first[:day],
18
- open: ary.first[:open],
19
- high: ary.map{|x| x[:high]}.max,
20
- low: ary.map{|x| x[:low]}.min,
21
- close: ary.last[:close],
22
- volume: ary.map{|x| x[:volume]}.reduce(:+)
23
- }
24
- result.map{|k,v| result.delete(k) if v.nil?}
25
- result
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? or block.call(collector.last, bar)
31
- collector << bar
32
- else
43
+ if collector.empty? || block.call(collector.last, bar)
44
+ collector << bar
45
+ else
33
46
  new_bar = make_new_bar.call(collector)
34
- final << new_bar
35
- collector = [ bar ]
36
- end
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
- def is_valid_json?
3
- JSON.parse(self)
4
- return true
5
- rescue JSON::ParserError => e
6
- return false
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.3
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: 2020-12-22 00:00:00.000000000 Z
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