runby_pace 0.2.50.111 → 0.2.55

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.
Files changed (43) hide show
  1. checksums.yaml +13 -5
  2. data/.travis.yml +1 -5
  3. data/Gemfile +0 -4
  4. data/README.md +5 -16
  5. data/Rakefile +6 -40
  6. data/bin/_guard-core +16 -17
  7. data/bin/guard +16 -17
  8. data/lib/runby_pace.rb +1 -4
  9. data/lib/runby_pace/{pace_calculator.rb → pace_data.rb} +13 -29
  10. data/lib/runby_pace/pace_range.rb +9 -27
  11. data/lib/runby_pace/pace_time.rb +110 -0
  12. data/lib/runby_pace/run_type.rb +4 -12
  13. data/lib/runby_pace/run_types/all_run_types.template +4 -6
  14. data/lib/runby_pace/run_types/easy_run.rb +10 -31
  15. data/lib/runby_pace/run_types/find_divisor.rb +17 -13
  16. data/lib/runby_pace/run_types/long_run.rb +10 -32
  17. data/lib/runby_pace/version.rb +2 -17
  18. data/runby_pace.gemspec +5 -4
  19. metadata +18 -41
  20. data/.rubocop.yml +0 -10
  21. data/bin/runbypace +0 -15
  22. data/lib/runby_pace/cli/cli.rb +0 -127
  23. data/lib/runby_pace/cli/config.rb +0 -82
  24. data/lib/runby_pace/distance.rb +0 -135
  25. data/lib/runby_pace/distance_unit.rb +0 -89
  26. data/lib/runby_pace/golden_pace_set.rb +0 -50
  27. data/lib/runby_pace/pace.rb +0 -152
  28. data/lib/runby_pace/run_math.rb +0 -14
  29. data/lib/runby_pace/run_types/distance_run.rb +0 -55
  30. data/lib/runby_pace/run_types/fast_tempo_run.rb +0 -23
  31. data/lib/runby_pace/run_types/five_kilometer_race_run.rb +0 -22
  32. data/lib/runby_pace/run_types/mile_race_run.rb +0 -24
  33. data/lib/runby_pace/run_types/slow_tempo_run.rb +0 -22
  34. data/lib/runby_pace/run_types/tempo_run.rb +0 -54
  35. data/lib/runby_pace/run_types/ten_kilometer_race_run.rb +0 -23
  36. data/lib/runby_pace/runby_range.rb +0 -22
  37. data/lib/runby_pace/runby_time.rb +0 -138
  38. data/lib/runby_pace/runby_time_parser.rb +0 -80
  39. data/lib/runby_pace/speed.rb +0 -97
  40. data/lib/runby_pace/speed_range.rb +0 -30
  41. data/lib/runby_pace/utility/parameter_sanitizer.rb +0 -29
  42. data/lib/runby_pace/version.seed +0 -5
  43. data/misc/runbypace_logo.png +0 -0
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Runby
4
- # Base class for ranges of Runby data, e.g. PaceRange, SpeedRange, ...
5
- class RunbyRange
6
- attr_reader :fast, :slow
7
-
8
- def initialize
9
- @fast = nil
10
- @slow = nil
11
- raise 'RunbyRange is a base class for PaceRange and SpeedRange. Instantiate one of them instead.'
12
- end
13
-
14
- def to_s(format: :short)
15
- if @fast == @slow
16
- @fast.to_s(format: format)
17
- else
18
- "#{@fast}-#{@slow}"
19
- end
20
- end
21
- end
22
- end
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Runby
4
- # Represents a human-readable time in the format MM:ss
5
- class RunbyTime
6
- include Comparable
7
-
8
- attr_reader :time_s, :hours_part, :minutes_part, :seconds_part
9
-
10
- def self.new(time)
11
- return time if time.is_a? RunbyTime
12
- return RunbyTime.parse time if time.is_a?(String) || time.is_a?(Symbol)
13
- return from_minutes(time) if time.is_a? Numeric
14
- super
15
- end
16
-
17
- def initialize(time)
18
- init_from_parts time if time.is_a? RunbyTimeParser::TimeParts
19
- freeze
20
- end
21
-
22
- # @param [numeric] total_seconds
23
- def self.from_seconds(total_seconds)
24
- hours = total_seconds.abs.to_i / 60 / 60
25
- minutes = (total_seconds.abs.to_i / 60) % 60
26
- seconds = total_seconds.abs.to_i % 60
27
- if hours.positive?
28
- RunbyTime.new format('%d:%02d:%02d', hours, minutes, seconds)
29
- else
30
- RunbyTime.new format('%02d:%02d', minutes, seconds)
31
- end
32
- end
33
-
34
- # @param [numeric] total_minutes
35
- def self.from_minutes(total_minutes)
36
- from_seconds(total_minutes * 60.0)
37
- end
38
-
39
- # @param [numeric] total_hours
40
- def self.from_hours(total_hours)
41
- from_seconds(total_hours * 60.0 * 60.0)
42
- end
43
-
44
- def self.parse(str)
45
- RunbyTimeParser.parse str
46
- end
47
-
48
- def self.try_parse(str, is_five_k = false)
49
- time, error_message, warning_message = nil
50
- begin
51
- time = parse str
52
- rescue StandardError => ex
53
- error_message = "#{ex.message} (#{str})"
54
- end
55
- warning_message = check_5k_sanity(time) if !time.nil? && is_five_k
56
- { time: time, error: error_message, warning: warning_message }
57
- end
58
-
59
- def self.check_5k_sanity(time)
60
- return unless time.is_a? RunbyTime
61
- return '5K times of less than 14:00 are unlikely' if time.minutes_part < 14
62
- return '5K times of greater than 42:00 are not fully supported' if time.total_seconds > (42 * 60)
63
- end
64
-
65
- def to_s
66
- @time_s
67
- end
68
-
69
- def total_hours
70
- @hours_part + (@minutes_part / 60.0) + (@seconds_part / 60.0 / 60.0)
71
- end
72
-
73
- def total_seconds
74
- @hours_part * 60 * 60 + @minutes_part * 60 + @seconds_part
75
- end
76
-
77
- def total_minutes
78
- @hours_part * 60 + @minutes_part + (@seconds_part / 60.0)
79
- end
80
-
81
- # @param [RunbyTime] other
82
- def -(other)
83
- raise "Cannot subtract #{other.class} from a Runby::RunbyTime" unless other.is_a?(RunbyTime)
84
- RunbyTime.from_seconds(total_seconds - other.total_seconds)
85
- end
86
-
87
- # @param [RunbyTime] other
88
- def +(other)
89
- raise "Cannot add Runby::RunbyTime to a #{other.class}" unless other.is_a?(RunbyTime)
90
- RunbyTime.from_seconds(total_seconds + other.total_seconds)
91
- end
92
-
93
- # @param [Numeric] other
94
- # @return [RunbyTime]
95
- def *(other)
96
- raise "Cannot multiply Runby::RunbyTime with a #{other.class}" unless other.is_a?(Numeric)
97
- RunbyTime.from_minutes(total_minutes * other)
98
- end
99
-
100
- # @param [RunbyTime, Numeric] other
101
- # @return [Numeric, RunbyTime]
102
- def /(other)
103
- raise "Cannot divide Runby::RunbyTime by #{other.class}" unless other.is_a?(RunbyTime) || other.is_a?(Numeric)
104
- case other
105
- when RunbyTime
106
- total_seconds / other.total_seconds
107
- when Numeric
108
- RunbyTime.from_seconds(total_seconds / other)
109
- end
110
- end
111
-
112
- def <=>(other)
113
- raise "Unable to compare Runby::RunbyTime to #{other.class}(#{other})" unless [RunbyTime, String].include? other.class
114
- if other.is_a? RunbyTime
115
- total_seconds <=> other.total_seconds
116
- elsif other.is_a? String
117
- return 0 if @time_s == other
118
- total_seconds <=> RunbyTime.parse(other).total_seconds
119
- end
120
- end
121
-
122
- def almost_equals?(other_time, tolerance_time = '00:01')
123
- other_time = RunbyTime.new(other_time) if other_time.is_a?(String)
124
- tolerance = RunbyTime.new(tolerance_time)
125
- self >= (other_time - tolerance) && self <= (other_time + tolerance)
126
- end
127
-
128
- private
129
-
130
- # @param [RunbyTimeParser::TimeParts] parts
131
- def init_from_parts(parts)
132
- @time_s = parts.format
133
- @hours_part = parts[:hours]
134
- @minutes_part = parts[:minutes]
135
- @seconds_part = parts[:seconds]
136
- end
137
- end
138
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Runby
4
- # Helper class which parses strings and returns new RunbyTime(s)
5
- class RunbyTimeParser
6
- def self.parse(str)
7
- time = str.to_s.strip.chomp
8
- if time_string?(time)
9
- parts = TimeParts.new(time.split(':').reverse)
10
- elsif integer?(time)
11
- parts = extract_minutes_from_integer time
12
- elsif decimal?(time)
13
- parts = extract_minutes_from_decimal time
14
- else
15
- raise 'Invalid time format'
16
- end
17
- RunbyTime.new(parts)
18
- end
19
-
20
- def self.decimal?(str)
21
- str.match?(/^\d+[,. ]\d+$/)
22
- end
23
-
24
- def self.integer?(str)
25
- str.match?(/^\d+$/)
26
- end
27
-
28
- def self.time_string?(str)
29
- str.match?(/^\d?\d(:\d\d)+$/)
30
- end
31
-
32
- def self.extract_minutes_from_decimal(decimal_str)
33
- decimal_parts = decimal_str.split(/[,. ]/)
34
- minutes = decimal_parts[0].to_i
35
- seconds = (decimal_parts[1].to_i / 10.0 * 60).to_i
36
- TimeParts.new([seconds, minutes])
37
- end
38
-
39
- def self.extract_minutes_from_integer(integer_str)
40
- TimeParts.new([0, integer_str.to_i])
41
- end
42
-
43
- # Encapsulates the parts of a time string
44
- class TimeParts
45
- def initialize(parts_array)
46
- @keys = { seconds: 0, minutes: 1, hours: 2 }
47
- @parts = Array.new(@keys.count, 0)
48
- Range.new(0, parts_array.count - 1).each { |i| @parts[i] = parts_array[i] }
49
- validate
50
- freeze
51
- end
52
-
53
- def [](key)
54
- i = @keys[key]
55
- @parts[i].to_i
56
- end
57
-
58
- def format
59
- time_f = +''
60
- @parts.reverse_each do |part|
61
- time_f << ':' << part.to_s.rjust(2, '0')
62
- end
63
- # Remove leading ':'
64
- time_f.slice!(0)
65
- # Remove leading '00:00...'
66
- time_f.sub!(/^(?:00:)+(\d\d:\d\d)/, '\1') if time_f.length > 5
67
- # If the time looks like 00:48, only show 0:48
68
- time_f.slice!(0) if time_f.slice(0) == '0'
69
- time_f
70
- end
71
-
72
- def validate
73
- raise 'Hours must be less than 24' if self[:hours] > 23
74
- raise 'Minutes must be less than 60 if hours are supplied' if self[:hours].positive? && self[:minutes] > 59
75
- raise 'Minutes must be less than 99 if no hours are supplied' if self[:hours].zero? && self[:minutes] > 99
76
- raise 'Seconds must be less than 60' if self[:seconds] > 59
77
- end
78
- end
79
- end
80
- end
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Runby
4
- # Represents a speed consisting of a distance and a unit of time in which that distance was covered
5
- class Speed
6
- include Comparable
7
-
8
- attr_reader :distance
9
-
10
- def self.new(distance_or_multiplier, units = :km)
11
- return distance_or_multiplier if distance_or_multiplier.is_a? Speed
12
- if distance_or_multiplier.is_a? String
13
- parsed_speed = Speed.try_parse(distance_or_multiplier)
14
- return parsed_speed[:speed] unless parsed_speed[:error]
15
- end
16
- super
17
- end
18
-
19
- def initialize(distance_or_multiplier, units = :km)
20
- case distance_or_multiplier
21
- when Distance
22
- init_from_distance distance_or_multiplier
23
- when String
24
- # Already tried to parse it as a Speed string. Try parsing it as a Distance string.
25
- init_from_distance_string distance_or_multiplier
26
- when Numeric
27
- init_from_multiplier(distance_or_multiplier, units)
28
- else
29
- raise "Unable to initialize Runby::Speed from #{distance_or_multiplier}"
30
- end
31
- freeze
32
- end
33
-
34
- def to_s(format: :short)
35
- distance = @distance.to_s(format: format)
36
- case format
37
- when :short then "#{distance}/ph"
38
- when :long then "#{distance} per hour"
39
- else raise "Invalid string format #{format}"
40
- end
41
- end
42
-
43
- def as_pace
44
- time = Runby::RunbyTime.from_minutes(60.0 / @distance.multiplier)
45
- Runby::Pace.new(time, @distance.uom)
46
- end
47
-
48
- # @param [String] str is either a long-form speed such as "7.5 miles per hour" or a short-form speed like "7.5mi/ph"
49
- def self.parse(str)
50
- str = str.to_s.strip.chomp
51
- match = str.match(%r{^(?<distance>\d+(?:\.\d+)? ?[A-Za-z]+)(?: per hour|\/ph)$})
52
- raise "Invalid speed format (#{str})" unless match
53
- distance = Runby::Distance.new(match[:distance])
54
- Speed.new distance
55
- end
56
-
57
- def self.try_parse(str)
58
- speed = nil
59
- error_message = nil
60
- warning_message = nil
61
- begin
62
- speed = Speed.parse str
63
- rescue StandardError => ex
64
- error_message = "#{ex.message} (#{str})"
65
- end
66
- { speed: speed, error: error_message, warning: warning_message }
67
- end
68
-
69
- def <=>(other)
70
- raise "Unable to compare Runby::Speed to #{other.class}(#{other})" unless [Speed, String].include? other.class
71
- if other.is_a? String
72
- return 0 if to_s == other || to_s(format: :long) == other
73
- self <=> try_parse(other)[:speed]
74
- end
75
- @distance <=> other.distance
76
- end
77
-
78
- private
79
-
80
- def init_from_distance(distance)
81
- @distance = distance
82
- end
83
-
84
- def init_from_multiplier(multiplier, uom)
85
- @distance = Distance.new(uom, multiplier)
86
- end
87
-
88
- def init_from_distance_string(str)
89
- results = Distance.try_parse(str)
90
- unless results[:error]
91
- @distance = results[:distance]
92
- return
93
- end
94
- raise "'#{str}' is not recognized as a Speed or a Distance"
95
- end
96
- end
97
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'runby_range'
4
-
5
- module Runby
6
- # Represents a range of speeds, from fast to slow.
7
- class SpeedRange < RunbyRange
8
- def initialize(fast, slow)
9
- raise "Invalid fast speed value: #{fast}" unless fast.is_a?(Numeric) || fast.is_a?(Speed)
10
- raise "Invalid slow speed value: #{slow}" unless slow.is_a?(Numeric) || slow.is_a?(Speed)
11
- @fast = Runby::Speed.new(fast)
12
- @slow = Runby::Speed.new(slow)
13
- freeze
14
- end
15
-
16
- def as_pace_range
17
- Runby::PaceRange.new @fast.as_pace, @slow.as_pace
18
- end
19
-
20
- def to_s(format: :short)
21
- if @fast == @slow
22
- @fast.to_s(format: format)
23
- else
24
- fast_multiplier = format('%g', @fast.distance.multiplier.round(2))
25
- slow_multiplier = format('%g', @slow.distance.multiplier.round(2))
26
- @fast.to_s(format: format).sub(fast_multiplier.to_s, "#{fast_multiplier}-#{slow_multiplier}")
27
- end
28
- end
29
- end
30
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Runby
4
- module Utility
5
- # Helps sanitize method parameters. (See RSpec documentation for examples)
6
- class ParameterSanitizer
7
- attr_reader :parameter
8
-
9
- def initialize(parameter)
10
- @parameter = parameter
11
- end
12
-
13
- def self.sanitize(parameter)
14
- ParameterSanitizer.new parameter
15
- end
16
-
17
- def as(type)
18
- return @parameter if @parameter.is_a?(type)
19
- raise "Unable to sanitize parameter of type #{type}. Missing 'parse' method." unless type.respond_to? :parse
20
- type.parse(@parameter)
21
- end
22
- end
23
- end
24
-
25
- # Just a shortcut method
26
- def self.sanitize(parameter)
27
- Runby::Utility::ParameterSanitizer.sanitize(parameter)
28
- end
29
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Runby
4
- GENERATED_VERSION = '__VERSION__'
5
- end
Binary file