cotcube-helpers 0.1.2 → 0.1.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01aae33281c410de2818d90ab99e2125c5841ad2f6148a71c7950eb84bb07c33
4
- data.tar.gz: 1e4e1fbeaa9d7a3b71bff8fd0325f77ab390620ddd933992b6a138db48fbd77d
3
+ metadata.gz: 97c255de9c0dac3cad83de38b0b8e1eb21af6d27bdf751d58dcc013b9044a7bc
4
+ data.tar.gz: 01cb8859f054baf5c87d55d7b718d8261b48428a59e0e728acbb759855526804
5
5
  SHA512:
6
- metadata.gz: 2e15e9005df3ed00b77b90f12d99b67b92838034a2339ff9255b7c4986cbcd7764ed7fc01bf4de5edcd1032bd885d39de4d045529bc34a6826a6e97c9b954cdf
7
- data.tar.gz: edf104056c0a4ea83bf3384a4f5c10ed240f6638e04da12053a84322853d036b1f38bb75ddf96146a195bf2861be9f999bf743a80ff2a14968f5dbdc36b7d2a9
6
+ metadata.gz: 6358bd447301dd4a001b3e4f3ab6e80cd8d2e3073eef1ef9f05fa5c907bf7fd3a19245bd4ea4667bfaebade359e1dc6ea5b24a8f24cf2a8f43bfbb070b6e2567
7
+ data.tar.gz: a8876e8e07211f0648b2e646b721e52912bcf117f5b6d811f744368a86ab5c030a2f7ad6542464486fd6502ce9f7c8c51ff9cc5e04e401f45c9289a69e8eb1f5
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,27 @@
1
+ ## 0.1.5.4 (January 02, 2021)
2
+
3
+
4
+ ## 1.5.1.3 (January 02, 2021)
5
+ - hotfixing the hotfix (hello CI tools, c ya coming)
6
+
7
+ ## 1.5.1.2 (January 02, 2021)
8
+ - hotfix problem in Range.to_time_intervals
9
+
10
+ ## 0.1.5.1 (January 02, 2021)
11
+ - Hotfixing parallelize
12
+
13
+ ## 0.1.5 (January 02, 2021)
14
+ - applied new datetime helper to Range#to_time_intervals
15
+ - added new DateTime extension, containing 'to_seconds_since_sunday_morning'
16
+ - added #select_within to array_ext
17
+
18
+ ## 0.1.4 (December 27, 2020)
19
+ - applied cops
20
+ - added README for reduce; minor changes
21
+
22
+ ## 0.1.3 (December 22, 2020)
23
+ - added .reduce(bars: , to: ,*args, &block) to reduce a series of bars to a higher timeframe (though only 1hour and 1day are supported yet)
24
+
1
25
  ## 0.1.2 (December 21, 2020)
2
26
  - minor changes
3
27
  - minor fix to parallelize and application of positional arguments
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.2
1
+ 0.1.5.4
@@ -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,22 +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
22
  module Cotcube
20
23
  module Helpers
24
+ module_function :sub,
25
+ :parallelize,
26
+ :reduce,
27
+ :keystroke
21
28
 
22
- module_function :sub,
23
- :parallelize,
24
- :keystroke
25
-
26
-
27
-
28
29
  # please not that module_functions of source provided in private files must be published there
29
30
  end
30
31
  end
31
-
@@ -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,28 @@ 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
53
77
  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,87 @@
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:, timezone: Time.find_zone('America/Chicago'), ranges: nil)
6
+ unless step.is_a? ActiveSupport::Duration
7
+ raise ArgumentError,
8
+ ":step must be a 'ActiveSupport::Duration', like '15.minutes', but '#{step}' is a '#{step.class}'"
9
+ end
10
+ raise ArgumentError, "Sorry, currently supporting only 15.minutes, 1.hour, 1.day as :step" unless [15.minutes, 60.minutes, 1.hour, 1.day].include? step
3
11
 
4
- raise ArgumentError, ":step must be a 'ActiveSupport::Duration', like '15.minutes', but '#{step}' is a '#{step.class}'" unless step.is_a? ActiveSupport::Duration
12
+ valid_classes = [ActiveSupport::TimeWithZone, Time, Date, DateTime]
13
+ unless timezone.is_a? ActiveSupport::TimeZone
14
+ raise "Expecting 'ActiveSupport::TimeZone' for :timezone, got '#{timezone.class}"
15
+ end
5
16
 
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
17
  starting = self.begin
9
18
  ending = self.end
10
19
  starting = timezone.parse(starting) if starting.is_a? String
11
20
  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
21
+ unless valid_classes.include? starting.class
22
+ raise ArgumentError,
23
+ ":self.begin seems not to be proper time value: #{starting} is a #{starting.class}"
24
+ end
25
+ unless valid_classes.include? ending.class
26
+ raise ArgumentError,
27
+ ":self.end seems not to be proper time value: #{ending} is a #{ending.class}"
28
+ end
29
+
30
+ # here sub-day and super-day need to be distinguished, as they react differently to daylight time
31
+ # for super-day, just return an array containing all calendar days
32
+ if step.to_i >= 1.day
33
+ return (starting.to_date..ending.to_date).to_a.map{|x| x.to_datetime}
34
+ else
35
+
36
+ ##### The following is the actual big magic line, as it creates the raw target array:
37
+ #
38
+ result = (starting.to_time.to_i..ending.to_time.to_i).step(step).to_a.map { |x| timezone.at(x) }
39
+ #
40
+ # ###################<3##
41
+
42
+
43
+ # sub-day is checked for DST and filtered along provided ranges
44
+ # noinspection RubyNilAnalysis
45
+ starting_with_dst = result.first.dst?
46
+
47
+ # The following lambda is completely misplaces here.
48
+ # It should probably relocated to Cotcube::Bardata
49
+ # NOTE: In this current version 12 belongs to it succeeding hour
50
+ # i.e. 12am is right before 1am and 12pm right before 1pm
51
+ convert_to_sec_since = lambda do |clocking|
52
+ from_src, to_src = clocking.split(' - ')
53
+ regex = /^(?<hour>\d+):(?<minute>\d+)(?<morning>[pa]).?m.*/
54
+
55
+ from = from_src.match(regex)
56
+ to = to_src.match(regex)
57
+
58
+ from_i = from[:hour].to_i * 3600 + from[:minute].to_i * 60 + (from[:morning] == 'a' ? 2 : 1) * 12 * 3600
59
+ to_i = to[:hour].to_i * 3600 + to[:minute].to_i * 60 + (to[:morning] == 'a' ? 2 : 3) * 12 * 3600
60
+
61
+ (0...5).to_a.map { |i| [from_i + i * 24 * 3600, to_i + i * 24 * 3600] }
47
62
  end
63
+ convert_to_sec_since.call('9:00a.m - 5:00p.m.')
64
+
65
+ ranges ||= [
66
+ 61_200...144_000, # Sun 5pm .. Mon 4pm
67
+ 147_600...230_400, # Mon 5pm .. Tue 4pm
68
+ 234_000...316_800, # ...
69
+ 320_400...403_200,
70
+ 406_800...489_600
71
+ ]
72
+
73
+ # if there was a change towards daylight saving time, subtract 1 hour, otherwise add 1 hour
74
+ result.map! do |time|
75
+ if (not starting_with_dst) && time.dst?
76
+ time - 3600
77
+ elsif starting_with_dst && (not time.dst?)
78
+ time + 3600
79
+ else
80
+ time
81
+ end
82
+ end
83
+
84
+ result.select_within(ranges: ranges) { |x| x.to_datetime.to_seconds_since_monday_morning }
48
85
  end
49
- return result if ranges.empty?
50
- result.select{|x| ranges.map{|r| r.include? seconds_since_sunday_morning.call(x)}.reduce(:|) }
51
86
  end
52
87
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ # Missing top level documentation
5
+ module Helpers
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
39
+ end
40
+ collector = []
41
+ final = []
42
+ bars.each do |bar|
43
+ if collector.empty? || block.call(collector.last, bar)
44
+ collector << bar
45
+ else
46
+ new_bar = make_new_bar.call(collector)
47
+ final << new_bar
48
+ collector = [bar]
49
+ end
50
+ end
51
+ new_bar = make_new_bar.call(collector)
52
+ final << new_bar
53
+ final
54
+ end
55
+ end
56
+ end
@@ -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.2
4
+ version: 0.1.5.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: 2020-12-21 00:00:00.000000000 Z
11
+ date: 2021-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -82,11 +82,13 @@ 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
88
89
  - lib/cotcube-helpers/parallelize.rb
89
90
  - lib/cotcube-helpers/range_ext.rb
91
+ - lib/cotcube-helpers/reduce.rb
90
92
  - lib/cotcube-helpers/simple_output.rb
91
93
  - lib/cotcube-helpers/string_ext.rb
92
94
  - lib/cotcube-helpers/subpattern.rb