cotcube-helpers 0.1.1 → 0.1.5.1

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