calendarium-romanum 0.5.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +50 -0
  5. data/.travis.yml +23 -0
  6. data/.yardopts +3 -0
  7. data/Appraisals +67 -0
  8. data/CHANGELOG.md +488 -0
  9. data/Gemfile +26 -0
  10. data/Gemfile.lock +95 -0
  11. data/README.md +601 -0
  12. data/Rakefile +27 -0
  13. data/bin/calendariumrom +3 -0
  14. data/calendarium-romanum.gemspec +31 -0
  15. data/config/locales/cs.yml +4 -0
  16. data/config/locales/en.yml +20 -14
  17. data/config/locales/es.yml +94 -0
  18. data/config/locales/fr.yml +6 -0
  19. data/config/locales/it.yml +6 -0
  20. data/config/locales/la.yml +6 -0
  21. data/config/locales/pt.yml +94 -0
  22. data/data/README.md +70 -24
  23. data/data/czech-brno-cs.txt +4 -6
  24. data/data/czech-budejovice-cs.txt +4 -6
  25. data/data/czech-cechy-cs.txt +4 -5
  26. data/data/czech-cs.txt +239 -235
  27. data/data/czech-hradec-cs.txt +3 -5
  28. data/data/czech-litomerice-cs.txt +5 -7
  29. data/data/czech-morava-cs.txt +4 -5
  30. data/data/czech-olomouc-cs.txt +2 -4
  31. data/data/czech-ostrava-cs.txt +3 -5
  32. data/data/czech-plzen-cs.txt +3 -5
  33. data/data/czech-praha-cs.txt +3 -4
  34. data/data/easter_dates.txt +67 -0
  35. data/data/universal-1969-la.txt +234 -0
  36. data/data/universal-en.txt +217 -211
  37. data/data/universal-es.txt +246 -0
  38. data/data/universal-fr.txt +217 -210
  39. data/data/universal-it.txt +217 -211
  40. data/data/universal-la.txt +217 -212
  41. data/data/universal-pt.txt +248 -0
  42. data/doc/data_readme.md +2 -0
  43. data/doc/images/class_diagram.png +0 -0
  44. data/doc/images/class_diagram.puml +44 -0
  45. data/doc/yard_readme.rdoc +76 -0
  46. data/lib/calendarium-romanum.rb +16 -2
  47. data/lib/calendarium-romanum/abstract_date.rb +15 -0
  48. data/lib/calendarium-romanum/calendar.rb +150 -33
  49. data/lib/calendarium-romanum/cli.rb +80 -100
  50. data/lib/calendarium-romanum/cli/comparator.rb +83 -0
  51. data/lib/calendarium-romanum/cli/date_parser.rb +30 -0
  52. data/lib/calendarium-romanum/cli/dumper.rb +68 -0
  53. data/lib/calendarium-romanum/cli/helper.rb +23 -0
  54. data/lib/calendarium-romanum/cli/querier.rb +73 -0
  55. data/lib/calendarium-romanum/cr.rb +16 -0
  56. data/lib/calendarium-romanum/data.rb +40 -8
  57. data/lib/calendarium-romanum/day.rb +187 -32
  58. data/lib/calendarium-romanum/enum.rb +41 -24
  59. data/lib/calendarium-romanum/enums.rb +127 -43
  60. data/lib/calendarium-romanum/errors.rb +1 -1
  61. data/lib/calendarium-romanum/ordinalizer.rb +10 -1
  62. data/lib/calendarium-romanum/perpetual_calendar.rb +58 -7
  63. data/lib/calendarium-romanum/rank.rb +39 -8
  64. data/lib/calendarium-romanum/rank_predicates.rb +43 -0
  65. data/lib/calendarium-romanum/sanctorale.rb +213 -23
  66. data/lib/calendarium-romanum/sanctorale_factory.rb +74 -3
  67. data/lib/calendarium-romanum/sanctorale_loader.rb +180 -0
  68. data/lib/calendarium-romanum/sanctorale_writer.rb +124 -0
  69. data/lib/calendarium-romanum/temporale.rb +222 -42
  70. data/lib/calendarium-romanum/temporale/celebration_factory.rb +68 -9
  71. data/lib/calendarium-romanum/temporale/date_helper.rb +85 -0
  72. data/lib/calendarium-romanum/temporale/dates.rb +52 -59
  73. data/lib/calendarium-romanum/temporale/easter_table.rb +27 -0
  74. data/lib/calendarium-romanum/temporale/extensions.rb +15 -0
  75. data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +16 -3
  76. data/lib/calendarium-romanum/temporale/extensions/dedication_before_all_saints.rb +73 -0
  77. data/lib/calendarium-romanum/transfers.rb +84 -24
  78. data/lib/calendarium-romanum/util.rb +21 -23
  79. data/lib/calendarium-romanum/version.rb +3 -2
  80. data/liturgical_law/1969_normae_universales.md +568 -0
  81. data/liturgical_law/1977_decretum_de_celebratione_baptismatis_domini.md +58 -0
  82. data/liturgical_law/1990_decretum_de_variatione_inducenda.md +67 -0
  83. data/liturgical_law/1998_notificatio_de_occurrentia.md +57 -0
  84. data/liturgical_law/2002_normae_universales.md +946 -0
  85. data/liturgical_law/2006_notification.md +37 -0
  86. data/liturgical_law/2012_declarationes.md +38 -0
  87. data/liturgical_law/2020_dubia_de_calendario_2022.md +100 -0
  88. data/liturgical_law/README.md +74 -0
  89. metadata +61 -38
  90. data/lib/calendarium-romanum/sanctoraleloader.rb +0 -122
  91. data/spec/abstract_date_spec.rb +0 -62
  92. data/spec/calendar_spec.rb +0 -559
  93. data/spec/celebration_factory_spec.rb +0 -16
  94. data/spec/celebration_spec.rb +0 -43
  95. data/spec/cli_spec.rb +0 -155
  96. data/spec/colour_spec.rb +0 -17
  97. data/spec/data_spec.rb +0 -23
  98. data/spec/date_parser_spec.rb +0 -68
  99. data/spec/date_spec.rb +0 -61
  100. data/spec/dates_spec.rb +0 -45
  101. data/spec/day_spec.rb +0 -108
  102. data/spec/enum_spec.rb +0 -51
  103. data/spec/i18n_spec.rb +0 -58
  104. data/spec/ordinalizer_spec.rb +0 -36
  105. data/spec/perpetual_calendar_spec.rb +0 -91
  106. data/spec/rank_spec.rb +0 -57
  107. data/spec/readme_spec.rb +0 -56
  108. data/spec/sanctorale_factory_spec.rb +0 -42
  109. data/spec/sanctorale_spec.rb +0 -191
  110. data/spec/sanctoraleloader_spec.rb +0 -176
  111. data/spec/season_spec.rb +0 -17
  112. data/spec/spec_helper.rb +0 -46
  113. data/spec/temporale_spec.rb +0 -572
@@ -0,0 +1,124 @@
1
+ require 'yaml'
2
+
3
+ module CalendariumRomanum
4
+
5
+ # Understands a custom plaintext calendar format
6
+ # and knows how to transform the {Celebration}s in a
7
+ # {Sanctorale} to this format.
8
+ #
9
+ # For specification of the data format see {file:data/README.md}
10
+ # of the data directory, For a complete example see e.g.
11
+ # {file:universal-en.txt the file describing General Roman Calendar}.
12
+ #
13
+ # @since 0.8.0
14
+ class SanctoraleWriter
15
+
16
+ # @api private
17
+ RANK_CODES = {
18
+ Ranks::TRIDUUM => 's1.1',
19
+ Ranks::PRIMARY => 's1.2',
20
+ Ranks::SOLEMNITY_GENERAL => 's',
21
+ Ranks::SOLEMNITY_PROPER => 's1.4',
22
+
23
+ Ranks::FEAST_LORD_GENERAL => 'f2.5',
24
+ Ranks::SUNDAY_UNPRIVILEGED => 'f2.6',
25
+ Ranks::FEAST_GENERAL => 'f',
26
+ Ranks::FEAST_PROPER => 'f2.8',
27
+ Ranks::FERIAL_PRIVILEGED => 'f2.9',
28
+
29
+ Ranks::MEMORIAL_GENERAL => 'm',
30
+ Ranks::MEMORIAL_PROPER => 'm3.11',
31
+ Ranks::MEMORIAL_OPTIONAL => 'm3.12',
32
+ Ranks::FERIAL => 'm3.13',
33
+
34
+ Ranks::COMMEMORATION => '4.0'
35
+ }.freeze
36
+
37
+ # @api private
38
+ COLOUR_CODES = {
39
+ Colours::WHITE => 'W',
40
+ Colours::VIOLET => 'V',
41
+ Colours::GREEN => 'G',
42
+ Colours::RED => 'R'
43
+ }.freeze
44
+
45
+ # @param front_matter [Boolean] Should YAML front matter be written?
46
+ def initialize(front_matter: true)
47
+ @write_front_matter = front_matter
48
+ end
49
+
50
+ # Write to an object which understands +#<<+
51
+ #
52
+ # @param src [Sanctorale]
53
+ # source of the loaded data
54
+ # @param dest [String, File, #<<]
55
+ # object to populate. If not provided, a new {String}
56
+ # instance will be created and returned
57
+ # @return [String]
58
+ def write(src, dest = nil)
59
+ dest ||= String.new
60
+
61
+ # Write metadata to YAML if present
62
+ unless (!@write_front_matter) || src.metadata.nil? || src.metadata.empty?
63
+ dest << src.metadata.to_yaml
64
+ dest << "---\n"
65
+ end
66
+
67
+ # Write each celebration, grouped by month with headings
68
+ current_month = 0
69
+ src.each_day.sort_by{ |date, _| date }.each do |date, celebrations|
70
+ if date.month > current_month
71
+ current_month = date.month
72
+ dest << "\n= #{current_month}\n"
73
+ end
74
+
75
+ celebrations.each do |c|
76
+ dest << celebration_line(date, c)
77
+ dest << "\n"
78
+ end
79
+ end
80
+
81
+ dest
82
+ end
83
+
84
+ alias write_to_string write
85
+
86
+ # Write to a filesystem path
87
+ #
88
+ # @param sanctorale [Sanctorale]
89
+ # @param filename [String]
90
+ # @param encoding [String]
91
+ # @return [void]
92
+ def write_to_file(sanctorale, filename, encoding = 'utf-8')
93
+ File.open(filename, 'w', encoding: encoding) do |f|
94
+ write(sanctorale, f)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ # Convert a {Celebration} to a {String} for writing
101
+ def celebration_line(date, celebration)
102
+ line = "#{date.day} "
103
+
104
+ unless celebration.rank == Ranks::MEMORIAL_OPTIONAL
105
+ code = RANK_CODES[celebration.rank]
106
+ line << "#{code} "
107
+ end
108
+
109
+ unless celebration.colour == Colours::WHITE
110
+ code = COLOUR_CODES[celebration.colour]
111
+ line << "#{code} "
112
+ end
113
+
114
+ unless celebration.symbol.nil?
115
+ line << "#{celebration.symbol} "
116
+ end
117
+
118
+ line << ': '
119
+ line << celebration.title
120
+
121
+ line
122
+ end
123
+ end
124
+ end
@@ -1,51 +1,82 @@
1
- require 'date'
2
-
3
1
  module CalendariumRomanum
4
2
 
5
- # determine seasons and dates of the Temporale feasts of the given year
3
+ # One of the two main {Calendar} components.
4
+ # Handles seasons and celebrations of the temporale cycle
5
+ # for a given liturgical year.
6
6
  class Temporale
7
7
 
8
+ # How many days in a week
8
9
  WEEK = 7
9
10
 
11
+ # Which solemnities can be transferred to Sunday
10
12
  SUNDAY_TRANSFERABLE_SOLEMNITIES =
11
13
  %i(epiphany ascension corpus_christi).freeze
12
14
 
13
- # year is Integer - the civil year when the liturgical year begins
15
+ # @param year [Integer]
16
+ # the civil year when the liturgical year _begins_
17
+ # @param extensions [Array<#each_celebration>]
18
+ # extensions implementing custom temporale celebrations
19
+ # @param transfer_to_sunday [Array<Symbol>]
20
+ # which solemnities should be transferred to a nearby
21
+ # Sunday - see {SUNDAY_TRANSFERABLE_SOLEMNITIES}
22
+ # for possible values
14
23
  def initialize(year, extensions: [], transfer_to_sunday: [])
15
24
  @year = year
16
25
 
17
26
  @extensions = extensions
18
- @transfer_to_sunday = transfer_to_sunday
27
+ @transfer_to_sunday = transfer_to_sunday.sort
19
28
  validate_sunday_transfer!
20
29
 
21
30
  prepare_solemnities
22
31
  end
23
32
 
33
+ # @return [Integer]
24
34
  attr_reader :year
25
35
 
26
36
  class << self
27
37
  # Determines liturgical year for the given date
38
+ #
39
+ # @param date [Date]
40
+ # @return [Integer]
28
41
  def liturgical_year(date)
29
42
  year = date.year
30
- temporale = Temporale.new year
31
43
 
32
- if date < temporale.first_advent_sunday
44
+ if date < Dates.first_advent_sunday(year)
33
45
  return year - 1
34
46
  end
35
47
 
36
48
  year
37
49
  end
38
50
 
39
- # creates a Calendar for the liturgical year including given
51
+ # Creates an instance for the liturgical year including given
40
52
  # date
53
+ #
54
+ # @param date [Date]
55
+ # @return [Temporale]
41
56
  def for_day(date)
42
57
  new(liturgical_year(date))
43
58
  end
44
59
 
60
+ # Factory method creating temporale {Celebration}s
61
+ # with sensible defaults
62
+ #
63
+ # See {Celebration#initialize} for argument description.
64
+ def create_celebration(title, rank, colour, symbol: nil, date: nil, sunday: nil)
65
+ Celebration.new(
66
+ title: title,
67
+ rank: rank,
68
+ colour: colour,
69
+ symbol: symbol,
70
+ date: date,
71
+ cycle: :temporale,
72
+ sunday: sunday
73
+ )
74
+ end
75
+
45
76
  C = Struct.new(:date_method, :celebration)
46
77
  private_constant :C
47
78
 
48
- # implementation detail, not to be touched by client code
79
+ # @api private
49
80
  def celebrations
50
81
  @celebrations ||=
51
82
  begin
@@ -64,6 +95,7 @@ module CalendariumRomanum
64
95
  pentecost
65
96
  holy_trinity
66
97
  corpus_christi
98
+ mother_of_church
67
99
  sacred_heart
68
100
  christ_king
69
101
  immaculate_heart
@@ -74,30 +106,50 @@ module CalendariumRomanum
74
106
  CelebrationFactory.public_send(symbol)
75
107
  )
76
108
  end
77
- # Immaculate Heart of Mary is actually (currently the only one)
78
- # movable *sanctorale* feast, but as it would make little sense
109
+ # Immaculate Heart of Mary and Mary, Mother of the Church
110
+ # are actually movable *sanctorale* feasts,
111
+ # but as it would make little sense
79
112
  # to add support for movable sanctorale feasts because of
80
- # a single one, we cheat a bit and handle it in temporale.
113
+ # two, we cheat a bit and handle them in temporale.
81
114
  end
82
115
  end
83
116
  end
84
117
 
118
+ # Does this instance transfer the specified solemnity to Sunday?
119
+ #
120
+ # @param solemnity [Symbol]
121
+ # @return [Boolean]
85
122
  def transferred_to_sunday?(solemnity)
86
123
  @transfer_to_sunday.include?(solemnity)
87
124
  end
88
125
 
126
+ # First day of the liturgical year
127
+ #
128
+ # @return [Date]
89
129
  def start_date
90
130
  first_advent_sunday
91
131
  end
92
132
 
133
+ # Last day of the liturgical year
134
+ #
135
+ # @return [Date]
93
136
  def end_date
94
137
  Dates.first_advent_sunday(year + 1) - 1
95
138
  end
96
139
 
140
+ # Date range of the liturgical year
141
+ #
142
+ # @return [Range<Date>]
97
143
  def date_range
98
144
  start_date .. end_date
99
145
  end
100
146
 
147
+ # Check that the date belongs to the liturgical year.
148
+ # If it does not, throw exception.
149
+ #
150
+ # @param date [Date]
151
+ # @return [void]
152
+ # @raise [RangeError]
101
153
  def range_check(date)
102
154
  # necessary in order to handle Date correctly
103
155
  date = date.to_date if date.class != Date
@@ -107,6 +159,44 @@ module CalendariumRomanum
107
159
  end
108
160
  end
109
161
 
162
+ # @!method nativity
163
+ # @return [Date]
164
+ # @!method holy_family
165
+ # @return [Date]
166
+ # @!method mother_of_god
167
+ # @return [Date]
168
+ # @!method epiphany
169
+ # @return [Date]
170
+ # @!method baptism_of_lord
171
+ # @return [Date]
172
+ # @!method ash_wednesday
173
+ # @return [Date]
174
+ # @!method good_friday
175
+ # @return [Date]
176
+ # @!method holy_saturday
177
+ # @return [Date]
178
+ # @!method palm_sunday
179
+ # @return [Date]
180
+ # @!method easter_sunday
181
+ # @return [Date]
182
+ # @!method ascension
183
+ # @return [Date]
184
+ # @!method pentecost
185
+ # @return [Date]
186
+ # @!method holy_trinity
187
+ # @return [Date]
188
+ # @!method corpus_christi
189
+ # @return [Date]
190
+ # @!method mother_of_church
191
+ # @return [Date]
192
+ # @!method sacred_heart
193
+ # @return [Date]
194
+ # @!method christ_king
195
+ # @return [Date]
196
+ # @!method immaculate_heart
197
+ # @return [Date]
198
+ # @!method first_advent_sunday
199
+ # @return [Date]
110
200
  (celebrations.collect(&:date_method) + [:first_advent_sunday])
111
201
  .each do |feast|
112
202
  if SUNDAY_TRANSFERABLE_SOLEMNITIES.include? feast
@@ -124,24 +214,33 @@ module CalendariumRomanum
124
214
  end
125
215
  end
126
216
 
127
- # which liturgical season is it?
217
+ # Determine liturgical season for a given date
218
+ #
219
+ # @param date [Date]
220
+ # @return [Season]
221
+ # @raise [RangeError]
222
+ # if the given date doesn't belong to the liturgical year
128
223
  def season(date)
129
224
  range_check date
130
225
 
131
- if (first_advent_sunday <= date) &&
226
+ if first_advent_sunday <= date &&
132
227
  nativity > date
133
228
  Seasons::ADVENT
134
229
 
135
- elsif (nativity <= date) &&
136
- (baptism_of_lord >= date)
230
+ elsif nativity <= date &&
231
+ baptism_of_lord >= date
137
232
  Seasons::CHRISTMAS
138
233
 
139
- elsif (ash_wednesday <= date) &&
140
- easter_sunday > date
234
+ elsif ash_wednesday <= date &&
235
+ good_friday > date
141
236
  Seasons::LENT
142
237
 
143
- elsif (easter_sunday <= date) &&
144
- (pentecost >= date)
238
+ elsif good_friday <= date &&
239
+ easter_sunday >= date
240
+ Seasons::TRIDUUM
241
+
242
+ elsif easter_sunday < date &&
243
+ pentecost >= date
145
244
  Seasons::EASTER
146
245
 
147
246
  else
@@ -149,6 +248,10 @@ module CalendariumRomanum
149
248
  end
150
249
  end
151
250
 
251
+ # When the specified liturgical season begins
252
+ #
253
+ # @param s [Season]
254
+ # @return [Date]
152
255
  def season_beginning(s)
153
256
  case s
154
257
  when Seasons::ADVENT
@@ -157,15 +260,21 @@ module CalendariumRomanum
157
260
  nativity
158
261
  when Seasons::LENT
159
262
  ash_wednesday
263
+ when Seasons::TRIDUUM
264
+ good_friday
160
265
  when Seasons::EASTER
161
- easter_sunday
162
- when Seasons::ORDINARY # ordinary time
266
+ easter_sunday + 1
267
+ when Seasons::ORDINARY
163
268
  baptism_of_lord + 1
164
269
  else
165
270
  raise ArgumentError.new('unsupported season')
166
271
  end
167
272
  end
168
273
 
274
+ # Determine week of a season for a given date
275
+ #
276
+ # @param seasonn [Season]
277
+ # @param date [Date]
169
278
  def season_week(seasonn, date)
170
279
  week1_beginning = season_beginning = season_beginning(seasonn)
171
280
  unless season_beginning.sunday?
@@ -174,11 +283,13 @@ module CalendariumRomanum
174
283
 
175
284
  week = date_difference(date, week1_beginning) / WEEK + 1
176
285
 
177
- if seasonn == Seasons::ORDINARY
286
+ if seasonn == Seasons::ORDINARY || seasonn == Seasons::EASTER
178
287
  # ordinary time does not begin with Sunday, but the first week
179
288
  # is week 1, not 0
180
289
  week += 1
290
+ end
181
291
 
292
+ if seasonn == Seasons::ORDINARY
182
293
  if date > pentecost
183
294
  weeks_after_date = date_difference(Dates.first_advent_sunday(@year + 1), date) / WEEK
184
295
  week = 34 - weeks_after_date
@@ -189,10 +300,25 @@ module CalendariumRomanum
189
300
  week
190
301
  end
191
302
 
192
- # returns a Celebration
193
- # scheduled for the given day
303
+ # Retrieve temporale celebration for the given day
304
+ #
305
+ # @param date [Date]
306
+ # @return [Celebration]
307
+ # @since 0.6.0
308
+ def [](date)
309
+ sw = season_and_week(date)
310
+
311
+ @solemnities[date] || @feasts[date] || sunday(date, sw) || @memorials[date] || ferial(date, sw)
312
+ end
313
+
314
+ # Retrieve temporale celebration for the given day
194
315
  #
195
- # expected arguments: Date or two Integers (month, day)
316
+ # @overload get(date)
317
+ # @param date [Date]
318
+ # @overload get(month, day)
319
+ # @param month [Integer]
320
+ # @param day [Integer]
321
+ # @return (see #[])
196
322
  def get(*args)
197
323
  if args.size == 1 && args[0].is_a?(Date)
198
324
  date = args[0]
@@ -204,35 +330,80 @@ module CalendariumRomanum
204
330
  end
205
331
  end
206
332
 
207
- @solemnities[date] || @feasts[date] || sunday(date) || @memorials[date] || ferial(date)
333
+ self[date]
334
+ end
335
+
336
+ # Enumerates dates and celebrations
337
+ #
338
+ # @yield [Date, Celebration]
339
+ # @return [void, Enumerator] if called without a block, returns +Enumerator+
340
+ # @since 0.8.0
341
+ def each_day
342
+ return to_enum(__method__) unless block_given?
343
+
344
+ date_range.each {|date| yield date, self[date] }
345
+ end
346
+
347
+ # @return [Boolean]
348
+ # @since 0.6.0
349
+ def ==(b)
350
+ self.class == b.class &&
351
+ year == b.year &&
352
+ transfer_to_sunday == b.transfer_to_sunday &&
353
+ Set.new(extensions) == Set.new(b.extensions)
354
+ end
355
+
356
+ # Does this instance provide celebration identified by symbol +symbol+?
357
+ #
358
+ # @param symbol [Symbol]
359
+ # @return [Boolean]
360
+ # @since 0.9.0
361
+ def provides_celebration?(symbol)
362
+ @all_celebration_symbols.include? symbol
208
363
  end
209
364
 
365
+ protected
366
+
367
+ attr_reader :transfer_to_sunday, :extensions
368
+
210
369
  private
211
370
 
371
+ SeasonWeek = Struct.new(:season, :week)
372
+ private_constant :SeasonWeek
373
+
374
+ def season_and_week(date)
375
+ s = season(date)
376
+ w = season_week(s, date)
377
+
378
+ SeasonWeek.new(s, w)
379
+ end
380
+
212
381
  # seasons when Sundays have higher rank
213
382
  SEASONS_SUNDAY_PRIMARY = [Seasons::ADVENT, Seasons::LENT, Seasons::EASTER].freeze
214
383
 
215
- def sunday(date)
384
+ def sunday(date, season_week)
216
385
  return nil unless date.sunday?
217
386
 
218
- seas = season date
219
387
  rank = Ranks::SUNDAY_UNPRIVILEGED
220
- if SEASONS_SUNDAY_PRIMARY.include?(seas)
388
+ if SEASONS_SUNDAY_PRIMARY.include?(season_week.season)
221
389
  rank = Ranks::PRIMARY
222
390
  end
223
391
 
224
- week = Ordinalizer.ordinal season_week(seas, date)
225
- title = I18n.t "temporale.#{seas.to_sym}.sunday", week: week
392
+ week = Ordinalizer.ordinal season_week.week
393
+ title = I18n.t "temporale.#{season_week.season.to_sym}.sunday", week: week
226
394
 
227
- Celebration.new title, rank, seas.colour
395
+ self.class.create_celebration title, rank, season_week.season.colour, sunday: true
228
396
  end
229
397
 
230
- def ferial(date)
231
- seas = season date
232
- week = season_week(seas, date)
398
+ def ferial(date, season_week = nil)
399
+ # Normally +season_week+ is provided, but the method is once called also from Calendar
400
+ # and we definitely don't want Calendar to care that much about Temporale internals
401
+ # So as to know how to retrieve the value, so in that case we provide it ourselves.
402
+ season_week ||= season_and_week(date)
403
+
233
404
  rank = Ranks::FERIAL
234
405
  title = nil
235
- case seas
406
+ case season_week.season
236
407
  when Seasons::ADVENT
237
408
  if date >= Date.new(@year, 12, 17)
238
409
  rank = Ranks::FERIAL_PRIVILEGED
@@ -249,7 +420,7 @@ module CalendariumRomanum
249
420
  title = I18n.t 'temporale.christmas.after_epiphany.ferial', weekday: I18n.t("weekday.#{date.wday}")
250
421
  end
251
422
  when Seasons::LENT
252
- if week == 0
423
+ if season_week.week == 0
253
424
  title = I18n.t 'temporale.lent.after_ashes.ferial', weekday: I18n.t("weekday.#{date.wday}")
254
425
  elsif date > palm_sunday
255
426
  rank = Ranks::PRIMARY
@@ -257,16 +428,16 @@ module CalendariumRomanum
257
428
  end
258
429
  rank = Ranks::FERIAL_PRIVILEGED unless rank > Ranks::FERIAL_PRIVILEGED
259
430
  when Seasons::EASTER
260
- if week == 1
431
+ if season_week.week == 1
261
432
  rank = Ranks::PRIMARY
262
433
  title = I18n.t 'temporale.easter.octave.ferial', weekday: I18n.t("weekday.#{date.wday}")
263
434
  end
264
435
  end
265
436
 
266
- week_ord = Ordinalizer.ordinal week
267
- title ||= I18n.t "temporale.#{seas.to_sym}.ferial", week: week_ord, weekday: I18n.t("weekday.#{date.wday}")
437
+ week_ord = Ordinalizer.ordinal season_week.week
438
+ title ||= I18n.t "temporale.#{season_week.season.to_sym}.ferial", week: week_ord, weekday: I18n.t("weekday.#{date.wday}")
268
439
 
269
- Celebration.new title, rank, seas.colour
440
+ self.class.create_celebration title, rank, season_week.season.colour
270
441
  end
271
442
 
272
443
  # helper: difference between two Dates in days
@@ -294,6 +465,15 @@ module CalendariumRomanum
294
465
  prepare_celebration_date date_proc, celebration
295
466
  end
296
467
  end
468
+
469
+ @all_celebration_symbols = Set.new(
470
+ @solemnities
471
+ .merge(@feasts)
472
+ .merge(@memorials)
473
+ .each_value
474
+ .collect(&:symbol)
475
+ .compact # all should have a symbol, but we really want to prevent nil here
476
+ )
297
477
  end
298
478
 
299
479
  def prepare_celebration_date(date_method, celebration)