cotcube-helpers 0.1.1 → 0.1.5.1

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: 819dae89b6428b3cec4226d40f4afa3b790108b2e7a0f8ff9784b49532a894e9
4
- data.tar.gz: 2e4823aa3a4f86efa8d94ff07be306cb991a7bd08504cae0209025127e67d472
3
+ metadata.gz: e3d088e43fd870ff67a238501fd680f1c5688c7108bc90ace92e01cd8c33627b
4
+ data.tar.gz: 72b07a1f0e502a29e9cb818d62a8d398fe5e094efade358300eab4578b64ceae
5
5
  SHA512:
6
- metadata.gz: 46647ed87173997191a22b05072e0ce4b53bba9be4a222d86a92605bf35d9c3d4d33ed0381c6ab3e46106e21851d10f4b9b3b178136bf780bdcf89ce055cc469
7
- data.tar.gz: 7acdf173d676c7db61e6e2a1aa3111b22ae00c55768611d7355b4f13216c225f9e5ef9204a23796694b95d21d955e7a53e72d3b62b390e3735786aceb0b67801
6
+ metadata.gz: 7b92ec6b178328bd6944f61828848d1d1c4aa164e9a3680054345189503d237a809ea082441eeab476ff693eeebdf5bf27bec300b3606af589598da946ce1c0a
7
+ data.tar.gz: 00d63e82c5ed3cfcc9986f4ec3222e2282a692ed0ad54602cb4e212b9285d8c51182d9c87a4e8b9672d2f305409b6f01d27437b73aa40df6872a35e4ea962b8e
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,23 @@
1
+ ## 0.1.5.1 (January 02, 2021)
2
+ - Hotfixing parallelize
3
+
4
+ ## 0.1.5 (January 02, 2021)
5
+ - applied new datetime helper to Range#to_time_intervals
6
+ - added new DateTime extension, containing 'to_seconds_since_sunday_morning'
7
+ - added #select_within to array_ext
8
+
9
+ ## 0.1.4 (December 27, 2020)
10
+ - applied cops
11
+ - added README for reduce; minor changes
12
+
13
+ ## 0.1.3 (December 22, 2020)
14
+ - added .reduce(bars: , to: ,*args, &block) to reduce a series of bars to a higher timeframe (though only 1hour and 1day are supported yet)
15
+
16
+ ## 0.1.2 (December 21, 2020)
17
+ - minor changes
18
+ - minor fix to parallelize and application of positional arguments
19
+ - added license and README
20
+
1
21
  ## 0.1.1 (December 21, 2020)
2
22
 
3
23
 
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
-
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Benjamin L. Tischendorf
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,107 @@
1
+ ## Cotcube::Helpers
2
+
3
+ Just a collection of helpers not fitting into other repositories of the Cotcube suite. Also usable aside of Cotcube. Where appropriate, these are provided as core\_extensions, otherwise within the model Cotcube::Helpers
4
+
5
+ #### Extending Array
6
+
7
+ ##### compact\_or\_nil
8
+
9
+ Returns the array compacted--or just nil, when the result is empty?
10
+
11
+ ##### split\_by(attr)
12
+
13
+ This is a rather old implementation. Most probably something I developed before I met Array#group\_by.
14
+
15
+ ##### pairwise(&block) / one_by_one(&block)
16
+
17
+ Yields block on each consecutive pair of the array, hence returning array.size-1 results.
18
+
19
+ ##### triplewise(&block)
20
+
21
+ Yields block on each consecutive triple of the array, hence returning array.size-2 results.
22
+
23
+ #### Extending Enumerator
24
+
25
+ ##### shy\_peek
26
+
27
+ Peeks into the successor without raising if there is none available--returning nil instead.
28
+
29
+ #### Extending Hash
30
+
31
+ ##### keys\_to\_sym
32
+
33
+ Transforms the keys of a Hash (recursivly for subsequent Arrays and Hashes) into symbols.
34
+
35
+ #### Extending Range
36
+
37
+ ##### to\_time\_intervals(timezone: Time.find\_zone('America/Chicago'), step:, ranges: nil)
38
+
39
+ Uses the range of *date alikes* delivered to create an Array of time periods of length :step
40
+ (which is a ActiveSupport::Duration, e.g. 5.minutes or 1.hour).
41
+
42
+ When the step is sub-day, the periods are cleared for DST-changes.
43
+
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
47
+ of ranges, providing seconds starting at Sunday morning midnight. Here is the default as example:
48
+
49
+ ```
50
+ ranges ||= [
51
+ 61200...144000, # Sun 5pm .. Mon 4pm
52
+ 147600...230400, # Mon 5pm .. Tue 4pm
53
+ 234000...316800, # ...
54
+ 320400...403300,
55
+ 406800...489600
56
+ ]
57
+ ```
58
+
59
+ #### Extending String
60
+
61
+ ##### is\_valid\_json?
62
+
63
+ #### Subpattern
64
+
65
+ ##### sub(minimum: 1) { [:hyper, :mega] }
66
+
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'}
70
+ but only 'abc' and 'abcd' will match sub(3){'abcd'}
71
+
72
+ It is developed for evaluating user input, where abbreviation of incoming commands
73
+ is desirable (h for hoover and hyper, what will translate to sub(2){'hoover'} and sub(2){hyper})
74
+
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).
78
+
79
+ *Paired with keystroke() it allows an easy build of an inputhandler.*
80
+
81
+ #### SimpleOutput
82
+
83
+ This is a very simple class, that mocks a more complex output handler. It provides puts, print,
84
+ puts! and print!. The actual OutputHandler is another project that needs to be rewritten. Once
85
+ that is done, SimpleOutput will be replaced. The new OutputHandler is a tool to handle information
86
+ flow like logs, to pause and continue output.
87
+
88
+ #### Input
89
+
90
+ ##### keystroke(quit: false)
91
+
92
+ A version of STDIN.gets, that does not wait for pressing 'enter' but instantly returns the content
93
+ of the keystroke.
94
+
95
+ *Paired with subpattern it allows an easy build of an InputHandler.*
96
+
97
+ #### Parallelize
98
+
99
+ ##### parallelize(ary, processes: 1, threads: 1, progress: "", &block)
100
+
101
+ Based on https://github.com/grosser/parallel, it is a quite convenient way to parallelize tasks.
102
+
103
+ #### Reduce
104
+
105
+ ##### reduce(bars: , to: nil, datelike: :datetime, &block)
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
1
+ 0.1.5.1
@@ -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,33 +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, opts = {}, &block)
5
- processes = opts[:processes].nil? ? 1 : opts[:processes]
6
- threads_per_process = opts[:threads ].nil? ? 1 : opts[:threads]
7
- progress = opts[:progress ].nil? ? "" : opts[:progress]
8
- chunks = []
9
- if processes == 0 or processes == 1
10
- r = Parallel.map(ary, in_threads: threads_per_process) {|u| v = yield(u); v}
11
- elsif [0,1].include?(threads_per_process)
12
- r = 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)
13
13
  else
14
- ary.each_slice(threads_per_process) {|chunk| chunks << chunk }
15
- if progress == ""
16
- r = Parallel.map(chunks, :in_processes => processes) do |chunk|
17
- Parallel.map(chunk, in_threads: threads_per_process) do |unit|
18
- yield(unit)
19
- end
20
- end
21
- else
22
- r = Parallel.map(ary, :progress => progress, :in_processes => processes) do |chunk|
23
- Parallel.map(chunk, in_threads: threads_per_process) do |unit|
24
- yield(unit)
25
- end
26
- end
27
- 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
28
24
  end
29
- return r
25
+ result
30
26
  end
31
-
32
27
  end
33
28
  end
@@ -1,52 +1,88 @@
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
3
10
 
4
- raise ArgumentError, ":step must be a 'ActiveSupport::Duration', like '15.minutes', but '#{step}' is a '#{step.class}'" unless step.is_a? ActiveSupport::Duration
11
+ valid_classes = [ActiveSupport::TimeWithZone, Time, Date, DateTime]
12
+ unless timezone.is_a? ActiveSupport::TimeZone
13
+ raise "Expecting 'ActiveSupport::TimeZone' for :timezone, got '#{timezone.class}"
14
+ end
5
15
 
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
16
  starting = self.begin
9
17
  ending = self.end
10
18
  starting = timezone.parse(starting) if starting.is_a? String
11
19
  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
20
+ unless valid_classes.include? starting.class
21
+ raise ArgumentError,
22
+ ":self.begin seems not to be proper time value: #{starting} is a #{starting.class}"
23
+ end
24
+ unless valid_classes.include? ending.class
25
+ raise ArgumentError,
26
+ ":self.end seems not to be proper time value: #{ending} is a #{ending.class}"
27
+ end
14
28
 
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)}
29
+ ##### The following is the actual big magic line, as it creates the raw target array:
18
30
  #
19
- ####################<3
20
-
31
+ result = (starting.to_time.to_i..ending.to_time.to_i).step(step).to_a.map { |x| timezone.at(x) }
32
+ #
33
+ # ###################<3##
21
34
 
22
35
  # with step.to_i >= 86400 we are risking stuff like 25.hours to return bogus
23
36
  # also notice: When using this with swaps, you will loose 1 hour (#f**k_it)
24
37
  #
25
38
  # 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
39
+ if step.to_i >= 86_400
40
+ return result.select do |x|
41
+ (not ranges.nil?) && ranges.empty? ? true : (not [6, 0].include?(x.wday))
42
+ end
43
+ end
27
44
 
28
45
  # sub-day is checked for DST and filtered along provided ranges
46
+ # noinspection RubyNilAnalysis
29
47
  starting_with_dst = result.first.dst?
30
- seconds_since_sunday_morning = lambda {|x| x.wday * 86400 + x.hour * 3600 + x.min * 60 + x.sec}
48
+
49
+ # The following lambda is completely misplaces here.
50
+ # It should probably relocated to Cotcube::Bardata
51
+ # NOTE: In this current version 12 belongs to it succeeding hour
52
+ # i.e. 12am is right before 1am and 12pm right before 1pm
53
+ convert_to_sec_since = lambda do |clocking|
54
+ from_src, to_src = clocking.split(' - ')
55
+ regex = /^(?<hour>\d+):(?<minute>\d+)(?<morning>[pa]).?m.*/
56
+
57
+ from = from_src.match(regex)
58
+ to = to_src.match(regex)
59
+
60
+ from_i = from[:hour].to_i * 3600 + from[:minute].to_i * 60 + (from[:morning] == 'a' ? 2 : 1) * 12 * 3600
61
+ to_i = to[:hour].to_i * 3600 + to[:minute].to_i * 60 + (to[:morning] == 'a' ? 2 : 3) * 12 * 3600
62
+
63
+ (0...5).to_a.map { |i| [from_i + i * 24 * 3600, to_i + i * 24 * 3600] }
64
+ end
65
+ convert_to_sec_since.call('9:00a.m - 5:00p.m.')
66
+
31
67
  ranges ||= [
32
- 61200..143999,
33
- 147600..230399,
34
- 234000..316799,
35
- 320400..403199,
36
- 406800..489599
68
+ 61_200...144_000, # Sun 5pm .. Mon 4pm
69
+ 147_600...230_400, # Mon 5pm .. Tue 4pm
70
+ 234_000...316_800, # ...
71
+ 320_400...403_200,
72
+ 406_800...489_600
37
73
  ]
38
74
 
39
- # if there was a change towards daylight saving time, substract 1 hour, otherwise add 1 hour
75
+ # if there was a change towards daylight saving time, subtract 1 hour, otherwise add 1 hour
40
76
  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
77
+ if (not starting_with_dst) && time.dst?
78
+ time - 3600
79
+ elsif starting_with_dst && (not time.dst?)
80
+ time + 3600
45
81
  else
46
- time
82
+ time
47
83
  end
48
84
  end
49
- return result if ranges.empty?
50
- result.select{|x| ranges.map{|r| r.include? seconds_since_sunday_morning.call(x)}.reduce(:|) }
85
+
86
+ result.select_within(ranges: ranges) { |x| x.to_datetime.to_seconds_since_monday_morning }
51
87
  end
52
88
  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,18 +1,20 @@
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})
12
14
  #
13
15
  # To extend functionality even more, it is possible to send a group of patterns to, like
14
16
  # sub(2){[:hyper,:mega]}, what will respond truthy to "hy" and "meg" but not to "m" or "hypo"
15
- def sub(minimum = 1)
17
+ def sub(minimum: 1)
16
18
  pattern = yield
17
19
  case pattern
18
20
  when String, Symbol, NilClass
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.1
4
+ version: 0.1.5.1
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
@@ -76,15 +76,19 @@ files:
76
76
  - ".irbrc.rb"
77
77
  - CHANGELOG.md
78
78
  - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
79
81
  - VERSION
80
82
  - cotcube-helpers.gemspec
81
83
  - lib/cotcube-helpers.rb
82
84
  - lib/cotcube-helpers/array_ext.rb
85
+ - lib/cotcube-helpers/datetime_ext.rb
83
86
  - lib/cotcube-helpers/enum_ext.rb
84
87
  - lib/cotcube-helpers/hash_ext.rb
85
88
  - lib/cotcube-helpers/input.rb
86
89
  - lib/cotcube-helpers/parallelize.rb
87
90
  - lib/cotcube-helpers/range_ext.rb
91
+ - lib/cotcube-helpers/reduce.rb
88
92
  - lib/cotcube-helpers/simple_output.rb
89
93
  - lib/cotcube-helpers/string_ext.rb
90
94
  - lib/cotcube-helpers/subpattern.rb