cotcube-helpers 0.1.3 → 0.1.6

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