runby_pace 0.2.50.111 → 0.2.55
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/.travis.yml +1 -5
- data/Gemfile +0 -4
- data/README.md +5 -16
- data/Rakefile +6 -40
- data/bin/_guard-core +16 -17
- data/bin/guard +16 -17
- data/lib/runby_pace.rb +1 -4
- data/lib/runby_pace/{pace_calculator.rb → pace_data.rb} +13 -29
- data/lib/runby_pace/pace_range.rb +9 -27
- data/lib/runby_pace/pace_time.rb +110 -0
- data/lib/runby_pace/run_type.rb +4 -12
- data/lib/runby_pace/run_types/all_run_types.template +4 -6
- data/lib/runby_pace/run_types/easy_run.rb +10 -31
- data/lib/runby_pace/run_types/find_divisor.rb +17 -13
- data/lib/runby_pace/run_types/long_run.rb +10 -32
- data/lib/runby_pace/version.rb +2 -17
- data/runby_pace.gemspec +5 -4
- metadata +18 -41
- data/.rubocop.yml +0 -10
- data/bin/runbypace +0 -15
- data/lib/runby_pace/cli/cli.rb +0 -127
- data/lib/runby_pace/cli/config.rb +0 -82
- data/lib/runby_pace/distance.rb +0 -135
- data/lib/runby_pace/distance_unit.rb +0 -89
- data/lib/runby_pace/golden_pace_set.rb +0 -50
- data/lib/runby_pace/pace.rb +0 -152
- data/lib/runby_pace/run_math.rb +0 -14
- data/lib/runby_pace/run_types/distance_run.rb +0 -55
- data/lib/runby_pace/run_types/fast_tempo_run.rb +0 -23
- data/lib/runby_pace/run_types/five_kilometer_race_run.rb +0 -22
- data/lib/runby_pace/run_types/mile_race_run.rb +0 -24
- data/lib/runby_pace/run_types/slow_tempo_run.rb +0 -22
- data/lib/runby_pace/run_types/tempo_run.rb +0 -54
- data/lib/runby_pace/run_types/ten_kilometer_race_run.rb +0 -23
- data/lib/runby_pace/runby_range.rb +0 -22
- data/lib/runby_pace/runby_time.rb +0 -138
- data/lib/runby_pace/runby_time_parser.rb +0 -80
- data/lib/runby_pace/speed.rb +0 -97
- data/lib/runby_pace/speed_range.rb +0 -30
- data/lib/runby_pace/utility/parameter_sanitizer.rb +0 -29
- data/lib/runby_pace/version.seed +0 -5
- data/misc/runbypace_logo.png +0 -0
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
|
5
|
-
# Maps a set of 5K race times with their pre-calculated pace recommendations.
|
6
|
-
# This is useful in testing as well as defining the fastest and slowest supported 5K times.
|
7
|
-
# GoldenPaceSet could conceivably be used to pre-compute a large number of recommended paces,
|
8
|
-
# thus reducing runtime CPU overhead.
|
9
|
-
class GoldenPaceSet
|
10
|
-
include Enumerable
|
11
|
-
|
12
|
-
attr_reader :paces
|
13
|
-
|
14
|
-
# The fastest 5K time supported by RunbyPace
|
15
|
-
FASTEST_5K = :'14:00'
|
16
|
-
|
17
|
-
# The slowest 5K time supported by RunbyPace
|
18
|
-
SLOWEST_5K = :'42:00'
|
19
|
-
|
20
|
-
# @param [Hash] paces_hash is a hash mapping 5K time symbols to times, represented as strings.
|
21
|
-
# An example paces_hash is {'14:00':'4:00', '15:00':'4:55'}
|
22
|
-
def initialize(paces_hash)
|
23
|
-
@paces = {}
|
24
|
-
paces_hash.each { |five_k_time, recommended_pace| @paces[five_k_time.to_sym] = Pace.new(recommended_pace) }
|
25
|
-
end
|
26
|
-
|
27
|
-
def each
|
28
|
-
@paces.each do |h, v|
|
29
|
-
yield h, v
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns first/fastest recommended pace in the set
|
34
|
-
def first
|
35
|
-
@paces[FASTEST_5K]
|
36
|
-
end
|
37
|
-
alias fastest first
|
38
|
-
|
39
|
-
# Return the last/slowest recommended pace in the set
|
40
|
-
def last
|
41
|
-
@paces[SLOWEST_5K]
|
42
|
-
end
|
43
|
-
alias slowest last
|
44
|
-
|
45
|
-
# Creates and returns a new GoldenPaceSet with only two entries
|
46
|
-
def self.new_from_endpoints(fastest, slowest)
|
47
|
-
GoldenPaceSet.new(FASTEST_5K => fastest, SLOWEST_5K => slowest)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
data/lib/runby_pace/pace.rb
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
# Represents a pace consisting of a distance and a time in which that distance was covered
|
5
|
-
class Pace
|
6
|
-
include Comparable
|
7
|
-
|
8
|
-
attr_reader :time, :distance
|
9
|
-
|
10
|
-
def self.new(time_or_pace, distance = '1K')
|
11
|
-
return time_or_pace if time_or_pace.is_a? Pace
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(time_or_pace, distance = '1K')
|
16
|
-
case time_or_pace
|
17
|
-
when RunbyTime
|
18
|
-
init_from_time time_or_pace, distance
|
19
|
-
when String
|
20
|
-
init_from_string time_or_pace, distance
|
21
|
-
else
|
22
|
-
raise 'Invalid Time or Pace'
|
23
|
-
end
|
24
|
-
freeze
|
25
|
-
end
|
26
|
-
|
27
|
-
def convert_to(target_distance)
|
28
|
-
target_distance = Distance.new(target_distance) unless target_distance.is_a?(Distance)
|
29
|
-
return self if @distance == target_distance
|
30
|
-
conversion_factor = target_distance / @distance
|
31
|
-
Pace.new @time * conversion_factor, target_distance
|
32
|
-
end
|
33
|
-
|
34
|
-
def to_s(format: :short)
|
35
|
-
leading_one_regex = /^1 ?/
|
36
|
-
distance_s = @distance.to_s(format: format).gsub(leading_one_regex, '')
|
37
|
-
case format
|
38
|
-
when :short then "#{time} p/#{distance_s}"
|
39
|
-
when :long then "#{time} per #{distance_s}"
|
40
|
-
else raise "Invalid string format #{format}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def as_speed
|
45
|
-
total_minutes = @time.total_minutes
|
46
|
-
multiplier = total_minutes.positive? ? (60 / total_minutes).round(2) : 0
|
47
|
-
distance = Runby::Distance.new(@distance.uom, multiplier)
|
48
|
-
Runby::Speed.new distance
|
49
|
-
end
|
50
|
-
|
51
|
-
def meters_per_minute
|
52
|
-
total_minutes = @time.total_minutes
|
53
|
-
return 0 unless total_minutes.positive?
|
54
|
-
@distance.meters / total_minutes
|
55
|
-
end
|
56
|
-
|
57
|
-
# @param [String] str is either a long-form pace such as "10:00 per mile" or a short-form pace like "10:00 p/mi"
|
58
|
-
def self.parse(str)
|
59
|
-
str = str.to_s.strip.chomp
|
60
|
-
match = str.match %r{^(?<time>[:\d]*) ?(?: per |p\/)(?<distance>(?:[\d.]+ ?)?\w+)$}
|
61
|
-
raise "Invalid pace format (#{str})" unless match
|
62
|
-
time = Runby::RunbyTime.new(match[:time])
|
63
|
-
distance = Runby::Distance.new(match[:distance])
|
64
|
-
Pace.new time, distance
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.try_parse(str)
|
68
|
-
pace = nil
|
69
|
-
error_message = nil
|
70
|
-
warning_message = nil
|
71
|
-
begin
|
72
|
-
pace = Pace.parse str
|
73
|
-
rescue StandardError => ex
|
74
|
-
error_message = ex.message
|
75
|
-
end
|
76
|
-
{ pace: pace, error: error_message, warning: warning_message }
|
77
|
-
end
|
78
|
-
|
79
|
-
def <=>(other)
|
80
|
-
raise "Unable to compare Runby::Pace to #{other.class}(#{other})" unless [Pace, RunbyTime, String].include? other.class
|
81
|
-
if other.is_a? Pace
|
82
|
-
meters_per_minute.round(2) <=> other.meters_per_minute.round(2)
|
83
|
-
elsif other.is_a? RunbyTime
|
84
|
-
@time <=> other
|
85
|
-
elsif other.is_a? String
|
86
|
-
return 0 if to_s == other || to_s(format: :long) == other
|
87
|
-
return 0 if @time == other
|
88
|
-
self <=> try_parse(other)[:pace]
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def almost_equals?(other_pace, tolerance_time = '00:01')
|
93
|
-
if other_pace.is_a?(RunbyTime)
|
94
|
-
return almost_equals?(Pace.new(other_pace, @distance), tolerance_time)
|
95
|
-
end
|
96
|
-
if other_pace.is_a?(String)
|
97
|
-
return almost_equals?(Pace.new(other_pace, @distance), tolerance_time) if other_pace.match?(/^\d?\d:\d\d$/)
|
98
|
-
other_pace = Pace.parse(other_pace)
|
99
|
-
end
|
100
|
-
tolerance = RunbyTime.new(tolerance_time)
|
101
|
-
fast_end = (self - tolerance)
|
102
|
-
slow_end = (self + tolerance)
|
103
|
-
slow_end <= other_pace && other_pace <= fast_end
|
104
|
-
end
|
105
|
-
|
106
|
-
# @param [Pace, RunbyTime] other
|
107
|
-
def -(other)
|
108
|
-
if other.is_a?(Pace)
|
109
|
-
Pace.new(@time - other.convert_to(@distance).time, @distance)
|
110
|
-
elsif other.is_a?(RunbyTime)
|
111
|
-
Pace.new(@time - other, @distance)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# @param [Pace, RunbyTime] other
|
116
|
-
def +(other)
|
117
|
-
if other.is_a?(Pace)
|
118
|
-
Pace.new(@time + other.convert_to(@distance).time, @distance)
|
119
|
-
elsif other.is_a?(RunbyTime)
|
120
|
-
Pace.new(@time + other, @distance)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def distance_covered_over_time(time)
|
125
|
-
time = Runby.sanitize(time).as(RunbyTime)
|
126
|
-
if time.total_minutes.zero? || @distance.multiplier.zero?
|
127
|
-
return Runby::Distance.new(@distance.uom, 0)
|
128
|
-
end
|
129
|
-
divisor = @time.total_minutes / time.total_minutes / @distance.multiplier
|
130
|
-
distance_covered = Runby::Distance.new(@distance.uom, 1 / divisor)
|
131
|
-
distance_covered
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
|
-
def init_from_string(string, distance = '1K')
|
137
|
-
pace = Pace.try_parse(string)
|
138
|
-
if pace[:pace]
|
139
|
-
@time = pace[:pace].time
|
140
|
-
@distance = pace[:pace].distance
|
141
|
-
return
|
142
|
-
end
|
143
|
-
@time = Runby::RunbyTime.new string
|
144
|
-
@distance = Runby::Distance.new distance
|
145
|
-
end
|
146
|
-
|
147
|
-
def init_from_time(time, distance)
|
148
|
-
@time = time
|
149
|
-
@distance = Runby::Distance.new distance
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
data/lib/runby_pace/run_math.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
# An assortment of mathematical functions related to running.
|
5
|
-
class RunMath
|
6
|
-
def self.predict_race_time(race1_distance, race1_time, target_distance)
|
7
|
-
race1_distance = Runby.sanitize(race1_distance).as(Distance)
|
8
|
-
race1_time = Runby.sanitize(race1_time).as(RunbyTime)
|
9
|
-
target_distance = Runby.sanitize(target_distance).as(Distance)
|
10
|
-
|
11
|
-
race1_time * (target_distance / race1_distance)**1.06
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
module RunTypes
|
5
|
-
# Defines the venerable "distance run", the backbone of any distance running program.
|
6
|
-
# Most of your runs should be at this pace. Harder than an "easy run" but still conversational.
|
7
|
-
class DistanceRun < RunType
|
8
|
-
attr_reader :slow_pace_calculator, :fast_pace_calculator
|
9
|
-
|
10
|
-
def description
|
11
|
-
'Distance Run'
|
12
|
-
end
|
13
|
-
|
14
|
-
def explanation
|
15
|
-
'Most of your weekly training should be comprised of Distance Runs. They are faster than easy runs, but you should still be able to carry on a conversation.'
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize
|
19
|
-
@fast_pace_calculator = PaceCalculator.new(GoldenPaces.fast, 3.675)
|
20
|
-
@slow_pace_calculator = PaceCalculator.new(GoldenPaces.slow, 2.175)
|
21
|
-
end
|
22
|
-
|
23
|
-
def lookup_pace(five_k_time, distance_units = :km)
|
24
|
-
fast = @fast_pace_calculator.calc(five_k_time, distance_units)
|
25
|
-
slow = @slow_pace_calculator.calc(five_k_time, distance_units)
|
26
|
-
PaceRange.new(fast, slow)
|
27
|
-
end
|
28
|
-
|
29
|
-
# Used in testing, contains hashes mapping 5K race times with the recommended pace-per-km for this run type.
|
30
|
-
class GoldenPaces
|
31
|
-
def self.fast
|
32
|
-
GoldenPaceSet.new('14:00': '03:44',
|
33
|
-
'15:00': '03:58',
|
34
|
-
'20:00': '05:09',
|
35
|
-
'25:00': '06:18',
|
36
|
-
'30:00': '07:24',
|
37
|
-
'35:00': '08:29',
|
38
|
-
'40:00': '09:33',
|
39
|
-
'42:00': '09:58')
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.slow
|
43
|
-
GoldenPaceSet.new('14:00': '04:17',
|
44
|
-
'15:00': '04:33',
|
45
|
-
'20:00': '05:53',
|
46
|
-
'25:00': '07:09',
|
47
|
-
'30:00': '08:23',
|
48
|
-
'35:00': '09:33',
|
49
|
-
'40:00': '10:42',
|
50
|
-
'42:00': '11:10')
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative 'tempo_run'
|
3
|
-
|
4
|
-
module Runby
|
5
|
-
module RunTypes
|
6
|
-
# The "fast tempo" pace roughly equates to your half-marathon pace.
|
7
|
-
# It's a pace you could maintain for about an hour, if pressed.
|
8
|
-
class FastTempoRun < TempoRun
|
9
|
-
def description
|
10
|
-
'Fast Tempo Run'
|
11
|
-
end
|
12
|
-
|
13
|
-
def explanation
|
14
|
-
'The fast tempo run is an interval workout of 15-25 minutes per repetition. The pace roughly corresponds to that of your half-marathon race pace.'
|
15
|
-
end
|
16
|
-
|
17
|
-
def lookup_pace(five_k_time, distance_units = :km)
|
18
|
-
fast = @fast_pace_calculator.calc(five_k_time, distance_units)
|
19
|
-
PaceRange.new(fast, fast)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
module RunTypes
|
5
|
-
# Your 5K race pace, which is also useful for running repetitions at this pace
|
6
|
-
class FiveKilometerRaceRun < RunType
|
7
|
-
def description
|
8
|
-
'5K Race Pace'
|
9
|
-
end
|
10
|
-
|
11
|
-
def explanation
|
12
|
-
'The 5K race (~3.1 miles) is an excellent gauge of overall fitness. Running repetitions of varying duration at 5K race pace can boost stroke volume, blood volume, mitochondrial and capillary density, etc.'
|
13
|
-
end
|
14
|
-
|
15
|
-
def lookup_pace(five_k_time, distance_units = :km)
|
16
|
-
five_k_time = RunbyTime.new(five_k_time)
|
17
|
-
pace = Pace.new(five_k_time / 5, '1km').convert_to(distance_units)
|
18
|
-
PaceRange.new(pace, pace)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
module RunTypes
|
5
|
-
# Your mile race pace, which is also useful for running repetitions at this pace
|
6
|
-
class MileRaceRun < RunType
|
7
|
-
def description
|
8
|
-
'Mile Race Pace'
|
9
|
-
end
|
10
|
-
|
11
|
-
def explanation
|
12
|
-
'Repetitions run at a pace you would use to race one mile can increase the stroke volume of your heart, strengthen your lungs, increase the number of capillaries around your intermediate and fast twitch fibers, and increase mitochondrial densities around the same.'
|
13
|
-
end
|
14
|
-
|
15
|
-
def lookup_pace(five_k_time, distance_units = :km)
|
16
|
-
five_k_time = RunbyTime.new(five_k_time)
|
17
|
-
mile = Distance.new('1 mile')
|
18
|
-
mile_time = RunMath.predict_race_time('5K', five_k_time, mile)
|
19
|
-
pace = Pace.new(mile_time, mile).convert_to(distance_units)
|
20
|
-
PaceRange.new(pace, pace)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative 'tempo_run'
|
3
|
-
|
4
|
-
module Runby
|
5
|
-
module RunTypes
|
6
|
-
# The "slow tempo" pace roughly equates to your marathon pace.
|
7
|
-
class SlowTempoRun < TempoRun
|
8
|
-
def description
|
9
|
-
'Slow Tempo Run'
|
10
|
-
end
|
11
|
-
|
12
|
-
def explanation
|
13
|
-
'The slow tempo run is an interval workout of 20-40 minutes per repetition. The pace roughly corresponds to that of your marathon race pace.'
|
14
|
-
end
|
15
|
-
|
16
|
-
def lookup_pace(five_k_time, distance_units = :km)
|
17
|
-
slow = @slow_pace_calculator.calc(five_k_time, distance_units)
|
18
|
-
PaceRange.new(slow, slow)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
module RunTypes
|
5
|
-
# Combines the fast and slow tempo runs into one convenient range of paces
|
6
|
-
class TempoRun < RunType
|
7
|
-
attr_reader :slow_pace_calculator, :fast_pace_calculator
|
8
|
-
|
9
|
-
def description
|
10
|
-
'Tempo Run'
|
11
|
-
end
|
12
|
-
|
13
|
-
def explanation
|
14
|
-
'Ran at a comfortably hard pace that you could maintain for about an hour, if pressed. However, tempo runs are interval workouts, so you won\'t run for longer than 15-40 minutes per repetition'
|
15
|
-
end
|
16
|
-
|
17
|
-
def initialize
|
18
|
-
@fast_pace_calculator = PaceCalculator.new(GoldenPaces.fast, 4.025)
|
19
|
-
@slow_pace_calculator = PaceCalculator.new(GoldenPaces.slow, 3.725)
|
20
|
-
end
|
21
|
-
|
22
|
-
def lookup_pace(five_k_time, distance_units = :km)
|
23
|
-
fast = @fast_pace_calculator.calc(five_k_time, distance_units)
|
24
|
-
slow = @slow_pace_calculator.calc(five_k_time, distance_units)
|
25
|
-
PaceRange.new(fast, slow)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Used in testing, contains hashes mapping 5K race times with the recommended pace-per-km for this run type.
|
29
|
-
class GoldenPaces
|
30
|
-
def self.fast
|
31
|
-
GoldenPaceSet.new('14:00': '03:07',
|
32
|
-
'15:00': '03:20',
|
33
|
-
'20:00': '04:21',
|
34
|
-
'25:00': '05:20',
|
35
|
-
'30:00': '06:19',
|
36
|
-
'35:00': '07:16',
|
37
|
-
'40:00': '08:12',
|
38
|
-
'42:00': '08:35')
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.slow
|
42
|
-
GoldenPaceSet.new('14:00': '03:18',
|
43
|
-
'15:00': '03:31',
|
44
|
-
'20:00': '04:35',
|
45
|
-
'25:00': '05:37',
|
46
|
-
'30:00': '06:38',
|
47
|
-
'35:00': '07:38',
|
48
|
-
'40:00': '08:36',
|
49
|
-
'42:00': '08:59')
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
module RunTypes
|
5
|
-
# Your 10K race pace, which is also useful for running repetitions at this pace
|
6
|
-
class TenKilometerRaceRun < RunType
|
7
|
-
def description
|
8
|
-
'10K Race Pace'
|
9
|
-
end
|
10
|
-
|
11
|
-
def explanation
|
12
|
-
'Repetitions ran at 10K race pace (10 km is about 6.2 miles) are like 5K pace repetitions, but less intense. They elicit many of the same benefits and help increase speed in races from the 10K to the half-marathon.'
|
13
|
-
end
|
14
|
-
|
15
|
-
def lookup_pace(five_k_time, distance_units = :km)
|
16
|
-
five_k_time = RunbyTime.new(five_k_time)
|
17
|
-
ten_k_time = RunMath.predict_race_time('5K', five_k_time, '10K')
|
18
|
-
pace = Pace.new(ten_k_time / 10, '1km').convert_to(distance_units)
|
19
|
-
PaceRange.new(pace, pace)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|