run_ical 0.6.0 → 0.7.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -0
  3. data/lib/run_ical/data/advanced_10_k.rb +4 -1
  4. data/lib/run_ical/data/advanced_15_k.rb +4 -1
  5. data/lib/run_ical/data/advanced_5_k.rb +4 -1
  6. data/lib/run_ical/data/advanced_8_k.rb +4 -1
  7. data/lib/run_ical/data/half_marathon_advanced.rb +4 -1
  8. data/lib/run_ical/data/half_marathon_hm3.rb +4 -1
  9. data/lib/run_ical/data/half_marathon_intermediate1.rb +4 -1
  10. data/lib/run_ical/data/half_marathon_intermediate2.rb +4 -1
  11. data/lib/run_ical/data/half_marathon_novice1.rb +4 -1
  12. data/lib/run_ical/data/half_marathon_novice2.rb +4 -1
  13. data/lib/run_ical/data/half_marathon_walkers.rb +4 -1
  14. data/lib/run_ical/data/intermediate_10_k.rb +4 -1
  15. data/lib/run_ical/data/intermediate_15_k.rb +4 -1
  16. data/lib/run_ical/data/intermediate_5_k.rb +4 -1
  17. data/lib/run_ical/data/intermediate_8_k.rb +4 -1
  18. data/lib/run_ical/data/marathon_3.rb +4 -1
  19. data/lib/run_ical/data/marathon_advanced1.rb +4 -1
  20. data/lib/run_ical/data/marathon_advanced2.rb +4 -1
  21. data/lib/run_ical/data/marathon_boston_bound.rb +4 -1
  22. data/lib/run_ical/data/marathon_dopey_challenge.rb +4 -1
  23. data/lib/run_ical/data/marathon_intermediate1.rb +4 -1
  24. data/lib/run_ical/data/marathon_intermediate2.rb +4 -1
  25. data/lib/run_ical/data/marathon_novice1.rb +4 -1
  26. data/lib/run_ical/data/marathon_novice2.rb +4 -1
  27. data/lib/run_ical/data/marathon_novice_supreme.rb +4 -1
  28. data/lib/run_ical/data/marathon_personal_best.rb +4 -1
  29. data/lib/run_ical/data/marathon_senior.rb +4 -1
  30. data/lib/run_ical/data/novice_10_k.rb +4 -1
  31. data/lib/run_ical/data/novice_15_k.rb +4 -1
  32. data/lib/run_ical/data/novice_5_k.rb +4 -1
  33. data/lib/run_ical/data/novice_8_k.rb +4 -1
  34. data/lib/run_ical/data/walkers_5_k.rb +4 -1
  35. data/lib/run_ical/data/walking_10_k.rb +4 -1
  36. data/lib/run_ical/data.rb +33 -0
  37. data/lib/run_ical/version.rb +1 -1
  38. data/lib/run_ical.rb +1 -0
  39. data/tasks/higdon_programs.yml +74 -42
  40. data/tasks/refresh_data.rake +16 -5
  41. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 011dd6fbda180b144d3293c51cec404af07518f6ddc059b1ce36fe3d35a63436
4
- data.tar.gz: f8f78bebb340cf8d8d70953689187a640370031f8e47eaef4457ffad6c619e6d
3
+ metadata.gz: d5ca004077cbcc81dfe3584debff9c157b21fb439521950aa28cc6739c07d520
4
+ data.tar.gz: 39406a1d9aa8720c62cc6df951746d569a12cc8efac6585c382d2413582d0ebb
5
5
  SHA512:
6
- metadata.gz: db8c4844e30abe1aca9220e918b40bdda82983d2e5cafa118f919c16d71acfb2aec293ddca05580af2cc4a35f409e41ec41b303f7c7ad2a5a6daed3b53334345
7
- data.tar.gz: 2a84b004dd7987b3c88981698e20c5d4ae9c58c97fc30992ee7a33b4929b640b0a38e137f68493464bb03867d99d54c6fa15abde9364139d672089e20d61c6b7
6
+ metadata.gz: a7d516478167324349a6bccfea5d59a1e28098a581d510c0d5672571639858cc14be7c74a16439fc1bff685fc07616e7b2c3f4ef43ecdde162a6f430c7268ad7
7
+ data.tar.gz: 57a76a19c909f40af17cc1bc3286a669075ede76c27dcc7170f269c6b68d0302b0ac566d1ca3922d941e2343822ec2bacd778dc39a3d6ad01b150bd9aeba30bb
data/README.md CHANGED
@@ -50,6 +50,22 @@ program always lands on race day.
50
50
 
51
51
  `MarathonBostonBound` ends on a Monday (Patriots' Day); every other program ends on a Sunday.
52
52
 
53
+ ### Listing programs
54
+
55
+ `RunIcal::Data.constants` returns programs in load order, which is alphabetical by
56
+ filename and puts every Advanced plan ahead of every Novice one. To present them to
57
+ users, use the ordered helpers instead — distance ascending, then difficulty:
58
+
59
+ ```ruby
60
+ RunIcal::Data.programs # => [Novice5K, Intermediate5K, Advanced5K, Walkers5K, Novice8K, ...]
61
+ RunIcal::Data.by_distance # => { "5-K" => [...], "8-K" => [...], ..., "Marathon" => [...] }
62
+ RunIcal::Data.find("Novice5K")
63
+ ```
64
+
65
+ Each program exposes `DISTANCE`, `LEVEL`, `POSITION` and `DESCRIPTION`
66
+ (`"<DISTANCE> - <LEVEL>"`). Display order is the order of `tasks/higdon_programs.yml`
67
+ — reorder there and re-run the refresh to change it.
68
+
53
69
  ## Development
54
70
 
55
71
  ### Refreshing the training data
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Advanced10K
4
- DESCRIPTION = "10-K - Advanced"
4
+ DISTANCE = "10-K"
5
+ LEVEL = "Advanced"
6
+ POSITION = 9
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'advanced_10_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Advanced15K
4
- DESCRIPTION = "15-K - Advanced"
4
+ DISTANCE = "15-K"
5
+ LEVEL = "Advanced"
6
+ POSITION = 13
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'advanced_15_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Advanced5K
4
- DESCRIPTION = "5-K : Advanced"
4
+ DISTANCE = "5-K"
5
+ LEVEL = "Advanced"
6
+ POSITION = 2
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'advanced_5_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Advanced8K
4
- DESCRIPTION = "8-K - Advanced"
4
+ DISTANCE = "8-K"
5
+ LEVEL = "Advanced"
6
+ POSITION = 6
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'advanced_8_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class HalfMarathonAdvanced
4
- DESCRIPTION = "Half Marathon - Advanced"
4
+ DISTANCE = "Half Marathon"
5
+ LEVEL = "Advanced"
6
+ POSITION = 18
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'half_marathon_advanced.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class HalfMarathonHM3
4
- DESCRIPTION = "Half Marathon - HM3"
4
+ DISTANCE = "Half Marathon"
5
+ LEVEL = "HM3"
6
+ POSITION = 19
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'half_marathon_hm3.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class HalfMarathonIntermediate1
4
- DESCRIPTION = "Half Marathon - Intermediate 1"
4
+ DISTANCE = "Half Marathon"
5
+ LEVEL = "Intermediate 1"
6
+ POSITION = 16
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'half_marathon_intermediate1.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class HalfMarathonIntermediate2
4
- DESCRIPTION = "Half Marathon - Intermediate 2"
4
+ DISTANCE = "Half Marathon"
5
+ LEVEL = "Intermediate 2"
6
+ POSITION = 17
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'half_marathon_intermediate2.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class HalfMarathonNovice1
4
- DESCRIPTION = "Half Marathon - Novice 1"
4
+ DISTANCE = "Half Marathon"
5
+ LEVEL = "Novice 1"
6
+ POSITION = 14
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'half_marathon_novice1.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class HalfMarathonNovice2
4
- DESCRIPTION = "Half Marathon - Novice 2"
4
+ DISTANCE = "Half Marathon"
5
+ LEVEL = "Novice 2"
6
+ POSITION = 15
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'half_marathon_novice2.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class HalfMarathonWalkers
4
- DESCRIPTION = "Half Marathon - Walkers"
4
+ DISTANCE = "Half Marathon"
5
+ LEVEL = "Walkers"
6
+ POSITION = 20
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'half_marathon_walkers.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Intermediate10K
4
- DESCRIPTION = "10-K - Intermediate"
4
+ DISTANCE = "10-K"
5
+ LEVEL = "Intermediate"
6
+ POSITION = 8
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'intermediate_10_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Intermediate15K
4
- DESCRIPTION = "15-K - Intermediate"
4
+ DISTANCE = "15-K"
5
+ LEVEL = "Intermediate"
6
+ POSITION = 12
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'intermediate_15_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Intermediate5K
4
- DESCRIPTION = "5-K : Intermediate"
4
+ DISTANCE = "5-K"
5
+ LEVEL = "Intermediate"
6
+ POSITION = 1
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'intermediate_5_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Intermediate8K
4
- DESCRIPTION = "8-K - Intermediate"
4
+ DISTANCE = "8-K"
5
+ LEVEL = "Intermediate"
6
+ POSITION = 5
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'intermediate_8_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Marathon3
4
- DESCRIPTION = "Marathon : Marathon 3"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Marathon 3"
6
+ POSITION = 30
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_3.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonAdvanced1
4
- DESCRIPTION = "Marathon : Advanced 1"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Advanced 1"
6
+ POSITION = 25
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_advanced1.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonAdvanced2
4
- DESCRIPTION = "Marathon : Advanced 2"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Advanced 2"
6
+ POSITION = 26
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_advanced2.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonBostonBound
4
- DESCRIPTION = "Marathon : Boston Bound"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Boston Bound"
6
+ POSITION = 32
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_boston_bound.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonDopeyChallenge
4
- DESCRIPTION = "Marathon : Dopey Challenge"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Dopey Challenge"
6
+ POSITION = 31
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_dopey_challenge.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonIntermediate1
4
- DESCRIPTION = "Marathon : Intermediate 1"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Intermediate 1"
6
+ POSITION = 23
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_intermediate1.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonIntermediate2
4
- DESCRIPTION = "Marathon : Intermediate 2"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Intermediate 2"
6
+ POSITION = 24
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_intermediate2.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonNovice1
4
- DESCRIPTION = "Marathon : Novice 1"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Novice 1"
6
+ POSITION = 21
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_novice1.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonNovice2
4
- DESCRIPTION = "Marathon : Novice 2"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Novice 2"
6
+ POSITION = 22
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_novice2.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonNoviceSupreme
4
- DESCRIPTION = "Marathon : Novice Supreme"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Novice Supreme"
6
+ POSITION = 27
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_novice_supreme.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonPersonalBest
4
- DESCRIPTION = "Marathon : Personal Best"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Personal Best"
6
+ POSITION = 28
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_personal_best.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class MarathonSenior
4
- DESCRIPTION = "Marathon : Senior"
4
+ DISTANCE = "Marathon"
5
+ LEVEL = "Senior"
6
+ POSITION = 29
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'marathon_senior.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Novice10K
4
- DESCRIPTION = "10-K - Novice"
4
+ DISTANCE = "10-K"
5
+ LEVEL = "Novice"
6
+ POSITION = 7
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'novice_10_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Novice15K
4
- DESCRIPTION = "15-K - Novice"
4
+ DISTANCE = "15-K"
5
+ LEVEL = "Novice"
6
+ POSITION = 11
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'novice_15_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Novice5K
4
- DESCRIPTION = "5-K : Novice"
4
+ DISTANCE = "5-K"
5
+ LEVEL = "Novice"
6
+ POSITION = 0
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'novice_5_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Novice8K
4
- DESCRIPTION = "8-K - Novice"
4
+ DISTANCE = "8-K"
5
+ LEVEL = "Novice"
6
+ POSITION = 4
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'novice_8_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Walkers5K
4
- DESCRIPTION = "5-K - Walkers"
4
+ DISTANCE = "5-K"
5
+ LEVEL = "Walkers"
6
+ POSITION = 3
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'walkers_5_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -1,7 +1,10 @@
1
1
  module RunIcal
2
2
  module Data
3
3
  class Walking10K
4
- DESCRIPTION = "10-K - Walking"
4
+ DISTANCE = "10-K"
5
+ LEVEL = "Walkers"
6
+ POSITION = 10
7
+ DESCRIPTION = "#{DISTANCE} - #{LEVEL}"
5
8
  DATA = YAML.safe_load_file(File.join(__dir__, 'walking_10_k.yaml'), symbolize_names: true)
6
9
  end
7
10
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunIcal
4
+ # Namespace for the training plans in lib/run_ical/data.
5
+ #
6
+ # `constants` returns them in load order, which is alphabetical by filename and
7
+ # puts every Advanced plan before every Novice one. These helpers present them
8
+ # the way a runner would expect: by distance ascending, then by difficulty.
9
+ module Data
10
+ class << self
11
+ # Every program class, ordered for display.
12
+ def programs
13
+ constants.map { |name| const_get(name) }.sort_by { |program| program::POSITION }
14
+ end
15
+
16
+ # Programs grouped by distance, both groups and members in display order.
17
+ # Suitable for rendering <optgroup>s.
18
+ #
19
+ # RunIcal::Data.by_distance["5-K"].map(&:DESCRIPTION)
20
+ # #=> ["5-K - Novice", "5-K - Intermediate", ...]
21
+ def by_distance
22
+ programs.group_by { |program| program::DISTANCE }
23
+ end
24
+
25
+ # Look up a program class by its name, or nil.
26
+ def find(name)
27
+ return nil unless constants.include?(name.to_sym)
28
+
29
+ const_get(name.to_sym)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RunIcal
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/run_ical.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "run_ical/version"
4
4
  require "yaml"
5
5
  require "base64"
6
+ require "run_ical/data"
6
7
  Dir[File.expand_path("run_ical/data/*.rb", __dir__)].each do |file|
7
8
  require file
8
9
  end
@@ -1,179 +1,211 @@
1
1
  # Source manifest for `rake data:refresh`.
2
2
  #
3
- # One entry per training program. `class` is the public API name passed to
4
- # RunIcal::Generator.generate(program:) -- never rename an existing one.
3
+ # One entry per training program, in the order they should be presented to users:
4
+ # by distance ascending, then by difficulty. `rake data:refresh` emits POSITION
5
+ # from this order, so reordering here reorders RunIcal::Data.programs.
6
+ #
7
+ # `class` is the public API name passed to RunIcal::Generator.generate(program:)
8
+ # -- never rename an existing one. DESCRIPTION is derived as "<distance> - <level>".
5
9
  # `weeks` is asserted against the scraped table so a silent site change fails loudly.
6
10
  ---
7
11
  - class: Novice5K
8
12
  file: novice_5_k
9
- description: "5-K : Novice"
13
+ distance: "5-K"
14
+ level: "Novice"
10
15
  weeks: 8
11
16
  url: https://www.halhigdon.com/training-programs/5k-training/novice-5k/
12
17
  - class: Intermediate5K
13
18
  file: intermediate_5_k
14
- description: "5-K : Intermediate"
19
+ distance: "5-K"
20
+ level: "Intermediate"
15
21
  weeks: 8
16
22
  url: https://www.halhigdon.com/training-programs/5k-training/intermediate-5k/
17
23
  - class: Advanced5K
18
24
  file: advanced_5_k
19
- description: "5-K : Advanced"
25
+ distance: "5-K"
26
+ level: "Advanced"
20
27
  weeks: 8
21
28
  url: https://www.halhigdon.com/training-programs/5k-training/advanced-5k/
22
29
  - class: Walkers5K
23
30
  file: walkers_5_k
24
- description: "5-K - Walkers"
31
+ distance: "5-K"
32
+ level: "Walkers"
25
33
  weeks: 8
26
34
  url: https://www.halhigdon.com/training-programs/5k-training/walkers-5k/
27
-
28
35
  - class: Novice8K
29
36
  file: novice_8_k
30
- description: "8-K - Novice"
37
+ distance: "8-K"
38
+ level: "Novice"
31
39
  weeks: 8
32
40
  url: https://www.halhigdon.com/training-programs/8k-training/novice-8k/
33
41
  - class: Intermediate8K
34
42
  file: intermediate_8_k
35
- description: "8-K - Intermediate"
43
+ distance: "8-K"
44
+ level: "Intermediate"
36
45
  weeks: 8
37
46
  url: https://www.halhigdon.com/training-programs/8k-training/intermediate-8k/
38
47
  - class: Advanced8K
39
48
  file: advanced_8_k
40
- description: "8-K - Advanced"
49
+ distance: "8-K"
50
+ level: "Advanced"
41
51
  weeks: 8
42
52
  url: https://www.halhigdon.com/training-programs/8k-training/advanced-8k/
43
-
44
53
  - class: Novice10K
45
54
  file: novice_10_k
46
- description: "10-K - Novice"
55
+ distance: "10-K"
56
+ level: "Novice"
47
57
  weeks: 8
48
58
  url: https://www.halhigdon.com/training-programs/10k-training/novice-10k/
49
59
  - class: Intermediate10K
50
60
  file: intermediate_10_k
51
- description: "10-K - Intermediate"
61
+ distance: "10-K"
62
+ level: "Intermediate"
52
63
  weeks: 8
53
64
  url: https://www.halhigdon.com/training-programs/10k-training/intermediate-10k/
54
65
  - class: Advanced10K
55
66
  file: advanced_10_k
56
- description: "10-K - Advanced"
67
+ distance: "10-K"
68
+ level: "Advanced"
57
69
  weeks: 8
58
70
  url: https://www.halhigdon.com/training-programs/10k-training/advanced-10k/
59
71
  - class: Walking10K
60
72
  file: walking_10_k
61
- description: "10-K - Walking"
73
+ distance: "10-K"
74
+ level: "Walkers"
62
75
  weeks: 8
63
76
  url: https://www.halhigdon.com/training-programs/10k-training/walkers-10k/
64
-
65
77
  - class: Novice15K
66
78
  file: novice_15_k
67
- description: "15-K - Novice"
79
+ distance: "15-K"
80
+ level: "Novice"
68
81
  weeks: 10
69
82
  url: https://www.halhigdon.com/training-programs/15k-10-mile-training/novice-15k-10-mile/
70
83
  - class: Intermediate15K
71
84
  file: intermediate_15_k
72
- description: "15-K - Intermediate"
85
+ distance: "15-K"
86
+ level: "Intermediate"
73
87
  weeks: 10
74
88
  url: https://www.halhigdon.com/training-programs/15k-10-mile-training/intermediate-15k-10-mile/
75
89
  - class: Advanced15K
76
90
  file: advanced_15_k
77
- description: "15-K - Advanced"
91
+ distance: "15-K"
92
+ level: "Advanced"
78
93
  weeks: 10
79
94
  url: https://www.halhigdon.com/training-programs/15k-10-mile-training/advanced-15k-10-mile/
80
-
81
95
  - class: HalfMarathonNovice1
82
96
  file: half_marathon_novice1
83
- description: "Half Marathon - Novice 1"
97
+ distance: "Half Marathon"
98
+ level: "Novice 1"
84
99
  weeks: 12
85
100
  url: https://www.halhigdon.com/training-programs/half-marathon-training/novice-1-half-marathon/
86
101
  - class: HalfMarathonNovice2
87
102
  file: half_marathon_novice2
88
- description: "Half Marathon - Novice 2"
103
+ distance: "Half Marathon"
104
+ level: "Novice 2"
89
105
  weeks: 12
90
106
  url: https://www.halhigdon.com/training-programs/half-marathon-training/novice-2-half-marathon/
91
107
  - class: HalfMarathonIntermediate1
92
108
  file: half_marathon_intermediate1
93
- description: "Half Marathon - Intermediate 1"
109
+ distance: "Half Marathon"
110
+ level: "Intermediate 1"
94
111
  weeks: 12
95
112
  url: https://www.halhigdon.com/training-programs/half-marathon-training/intermediate-1-half-marathon/
96
113
  - class: HalfMarathonIntermediate2
97
114
  file: half_marathon_intermediate2
98
- description: "Half Marathon - Intermediate 2"
115
+ distance: "Half Marathon"
116
+ level: "Intermediate 2"
99
117
  weeks: 12
100
118
  url: https://www.halhigdon.com/training-programs/half-marathon-training/intermediate-2-half-marathon/
101
119
  - class: HalfMarathonAdvanced
102
120
  file: half_marathon_advanced
103
- description: "Half Marathon - Advanced"
121
+ distance: "Half Marathon"
122
+ level: "Advanced"
104
123
  weeks: 12
105
124
  url: https://www.halhigdon.com/training-programs/half-marathon-training/advanced-half-marathon/
106
125
  - class: HalfMarathonHM3
107
126
  file: half_marathon_hm3
108
- description: "Half Marathon - HM3"
127
+ distance: "Half Marathon"
128
+ level: "HM3"
109
129
  weeks: 12
110
130
  url: https://www.halhigdon.com/training-programs/half-marathon-training/half-marathon-3/
111
131
  - class: HalfMarathonWalkers
112
132
  file: half_marathon_walkers
113
- description: "Half Marathon - Walkers"
133
+ distance: "Half Marathon"
134
+ level: "Walkers"
114
135
  weeks: 12
115
136
  url: https://www.halhigdon.com/training-programs/half-marathon-training/walkers-half-marathon/
116
-
117
137
  - class: MarathonNovice1
118
138
  file: marathon_novice1
119
- description: "Marathon : Novice 1"
139
+ distance: "Marathon"
140
+ level: "Novice 1"
120
141
  weeks: 18
121
142
  url: https://www.halhigdon.com/training-programs/marathon-training/novice-1-marathon/
122
143
  - class: MarathonNovice2
123
144
  file: marathon_novice2
124
- description: "Marathon : Novice 2"
145
+ distance: "Marathon"
146
+ level: "Novice 2"
125
147
  weeks: 18
126
148
  url: https://www.halhigdon.com/training-programs/marathon-training/novice-2-marathon/
127
149
  - class: MarathonIntermediate1
128
150
  file: marathon_intermediate1
129
- description: "Marathon : Intermediate 1"
151
+ distance: "Marathon"
152
+ level: "Intermediate 1"
130
153
  weeks: 18
131
154
  url: https://www.halhigdon.com/training-programs/marathon-training/intermediate-1-marathon/
132
155
  - class: MarathonIntermediate2
133
156
  file: marathon_intermediate2
134
- description: "Marathon : Intermediate 2"
157
+ distance: "Marathon"
158
+ level: "Intermediate 2"
135
159
  weeks: 18
136
160
  url: https://www.halhigdon.com/training-programs/marathon-training/intermediate-2-marathon/
137
161
  - class: MarathonAdvanced1
138
162
  file: marathon_advanced1
139
- description: "Marathon : Advanced 1"
163
+ distance: "Marathon"
164
+ level: "Advanced 1"
140
165
  weeks: 18
141
166
  url: https://www.halhigdon.com/training-programs/marathon-training/advanced-1-marathon/
142
167
  - class: MarathonAdvanced2
143
168
  file: marathon_advanced2
144
- description: "Marathon : Advanced 2"
169
+ distance: "Marathon"
170
+ level: "Advanced 2"
145
171
  weeks: 18
146
172
  url: https://www.halhigdon.com/training-programs/marathon-training/advanced-2-marathon/
147
173
  - class: MarathonNoviceSupreme
148
174
  file: marathon_novice_supreme
149
- description: "Marathon : Novice Supreme"
175
+ distance: "Marathon"
176
+ level: "Novice Supreme"
150
177
  weeks: 30
151
178
  url: https://www.halhigdon.com/training-programs/marathon-training/novice-supreme/
152
179
  - class: MarathonPersonalBest
153
180
  file: marathon_personal_best
154
- description: "Marathon : Personal Best"
181
+ distance: "Marathon"
182
+ level: "Personal Best"
155
183
  weeks: 30
156
184
  url: https://www.halhigdon.com/training-programs/marathon-training/personal-best/
157
185
  - class: MarathonSenior
158
186
  file: marathon_senior
159
- description: "Marathon : Senior"
187
+ distance: "Marathon"
188
+ level: "Senior"
160
189
  weeks: 8
161
190
  url: https://www.halhigdon.com/training-programs/marathon-training/senior/
162
191
  - class: Marathon3
163
192
  file: marathon_3
164
- description: "Marathon : Marathon 3"
193
+ distance: "Marathon"
194
+ level: "Marathon 3"
165
195
  weeks: 24
166
196
  url: https://www.halhigdon.com/training-programs/marathon-training/marathon-3/
167
197
  - class: MarathonDopeyChallenge
168
198
  file: marathon_dopey_challenge
169
- description: "Marathon : Dopey Challenge"
199
+ distance: "Marathon"
200
+ level: "Dopey Challenge"
170
201
  weeks: 18
171
202
  url: https://www.halhigdon.com/training-programs/marathon-training/dopey-challenge/
172
- # Boston is run on Patriots' Day (a Monday), so the final table row holds a single
173
- # Monday cell instead of a full week. The scraper truncates to 12 full weeks + race day.
203
+ # Boston is run on Patriots Day (a Monday), so its final table row holds a single
204
+ # Monday cell instead of a full week. The scraper truncates to 12 weeks + race day.
174
205
  - class: MarathonBostonBound
175
206
  file: marathon_boston_bound
176
- description: "Marathon : Boston Bound"
207
+ distance: "Marathon"
208
+ level: "Boston Bound"
177
209
  weeks: 13
178
210
  partial_final_week: true
179
211
  url: https://www.halhigdon.com/training-programs/marathon-training/boston-bound/
@@ -26,9 +26,11 @@ module HigdonScraper
26
26
 
27
27
  module_function
28
28
 
29
- # Every program in the manifest, or the single one named.
29
+ # Every program in the manifest, or the single one named. Position is taken from
30
+ # the full manifest so refreshing one program still writes the right ordering.
30
31
  def programs(only = nil)
31
32
  all = YAML.safe_load_file(MANIFEST)
33
+ all.each_with_index { |program, index| program["position"] = index }
32
34
  return all unless only
33
35
 
34
36
  selected = all.select { |program| program["class"] == only }
@@ -117,7 +119,7 @@ module HigdonScraper
117
119
 
118
120
  def write(program, activities)
119
121
  write_yaml(program, activities)
120
- write_loader(program)
122
+ write_loader(program, program["position"])
121
123
  end
122
124
 
123
125
  # Ruby's emitter quotes numeric-looking strings, which keeps the marathon plans'
@@ -127,12 +129,21 @@ module HigdonScraper
127
129
  File.write(File.join(DATA_DIR, "#{program['file']}.yaml"), body.to_yaml)
128
130
  end
129
131
 
130
- def write_loader(program)
131
- File.write(File.join(DATA_DIR, "#{program['file']}.rb"), <<~RUBY)
132
+ def write_loader(program, position)
133
+ File.write(File.join(DATA_DIR, "#{program['file']}.rb"), loader_source(program, position))
134
+ end
135
+
136
+ # POSITION is the program's index in the manifest, which is ordered by distance
137
+ # then difficulty -- see RunIcal::Data.programs.
138
+ def loader_source(program, position)
139
+ <<~RUBY
132
140
  module RunIcal
133
141
  module Data
134
142
  class #{program['class']}
135
- DESCRIPTION = #{program['description'].inspect}
143
+ DISTANCE = #{program['distance'].inspect}
144
+ LEVEL = #{program['level'].inspect}
145
+ POSITION = #{position}
146
+ DESCRIPTION = "\#{DISTANCE} - \#{LEVEL}"
136
147
  DATA = YAML.safe_load_file(File.join(__dir__, '#{program['file']}.yaml'), symbolize_names: true)
137
148
  end
138
149
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: run_ical
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Cashin
@@ -225,6 +225,7 @@ files:
225
225
  - bin/setup
226
226
  - exe/run_ical
227
227
  - lib/run_ical.rb
228
+ - lib/run_ical/data.rb
228
229
  - lib/run_ical/data/advanced_10_k.rb
229
230
  - lib/run_ical/data/advanced_10_k.yaml
230
231
  - lib/run_ical/data/advanced_15_k.rb