calendarium-romanum 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/cs.yml +14 -1
  3. data/config/locales/en.yml +12 -1
  4. data/config/locales/fr.yml +87 -0
  5. data/config/locales/it.yml +15 -2
  6. data/config/locales/la.yml +14 -1
  7. data/data/czech-cs.txt +1 -2
  8. data/data/universal-en.txt +1 -1
  9. data/data/universal-fr.txt +239 -0
  10. data/data/universal-it.txt +1 -1
  11. data/data/universal-la.txt +1 -1
  12. data/lib/calendarium-romanum.rb +1 -0
  13. data/lib/calendarium-romanum/calendar.rb +30 -18
  14. data/lib/calendarium-romanum/cli.rb +8 -4
  15. data/lib/calendarium-romanum/data.rb +1 -0
  16. data/lib/calendarium-romanum/day.rb +15 -0
  17. data/lib/calendarium-romanum/enums.rb +12 -2
  18. data/lib/calendarium-romanum/ordinalizer.rb +7 -4
  19. data/lib/calendarium-romanum/perpetual_calendar.rb +49 -0
  20. data/lib/calendarium-romanum/rank.rb +1 -1
  21. data/lib/calendarium-romanum/sanctorale.rb +8 -1
  22. data/lib/calendarium-romanum/temporale.rb +63 -36
  23. data/lib/calendarium-romanum/temporale/dates.rb +38 -25
  24. data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +4 -4
  25. data/lib/calendarium-romanum/version.rb +1 -1
  26. data/spec/calendar_spec.rb +233 -255
  27. data/spec/celebration_spec.rb +23 -0
  28. data/spec/colour_spec.rb +17 -0
  29. data/spec/data_spec.rb +1 -1
  30. data/spec/day_spec.rb +59 -0
  31. data/spec/ordinalizer_spec.rb +22 -0
  32. data/spec/perpetual_calendar_spec.rb +91 -0
  33. data/spec/rank_spec.rb +22 -7
  34. data/spec/sanctorale_spec.rb +84 -60
  35. data/spec/season_spec.rb +17 -0
  36. data/spec/spec_helper.rb +2 -2
  37. data/spec/temporale_spec.rb +62 -43
  38. metadata +12 -3
@@ -199,7 +199,7 @@ locale: it
199
199
 
200
200
  = 11
201
201
  1 s : Tutti i Santi
202
- 2 : Commemorazione di tutti i fedeli defunti
202
+ 2 s : Commemorazione di tutti i fedeli defunti
203
203
  3 R : San Martino de Porres, religioso
204
204
  4 m : San Carlo Borromeo, vescovo
205
205
  9 f : Dedicazione della basilica Lateranense
@@ -199,7 +199,7 @@ locale: la
199
199
 
200
200
  = 11
201
201
  1 s : OMNIUM SANCTORUM
202
- 2 : IN COMMEMORATIONE OMNIUM FIDELIUM DEFUNCTORUM
202
+ 2 s : IN COMMEMORATIONE OMNIUM FIDELIUM DEFUNCTORUM
203
203
  3 R : S. Martini de Porres, religiosi
204
204
  4 m : S. Caroli Borromeo, episcopi
205
205
  9 f : IN DEDICATIONE BASILICAE LATERANENSIS
@@ -6,6 +6,7 @@ enum
6
6
  enums
7
7
  data
8
8
  calendar
9
+ perpetual_calendar
9
10
  temporale
10
11
  temporale/dates
11
12
  temporale/extensions/christ_eternal_priest
@@ -6,17 +6,26 @@ module CalendariumRomanum
6
6
  # Provides complete information concerning a liturgical year,
7
7
  # it's days and celebrations occurring on them.
8
8
  class Calendar
9
-
10
9
  extend Forwardable
11
10
 
11
+ # Day when the implemented calendar system became effective
12
+ EFFECTIVE_FROM = Date.new(1970, 1, 1).freeze
13
+
12
14
  # year: Integer
13
15
  # returns a calendar for the liturgical year beginning with
14
16
  # Advent of the specified civil year.
15
- def initialize(year, sanctorale=nil, temporale_class=nil)
17
+ def initialize(year, sanctorale=nil, temporale=nil)
18
+ if year < (EFFECTIVE_FROM.year - 1)
19
+ raise system_not_effective
20
+ end
21
+
22
+ if temporale && temporale.year != year
23
+ raise ArgumentError.new("Temporale year must be the same as year.")
24
+ end
25
+
16
26
  @year = year
17
27
  @sanctorale = sanctorale || Sanctorale.new
18
- @temporale_class = temporale_class || Temporale
19
- @temporale = @temporale_class.new(year)
28
+ @temporale = temporale || Temporale.new(year)
20
29
  @transferred = Transfers.new(@temporale, @sanctorale)
21
30
  end
22
31
 
@@ -56,18 +65,6 @@ module CalendariumRomanum
56
65
  attr_reader :temporale
57
66
  attr_reader :sanctorale
58
67
 
59
- # returns a Calendar for the subsequent year
60
- def succ
61
- c = Calendar.new @year + 1, @sanctorale, @temporale_class
62
- return c
63
- end
64
-
65
- # returns a Calendar for the previous year
66
- def pred
67
- c = Calendar.new @year - 1, @sanctorale, @temporale_class
68
- return c
69
- end
70
-
71
68
  def ==(obj)
72
69
  unless obj.is_a? Calendar
73
70
  return false
@@ -91,6 +88,10 @@ module CalendariumRomanum
91
88
  range_check date
92
89
  end
93
90
 
91
+ if date < EFFECTIVE_FROM
92
+ raise system_not_effective
93
+ end
94
+
94
95
  s = @temporale.season(date)
95
96
  return Day.new(
96
97
  date: date,
@@ -110,6 +111,14 @@ module CalendariumRomanum
110
111
  @year % 2 + 1
111
112
  end
112
113
 
114
+ def freeze
115
+ @temporale.freeze
116
+ @sanctorale.freeze
117
+ super
118
+ end
119
+
120
+ private
121
+
113
122
  def celebrations_for(date)
114
123
  tr = @transferred.get(date)
115
124
  return [tr] if tr
@@ -120,8 +129,7 @@ module CalendariumRomanum
120
129
  unless st.empty?
121
130
  if st.first.rank > t.rank
122
131
  if st.first.rank == Ranks::MEMORIAL_OPTIONAL
123
- st.unshift t
124
- return st
132
+ return st.dup.unshift t
125
133
  else
126
134
  return st
127
135
  end
@@ -136,5 +144,9 @@ module CalendariumRomanum
136
144
 
137
145
  return [t]
138
146
  end
147
+
148
+ def system_not_effective
149
+ RangeError.new('Year out of range. Implemented calendar system has been in use only since 1st January 1970.')
150
+ end
139
151
  end # class Calendar
140
152
  end
@@ -85,16 +85,20 @@ module CalendariumRomanum
85
85
  ca = celebs[0][i]
86
86
  cb = celebs[1][i]
87
87
 
88
- _print_cel = Proc.new {|c| puts "#{c.rank.priority} #{c.colour} | #{c.title}" }
89
-
90
88
  if ca.rank != cb.rank || ca.colour != cb.colour
91
89
  puts "#{d.month}/#{d.day}"
92
- _print_cel.call ca
93
- _print_cel.call cb
90
+ print_celebration ca
91
+ print_celebration cb
94
92
  puts
95
93
  end
96
94
  end
97
95
  end
98
96
  end
97
+
98
+ private
99
+
100
+ def print_celebration(c)
101
+ puts "#{c.rank.priority} #{c.colour.symbol} | #{c.title}"
102
+ end
99
103
  end
100
104
  end
@@ -20,6 +20,7 @@ module CalendariumRomanum
20
20
  [
21
21
  GENERAL_ROMAN_LATIN = SanctoraleFile.new('universal-la.txt'),
22
22
  GENERAL_ROMAN_ENGLISH = SanctoraleFile.new('universal-en.txt'),
23
+ GENERAL_ROMAN_FRENCH = SanctoraleFile.new('universal-fr.txt'),
23
24
  GENERAL_ROMAN_ITALIAN = SanctoraleFile.new('universal-it.txt'),
24
25
  CZECH = SanctoraleFile.new('czech-cs.txt')
25
26
  ] \
@@ -30,6 +30,14 @@ module CalendariumRomanum
30
30
 
31
31
  # an Array of Celebrations, possibly empty
32
32
  attr_reader :celebrations
33
+
34
+ def ==(other)
35
+ self.class == other.class &&
36
+ date == other.date &&
37
+ season == other.season &&
38
+ season_week == other.season_week &&
39
+ celebrations == other.celebrations
40
+ end
33
41
  end
34
42
 
35
43
  # information on one particular celebration of the liturgical year
@@ -58,5 +66,12 @@ module CalendariumRomanum
58
66
 
59
67
  attr_reader :colour
60
68
  alias_method :color, :colour
69
+
70
+ def ==(other)
71
+ self.class == other.class &&
72
+ title == other.title &&
73
+ rank == other.rank &&
74
+ colour == other.colour
75
+ end
61
76
  end
62
77
  end
@@ -3,14 +3,19 @@ module CalendariumRomanum
3
3
  class Colour
4
4
  def initialize(symbol)
5
5
  @symbol = symbol
6
+ @i18n_key = "colour.#{@symbol}"
6
7
  end
7
8
 
8
9
  attr_reader :symbol
9
10
  alias to_sym symbol
11
+
12
+ def name
13
+ I18n.t @i18n_key
14
+ end
10
15
  end
11
16
 
12
17
  class Colours < Enum
13
- values do
18
+ values(index_by: :symbol) do
14
19
  [
15
20
  GREEN = Colour.new(:green),
16
21
  VIOLET = Colour.new(:violet),
@@ -26,14 +31,19 @@ module CalendariumRomanum
26
31
  def initialize(symbol, colour)
27
32
  @symbol = symbol
28
33
  @colour = colour
34
+ @i18n_key = "temporale.season.#{@symbol}"
29
35
  end
30
36
 
31
37
  attr_reader :symbol, :colour
32
38
  alias to_sym symbol
39
+
40
+ def name
41
+ I18n.t @i18n_key
42
+ end
33
43
  end
34
44
 
35
45
  class Seasons < Enum
36
- values do
46
+ values(index_by: :symbol) do
37
47
  [
38
48
  ADVENT = Season.new(:advent, Colours::VIOLET),
39
49
  CHRISTMAS = Season.new(:christmas, Colours::WHITE),
@@ -21,13 +21,16 @@ module CalendariumRomanum
21
21
  end
22
22
 
23
23
  def english_ordinal(number)
24
- case number
24
+ modulo = number % 10
25
+ modulo = 9 if number / 10 == 1
26
+
27
+ case modulo
25
28
  when 1
26
- '1st'
29
+ "#{number}st"
27
30
  when 2
28
- '2nd'
31
+ "#{number}nd"
29
32
  when 3
30
- '3rd'
33
+ "#{number}rd"
31
34
  else
32
35
  "#{number}th"
33
36
  end
@@ -0,0 +1,49 @@
1
+ module CalendariumRomanum
2
+ # When you want to query a calendar without caring about
3
+ # civil and liturgical years and Calendar instances
4
+ class PerpetualCalendar
5
+ def initialize(sanctorale: nil, temporale_factory: nil, temporale_options: nil, cache: {})
6
+ if temporale_factory && temporale_options
7
+ raise ArgumentError.new('Specify either temporale_factory or temporale_options, not both')
8
+ end
9
+
10
+ @sanctorale = sanctorale
11
+ @temporale_factory = temporale_factory || build_temporale_factory(temporale_options)
12
+
13
+ @cache = cache
14
+ end
15
+
16
+ # returns a resolved Day
17
+ def day(*args)
18
+ calendar_for(*args).day(*args)
19
+ end
20
+
21
+ # returns a Calendar instance for the liturgical year containing
22
+ # the specified day
23
+ def calendar_for(*args)
24
+ date = Calendar.mk_date(*args)
25
+ year = Temporale.liturgical_year date
26
+ calendar_instance year
27
+ end
28
+
29
+ # returns a Calendar instance for the specified liturgical year
30
+ def calendar_for_year(year)
31
+ calendar_instance year
32
+ end
33
+
34
+ private
35
+
36
+ def build_temporale_factory(temporale_options)
37
+ temporale_options ||= {}
38
+ lambda {|year| Temporale.new(year, **temporale_options) }
39
+ end
40
+
41
+ def calendar_instance(year)
42
+ if @cache.has_key? year
43
+ @cache[year]
44
+ else
45
+ @cache[year] = Calendar.new(year, @sanctorale, @temporale_factory.call(year))
46
+ end
47
+ end
48
+ end
49
+ end
@@ -34,7 +34,7 @@ module CalendariumRomanum
34
34
  end
35
35
 
36
36
  def memorial?
37
- priority.to_i == 3
37
+ priority.to_i == 3 && priority <= 3.12
38
38
  end
39
39
  end
40
40
  end
@@ -43,7 +43,7 @@ module CalendariumRomanum
43
43
  @solemnities.delete date
44
44
  end
45
45
 
46
- @days[date] = celebrations
46
+ @days[date] = celebrations.dup
47
47
  end
48
48
 
49
49
  # adds all Celebrations from another instance
@@ -85,5 +85,12 @@ module CalendariumRomanum
85
85
  def empty?
86
86
  @days.empty?
87
87
  end
88
+
89
+ def freeze
90
+ @days.freeze
91
+ @days.values.each &:freeze
92
+ @solemnities.freeze
93
+ super
94
+ end
88
95
  end
89
96
  end
@@ -7,22 +7,23 @@ module CalendariumRomanum
7
7
 
8
8
  WEEK = 7
9
9
 
10
+ SUNDAY_TRANSFERABLE_SOLEMNITIES =
11
+ %i(epiphany ascension corpus_christi).freeze
12
+
10
13
  # year is Integer - the civil year when the liturgical year begins
11
- def initialize(year)
14
+ def initialize(year, extensions: [], transfer_to_sunday: [])
12
15
  @year = year
16
+
17
+ @extensions = extensions
18
+ @transfer_to_sunday = transfer_to_sunday
19
+ validate_sunday_transfer!
20
+
13
21
  prepare_solemnities
14
22
  end
15
23
 
16
24
  attr_reader :year
17
25
 
18
26
  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
27
  # Determines liturgical year for the given date
27
28
  def liturgical_year(date)
28
29
  year = date.year
@@ -62,7 +63,7 @@ module CalendariumRomanum
62
63
  c(:ascension, Ranks::PRIMARY),
63
64
  c(:pentecost, Ranks::PRIMARY, Colours::RED),
64
65
  c(:holy_trinity, Ranks::SOLEMNITY_GENERAL),
65
- c(:body_blood, Ranks::SOLEMNITY_GENERAL),
66
+ c(:corpus_christi, Ranks::SOLEMNITY_GENERAL),
66
67
  c(:sacred_heart, Ranks::SOLEMNITY_GENERAL),
67
68
  c(:christ_king, Ranks::SOLEMNITY_GENERAL),
68
69
 
@@ -75,15 +76,6 @@ module CalendariumRomanum
75
76
  end
76
77
  end
77
78
 
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
79
  private
88
80
 
89
81
  def c(date_method, rank, colour=Colours::WHITE)
@@ -96,6 +88,10 @@ module CalendariumRomanum
96
88
  end
97
89
  end
98
90
 
91
+ def transferred_to_sunday?(solemnity)
92
+ @transfer_to_sunday.include?(solemnity)
93
+ end
94
+
99
95
  def start_date
100
96
  first_advent_sunday
101
97
  end
@@ -132,13 +128,23 @@ module CalendariumRomanum
132
128
  ascension
133
129
  pentecost
134
130
  holy_trinity
135
- body_blood
131
+ corpus_christi
136
132
  sacred_heart
137
133
  immaculate_heart
138
134
  christ_king
139
135
  ).each do |feast|
140
- define_method feast do
141
- Dates.public_send feast, year
136
+ if SUNDAY_TRANSFERABLE_SOLEMNITIES.include? feast
137
+ define_method feast do
138
+ Dates.public_send feast, year, sunday: transferred_to_sunday?(feast)
139
+ end
140
+ elsif feast == :baptism_of_lord
141
+ define_method feast do
142
+ Dates.public_send feast, year, epiphany_on_sunday: transferred_to_sunday?(:epiphany)
143
+ end
144
+ else
145
+ define_method feast do
146
+ Dates.public_send feast, year
147
+ end
142
148
  end
143
149
  end
144
150
 
@@ -188,7 +194,7 @@ module CalendariumRomanum
188
194
  week1_beginning = Dates.sunday_after(season_beginning)
189
195
  end
190
196
 
191
- week = date_difference(date, week1_beginning) / Temporale::WEEK + 1
197
+ week = date_difference(date, week1_beginning) / WEEK + 1
192
198
 
193
199
  if seasonn == Seasons::ORDINARY
194
200
  # ordinary time does not begin with Sunday, but the first week
@@ -295,22 +301,43 @@ module CalendariumRomanum
295
301
  @memorials = {}
296
302
 
297
303
  self.class.celebrations.each do |c|
298
- if c.date_method.is_a? Proc
299
- date = instance_eval &c.date_method
304
+ prepare_celebration_date c.date_method, c.celebration
305
+ end
306
+
307
+ @extensions.each do |extension|
308
+ extension.each_celebration do |date_method, celebration|
309
+ date_proc = date_method
310
+ if date_method.is_a? Symbol
311
+ date_proc = extension.method(date_method)
312
+ end
313
+
314
+ prepare_celebration_date date_proc, celebration
315
+ end
316
+ end
317
+ end
318
+
319
+ def prepare_celebration_date(date_method, celebration)
320
+ if date_method.respond_to? :call
321
+ date = date_method.call(year)
322
+ else
323
+ date = public_send(date_method)
324
+ end
325
+
326
+ add_to =
327
+ if celebration.feast?
328
+ @feasts
329
+ elsif celebration.memorial?
330
+ @memorials
300
331
  else
301
- date = public_send(c.date_method)
332
+ @solemnities
302
333
  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
334
+ add_to[date] = celebration
335
+ end
336
+
337
+ def validate_sunday_transfer!
338
+ unsupported = @transfer_to_sunday - SUNDAY_TRANSFERABLE_SOLEMNITIES
339
+ unless unsupported.empty?
340
+ raise RuntimeError.new("Transfer of #{unsupported.inspect} to a Sunday not supported. Only #{SUNDAY_TRANSFERABLE_SOLEMNITIES} are allowed.")
314
341
  end
315
342
  end
316
343
  end