calcpace 1.18.0 → 1.18.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 +4 -4
- data/CHANGELOG.md +42 -11
- data/README.md +13 -5
- data/lib/calcpace/age_grading.rb +14 -2
- data/lib/calcpace/data/wma_2023_open_standards.yml +6 -4
- data/lib/calcpace/errors.rb +3 -0
- data/lib/calcpace/version.rb +1 -1
- data/lib/calcpace.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70f0253d8a81513d2f48082bc39c96a1c9c324fc29ffb7f64ffde0590e5134c7
|
|
4
|
+
data.tar.gz: 2add58bc0d0f904e1e4cf6e08379e495bdeef2ee74bdcb068b973e4e4f0c995a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9abfbeae4e5cccd4c9634f0abcfa7bc51a29d317f47db86aceffd4afe04b17a629f0075440cc3729fd0893d614b007d16d3f5318ee3e4c671226832fb2373327
|
|
7
|
+
data.tar.gz: 8d6f1e3e2cd0e10a0436bbdd9ea7ebfc1f0a6973d2e69a83c0419c17ce0ae12c554790d1f80b5fedf2ad047ec6c5092c03bb58710da2bdc76434b71ac9ccc071
|
data/CHANGELOG.md
CHANGED
|
@@ -7,14 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [1.18.
|
|
10
|
+
## [1.18.1] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `age_grade_label(Float::NAN)` raised an opaque `NoMethodError` (`undefined
|
|
14
|
+
method '[]' for nil`) instead of a clear error; it now raises
|
|
15
|
+
`ArgumentError`. `Float::INFINITY` is unchanged and still maps to the top
|
|
16
|
+
band, "Approximate World Record Level".
|
|
11
17
|
|
|
12
18
|
### Changed
|
|
13
|
-
- `
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
- `AGE_GRADE_LABELS` is now sorted by `min` descending at load time and
|
|
20
|
+
validated to be strictly descending with a `min` of exactly `0.0` on the
|
|
21
|
+
last entry, raising `Calcpace::InvalidDataError` at require time otherwise.
|
|
22
|
+
`age_grade_label` walks the list in order and returns the first match, so
|
|
23
|
+
an out-of-order entry in the data file would have silently misclassified
|
|
24
|
+
percentages, and a missing zero floor raised an opaque `NoMethodError`
|
|
25
|
+
instead of failing loudly.
|
|
26
|
+
- Corrected the provenance note for the age-grade categories in the README
|
|
27
|
+
and in `wma_2023_open_standards.yml`. The WMA / Alan Jones (Howard Grubb)
|
|
28
|
+
tables are numeric age factors and open standards only — they define no
|
|
29
|
+
categories at all. The bands from Local Class (60%) upward follow the
|
|
30
|
+
USATF Masters / National Masters News convention; the three bands below
|
|
31
|
+
60% remain calcpace's own extension for recreational runners. No band
|
|
32
|
+
boundaries or labels changed, only the attribution text.
|
|
33
|
+
- Documented in the README that the bands apply to the percentage as
|
|
34
|
+
reported by `age_grade` (rounded to one decimal), so calling
|
|
35
|
+
`age_grade_label` directly on an unrounded value can land one band lower
|
|
36
|
+
at an edge.
|
|
37
|
+
|
|
38
|
+
## [1.18.0] - 2026-09-06
|
|
39
|
+
|
|
40
|
+
### Breaking
|
|
41
|
+
- **`age_grade[:category]` and `age_grade_label` return different strings for
|
|
42
|
+
anything below 60%.** The old single `"Developing"` band below 60% is now
|
|
43
|
+
three bands: `"Intermediate"` (50–59.9%), `"Recreational"` (40–49.9%) and
|
|
44
|
+
`"Active Beginner"` (below 40%). Most recreational runners grade under 60%,
|
|
45
|
+
so the old catch-all told nearly every one of them the same thing. A caller
|
|
46
|
+
matching on the string `"Developing"` must update — that string is no
|
|
47
|
+
longer returned by either method.
|
|
18
48
|
|
|
19
49
|
```ruby
|
|
20
50
|
calc.age_grade_label(55.0) # => "Intermediate" (was "Developing")
|
|
@@ -22,10 +52,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
22
52
|
calc.age_grade_label(30.0) # => "Active Beginner" (was "Developing")
|
|
23
53
|
```
|
|
24
54
|
|
|
25
|
-
The bands at 60% and above are untouched, and
|
|
26
|
-
`age_grade_percent`, `age_graded_time_seconds` and the rest are
|
|
27
|
-
|
|
28
|
-
`"Developing"` need updating.
|
|
55
|
+
The bands at 60% and above are untouched, and the numeric fields —
|
|
56
|
+
`age_grade_percent`, `age_graded_time_seconds` and the rest — are
|
|
57
|
+
unaffected; only the two label-returning methods changed.
|
|
29
58
|
|
|
30
59
|
## [1.17.0] - 2026-09-05
|
|
31
60
|
|
|
@@ -620,7 +649,9 @@ predictors are untouched.
|
|
|
620
649
|
|
|
621
650
|
See git history for changes in earlier versions.
|
|
622
651
|
|
|
623
|
-
[Unreleased]: https://github.com/0jonjo/calcpace/compare/v1.
|
|
652
|
+
[Unreleased]: https://github.com/0jonjo/calcpace/compare/v1.18.1...HEAD
|
|
653
|
+
[1.18.1]: https://github.com/0jonjo/calcpace/compare/v1.18.0...v1.18.1
|
|
654
|
+
[1.18.0]: https://github.com/0jonjo/calcpace/compare/v1.17.0...v1.18.0
|
|
624
655
|
[1.17.0]: https://github.com/0jonjo/calcpace/compare/v1.16.0...v1.17.0
|
|
625
656
|
[1.16.0]: https://github.com/0jonjo/calcpace/compare/v1.15.0...v1.16.0
|
|
626
657
|
[1.15.0]: https://github.com/0jonjo/calcpace/compare/v1.14.0...v1.15.0
|
data/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A Ruby gem for runners: pace, time, and distance calculations, unit conversions,
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem 'calcpace', '~> 1.18.
|
|
10
|
+
gem 'calcpace', '~> 1.18.1'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
@@ -276,10 +276,16 @@ calc.age_grade_label(65.2) # => "Local Clas
|
|
|
276
276
|
| 40–49.9% | Recreational |
|
|
277
277
|
| below 40% | Active Beginner |
|
|
278
278
|
|
|
279
|
-
The
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
The WMA / Alan Jones (Howard Grubb) tables are numeric age factors and open
|
|
280
|
+
standards only — they define no categories at all. The bands from Local Class
|
|
281
|
+
(60%) upward follow the USATF Masters / National Masters News convention; the
|
|
282
|
+
three bands below 60% are calcpace's own extension — most recreational
|
|
283
|
+
runners land there, and one catch-all label for everybody under 60% tells
|
|
284
|
+
them nothing.
|
|
285
|
+
|
|
286
|
+
The bands apply to the percentage as reported by `age_grade`, rounded to one
|
|
287
|
+
decimal, so calling `age_grade_label` directly on an unrounded value can land
|
|
288
|
+
one band lower at an edge.
|
|
283
289
|
|
|
284
290
|
Supported distances: 5K, 10K, half marathon, marathon.
|
|
285
291
|
|
|
@@ -685,6 +691,8 @@ All errors inherit from `Calcpace::Error`:
|
|
|
685
691
|
- `Calcpace::InvalidTimeFormatError` — time string not in `HH:MM:SS` or `MM:SS` format
|
|
686
692
|
- `Calcpace::UnsupportedUnitError` — unknown conversion (`convert`) or unknown
|
|
687
693
|
`unit:` / `distance_unit:` keyword
|
|
694
|
+
- `Calcpace::InvalidDataError` — the bundled data table failed its load-time
|
|
695
|
+
consistency check (raised on `require`, never for user input)
|
|
688
696
|
|
|
689
697
|
Argument validation that is not about units or numbers raises a plain `ArgumentError`:
|
|
690
698
|
unknown race names, unsupported age-grading distances, and invalid `age` / `sex` values.
|
data/lib/calcpace/age_grading.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'yaml'
|
|
4
|
+
require_relative 'errors'
|
|
4
5
|
|
|
5
6
|
# Module for age-grading race performances with a versioned table
|
|
6
7
|
#
|
|
@@ -28,9 +29,18 @@ module AgeGrading
|
|
|
28
29
|
aliases: false).freeze
|
|
29
30
|
TABLE_VERSION = OPEN_STANDARDS_DATA.fetch('meta').fetch('table_version').freeze
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
raw_age_grade_labels = OPEN_STANDARDS_DATA.fetch('age_grade_classifications').map do |entry|
|
|
32
33
|
{ min: entry.fetch('min').to_f, label: entry.fetch('label') }
|
|
33
|
-
end
|
|
34
|
+
end
|
|
35
|
+
AGE_GRADE_LABELS = raw_age_grade_labels.sort_by { |entry| -entry[:min] }.freeze
|
|
36
|
+
|
|
37
|
+
age_grade_label_mins = AGE_GRADE_LABELS.map { |entry| entry[:min] }
|
|
38
|
+
unless age_grade_label_mins.all?(&:finite?) &&
|
|
39
|
+
age_grade_label_mins == age_grade_label_mins.uniq &&
|
|
40
|
+
age_grade_label_mins.last == 0.0
|
|
41
|
+
raise Calcpace::InvalidDataError,
|
|
42
|
+
"age_grade_classifications must have unique finite mins ending at 0.0, got #{age_grade_label_mins.inspect}"
|
|
43
|
+
end
|
|
34
44
|
|
|
35
45
|
DISTANCE_TO_METERS = {
|
|
36
46
|
5.0 => '5000',
|
|
@@ -115,6 +125,7 @@ module AgeGrading
|
|
|
115
125
|
#
|
|
116
126
|
# @param percent [Numeric] age-grade percentage
|
|
117
127
|
# @return [String] category label
|
|
128
|
+
# @raise [ArgumentError] when percent is not numeric, is negative, or is NaN
|
|
118
129
|
def age_grade_label(percent)
|
|
119
130
|
percent_value = begin
|
|
120
131
|
Float(percent)
|
|
@@ -123,6 +134,7 @@ module AgeGrading
|
|
|
123
134
|
end
|
|
124
135
|
|
|
125
136
|
raise ArgumentError, 'Age-grade percent must be greater than or equal to 0' if percent_value.negative?
|
|
137
|
+
raise ArgumentError, 'Age-grade percent must be a number' if percent_value.nan?
|
|
126
138
|
|
|
127
139
|
AGE_GRADE_LABELS.find { |entry| percent_value >= entry[:min] }[:label]
|
|
128
140
|
end
|
|
@@ -3,10 +3,12 @@ meta:
|
|
|
3
3
|
url: "https://howardgrubb.co.uk/athletics/wmatnf23.html"
|
|
4
4
|
table_version: "WMA_2023_ONE_YEAR_FACTORS_V1"
|
|
5
5
|
|
|
6
|
-
# The
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# The WMA / Alan Jones (Howard Grubb) tables are numeric age factors and open
|
|
7
|
+
# standards only — they define no categories at all. The bands from Local
|
|
8
|
+
# Class (60%) upward follow the USATF Masters / National Masters News
|
|
9
|
+
# convention; the three bands under 60% are Calcpace's own extension, added
|
|
10
|
+
# to give recreational runners a meaningful label instead of a single
|
|
11
|
+
# catch-all.
|
|
10
12
|
age_grade_classifications:
|
|
11
13
|
- min: 100.0
|
|
12
14
|
label: "Approximate World Record Level"
|
data/lib/calcpace/errors.rb
CHANGED
data/lib/calcpace/version.rb
CHANGED
data/lib/calcpace.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'calcpace/errors'
|
|
3
4
|
require_relative 'calcpace/calculator'
|
|
4
5
|
require_relative 'calcpace/cameron_predictor'
|
|
5
6
|
require_relative 'calcpace/age_grading'
|
|
@@ -7,7 +8,6 @@ require_relative 'calcpace/environmental_adjuster'
|
|
|
7
8
|
require_relative 'calcpace/checker'
|
|
8
9
|
require_relative 'calcpace/converter'
|
|
9
10
|
require_relative 'calcpace/converter_chain'
|
|
10
|
-
require_relative 'calcpace/errors'
|
|
11
11
|
require_relative 'calcpace/fitness_predictor'
|
|
12
12
|
require_relative 'calcpace/lap_analyzer'
|
|
13
13
|
require_relative 'calcpace/pace_calculator'
|