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.
- 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
data/bin/runbypace
DELETED
data/lib/runby_pace/cli/cli.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'readline'
|
4
|
-
require 'optparse'
|
5
|
-
require_relative 'config'
|
6
|
-
|
7
|
-
module Runby
|
8
|
-
module Cli
|
9
|
-
# Command line interface and REPL for RunbyPace
|
10
|
-
class Cli
|
11
|
-
def initialize(args = ARGV)
|
12
|
-
@args = args
|
13
|
-
@config = Config.new
|
14
|
-
@options = parse_options args
|
15
|
-
end
|
16
|
-
|
17
|
-
def run
|
18
|
-
puts 'Runby Pace REPL!'
|
19
|
-
bnd = binding
|
20
|
-
while (input = Readline.readline('🏃 ', true))
|
21
|
-
begin
|
22
|
-
result = bnd.eval input
|
23
|
-
rescue StandardError => e
|
24
|
-
puts "#{e.class}: #{e.message}"
|
25
|
-
else
|
26
|
-
puts result
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def print_targets(five_k_time, distance_units = :mi)
|
32
|
-
five_k_time = @config['five_k_time'] if five_k_time.nil?
|
33
|
-
|
34
|
-
five_k_time = Runby.sanitize(five_k_time).as(RunbyTime)
|
35
|
-
puts "\nIf you can run a 5K in #{five_k_time}, your training paces should be:"
|
36
|
-
paces = []
|
37
|
-
RunTypes.all_classes.each do |run_type|
|
38
|
-
run = run_type.new
|
39
|
-
paces.push(description: run.description, pace: run.lookup_pace(five_k_time, distance_units))
|
40
|
-
end
|
41
|
-
paces.sort_by { |p| p[:pace].fast }.reverse_each { |p| puts " #{p[:description]}: #{p[:pace]}" }
|
42
|
-
nil
|
43
|
-
end
|
44
|
-
|
45
|
-
# -- Shortcuts for the REPL
|
46
|
-
def di(*args)
|
47
|
-
Distance.new(*args)
|
48
|
-
end
|
49
|
-
|
50
|
-
def du(*args)
|
51
|
-
DistanceUnit.new(*args)
|
52
|
-
end
|
53
|
-
|
54
|
-
def pc(*args)
|
55
|
-
Pace.new(*args)
|
56
|
-
end
|
57
|
-
|
58
|
-
def sp(*args)
|
59
|
-
Speed.new(*args)
|
60
|
-
end
|
61
|
-
|
62
|
-
def tm(*args)
|
63
|
-
RunbyTime.new(*args)
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def parse_options(options)
|
69
|
-
args = { targets: nil }
|
70
|
-
|
71
|
-
OptionParser.new do |opts|
|
72
|
-
opts.banner = 'Usage: runbypace.rb [options]'
|
73
|
-
|
74
|
-
opts.on('-h', '--help', 'Display this help message') do
|
75
|
-
puts opts
|
76
|
-
exit
|
77
|
-
end
|
78
|
-
|
79
|
-
opts.on('-c', '--config [SETTING][=NEW_VALUE]', 'Get or set a configuration value') do |config|
|
80
|
-
manage_config config
|
81
|
-
exit
|
82
|
-
end
|
83
|
-
|
84
|
-
opts.on('-t', '--targets [5K race time]', 'Show target paces') do |targets|
|
85
|
-
args[:targets] = targets
|
86
|
-
print_targets targets
|
87
|
-
exit
|
88
|
-
end
|
89
|
-
end.parse!(options)
|
90
|
-
args
|
91
|
-
end
|
92
|
-
|
93
|
-
def manage_config(config)
|
94
|
-
c = parse_config_setting config
|
95
|
-
unless c.key
|
96
|
-
# No key specified. Print all settings.
|
97
|
-
@config.pretty_print
|
98
|
-
return
|
99
|
-
end
|
100
|
-
if c.value
|
101
|
-
# Set setting "key" to new "value"
|
102
|
-
@config[c.key] = c.value
|
103
|
-
return
|
104
|
-
end
|
105
|
-
if c.clear_setting
|
106
|
-
@config[c.key] = nil
|
107
|
-
else
|
108
|
-
# Print the value of setting "key"
|
109
|
-
p @config[c.key]
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def parse_config_setting(setting)
|
114
|
-
setting = '' if setting.nil?
|
115
|
-
Class.new do
|
116
|
-
attr_reader :key, :value, :clear_setting
|
117
|
-
def initialize(setting)
|
118
|
-
tokens = setting.split('=')
|
119
|
-
@key = tokens[0]
|
120
|
-
@value = tokens[1]
|
121
|
-
@clear_setting = (@value.nil? && setting.include?('='))
|
122
|
-
end
|
123
|
-
end.new(setting)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'yaml'
|
4
|
-
require 'pp'
|
5
|
-
|
6
|
-
module Runby
|
7
|
-
#
|
8
|
-
module Cli
|
9
|
-
class Config
|
10
|
-
USER_CONFIG_PATH = File.expand_path('~/.runbypace').freeze
|
11
|
-
|
12
|
-
VALID_OPTIONS = {
|
13
|
-
five_k_time: { validate_as: RunbyTime }
|
14
|
-
}.freeze
|
15
|
-
|
16
|
-
def initialize
|
17
|
-
@settings = load_user_settings
|
18
|
-
end
|
19
|
-
|
20
|
-
def load_user_settings
|
21
|
-
if File.exist? USER_CONFIG_PATH
|
22
|
-
YAML.load_file USER_CONFIG_PATH
|
23
|
-
else
|
24
|
-
{}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def store_user_settings
|
29
|
-
File.open(USER_CONFIG_PATH, 'w') { |file| file.write @settings.to_yaml }
|
30
|
-
end
|
31
|
-
|
32
|
-
def [](key)
|
33
|
-
return unless known_setting?(key)
|
34
|
-
return unless option_configured?(key)
|
35
|
-
"#{key} => #{@settings[key]}"
|
36
|
-
end
|
37
|
-
|
38
|
-
def []=(key, value)
|
39
|
-
return unless known_setting?(key)
|
40
|
-
if value
|
41
|
-
value = sanitize_value key, value
|
42
|
-
return unless value
|
43
|
-
@settings[key] = value.to_s
|
44
|
-
else
|
45
|
-
@settings.delete(key)
|
46
|
-
end
|
47
|
-
store_user_settings
|
48
|
-
end
|
49
|
-
|
50
|
-
def pretty_print
|
51
|
-
pp @settings
|
52
|
-
end
|
53
|
-
|
54
|
-
def known_setting?(key)
|
55
|
-
unless VALID_OPTIONS.key?(key.to_sym)
|
56
|
-
puts "Unknown setting #{key}"
|
57
|
-
return false
|
58
|
-
end
|
59
|
-
true
|
60
|
-
end
|
61
|
-
|
62
|
-
def sanitize_value(key, value)
|
63
|
-
cls = VALID_OPTIONS[key.to_sym][:validate_as]
|
64
|
-
begin
|
65
|
-
value = Runby.sanitize(value).as(cls)
|
66
|
-
rescue StandardError => ex
|
67
|
-
value = nil
|
68
|
-
p ex.message
|
69
|
-
end
|
70
|
-
value
|
71
|
-
end
|
72
|
-
|
73
|
-
def option_configured?(key)
|
74
|
-
unless @settings.key? key
|
75
|
-
puts "#{key} not configured. Set with:\n\trunbypace --config #{key} VALUE"
|
76
|
-
false
|
77
|
-
end
|
78
|
-
true
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
data/lib/runby_pace/distance.rb
DELETED
@@ -1,135 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
# Represents a distance (distance UOM and multiplier)
|
5
|
-
class Distance
|
6
|
-
include Comparable
|
7
|
-
|
8
|
-
attr_reader :uom, :multiplier
|
9
|
-
|
10
|
-
def self.new(uom = :km, multiplier = 1)
|
11
|
-
return uom if uom.is_a? Distance
|
12
|
-
return Distance.parse uom if uom.is_a? String
|
13
|
-
super
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(uom = :km, multiplier = 1)
|
17
|
-
case uom
|
18
|
-
when DistanceUnit
|
19
|
-
init_from_distance_unit uom, multiplier
|
20
|
-
when Symbol
|
21
|
-
init_from_symbol uom, multiplier
|
22
|
-
else
|
23
|
-
raise 'Invalid distance unit of measure'
|
24
|
-
end
|
25
|
-
freeze
|
26
|
-
end
|
27
|
-
|
28
|
-
def convert_to(target_uom)
|
29
|
-
target_uom = DistanceUnit.new target_uom unless target_uom.is_a?(DistanceUnit)
|
30
|
-
return self if @uom == target_uom
|
31
|
-
target_multiplier = kilometers / (target_uom.conversion_factor * 1.0)
|
32
|
-
Distance.new target_uom, target_multiplier
|
33
|
-
end
|
34
|
-
|
35
|
-
def meters
|
36
|
-
kilometers * 1000.0
|
37
|
-
end
|
38
|
-
|
39
|
-
def kilometers
|
40
|
-
@multiplier * @uom.conversion_factor
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.parse(str)
|
44
|
-
str = str.strip.chomp.downcase
|
45
|
-
multiplier = str.scan(/[\d,.]+/).first
|
46
|
-
multiplier = multiplier.nil? ? 1 : multiplier.to_f
|
47
|
-
uom = str.scan(/[-_a-z ]+$/).first
|
48
|
-
raise "Unable to find distance unit in '#{str}'" if uom.nil?
|
49
|
-
|
50
|
-
parsed_uom = Runby::DistanceUnit.parse uom
|
51
|
-
raise "'#{uom.strip}' is not recognized as a distance unit" if parsed_uom.nil?
|
52
|
-
|
53
|
-
new parsed_uom, multiplier
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.try_parse(str)
|
57
|
-
distance, error_message = nil
|
58
|
-
begin
|
59
|
-
distance = parse str
|
60
|
-
rescue StandardError => ex
|
61
|
-
error_message = ex.message.to_s
|
62
|
-
end
|
63
|
-
{ distance: distance, error: error_message }
|
64
|
-
end
|
65
|
-
|
66
|
-
def to_s(format: :short)
|
67
|
-
formatted_multiplier = format('%g', @multiplier.round(2))
|
68
|
-
case format
|
69
|
-
when :short then "#{formatted_multiplier} #{@uom.to_s(format: format)}"
|
70
|
-
when :long then "#{formatted_multiplier} #{@uom.to_s(format: format, pluralize: (@multiplier > 1))}"
|
71
|
-
else raise "Invalid string format #{format}"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# @param [Distance, String] other
|
76
|
-
def <=>(other)
|
77
|
-
raise "Unable to compare Runby::Distance to #{other.class}(#{other})" unless [Distance, String].include? other.class
|
78
|
-
if other.is_a?(String)
|
79
|
-
return 0 if to_s == other || to_s(format: :long) == other
|
80
|
-
return self <=> Distance.try_parse(other)[:distance]
|
81
|
-
end
|
82
|
-
kilometers <=> other.kilometers
|
83
|
-
end
|
84
|
-
|
85
|
-
# @param [Distance] other
|
86
|
-
# @return [Distance]
|
87
|
-
def +(other)
|
88
|
-
raise "Cannot add Runby::Distance to #{other.class}" unless other.is_a?(Distance)
|
89
|
-
sum_in_km = Distance.new(:km, kilometers + other.kilometers)
|
90
|
-
sum_in_km.convert_to(@uom)
|
91
|
-
end
|
92
|
-
|
93
|
-
# @param [Distance] other
|
94
|
-
# @return [Distance]
|
95
|
-
def -(other)
|
96
|
-
raise "Cannot add Runby::Distance to #{other.class}" unless other.is_a?(Distance)
|
97
|
-
sum_in_km = Distance.new(:km, kilometers - other.kilometers)
|
98
|
-
sum_in_km.convert_to(@uom)
|
99
|
-
end
|
100
|
-
|
101
|
-
# @param [Numeric] other
|
102
|
-
# @return [Distance]
|
103
|
-
def *(other)
|
104
|
-
raise "Cannot multiply Runby::Distance by #{other.class}" unless other.is_a?(Numeric)
|
105
|
-
product_in_km = Distance.new(:km, kilometers * other)
|
106
|
-
product_in_km.convert_to(@uom)
|
107
|
-
end
|
108
|
-
|
109
|
-
# @param [Numeric, Distance] other
|
110
|
-
# @return [Distance, Numeric]
|
111
|
-
def /(other)
|
112
|
-
raise "Cannot divide Runby::Distance by #{other.class}" unless other.is_a?(Numeric) || other.is_a?(Distance)
|
113
|
-
if other.is_a?(Numeric)
|
114
|
-
quotient_in_km = Distance.new(:km, kilometers / other)
|
115
|
-
return quotient_in_km.convert_to(@uom)
|
116
|
-
elsif other.is_a?(Distance)
|
117
|
-
return kilometers / other.kilometers
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
def init_from_distance_unit(uom, multiplier)
|
124
|
-
@uom = uom
|
125
|
-
@multiplier = multiplier
|
126
|
-
end
|
127
|
-
|
128
|
-
def init_from_symbol(distance_uom_symbol, multiplier)
|
129
|
-
raise "Unknown unit of measure #{distance_uom_symbol}" unless Runby::DistanceUnit.known_uom? distance_uom_symbol
|
130
|
-
raise 'Invalid multiplier' unless multiplier.is_a?(Numeric)
|
131
|
-
@uom = DistanceUnit.new distance_uom_symbol
|
132
|
-
@multiplier = multiplier * 1.0
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Runby
|
4
|
-
# Represents the distance units (e.g. kilometers, miles) used in paces
|
5
|
-
# including the human-readable description of each unit
|
6
|
-
# and the factor used to convert it to kilometers.
|
7
|
-
class DistanceUnit
|
8
|
-
attr_reader :symbol, :description, :conversion_factor
|
9
|
-
|
10
|
-
def self.new(unit_of_measure)
|
11
|
-
return unit_of_measure if unit_of_measure.is_a? DistanceUnit
|
12
|
-
return DistanceUnit.parse(unit_of_measure) if unit_of_measure.is_a? String
|
13
|
-
super
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(unit_of_measure)
|
17
|
-
raise "':#{unit_of_measure}' is an unknown unit of measure" unless DistanceUnit.known_uom? unit_of_measure
|
18
|
-
@symbol = unit_of_measure
|
19
|
-
@conversion_factor = UOM_DEFINITIONS[@symbol][:conversion_factor]
|
20
|
-
@description = UOM_DEFINITIONS[@symbol][:description]
|
21
|
-
freeze
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_s(format: :long, pluralize: false)
|
25
|
-
case format
|
26
|
-
when :short then @symbol.to_s
|
27
|
-
when :long then pluralize ? description_plural : @description
|
28
|
-
else raise "Invalid string format #{format}"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def description_plural
|
33
|
-
UOM_DEFINITIONS[@symbol][:description_plural]
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.parse(description)
|
37
|
-
return new description if description.is_a? Symbol
|
38
|
-
description = description.strip.chomp.downcase
|
39
|
-
found_uom = nil
|
40
|
-
UOM_DEFINITIONS.each do |uom, details|
|
41
|
-
if details[:synonyms].include? description
|
42
|
-
found_uom = uom
|
43
|
-
break
|
44
|
-
end
|
45
|
-
end
|
46
|
-
raise "Error parsing distance unit '#{description}'" unless found_uom
|
47
|
-
DistanceUnit.new found_uom
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.try_parse(str)
|
51
|
-
uom, error_message = nil
|
52
|
-
begin
|
53
|
-
uom = parse str
|
54
|
-
rescue StandardError => ex
|
55
|
-
error_message = ex.message
|
56
|
-
end
|
57
|
-
{ uom: uom, error: error_message }
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.known_uom?(symbol)
|
61
|
-
UOM_DEFINITIONS.key?(symbol)
|
62
|
-
end
|
63
|
-
|
64
|
-
def ==(other)
|
65
|
-
if other.is_a? DistanceUnit
|
66
|
-
@symbol == other.symbol
|
67
|
-
elsif other.is_a? String
|
68
|
-
self == DistanceUnit.parse(other)
|
69
|
-
else
|
70
|
-
raise "Unable to compare DistanceUnit to #{other.class}(#{other})"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
UOM_DEFINITIONS =
|
75
|
-
{ km: { description: 'kilometer', description_plural: 'kilometers', conversion_factor: 1.0,
|
76
|
-
synonyms: %w[k km kms kilometer kilometers] },
|
77
|
-
m: { description: 'meter', description_plural: 'meters', conversion_factor: 0.001,
|
78
|
-
synonyms: %w[m meter meters] },
|
79
|
-
mi: { description: 'mile', description_plural: 'miles', conversion_factor: 1.609344,
|
80
|
-
synonyms: %w[mi mile miles] },
|
81
|
-
ft: { description: 'foot', description_plural: 'feet', conversion_factor: 0.0003048,
|
82
|
-
synonyms: %w[ft foot feet] },
|
83
|
-
yd: { description: 'yard', description_plural: 'yards', conversion_factor: 1093.61,
|
84
|
-
synonyms: %w[y yd yds yard yards] },
|
85
|
-
# Fun distance unit of measures
|
86
|
-
marathon: { description: 'marathon', description_plural: 'marathons', conversion_factor: 42.1648128,
|
87
|
-
synonyms: %w[marathon] } }.freeze
|
88
|
-
end
|
89
|
-
end
|