calendarium-romanum 0.2.1 → 0.3.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/cs.yml +37 -1
  3. data/config/locales/en.yml +37 -1
  4. data/config/locales/it.yml +36 -2
  5. data/config/locales/la.yml +35 -1
  6. data/data/README.md +62 -4
  7. data/data/czech-brno-cs.txt +10 -2
  8. data/data/czech-budejovice-cs.txt +10 -2
  9. data/data/czech-cechy-cs.txt +9 -2
  10. data/data/czech-cs.txt +7 -1
  11. data/data/czech-hradec-cs.txt +10 -2
  12. data/data/czech-litomerice-cs.txt +10 -2
  13. data/data/czech-morava-cs.txt +9 -2
  14. data/data/czech-olomouc-cs.txt +10 -2
  15. data/data/czech-ostrava-cs.txt +10 -2
  16. data/data/czech-plzen-cs.txt +10 -2
  17. data/data/czech-praha-cs.txt +10 -2
  18. data/data/universal-en.txt +4 -1
  19. data/data/universal-it.txt +4 -1
  20. data/data/universal-la.txt +4 -1
  21. data/lib/calendarium-romanum.rb +3 -0
  22. data/lib/calendarium-romanum/calendar.rb +13 -6
  23. data/lib/calendarium-romanum/data.rb +1 -1
  24. data/lib/calendarium-romanum/day.rb +0 -3
  25. data/lib/calendarium-romanum/enums.rb +41 -21
  26. data/lib/calendarium-romanum/ordinalizer.rb +37 -0
  27. data/lib/calendarium-romanum/sanctoraleloader.rb +13 -2
  28. data/lib/calendarium-romanum/temporale.rb +145 -232
  29. data/lib/calendarium-romanum/temporale/dates.rb +151 -0
  30. data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +25 -0
  31. data/lib/calendarium-romanum/transfers.rb +23 -5
  32. data/lib/calendarium-romanum/version.rb +1 -1
  33. data/spec/calendar_spec.rb +72 -19
  34. data/spec/dates_spec.rb +45 -0
  35. data/spec/rank_spec.rb +2 -2
  36. data/spec/readme_spec.rb +11 -9
  37. data/spec/spec_helper.rb +16 -1
  38. data/spec/temporale_spec.rb +252 -126
  39. metadata +19 -1
@@ -0,0 +1,151 @@
1
+ module CalendariumRomanum
2
+ class Temporale
3
+ # dates of movable feasts
4
+ module Dates
5
+ def self.first_advent_sunday(year)
6
+ sunday_before(nativity(year)) - 3 * Temporale::WEEK
7
+ end
8
+
9
+ def self.nativity(year)
10
+ Date.new(year, 12, 25)
11
+ end
12
+
13
+ def self.holy_family(year)
14
+ xmas = nativity(year)
15
+ if xmas.sunday?
16
+ return Date.new(year, 12, 30)
17
+ else
18
+ sunday_after(xmas)
19
+ end
20
+ end
21
+
22
+ def self.mother_of_god(year)
23
+ octave_of(nativity(year))
24
+ end
25
+
26
+ def self.epiphany(year)
27
+ Date.new(year+1, 1, 6)
28
+ end
29
+
30
+ def self.baptism_of_lord(year)
31
+ sunday_after epiphany(year)
32
+ end
33
+
34
+ def self.ash_wednesday(year)
35
+ easter_sunday(year) - (6 * Temporale::WEEK + 4)
36
+ end
37
+
38
+ def self.easter_sunday(year)
39
+ year += 1
40
+
41
+ # algorithm below taken from the 'easter' gem:
42
+ # https://github.com/jrobertson/easter
43
+
44
+ golden_number = (year % 19) + 1
45
+ if year <= 1752 then
46
+ # Julian calendar
47
+ dominical_number = (year + (year / 4) + 5) % 7
48
+ paschal_full_moon = (3 - (11 * golden_number) - 7) % 30
49
+ else
50
+ # Gregorian calendar
51
+ dominical_number = (year + (year / 4) - (year / 100) + (year / 400)) % 7
52
+ solar_correction = (year - 1600) / 100 - (year - 1600) / 400
53
+ lunar_correction = (((year - 1400) / 100) * 8) / 25
54
+ paschal_full_moon = (3 - 11 * golden_number + solar_correction - lunar_correction) % 30
55
+ end
56
+ dominical_number += 7 until dominical_number > 0
57
+ paschal_full_moon += 30 until paschal_full_moon > 0
58
+ paschal_full_moon -= 1 if paschal_full_moon == 29 or (paschal_full_moon == 28 and golden_number > 11)
59
+ difference = (4 - paschal_full_moon - dominical_number) % 7
60
+ difference += 7 if difference < 0
61
+ day_easter = paschal_full_moon + difference + 1
62
+ if day_easter < 11 then
63
+ # Easter occurs in March.
64
+ return Date.new(y=year, m=3, d=day_easter + 21)
65
+ else
66
+ # Easter occurs in April.
67
+ return Date.new(y=year, m=4, d=day_easter - 10)
68
+ end
69
+ end
70
+
71
+ def self.palm_sunday(year)
72
+ easter_sunday(year) - 7
73
+ end
74
+
75
+ def self.good_friday(year)
76
+ easter_sunday(year) - 2
77
+ end
78
+
79
+ def self.holy_saturday(year)
80
+ easter_sunday(year) - 1
81
+ end
82
+
83
+ def self.ascension(year)
84
+ pentecost(year) - 10
85
+ end
86
+
87
+ def self.pentecost(year)
88
+ easter_sunday(year) + 7 * Temporale::WEEK
89
+ end
90
+
91
+ def self.holy_trinity(year)
92
+ octave_of(pentecost(year))
93
+ end
94
+
95
+ def self.body_blood(year)
96
+ holy_trinity(year) + 4
97
+ end
98
+
99
+ def self.sacred_heart(year)
100
+ body_blood(year) + 8
101
+ end
102
+
103
+ def self.immaculate_heart(year)
104
+ pentecost(year) + 20
105
+ end
106
+
107
+ def self.christ_king(year)
108
+ first_advent_sunday(year + 1) - 7
109
+ end
110
+
111
+ # utility methods
112
+
113
+ def self.weekday_before(weekday, date)
114
+ if date.wday == weekday then
115
+ return date - Temporale::WEEK
116
+ elsif weekday < date.wday
117
+ return date - (date.wday - weekday)
118
+ else
119
+ return date - (date.wday + Temporale::WEEK - weekday)
120
+ end
121
+ end
122
+
123
+ def self.weekday_after(weekday, date)
124
+ if date.wday == weekday then
125
+ return date + Temporale::WEEK
126
+ elsif weekday > date.wday
127
+ return date + (weekday - date.wday)
128
+ else
129
+ return date + (Temporale::WEEK - date.wday + weekday)
130
+ end
131
+ end
132
+
133
+ def self.octave_of(date)
134
+ date + Temporale::WEEK
135
+ end
136
+
137
+ class << self
138
+ WEEKDAYS = %w{sunday monday tuesday wednesday thursday friday saturday}
139
+ WEEKDAYS.each_with_index do |weekday, weekday_i|
140
+ define_method "#{weekday}_before" do |date|
141
+ send('weekday_before', weekday_i, date)
142
+ end
143
+
144
+ define_method "#{weekday}_after" do |date|
145
+ send('weekday_after', weekday_i, date)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,25 @@
1
+ module CalendariumRomanum
2
+ class Temporale
3
+ module Extensions
4
+ # Temporale extension adding feast of Christ Eternal Priests,
5
+ # included in some local calendars
6
+ module ChristEternalPriest
7
+ def self.included(mod)
8
+ mod.add_celebration(
9
+ :christ_eternal_priest,
10
+ Celebration.new(
11
+ proc { I18n.t('temporale.extension.christ_eternal_priest') },
12
+ Ranks::FEAST_PROPER,
13
+ Colours::WHITE
14
+ )
15
+ )
16
+ end
17
+
18
+ # method computing date
19
+ def christ_eternal_priest
20
+ pentecost + 4
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -4,9 +4,11 @@ module CalendariumRomanum
4
4
  class Transfers
5
5
  def initialize(temporale, sanctorale)
6
6
  @transferred = {}
7
+ @temporale = temporale
8
+ @sanctorale = sanctorale
7
9
 
8
10
  dates = sanctorale.solemnities.keys.collect do |abstract_date|
9
- temporale.concretize_abstract_date abstract_date
11
+ concretize_abstract_date abstract_date
10
12
  end.sort
11
13
 
12
14
  dates.each do |date|
@@ -21,7 +23,7 @@ module CalendariumRomanum
21
23
  transfer_to = date
22
24
  begin
23
25
  transfer_to = transfer_to.succ
24
- end until valid_destination?(transfer_to, temporale, sanctorale)
26
+ end until valid_destination?(transfer_to)
25
27
  @transferred[transfer_to] = loser
26
28
  end
27
29
  end
@@ -32,13 +34,29 @@ module CalendariumRomanum
32
34
 
33
35
  private
34
36
 
35
- def valid_destination?(day, temporale, sanctorale)
36
- return false if temporale.get(day).rank >= Ranks::FEAST_PROPER
37
+ def valid_destination?(day)
38
+ return false if @temporale.get(day).rank >= Ranks::FEAST_PROPER
37
39
 
38
- sc = sanctorale.get(day)
40
+ sc = @sanctorale.get(day)
39
41
  return false if sc.size > 0 && sc.first.rank >= Ranks::FEAST_PROPER
40
42
 
41
43
  true
42
44
  end
45
+
46
+ # Converts an AbstractDate to a Date in the given
47
+ # liturgical year.
48
+ # It isn't guaranteed to work well (and probably doesn't work well)
49
+ # for the grey zone of dates between earliest and latest
50
+ # possible date of the first Advent Sunday, but that's no problem
51
+ # as long as there are no sanctorale solemnities in this
52
+ # date range.
53
+ def concretize_abstract_date(abstract_date)
54
+ d = abstract_date.concretize(@temporale.year + 1)
55
+ if @temporale.date_range.include? d
56
+ d
57
+ else
58
+ abstract_date.concretize(@temporale.year)
59
+ end
60
+ end
43
61
  end
44
62
  end
@@ -1,3 +1,3 @@
1
1
  module CalendariumRomanum
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -9,36 +9,36 @@ describe CR::Calendar do
9
9
 
10
10
  describe '#==' do
11
11
  it 'considers calendars with the same year same' do
12
- described_class.new(2014).should == described_class.new(2014)
12
+ expect(described_class.new(2014) == described_class.new(2014)).to be true
13
13
  end
14
14
 
15
15
  it 'considers calendars with different year different' do
16
- described_class.new(2014).should_not == described_class.new(2010)
16
+ expect(described_class.new(2014) == described_class.new(2010)).to be false
17
17
  end
18
18
  end
19
19
 
20
20
  describe '#lectionary' do
21
21
  it 'detects correctly' do
22
- described_class.new(2014).lectionary.should eq :B
23
- described_class.new(2013).lectionary.should eq :A
24
- described_class.new(2012).lectionary.should eq :C
22
+ expect(described_class.new(2014).lectionary).to eq :B
23
+ expect(described_class.new(2013).lectionary).to eq :A
24
+ expect(described_class.new(2012).lectionary).to eq :C
25
25
  end
26
26
  end
27
27
 
28
28
  describe '#ferial_lectionary' do
29
29
  it 'detects correctly' do
30
- described_class.new(2014).ferial_lectionary.should eq 1
31
- described_class.new(2013).ferial_lectionary.should eq 2
30
+ expect(described_class.new(2014).ferial_lectionary).to eq 1
31
+ expect(described_class.new(2013).ferial_lectionary).to eq 2
32
32
  end
33
33
  end
34
34
 
35
35
  describe '.for_day' do
36
36
  it 'continues the previous year\'s calendar in summer' do
37
- described_class.for_day(Date.new(2014, 6, 9)).should eq described_class.new(2013)
37
+ expect(described_class.for_day(Date.new(2014, 6, 9))).to eq described_class.new(2013)
38
38
  end
39
39
 
40
40
  it 'provides the current year\'s calendar in December' do
41
- described_class.for_day(Date.new(2014, 12, 20)).should eq described_class.new(2014)
41
+ expect(described_class.for_day(Date.new(2014, 12, 20))).to eq described_class.new(2014)
42
42
  end
43
43
  end
44
44
 
@@ -46,19 +46,19 @@ describe CR::Calendar do
46
46
  describe 'received arguments' do
47
47
  describe 'Date' do
48
48
  it 'returns a Day' do
49
- @c.day(Date.new(2013, 12, 10)).should be_a CR::Day
49
+ expect(@c.day(Date.new(2013, 12, 10))).to be_a CR::Day
50
50
  end
51
51
  end
52
52
 
53
53
  describe 'DateTime' do
54
54
  it 'returns a Day' do
55
- @c.day(DateTime.new(2013, 12, 10, 12, 10, 0)).should be_a CR::Day
55
+ expect(@c.day(DateTime.new(2013, 12, 10, 12, 10, 0))).to be_a CR::Day
56
56
  end
57
57
  end
58
58
 
59
59
  describe 'three Integers' do
60
60
  it 'returns a Day' do
61
- @c.day(2013, 12, 10).should be_a CR::Day
61
+ expect(@c.day(2013, 12, 10)).to be_a CR::Day
62
62
  end
63
63
  end
64
64
 
@@ -78,6 +78,24 @@ describe CR::Calendar do
78
78
  expect(day.date).to eq Date.new(2014, 4, 10)
79
79
  end
80
80
  end
81
+
82
+ describe 'invalid' do
83
+ describe 'absolutely' do
84
+ it 'fails' do
85
+ expect do
86
+ day = @c.day(0, 34)
87
+ end.to raise_exception(ArgumentError, 'invalid date')
88
+ end
89
+ end
90
+
91
+ describe 'for the given year' do
92
+ it 'fails' do
93
+ expect do
94
+ day = @c.day(2, 29)
95
+ end.to raise_exception(ArgumentError, 'invalid date')
96
+ end
97
+ end
98
+ end
81
99
  end
82
100
  end
83
101
 
@@ -88,7 +106,7 @@ describe CR::Calendar do
88
106
  describe 'temporale features' do
89
107
  describe 'season' do
90
108
  it 'detects Advent correctly' do
91
- @c.day(2013, 12, 10).season.should eq :advent
109
+ expect(@c.day(2013, 12, 10).season).to eq CR::Seasons::ADVENT
92
110
  end
93
111
  end
94
112
 
@@ -188,9 +206,7 @@ describe CR::Calendar do
188
206
 
189
207
  describe 'Temporale x Sanctorale resolution' do
190
208
  before :all do
191
- @s = CR::Sanctorale.new
192
- loader = CR::SanctoraleLoader.new
193
- loader.load_from_file(File.join(File.dirname(__FILE__), '..', 'data', 'universal-en.txt'), @s)
209
+ @s = CR::Data::GENERAL_ROMAN_ENGLISH.load
194
210
  @c = described_class.new 2013, @s
195
211
  end
196
212
 
@@ -217,13 +233,22 @@ describe CR::Calendar do
217
233
  expect(d.celebrations[1].title).to include 'Lellis'
218
234
  end
219
235
 
220
- it 'obligate memorial does suppress ferial' do
236
+ it 'obligatory memorial does suppress ferial' do
221
237
  d = @c.day(1, 17)
222
238
  expect(d.celebrations.size).to eq 1
223
239
 
224
240
  expect(d.celebrations[0].rank).to eq CR::Ranks::MEMORIAL_GENERAL
225
241
  end
226
242
 
243
+ it 'memorial in Lent becomes mere commemoration' do
244
+ d = @c.day(4, 2)
245
+ expect(d.celebrations.size).to eq 2
246
+
247
+ comm = d.celebrations[1]
248
+ expect(comm.rank).to eq CR::Ranks::COMMEMORATION
249
+ expect(comm.title).to eq 'Saint Francis of Paola, hermit'
250
+ end
251
+
227
252
  it 'Sunday suppresses feast' do
228
253
  san = CR::Sanctorale.new
229
254
 
@@ -251,7 +276,7 @@ describe CR::Calendar do
251
276
  celebs = c.day(d).celebrations
252
277
  expect(celebs.size).to eq 1
253
278
  expect(celebs[0].rank).to eq CR::Ranks::TRIDUUM
254
- expect(celebs[0].title).to eq 'Friday of the Passion of the Lord'
279
+ expect(celebs[0].title).to have_translation 'Friday of the Passion of the Lord'
255
280
 
256
281
  # it is transferred on a day after the Easter octave
257
282
  d = c.temporale.easter_sunday + 8
@@ -269,7 +294,7 @@ describe CR::Calendar do
269
294
  celebs = c.day(d).celebrations
270
295
  expect(celebs.size).to eq 1
271
296
  expect(celebs[0].rank).to eq CR::Ranks::TRIDUUM
272
- expect(celebs[0].title).to eq 'Friday of the Passion of the Lord'
297
+ expect(celebs[0].title).to have_translation 'Friday of the Passion of the Lord'
273
298
 
274
299
  # it is transferred on a day after the Easter octave
275
300
  d = c.temporale.easter_sunday + 8
@@ -286,6 +311,20 @@ describe CR::Calendar do
286
311
  expect(new_cal.year).to eq(@c.year - 1)
287
312
  expect(new_cal.sanctorale).to eq (@c.sanctorale)
288
313
  end
314
+
315
+ it 'preserves sanctorale' do
316
+ sanctorale = CR::Sanctorale.new
317
+ cal = described_class.new(2000, sanctorale)
318
+ new_cal = cal.pred
319
+ expect(new_cal.sanctorale).to be sanctorale
320
+ end
321
+
322
+ it 'preserves temporale class' do
323
+ t_class = Class.new(CR::Temporale)
324
+ cal = described_class.new(2000, nil, t_class)
325
+ new_cal = cal.pred
326
+ expect(new_cal.temporale).to be_a t_class
327
+ end
289
328
  end
290
329
 
291
330
  describe '#succ' do
@@ -294,6 +333,20 @@ describe CR::Calendar do
294
333
  expect(new_cal.year).to eq(@c.year + 1)
295
334
  expect(new_cal.sanctorale).to eq (@c.sanctorale)
296
335
  end
336
+
337
+ it 'preserves sanctorale' do
338
+ sanctorale = CR::Sanctorale.new
339
+ cal = described_class.new(2000, sanctorale)
340
+ new_cal = cal.succ
341
+ expect(new_cal.sanctorale).to be sanctorale
342
+ end
343
+
344
+ it 'preserves temporale class' do
345
+ t_class = Class.new(CR::Temporale)
346
+ cal = described_class.new(2000, nil, t_class)
347
+ new_cal = cal.succ
348
+ expect(new_cal.temporale).to be_a t_class
349
+ end
297
350
  end
298
351
  end
299
352
  end
@@ -0,0 +1,45 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe CR::Temporale::Dates do
4
+ let(:today) { Date.new 2014, 3, 16 }
5
+
6
+ describe '#weekday_before' do
7
+ describe 'works well for all 7 weekdays' do
8
+ [
9
+ [0, Date.new(2014, 3, 9)],
10
+ [1, Date.new(2014, 3, 10)],
11
+ [2, Date.new(2014, 3, 11)],
12
+ [3, Date.new(2014, 3, 12)],
13
+ [4, Date.new(2014, 3, 13)],
14
+ [5, Date.new(2014, 3, 14)],
15
+ [6, Date.new(2014, 3, 15)],
16
+ ].each do |e|
17
+ day_num, expected = e
18
+ it day_num do
19
+ actual = described_class.weekday_before(day_num, today)
20
+ expect(actual).to eq expected
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#weekday_after aliases' do
27
+ describe 'works well for all 7 weekdays' do
28
+ [
29
+ [:monday_after, Date.new(2014, 3, 17)],
30
+ [:tuesday_after, Date.new(2014, 3, 18)],
31
+ [:wednesday_after, Date.new(2014, 3, 19)],
32
+ [:thursday_after, Date.new(2014, 3, 20)],
33
+ [:friday_after, Date.new(2014, 3, 21)],
34
+ [:saturday_after, Date.new(2014, 3, 22)],
35
+ [:sunday_after, Date.new(2014, 3, 23)],
36
+ ].each do |e|
37
+ method, expected = e
38
+ it method do
39
+ actual = described_class.public_send(method, today)
40
+ expect(actual).to eq expected
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end