calendarium-romanum 0.3.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +47 -0
  5. data/.travis.yml +20 -0
  6. data/.yardopts +3 -0
  7. data/CHANGELOG.md +340 -0
  8. data/Gemfile +25 -0
  9. data/Gemfile.lock +86 -0
  10. data/README.md +515 -0
  11. data/Rakefile +9 -0
  12. data/bin/calendariumrom +4 -1
  13. data/calendarium-romanum.gemspec +26 -0
  14. data/config/locales/cs.yml +17 -1
  15. data/config/locales/en.yml +28 -14
  16. data/config/locales/es.yml +90 -0
  17. data/config/locales/fr.yml +90 -0
  18. data/config/locales/it.yml +18 -2
  19. data/config/locales/la.yml +17 -1
  20. data/data/README.md +43 -1
  21. data/data/czech-brno-cs.txt +4 -6
  22. data/data/czech-budejovice-cs.txt +4 -6
  23. data/data/czech-cechy-cs.txt +4 -5
  24. data/data/czech-cs.txt +237 -234
  25. data/data/czech-hradec-cs.txt +3 -5
  26. data/data/czech-litomerice-cs.txt +5 -7
  27. data/data/czech-morava-cs.txt +4 -5
  28. data/data/czech-olomouc-cs.txt +2 -4
  29. data/data/czech-ostrava-cs.txt +3 -5
  30. data/data/czech-plzen-cs.txt +3 -5
  31. data/data/czech-praha-cs.txt +3 -4
  32. data/data/universal-en.txt +214 -211
  33. data/data/universal-es.txt +243 -0
  34. data/data/universal-fr.txt +243 -0
  35. data/data/universal-it.txt +214 -211
  36. data/data/universal-la.txt +214 -210
  37. data/doc/data_readme.md +2 -0
  38. data/doc/images/class_diagram.png +0 -0
  39. data/doc/images/class_diagram.puml +44 -0
  40. data/doc/yard_readme.rdoc +76 -0
  41. data/lib/calendarium-romanum.rb +30 -21
  42. data/lib/calendarium-romanum/abstract_date.rb +12 -0
  43. data/lib/calendarium-romanum/calendar.rb +207 -52
  44. data/lib/calendarium-romanum/cli.rb +101 -52
  45. data/lib/calendarium-romanum/cr.rb +16 -0
  46. data/lib/calendarium-romanum/data.rb +46 -18
  47. data/lib/calendarium-romanum/day.rb +202 -20
  48. data/lib/calendarium-romanum/enum.rb +24 -5
  49. data/lib/calendarium-romanum/enums.rb +102 -36
  50. data/lib/calendarium-romanum/errors.rb +4 -0
  51. data/lib/calendarium-romanum/ordinalizer.rb +30 -6
  52. data/lib/calendarium-romanum/perpetual_calendar.rb +97 -0
  53. data/lib/calendarium-romanum/rank.rb +43 -6
  54. data/lib/calendarium-romanum/sanctorale.rb +170 -24
  55. data/lib/calendarium-romanum/sanctorale_factory.rb +74 -3
  56. data/lib/calendarium-romanum/sanctorale_loader.rb +176 -0
  57. data/lib/calendarium-romanum/temporale.rb +251 -119
  58. data/lib/calendarium-romanum/temporale/celebration_factory.rb +106 -0
  59. data/lib/calendarium-romanum/temporale/dates.rb +117 -36
  60. data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +18 -6
  61. data/lib/calendarium-romanum/transfers.rb +20 -1
  62. data/lib/calendarium-romanum/util.rb +36 -3
  63. data/lib/calendarium-romanum/version.rb +5 -1
  64. metadata +29 -21
  65. data/lib/calendarium-romanum/sanctoraleloader.rb +0 -115
  66. data/spec/abstract_date_spec.rb +0 -62
  67. data/spec/calendar_spec.rb +0 -352
  68. data/spec/cli_spec.rb +0 -26
  69. data/spec/data_spec.rb +0 -23
  70. data/spec/date_spec.rb +0 -61
  71. data/spec/dates_spec.rb +0 -45
  72. data/spec/enum_spec.rb +0 -51
  73. data/spec/i18n_spec.rb +0 -59
  74. data/spec/rank_spec.rb +0 -42
  75. data/spec/readme_spec.rb +0 -52
  76. data/spec/sanctorale_factory_spec.rb +0 -42
  77. data/spec/sanctorale_spec.rb +0 -167
  78. data/spec/sanctoraleloader_spec.rb +0 -171
  79. data/spec/spec_helper.rb +0 -35
  80. data/spec/temporale_spec.rb +0 -500
@@ -2,28 +2,44 @@ require 'date'
2
2
 
3
3
  module CalendariumRomanum
4
4
 
5
- # determine seasons and dates of the Temporale feasts of the given year
5
+ # One of the two main {Calendar} components.
6
+ # Handles seasons and celebrations of the temporale cycle
7
+ # for a given liturgical year.
6
8
  class Temporale
7
9
 
10
+ # How many days in a week
8
11
  WEEK = 7
9
12
 
10
- # year is Integer - the civil year when the liturgical year begins
11
- def initialize(year)
13
+ # Which solemnities can be transferred to Sunday
14
+ SUNDAY_TRANSFERABLE_SOLEMNITIES =
15
+ %i(epiphany ascension corpus_christi).freeze
16
+
17
+ # @param year [Fixnum]
18
+ # the civil year when the liturgical year _begins_
19
+ # @param extensions [Array<#each_celebration>]
20
+ # extensions implementing custom temporale celebrations
21
+ # @param transfer_to_sunday [Array<Symbol>]
22
+ # which solemnities should be transferred to a nearby
23
+ # Sunday - see {SUNDAY_TRANSFERABLE_SOLEMNITIES}
24
+ # for possible values
25
+ def initialize(year, extensions: [], transfer_to_sunday: [])
12
26
  @year = year
27
+
28
+ @extensions = extensions
29
+ @transfer_to_sunday = transfer_to_sunday.sort
30
+ validate_sunday_transfer!
31
+
13
32
  prepare_solemnities
14
33
  end
15
34
 
35
+ # @return [Fixnum]
16
36
  attr_reader :year
17
37
 
18
38
  class << self
19
- # Creates a subclass with specified extension modules included
20
- def with_extensions(*extensions)
21
- Class.new(self) do
22
- extensions.each {|e| include e }
23
- end
24
- end
25
-
26
39
  # Determines liturgical year for the given date
40
+ #
41
+ # @param date [Date]
42
+ # @return [Fixnum]
27
43
  def liturgical_year(date)
28
44
  year = date.year
29
45
  temporale = Temporale.new year
@@ -32,82 +48,103 @@ module CalendariumRomanum
32
48
  return year - 1
33
49
  end
34
50
 
35
- return year
51
+ year
36
52
  end
37
53
 
38
- # creates a Calendar for the liturgical year including given
54
+ # Creates an instance for the liturgical year including given
39
55
  # date
56
+ #
57
+ # @param date [Date]
58
+ # @return [Temporale]
40
59
  def for_day(date)
41
- return new(liturgical_year(date))
60
+ new(liturgical_year(date))
61
+ end
62
+
63
+ # Factory method creating temporale {Celebration}s
64
+ # with sensible defaults
65
+ #
66
+ # See {Celebration#initialize} for argument description.
67
+ def create_celebration(title, rank, colour, symbol: nil, date: nil)
68
+ Celebration.new(title, rank, colour, symbol, date, :temporale)
42
69
  end
43
70
 
44
71
  C = Struct.new(:date_method, :celebration)
45
72
  private_constant :C
46
73
 
47
- # implementation detail, not to be touched by client code
74
+ # @api private
48
75
  def celebrations
49
76
  @celebrations ||=
50
77
  begin
51
- [
52
- c(:nativity, Ranks::PRIMARY),
53
- c(:holy_family, Ranks::FEAST_LORD_GENERAL),
54
- c(:mother_of_god, Ranks::SOLEMNITY_GENERAL),
55
- c(:epiphany, Ranks::PRIMARY),
56
- c(:baptism_of_lord, Ranks::FEAST_LORD_GENERAL),
57
- c(:ash_wednesday, Ranks::PRIMARY, Colours::VIOLET),
58
- c(:good_friday, Ranks::TRIDUUM, Colours::RED),
59
- c(:holy_saturday, Ranks::TRIDUUM, Colours::VIOLET),
60
- c(:palm_sunday, Ranks::PRIMARY, Colours::RED),
61
- c(:easter_sunday, Ranks::TRIDUUM),
62
- c(:ascension, Ranks::PRIMARY),
63
- c(:pentecost, Ranks::PRIMARY, Colours::RED),
64
- c(:holy_trinity, Ranks::SOLEMNITY_GENERAL),
65
- c(:body_blood, Ranks::SOLEMNITY_GENERAL),
66
- c(:sacred_heart, Ranks::SOLEMNITY_GENERAL),
67
- c(:christ_king, Ranks::SOLEMNITY_GENERAL),
68
-
69
- # Immaculate Heart of Mary is actually (currently the only one)
70
- # movable *sanctorale* feast, but as it would make little sense
71
- # to add support for movable sanctorale feasts because of
72
- # a single one, we cheat a bit and handle it in temporale.
73
- c(:immaculate_heart, Ranks::MEMORIAL_GENERAL),
74
- ]
78
+ %i(
79
+ nativity
80
+ holy_family
81
+ mother_of_god
82
+ epiphany
83
+ baptism_of_lord
84
+ ash_wednesday
85
+ good_friday
86
+ holy_saturday
87
+ palm_sunday
88
+ easter_sunday
89
+ ascension
90
+ pentecost
91
+ holy_trinity
92
+ corpus_christi
93
+ mother_of_church
94
+ sacred_heart
95
+ christ_king
96
+ immaculate_heart
97
+ ).collect do |symbol|
98
+ date_method = symbol
99
+ C.new(
100
+ date_method,
101
+ CelebrationFactory.public_send(symbol)
102
+ )
103
+ end
104
+ # Immaculate Heart of Mary and Mary, Mother of the Church
105
+ # are actually movable *sanctorale* feasts,
106
+ # but as it would make little sense
107
+ # to add support for movable sanctorale feasts because of
108
+ # two, we cheat a bit and handle them in temporale.
75
109
  end
76
110
  end
111
+ end
77
112
 
78
- # Hook point for extensions to add new celebrations
79
- def add_celebration(date_method, celebration)
80
- if self == Temporale
81
- raise RuntimeError.new("Don't add celebrations to Temporale itself, subclass it and modify the subclass.")
82
- end
83
-
84
- celebrations << C.new(date_method, celebration)
85
- end
86
-
87
- private
88
-
89
- def c(date_method, rank, colour=Colours::WHITE)
90
- title = proc { I18n.t("temporale.solemnity.#{date_method}") }
91
-
92
- C.new(
93
- date_method,
94
- Celebration.new(title, rank, colour)
95
- )
96
- end
113
+ # Does this instance transfer the specified solemnity to Sunday?
114
+ #
115
+ # @param solemnity [Symbol]
116
+ # @return [Boolean]
117
+ def transferred_to_sunday?(solemnity)
118
+ @transfer_to_sunday.include?(solemnity)
97
119
  end
98
120
 
121
+ # First day of the liturgical year
122
+ #
123
+ # @return [Date]
99
124
  def start_date
100
125
  first_advent_sunday
101
126
  end
102
127
 
128
+ # Last day of the liturgical year
129
+ #
130
+ # @return [Date]
103
131
  def end_date
104
- Dates.first_advent_sunday(year+1) - 1
132
+ Dates.first_advent_sunday(year + 1) - 1
105
133
  end
106
134
 
135
+ # Date range of the liturgical year
136
+ #
137
+ # @return [Range<Date>]
107
138
  def date_range
108
139
  start_date .. end_date
109
140
  end
110
141
 
142
+ # Check that the date belongs to the liturgical year.
143
+ # If it does not, throw exception.
144
+ #
145
+ # @param date [Date]
146
+ # @return [void]
147
+ # @raise [RangeError]
111
148
  def range_check(date)
112
149
  # necessary in order to handle Date correctly
113
150
  date = date.to_date if date.class != Date
@@ -117,49 +154,84 @@ module CalendariumRomanum
117
154
  end
118
155
  end
119
156
 
120
- %i(
121
- first_advent_sunday
122
- nativity
123
- holy_family
124
- mother_of_god
125
- epiphany
126
- baptism_of_lord
127
- ash_wednesday
128
- palm_sunday
129
- good_friday
130
- holy_saturday
131
- easter_sunday
132
- ascension
133
- pentecost
134
- holy_trinity
135
- body_blood
136
- sacred_heart
137
- immaculate_heart
138
- christ_king
139
- ).each do |feast|
140
- define_method feast do
141
- Dates.public_send feast, year
157
+ # @!method nativity
158
+ # @return [Date]
159
+ # @!method holy_family
160
+ # @return [Date]
161
+ # @!method mother_of_god
162
+ # @return [Date]
163
+ # @!method epiphany
164
+ # @return [Date]
165
+ # @!method baptism_of_lord
166
+ # @return [Date]
167
+ # @!method ash_wednesday
168
+ # @return [Date]
169
+ # @!method good_friday
170
+ # @return [Date]
171
+ # @!method holy_saturday
172
+ # @return [Date]
173
+ # @!method palm_sunday
174
+ # @return [Date]
175
+ # @!method easter_sunday
176
+ # @return [Date]
177
+ # @!method ascension
178
+ # @return [Date]
179
+ # @!method pentecost
180
+ # @return [Date]
181
+ # @!method holy_trinity
182
+ # @return [Date]
183
+ # @!method corpus_christi
184
+ # @return [Date]
185
+ # @!method mother_of_church
186
+ # @return [Date]
187
+ # @!method sacred_heart
188
+ # @return [Date]
189
+ # @!method christ_king
190
+ # @return [Date]
191
+ # @!method immaculate_heart
192
+ # @return [Date]
193
+ # @!method first_advent_sunday
194
+ # @return [Date]
195
+ (celebrations.collect(&:date_method) + [:first_advent_sunday])
196
+ .each do |feast|
197
+ if SUNDAY_TRANSFERABLE_SOLEMNITIES.include? feast
198
+ define_method feast do
199
+ Dates.public_send feast, year, sunday: transferred_to_sunday?(feast)
200
+ end
201
+ elsif feast == :baptism_of_lord
202
+ define_method feast do
203
+ Dates.public_send feast, year, epiphany_on_sunday: transferred_to_sunday?(:epiphany)
204
+ end
205
+ else
206
+ define_method feast do
207
+ Dates.public_send feast, year
208
+ end
142
209
  end
143
210
  end
144
211
 
145
- # which liturgical season is it?
212
+ # Determine liturgical season for a given date
213
+ #
214
+ # @param date [Date]
215
+ # @return [Season]
216
+ # @raise [RangeError]
217
+ # if the given date doesn't belong to the liturgical year
146
218
  def season(date)
147
219
  range_check date
148
220
 
149
- if first_advent_sunday <= date and
150
- nativity > date then
221
+ if (first_advent_sunday <= date) &&
222
+ nativity > date
151
223
  Seasons::ADVENT
152
224
 
153
- elsif nativity <= date and
154
- baptism_of_lord >= date then
225
+ elsif (nativity <= date) &&
226
+ (baptism_of_lord >= date)
155
227
  Seasons::CHRISTMAS
156
228
 
157
- elsif ash_wednesday <= date and
158
- easter_sunday > date then
229
+ elsif (ash_wednesday <= date) &&
230
+ easter_sunday > date
159
231
  Seasons::LENT
160
232
 
161
- elsif easter_sunday <= date and
162
- pentecost >= date then
233
+ elsif (easter_sunday <= date) &&
234
+ (pentecost >= date)
163
235
  Seasons::EASTER
164
236
 
165
237
  else
@@ -167,6 +239,10 @@ module CalendariumRomanum
167
239
  end
168
240
  end
169
241
 
242
+ # When the specified liturgical season begins
243
+ #
244
+ # @param s [Season]
245
+ # @return [Date]
170
246
  def season_beginning(s)
171
247
  case s
172
248
  when Seasons::ADVENT
@@ -177,18 +253,24 @@ module CalendariumRomanum
177
253
  ash_wednesday
178
254
  when Seasons::EASTER
179
255
  easter_sunday
180
- else # ordinary time
181
- Dates.monday_after(baptism_of_lord)
256
+ when Seasons::ORDINARY # ordinary time
257
+ baptism_of_lord + 1
258
+ else
259
+ raise ArgumentError.new('unsupported season')
182
260
  end
183
261
  end
184
262
 
263
+ # Determine week of a season for a given date
264
+ #
265
+ # @param seasonn [Season]
266
+ # @param date [Date]
185
267
  def season_week(seasonn, date)
186
268
  week1_beginning = season_beginning = season_beginning(seasonn)
187
269
  unless season_beginning.sunday?
188
270
  week1_beginning = Dates.sunday_after(season_beginning)
189
271
  end
190
272
 
191
- week = date_difference(date, week1_beginning) / Temporale::WEEK + 1
273
+ week = date_difference(date, week1_beginning) / WEEK + 1
192
274
 
193
275
  if seasonn == Seasons::ORDINARY
194
276
  # ordinary time does not begin with Sunday, but the first week
@@ -196,19 +278,32 @@ module CalendariumRomanum
196
278
  week += 1
197
279
 
198
280
  if date > pentecost
199
- weeks_after_date = date_difference(Dates.first_advent_sunday(@year + 1), date) / 7
281
+ weeks_after_date = date_difference(Dates.first_advent_sunday(@year + 1), date) / WEEK
200
282
  week = 34 - weeks_after_date
201
283
  week += 1 if date.sunday?
202
284
  end
203
285
  end
204
286
 
205
- return week
287
+ week
288
+ end
289
+
290
+ # Retrieve temporale celebration for the given day
291
+ #
292
+ # @param date [Date]
293
+ # @return [Celebration]
294
+ # @since 0.6.0
295
+ def [](date)
296
+ @solemnities[date] || @feasts[date] || sunday(date) || @memorials[date] || ferial(date)
206
297
  end
207
298
 
208
- # returns a Celebration
209
- # scheduled for the given day
299
+ # Retrieve temporale celebration for the given day
210
300
  #
211
- # expected arguments: Date or two Integers (month, day)
301
+ # @overload get(date)
302
+ # @param date [Date]
303
+ # @overload get(month, day)
304
+ # @param month [Fixnum]
305
+ # @param day [Fixnum]
306
+ # @return (see #[])
212
307
  def get(*args)
213
308
  if args.size == 1 && args[0].is_a?(Date)
214
309
  date = args[0]
@@ -220,9 +315,22 @@ module CalendariumRomanum
220
315
  end
221
316
  end
222
317
 
223
- return @solemnities[date] || @feasts[date] || sunday(date) || @memorials[date] || ferial(date)
318
+ self[date]
224
319
  end
225
320
 
321
+ # @return [Boolean]
322
+ # @since 0.6.0
323
+ def ==(b)
324
+ self.class == b.class &&
325
+ year == b.year &&
326
+ transfer_to_sunday == b.transfer_to_sunday &&
327
+ Set.new(extensions) == Set.new(b.extensions)
328
+ end
329
+
330
+ protected
331
+
332
+ attr_reader :transfer_to_sunday, :extensions
333
+
226
334
  private
227
335
 
228
336
  # seasons when Sundays have higher rank
@@ -240,7 +348,7 @@ module CalendariumRomanum
240
348
  week = Ordinalizer.ordinal season_week(seas, date)
241
349
  title = I18n.t "temporale.#{seas.to_sym}.sunday", week: week
242
350
 
243
- return Celebration.new title, rank, seas.colour
351
+ self.class.create_celebration title, rank, seas.colour
244
352
  end
245
353
 
246
354
  def ferial(date)
@@ -252,6 +360,8 @@ module CalendariumRomanum
252
360
  when Seasons::ADVENT
253
361
  if date >= Date.new(@year, 12, 17)
254
362
  rank = Ranks::FERIAL_PRIVILEGED
363
+ nth = Ordinalizer.ordinal(date.day)
364
+ title = I18n.t 'temporale.advent.before_christmas', day: nth
255
365
  end
256
366
  when Seasons::CHRISTMAS
257
367
  if date < mother_of_god
@@ -260,32 +370,32 @@ module CalendariumRomanum
260
370
  nth = Ordinalizer.ordinal(date.day - nativity.day + 1) # 1-based counting
261
371
  title = I18n.t 'temporale.christmas.nativity_octave.ferial', day: nth
262
372
  elsif date > epiphany
263
- title = I18n.t "temporale.christmas.after_epiphany.ferial", weekday: I18n.t("weekday.#{date.wday}")
373
+ title = I18n.t 'temporale.christmas.after_epiphany.ferial', weekday: I18n.t("weekday.#{date.wday}")
264
374
  end
265
375
  when Seasons::LENT
266
376
  if week == 0
267
- title = I18n.t "temporale.lent.after_ashes.ferial", weekday: I18n.t("weekday.#{date.wday}")
377
+ title = I18n.t 'temporale.lent.after_ashes.ferial', weekday: I18n.t("weekday.#{date.wday}")
268
378
  elsif date > palm_sunday
269
379
  rank = Ranks::PRIMARY
270
- title = I18n.t "temporale.lent.holy_week.ferial", weekday: I18n.t("weekday.#{date.wday}")
380
+ title = I18n.t 'temporale.lent.holy_week.ferial', weekday: I18n.t("weekday.#{date.wday}")
271
381
  end
272
382
  rank = Ranks::FERIAL_PRIVILEGED unless rank > Ranks::FERIAL_PRIVILEGED
273
383
  when Seasons::EASTER
274
384
  if week == 1
275
385
  rank = Ranks::PRIMARY
276
- title = I18n.t "temporale.easter.octave.ferial", weekday: I18n.t("weekday.#{date.wday}")
386
+ title = I18n.t 'temporale.easter.octave.ferial', weekday: I18n.t("weekday.#{date.wday}")
277
387
  end
278
388
  end
279
389
 
280
390
  week_ord = Ordinalizer.ordinal week
281
391
  title ||= I18n.t "temporale.#{seas.to_sym}.ferial", week: week_ord, weekday: I18n.t("weekday.#{date.wday}")
282
392
 
283
- return Celebration.new title, rank, seas.colour
393
+ self.class.create_celebration title, rank, seas.colour
284
394
  end
285
395
 
286
396
  # helper: difference between two Dates in days
287
397
  def date_difference(d1, d2)
288
- return (d1 - d2).numerator
398
+ (d1 - d2).numerator
289
399
  end
290
400
 
291
401
  # prepare dates of temporale solemnities
@@ -295,22 +405,44 @@ module CalendariumRomanum
295
405
  @memorials = {}
296
406
 
297
407
  self.class.celebrations.each do |c|
298
- if c.date_method.is_a? Proc
299
- date = instance_eval &c.date_method
408
+ prepare_celebration_date c.date_method, c.celebration
409
+ end
410
+
411
+ @extensions.each do |extension|
412
+ extension.each_celebration do |date_method, celebration|
413
+ date_proc = date_method
414
+ if date_method.is_a? Symbol
415
+ date_proc = extension.method(date_method)
416
+ end
417
+
418
+ prepare_celebration_date date_proc, celebration
419
+ end
420
+ end
421
+ end
422
+
423
+ def prepare_celebration_date(date_method, celebration)
424
+ date =
425
+ if date_method.respond_to? :call
426
+ date_method.call(year)
300
427
  else
301
- date = public_send(c.date_method)
428
+ public_send(date_method)
302
429
  end
303
- celebration = c.celebration
304
-
305
- add_to =
306
- if celebration.feast?
307
- @feasts
308
- elsif celebration.memorial?
309
- @memorials
310
- else
311
- @solemnities
312
- end
313
- add_to[date] = celebration
430
+
431
+ add_to =
432
+ if celebration.feast?
433
+ @feasts
434
+ elsif celebration.memorial?
435
+ @memorials
436
+ else
437
+ @solemnities
438
+ end
439
+ add_to[date] = celebration
440
+ end
441
+
442
+ def validate_sunday_transfer!
443
+ unsupported = @transfer_to_sunday - SUNDAY_TRANSFERABLE_SOLEMNITIES
444
+ unless unsupported.empty?
445
+ raise RuntimeError.new("Transfer of #{unsupported.inspect} to a Sunday not supported. Only #{SUNDAY_TRANSFERABLE_SOLEMNITIES} are allowed.")
314
446
  end
315
447
  end
316
448
  end