calendarium-romanum 0.2.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/bin/calendariumrom +4 -1
- data/config/locales/cs.yml +54 -2
- data/config/locales/en.yml +64 -14
- data/config/locales/es.yml +90 -0
- data/config/locales/fr.yml +90 -0
- data/config/locales/it.yml +54 -4
- data/config/locales/la.yml +52 -2
- data/data/README.md +105 -5
- data/data/czech-brno-cs.txt +11 -5
- data/data/czech-budejovice-cs.txt +11 -5
- data/data/czech-cechy-cs.txt +11 -5
- data/data/czech-cs.txt +243 -234
- data/data/czech-hradec-cs.txt +10 -4
- data/data/czech-litomerice-cs.txt +12 -6
- data/data/czech-morava-cs.txt +11 -5
- data/data/czech-olomouc-cs.txt +9 -3
- data/data/czech-ostrava-cs.txt +10 -4
- data/data/czech-plzen-cs.txt +10 -4
- data/data/czech-praha-cs.txt +10 -3
- data/data/universal-en.txt +218 -212
- data/data/universal-es.txt +243 -0
- data/data/universal-fr.txt +243 -0
- data/data/universal-it.txt +218 -212
- data/data/universal-la.txt +218 -211
- data/lib/calendarium-romanum.rb +30 -18
- data/lib/calendarium-romanum/abstract_date.rb +12 -0
- data/lib/calendarium-romanum/calendar.rb +210 -48
- data/lib/calendarium-romanum/cli.rb +101 -52
- data/lib/calendarium-romanum/cr.rb +16 -0
- data/lib/calendarium-romanum/data.rb +46 -18
- data/lib/calendarium-romanum/day.rb +200 -21
- data/lib/calendarium-romanum/enum.rb +24 -5
- data/lib/calendarium-romanum/enums.rb +123 -37
- data/lib/calendarium-romanum/errors.rb +4 -0
- data/lib/calendarium-romanum/ordinalizer.rb +61 -0
- data/lib/calendarium-romanum/perpetual_calendar.rb +97 -0
- data/lib/calendarium-romanum/rank.rb +43 -6
- data/lib/calendarium-romanum/sanctorale.rb +142 -22
- data/lib/calendarium-romanum/sanctorale_factory.rb +74 -3
- data/lib/calendarium-romanum/sanctorale_loader.rb +176 -0
- data/lib/calendarium-romanum/temporale.rb +296 -251
- data/lib/calendarium-romanum/temporale/celebration_factory.rb +106 -0
- data/lib/calendarium-romanum/temporale/dates.rb +232 -0
- data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +37 -0
- data/lib/calendarium-romanum/transfers.rb +43 -6
- data/lib/calendarium-romanum/util.rb +36 -3
- data/lib/calendarium-romanum/version.rb +5 -1
- data/spec/abstract_date_spec.rb +11 -3
- data/spec/calendar_spec.rb +645 -188
- data/spec/celebration_factory_spec.rb +40 -0
- data/spec/celebration_spec.rb +67 -0
- data/spec/cli_spec.rb +154 -11
- data/spec/colour_spec.rb +22 -0
- data/spec/data_spec.rb +26 -3
- data/spec/date_parser_spec.rb +68 -0
- data/spec/date_spec.rb +8 -8
- data/spec/dates_spec.rb +73 -0
- data/spec/day_spec.rb +151 -0
- data/spec/i18n_spec.rb +11 -2
- data/spec/ordinalizer_spec.rb +44 -0
- data/spec/perpetual_calendar_spec.rb +125 -0
- data/spec/rank_spec.rb +42 -7
- data/spec/readme_spec.rb +18 -10
- data/spec/sanctorale_factory_spec.rb +113 -9
- data/spec/sanctorale_loader_spec.rb +229 -0
- data/spec/sanctorale_spec.rb +176 -62
- data/spec/season_spec.rb +22 -0
- data/spec/spec_helper.rb +27 -1
- data/spec/temporale_spec.rb +473 -154
- data/spec/year_spec.rb +25 -0
- metadata +42 -7
- data/lib/calendarium-romanum/sanctoraleloader.rb +0 -104
- data/spec/sanctoraleloader_spec.rb +0 -171
@@ -1,12 +1,19 @@
|
|
1
1
|
module CalendariumRomanum
|
2
2
|
|
3
|
+
# General utilities, not tied to the business domain
|
4
|
+
# of liturgical calendar computation.
|
3
5
|
module Util
|
4
6
|
|
5
7
|
# Abstract superclass for date enumerators.
|
8
|
+
# @abstract
|
6
9
|
class DateEnumerator
|
7
10
|
include Enumerable
|
8
11
|
|
12
|
+
# @yield [Date]
|
13
|
+
# @return [void, Enumerator]
|
9
14
|
def each
|
15
|
+
return to_enum(__method__) unless block_given?
|
16
|
+
|
10
17
|
d = @start
|
11
18
|
begin
|
12
19
|
yield d
|
@@ -14,14 +21,16 @@ module CalendariumRomanum
|
|
14
21
|
end until enumeration_over? d
|
15
22
|
end
|
16
23
|
|
24
|
+
# @param date [Date]
|
25
|
+
# @return [Boolean]
|
17
26
|
def enumeration_over?(date)
|
18
27
|
@start.send(@prop) != date.send(@prop)
|
19
28
|
end
|
20
29
|
|
21
|
-
|
30
|
+
alias each_day each
|
22
31
|
end
|
23
32
|
|
24
|
-
#
|
33
|
+
# Enumerates days of a year
|
25
34
|
class Year < DateEnumerator
|
26
35
|
def initialize(year)
|
27
36
|
@start = Date.new year, 1, 1
|
@@ -29,12 +38,36 @@ module CalendariumRomanum
|
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
32
|
-
#
|
41
|
+
# Enumerates days of a month
|
33
42
|
class Month < DateEnumerator
|
34
43
|
def initialize(year, month)
|
35
44
|
@start = Date.new year, month, 1
|
36
45
|
@prop = :month
|
37
46
|
end
|
38
47
|
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
class DateParser
|
51
|
+
def initialize(date_str)
|
52
|
+
if date_str =~ /((\d{4})(\/|-)?(\d{0,2})(\/|-)?(\d{0,2}))\z/ # Accepts YYYY-MM-DD, YYYY/MM/DD where both day and month are optional
|
53
|
+
year = Regexp.last_match(2).to_i
|
54
|
+
month = Regexp.last_match(4).to_i
|
55
|
+
day = Regexp.last_match(6).to_i
|
56
|
+
@date_range = if (day == 0) && (month == 0) # Only year is given
|
57
|
+
Year.new(year)
|
58
|
+
elsif day == 0 # Year and month are given
|
59
|
+
Month.new(year, month)
|
60
|
+
else
|
61
|
+
Date.new(year, month, day)..Date.new(year, month, day)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise ArgumentError, 'Unparseable date'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [DateEnumerator, Range]
|
69
|
+
attr_reader :date_range
|
70
|
+
end
|
71
|
+
|
39
72
|
end
|
40
73
|
end
|
data/spec/abstract_date_spec.rb
CHANGED
@@ -43,9 +43,17 @@ describe CR::AbstractDate do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
describe '#<' do
|
46
|
-
it
|
47
|
-
|
48
|
-
|
46
|
+
it 'days of the same month' do
|
47
|
+
expect(AD.new(1, 1)).to be < AD.new(1, 2)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'the same day, different months' do
|
51
|
+
expect(AD.new(1, 1)).to be < AD.new(2, 1)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'same' do
|
55
|
+
expect(AD.new(1, 1)).not_to be < AD.new(1, 1)
|
56
|
+
end
|
49
57
|
end
|
50
58
|
|
51
59
|
describe '#==' do
|
data/spec/calendar_spec.rb
CHANGED
@@ -1,183 +1,341 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
2
|
|
3
3
|
describe CR::Calendar do
|
4
|
+
before :all do
|
5
|
+
@c = described_class.new(2013).freeze
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:celfactory) { CR::Temporale::CelebrationFactory }
|
9
|
+
|
10
|
+
describe '.new' do
|
11
|
+
it 'throws RangeError on invalid year' do
|
12
|
+
expect do
|
13
|
+
described_class.new(1968)
|
14
|
+
end.to raise_exception(RangeError, /in use only since 1st January 1970/)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'throws ArgumentError when Temporale year does not match' do
|
18
|
+
year = 2000
|
19
|
+
temporale = CR::Temporale.new year
|
20
|
+
expect do
|
21
|
+
CR::Calendar.new(year + 1, nil, temporale)
|
22
|
+
end.to raise_exception ArgumentError
|
23
|
+
end
|
24
|
+
end
|
4
25
|
|
5
|
-
describe '
|
6
|
-
|
7
|
-
|
26
|
+
describe '.for_day' do
|
27
|
+
it 'continues the previous year\'s calendar in summer' do
|
28
|
+
expect(described_class.for_day(Date.new(2014, 6, 9))).to eq described_class.new(2013)
|
8
29
|
end
|
9
30
|
|
10
|
-
|
11
|
-
|
12
|
-
|
31
|
+
it 'provides the current year\'s calendar in December' do
|
32
|
+
expect(described_class.for_day(Date.new(2014, 12, 20))).to eq described_class.new(2014)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.mk_date' do
|
37
|
+
describe 'with a Date' do
|
38
|
+
it 'returns the Date' do
|
39
|
+
date = Date.today
|
40
|
+
expect(described_class.mk_date(date))
|
41
|
+
.to be date
|
13
42
|
end
|
43
|
+
end
|
14
44
|
|
15
|
-
|
16
|
-
|
45
|
+
describe 'with three integers' do
|
46
|
+
it 'builds a Date' do
|
47
|
+
expect(described_class.mk_date(1, 2, 3))
|
48
|
+
.to eq Date.new(1, 2, 3)
|
17
49
|
end
|
18
50
|
end
|
19
51
|
|
20
|
-
describe '
|
21
|
-
it '
|
22
|
-
described_class.
|
23
|
-
|
24
|
-
described_class.new(2012).lectionary.should eq :C
|
52
|
+
describe 'invalid arguments' do
|
53
|
+
it 'only two arguments' do
|
54
|
+
expect { described_class.mk_date(1, 2) }
|
55
|
+
.to raise_exception TypeError
|
25
56
|
end
|
26
57
|
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#each' do
|
61
|
+
let (:day_count) {(@c.temporale.start_date..@c.temporale.end_date).count}
|
27
62
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
63
|
+
it 'yields for each iteration' do
|
64
|
+
expect {|b| @c.each(&b) }.to yield_control
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'yields the expected number of times' do
|
68
|
+
expect {|b| @c.each(&b) }.to yield_control.exactly(day_count).times
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'yields calendar day instances' do
|
72
|
+
expected_class = Array.new(day_count, CR::Day)
|
73
|
+
expect {|b| @c.each(&b) }.to yield_successive_args(*expected_class)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns Enumerator if called without a block' do
|
77
|
+
expect(@c.each).to be_a Enumerator
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#==' do
|
82
|
+
let(:year) { 2014 }
|
83
|
+
let(:b) { described_class.new(year) }
|
84
|
+
|
85
|
+
describe 'year' do
|
86
|
+
let(:a) { described_class.new(year) }
|
87
|
+
|
88
|
+
it 'same' do
|
89
|
+
expect(a).to be == described_class.new(year)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'different' do
|
93
|
+
expect(a).not_to be == described_class.new(year + 1)
|
32
94
|
end
|
33
95
|
end
|
34
96
|
|
35
|
-
describe '
|
36
|
-
|
37
|
-
|
97
|
+
describe 'sanctorale' do
|
98
|
+
let(:sanctorale) { CR::Data::GENERAL_ROMAN_ENGLISH.load }
|
99
|
+
let(:a) { described_class.new(year, sanctorale) }
|
100
|
+
|
101
|
+
it 'same' do
|
102
|
+
expect(a).to be == described_class.new(year, sanctorale)
|
38
103
|
end
|
39
104
|
|
40
|
-
it '
|
41
|
-
|
105
|
+
it 'different' do
|
106
|
+
expect(a == b).to be false
|
42
107
|
end
|
43
108
|
end
|
44
109
|
|
45
|
-
describe '
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
110
|
+
describe 'temporale' do
|
111
|
+
let(:temporale) { CR::Temporale.new year, transfer_to_sunday: [:epiphany] }
|
112
|
+
let(:a) { described_class.new(year, nil, temporale) }
|
113
|
+
|
114
|
+
it 'same' do
|
115
|
+
expect(a).to be == described_class.new(year, nil, temporale)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'different' do
|
119
|
+
expect(a).not_to be == b
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'vespers' do
|
124
|
+
let(:a) { described_class.new(year, vespers: true) }
|
125
|
+
|
126
|
+
it 'same' do
|
127
|
+
expect(a).to be == described_class.new(year, vespers: true)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'different' do
|
131
|
+
expect(a == b).to be false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#lectionary' do
|
137
|
+
{
|
138
|
+
2014 => :B,
|
139
|
+
2013 => :A,
|
140
|
+
2012 => :C
|
141
|
+
}.each_pair do |year, cycle|
|
142
|
+
it year.to_s do
|
143
|
+
expect(described_class.new(year).lectionary).to eq cycle
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#ferial_lectionary' do
|
149
|
+
{
|
150
|
+
2014 => 1,
|
151
|
+
2013 => 2
|
152
|
+
}.each do |year, cycle|
|
153
|
+
it year.to_s do
|
154
|
+
expect(described_class.new(year).ferial_lectionary).to eq cycle
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#[]' do
|
160
|
+
describe 'received arguments' do
|
161
|
+
describe 'Date' do
|
162
|
+
it 'returns a Day' do
|
163
|
+
expect(@c[Date.new(2013, 12, 10)]).to be_a CR::Day
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe 'Date range' do
|
168
|
+
it 'returns an array of Days' do
|
169
|
+
array = @c[Date.new(2013, 12, 10)..Date.new(2014, 4, 10)]
|
170
|
+
expect(array).to be_a Array
|
171
|
+
array.each{|day| expect(day).to be_a CR::Day}
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#day' do
|
178
|
+
describe 'received arguments' do
|
179
|
+
describe 'Date' do
|
180
|
+
it 'returns a Day' do
|
181
|
+
expect(@c.day(Date.new(2013, 12, 10))).to be_a CR::Day
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'DateTime' do
|
186
|
+
it 'returns a Day' do
|
187
|
+
expect(@c.day(DateTime.new(2013, 12, 10, 12, 10, 0))).to be_a CR::Day
|
51
188
|
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe 'three Integers' do
|
192
|
+
it 'returns a Day' do
|
193
|
+
expect(@c.day(2013, 12, 10)).to be_a CR::Day
|
194
|
+
end
|
195
|
+
end
|
52
196
|
|
53
|
-
|
54
|
-
|
55
|
-
|
197
|
+
describe 'two integers' do
|
198
|
+
describe 'autumn' do
|
199
|
+
it 'supplies year' do
|
200
|
+
day = @c.day(12, 10)
|
201
|
+
expect(day).to be_a CR::Day
|
202
|
+
expect(day.date).to eq Date.new(2013, 12, 10)
|
56
203
|
end
|
57
204
|
end
|
58
205
|
|
59
|
-
describe '
|
60
|
-
it '
|
61
|
-
@c.day(
|
206
|
+
describe 'spring' do
|
207
|
+
it 'supplies year' do
|
208
|
+
day = @c.day(4, 10)
|
209
|
+
expect(day).to be_a CR::Day
|
210
|
+
expect(day.date).to eq Date.new(2014, 4, 10)
|
62
211
|
end
|
63
212
|
end
|
64
213
|
|
65
|
-
describe '
|
66
|
-
describe '
|
67
|
-
it '
|
68
|
-
|
69
|
-
|
70
|
-
|
214
|
+
describe 'invalid' do
|
215
|
+
describe 'absolutely' do
|
216
|
+
it 'fails' do
|
217
|
+
expect do
|
218
|
+
@c.day(0, 34)
|
219
|
+
end.to raise_exception(ArgumentError, 'invalid date')
|
71
220
|
end
|
72
221
|
end
|
73
222
|
|
74
|
-
describe '
|
75
|
-
it '
|
76
|
-
|
77
|
-
|
78
|
-
|
223
|
+
describe 'for the given year' do
|
224
|
+
it 'fails' do
|
225
|
+
expect do
|
226
|
+
@c.day(2, 29)
|
227
|
+
end.to raise_exception(ArgumentError, 'invalid date')
|
79
228
|
end
|
80
229
|
end
|
81
230
|
end
|
82
231
|
end
|
232
|
+
end
|
83
233
|
|
84
|
-
|
85
|
-
|
234
|
+
describe "date not included in the calendar's year" do
|
235
|
+
it 'throws RangeError' do
|
236
|
+
expect { @c.day(2000, 1, 1) }.to raise_exception RangeError
|
86
237
|
end
|
238
|
+
end
|
87
239
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
240
|
+
describe 'date before system effectiveness' do
|
241
|
+
it 'throws RangeError' do
|
242
|
+
c = described_class.new 1969
|
243
|
+
expect { c.day(1969, 12, 20) }.to raise_exception RangeError
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
describe 'temporale features' do
|
248
|
+
describe 'season' do
|
249
|
+
it 'detects Advent correctly' do
|
250
|
+
expect(@c.day(2013, 12, 10).season).to eq CR::Seasons::ADVENT
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe 'week of the season' do
|
255
|
+
describe 'Advent' do
|
256
|
+
it 'sets Advent week correctly' do
|
257
|
+
expect(@c.day(2013, 12, 10).season_week).to eq 2
|
258
|
+
expect(@c.day(2013, 12, 15).season_week).to eq 3
|
92
259
|
end
|
93
260
|
end
|
94
261
|
|
95
|
-
describe '
|
96
|
-
|
97
|
-
|
98
|
-
expect(@c.day(2013, 12, 10).season_week).to eq 2
|
99
|
-
expect(@c.day(2013, 12, 15).season_week).to eq 3
|
100
|
-
end
|
262
|
+
describe 'Christmas' do
|
263
|
+
it 'days before the first Sunday are week 0' do
|
264
|
+
expect(@c.day(2013, 12, 25).season_week).to eq 0
|
101
265
|
end
|
102
266
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
267
|
+
it 'first Sunday starts week 1' do
|
268
|
+
expect(@c.day(2013, 12, 29).season_week).to eq 1
|
269
|
+
end
|
270
|
+
end
|
107
271
|
|
108
|
-
|
109
|
-
|
110
|
-
|
272
|
+
describe 'Lent' do
|
273
|
+
it 'Ash Wednesday is week 0' do
|
274
|
+
expect(@c.day(2014, 3, 5).season_week).to eq 0
|
111
275
|
end
|
276
|
+
end
|
112
277
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
278
|
+
describe 'Easter' do
|
279
|
+
it 'Easter Sunday opens week 1' do
|
280
|
+
expect(@c.day(2014, 4, 20).season_week).to eq 1
|
117
281
|
end
|
282
|
+
end
|
118
283
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
284
|
+
describe 'Ordinary time' do
|
285
|
+
it 'Monday after Baptism of the Lord is week 1' do
|
286
|
+
expect(@c.day(2014, 1, 13).season_week).to eq 1
|
123
287
|
end
|
124
288
|
|
125
|
-
describe '
|
126
|
-
it '
|
127
|
-
|
289
|
+
describe 'after Pentecost' do
|
290
|
+
it '2014' do
|
291
|
+
c = described_class.new(2013)
|
292
|
+
expect(c.day(2014, 6, 9).season_week).to eq 10
|
128
293
|
end
|
129
294
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end
|
135
|
-
|
136
|
-
it '2015' do
|
137
|
-
c = described_class.new(2014)
|
138
|
-
expect(c.day(2015, 5, 25).season_week).to eq 8
|
139
|
-
end
|
295
|
+
it '2015' do
|
296
|
+
c = described_class.new(2014)
|
297
|
+
expect(c.day(2015, 5, 25).season_week).to eq 8
|
298
|
+
end
|
140
299
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
300
|
+
it '2016' do
|
301
|
+
c = described_class.new(2015)
|
302
|
+
expect(c.day(2016, 5, 16).season_week).to eq 7
|
303
|
+
end
|
145
304
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
305
|
+
it '2017' do
|
306
|
+
c = described_class.new(2016)
|
307
|
+
expect(c.day(2017, 6, 5).season_week).to eq 9
|
308
|
+
end
|
150
309
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
end
|
310
|
+
describe 'works correctly for the whole week' do
|
311
|
+
describe 'first' do
|
312
|
+
Date.new(2014, 6, 9).upto(Date.new(2014, 6, 14)) do |date|
|
313
|
+
it date do
|
314
|
+
expect(@c.day(date).season_week).to eq 10
|
157
315
|
end
|
158
316
|
end
|
317
|
+
end
|
159
318
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
end
|
319
|
+
describe 'second' do
|
320
|
+
Date.new(2014, 6, 15).upto(Date.new(2014, 6, 21)) do |date|
|
321
|
+
it date do
|
322
|
+
expect(@c.day(date).season_week).to eq 11
|
165
323
|
end
|
166
324
|
end
|
325
|
+
end
|
167
326
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
end
|
327
|
+
describe 'second last' do
|
328
|
+
Date.new(2014, 11, 16).upto(Date.new(2014, 11, 22)) do |date|
|
329
|
+
it date do
|
330
|
+
expect(@c.day(date).season_week).to eq 33
|
173
331
|
end
|
174
332
|
end
|
333
|
+
end
|
175
334
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
end
|
335
|
+
describe 'last' do
|
336
|
+
Date.new(2014, 11, 23).upto(Date.new(2014, 11, 29)) do |date|
|
337
|
+
it date do
|
338
|
+
expect(@c.day(date).season_week).to eq 34
|
181
339
|
end
|
182
340
|
end
|
183
341
|
end
|
@@ -185,115 +343,414 @@ describe CR::Calendar do
|
|
185
343
|
end
|
186
344
|
end
|
187
345
|
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe 'Temporale x Sanctorale resolution' do
|
349
|
+
before :all do
|
350
|
+
@s = CR::Data::GENERAL_ROMAN_ENGLISH.load
|
351
|
+
@c = described_class.new(2013, @s).freeze
|
352
|
+
end
|
353
|
+
|
354
|
+
it '"empty" day results in a ferial' do
|
355
|
+
d = @c.day(7, 2)
|
356
|
+
expect(d.celebrations.size).to eq 1
|
357
|
+
expect(d.celebrations[0].rank).to eq CR::Ranks::FERIAL
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'sanctorale feast' do
|
361
|
+
d = @c.day(7, 3)
|
362
|
+
expect(d.celebrations.size).to eq 1
|
363
|
+
expect(d.celebrations[0].rank).to eq CR::Ranks::FEAST_GENERAL
|
364
|
+
expect(d.celebrations[0].title).to include 'Thomas'
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'optional memorial does not suppress ferial' do
|
368
|
+
d = @c.day(7, 14)
|
369
|
+
expect(d.celebrations.size).to eq 2
|
370
|
+
|
371
|
+
expect(d.celebrations[0].rank).to eq CR::Ranks::FERIAL
|
372
|
+
|
373
|
+
expect(d.celebrations[1].rank).to eq CR::Ranks::MEMORIAL_OPTIONAL
|
374
|
+
expect(d.celebrations[1].title).to include 'Lellis'
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'obligatory memorial does suppress ferial' do
|
378
|
+
d = @c.day(1, 17)
|
379
|
+
expect(d.celebrations.size).to eq 1
|
380
|
+
|
381
|
+
expect(d.celebrations[0].rank).to eq CR::Ranks::MEMORIAL_GENERAL
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'memorial in Lent becomes mere commemoration' do
|
385
|
+
d = @c.day(4, 2)
|
386
|
+
expect(d.celebrations.size).to eq 2
|
387
|
+
|
388
|
+
comm = d.celebrations[1]
|
389
|
+
expect(comm.rank).to eq CR::Ranks::COMMEMORATION
|
390
|
+
expect(comm.title).to eq 'Saint Francis of Paola, hermit'
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'Sunday suppresses feast' do
|
394
|
+
san = CR::Sanctorale.new
|
395
|
+
|
396
|
+
d = Date.new 2015, 6, 28
|
397
|
+
expect(d).to be_sunday # ensure
|
398
|
+
san.add d.month, d.day, CR::Celebration.new('St. None, programmer', CR::Ranks::FEAST_GENERAL)
|
399
|
+
|
400
|
+
c = described_class.new 2014, san
|
401
|
+
|
402
|
+
celebs = c.day(d).celebrations
|
403
|
+
expect(celebs.size).to eq 1
|
404
|
+
expect(celebs[0].rank).to eq CR::Ranks::SUNDAY_UNPRIVILEGED
|
405
|
+
end
|
406
|
+
|
407
|
+
it 'suppressed fictive solemnity is transferred' do
|
408
|
+
san = CR::Sanctorale.new
|
409
|
+
|
410
|
+
d = CR::Temporale.new(2014).good_friday
|
411
|
+
st_none = CR::Celebration.new('St. None, abbot, founder of the Order of Programmers (OProg)', CR::Ranks::SOLEMNITY_PROPER)
|
412
|
+
san.add d.month, d.day, st_none
|
413
|
+
|
414
|
+
c = described_class.new 2014, san
|
415
|
+
|
416
|
+
# Good Friday suppresses the solemnity
|
417
|
+
celebs = c.day(d).celebrations
|
418
|
+
expect(celebs.size).to eq 1
|
419
|
+
expect(celebs[0]).to eq celfactory.good_friday
|
420
|
+
|
421
|
+
# it is transferred on a day after the Easter octave
|
422
|
+
d = c.temporale.easter_sunday + 8
|
423
|
+
celebs = c.day(d).celebrations
|
424
|
+
expect(celebs.size).to eq 1
|
425
|
+
expect(celebs[0]).to eq st_none
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'transfer of suppressed Annunciation (real world example)' do
|
429
|
+
c = described_class.new 2015, @s
|
430
|
+
|
431
|
+
d = Date.new(2016, 3, 25)
|
432
|
+
|
433
|
+
# Good Friday suppresses the solemnity
|
434
|
+
celebs = c.day(d).celebrations
|
435
|
+
expect(celebs.size).to eq 1
|
436
|
+
expect(celebs[0]).to eq celfactory.good_friday
|
437
|
+
|
438
|
+
# it is transferred on a day after the Easter octave
|
439
|
+
d = c.temporale.easter_sunday + 8
|
440
|
+
celebs = c.day(d).celebrations
|
441
|
+
expect(celebs.size).to eq 1
|
442
|
+
expect(celebs[0].title).to eq 'Annunciation of the Lord'
|
443
|
+
end
|
444
|
+
|
445
|
+
describe 'collision of Immaculate Heart with another obligatory memorial' do
|
446
|
+
let(:year) { 2002 }
|
447
|
+
let(:c) { described_class.new year, @s }
|
448
|
+
let(:date) { Date.new(2003, 6, 28) }
|
449
|
+
|
450
|
+
it 'makes both memorials optional' do
|
451
|
+
# make sure
|
452
|
+
expect(c.sanctorale.get(date).first.rank).to eq CR::Ranks::MEMORIAL_GENERAL
|
453
|
+
expect(c.temporale.get(date).rank).to eq CR::Ranks::MEMORIAL_GENERAL
|
454
|
+
|
455
|
+
celebrations = c.day(date).celebrations
|
456
|
+
expect(celebrations.size).to eq 3
|
457
|
+
|
458
|
+
expect(celebrations[0].rank).to eq CR::Ranks::FERIAL
|
459
|
+
expect(celebrations[1..-1].collect(&:rank).uniq)
|
460
|
+
.to eq([CR::Ranks::MEMORIAL_OPTIONAL])
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
describe 'collision of Mary, Mother of the Church with another obligatory memorial' do
|
465
|
+
let(:year) { 2019 }
|
466
|
+
let(:c) { described_class.new year, @s }
|
467
|
+
let(:date) { Date.new(2020, 6, 1) }
|
188
468
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
469
|
+
it 'the Marian memorial takes precedence' do
|
470
|
+
expect(c.sanctorale.get(date).first.rank).to eq CR::Ranks::MEMORIAL_GENERAL
|
471
|
+
expect(c.temporale.get(date).rank).to eq CR::Ranks::MEMORIAL_GENERAL
|
472
|
+
|
473
|
+
celebrations = c.day(date).celebrations
|
474
|
+
expect(celebrations.size).to eq 1
|
475
|
+
|
476
|
+
expect(celebrations[0].symbol).to eq :mother_of_church
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
describe 'Saturday memorial' do
|
482
|
+
let(:sanctorale) { double(CR::Sanctorale, solemnities: {}) }
|
483
|
+
let(:calendar) { described_class.new(2013, sanctorale) }
|
484
|
+
let(:celebrations) { calendar.day(date).celebrations }
|
485
|
+
|
486
|
+
describe 'free Saturday in Ordinary Time' do
|
487
|
+
let(:date) { Date.new(2014, 8, 16) }
|
488
|
+
|
489
|
+
before :each do
|
490
|
+
allow(sanctorale).to receive(:[]).and_return([])
|
491
|
+
end
|
492
|
+
|
493
|
+
it 'offers Saturday memorial' do
|
494
|
+
expect(celebrations)
|
495
|
+
.to include(an_object_eq_to(celfactory.saturday_memorial_bvm))
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
describe 'Saturday in Ordinary Time with optional memorial(s)' do
|
500
|
+
let(:date) { Date.new(2014, 8, 23) }
|
501
|
+
|
502
|
+
before :each do
|
503
|
+
memorial = CR::Celebration.new('', CR::Ranks::MEMORIAL_OPTIONAL)
|
504
|
+
allow(sanctorale).to receive(:[]).and_return([memorial])
|
195
505
|
end
|
196
506
|
|
197
|
-
it '
|
198
|
-
|
199
|
-
|
200
|
-
expect(d.celebrations[0].rank).to eq CR::Ranks::FERIAL
|
507
|
+
it 'offers Saturday memorial' do
|
508
|
+
expect(celebrations)
|
509
|
+
.to include(an_object_eq_to(celfactory.saturday_memorial_bvm))
|
201
510
|
end
|
511
|
+
end
|
202
512
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
513
|
+
describe 'non-free Saturday in Ordinary Time' do
|
514
|
+
let(:date) { Date.new(2014, 9, 13) }
|
515
|
+
|
516
|
+
before :each do
|
517
|
+
memorial = CR::Celebration.new('', CR::Ranks::MEMORIAL_GENERAL)
|
518
|
+
allow(sanctorale).to receive(:[]).and_return([memorial])
|
208
519
|
end
|
209
520
|
|
210
|
-
it '
|
211
|
-
|
212
|
-
|
521
|
+
it 'does not offer Saturday memorial' do
|
522
|
+
expect(celebrations)
|
523
|
+
.not_to include(an_object_eq_to(celfactory.saturday_memorial_bvm))
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
describe 'free Saturday in another season' do
|
528
|
+
let(:date) { Date.new(2013, 12, 14) }
|
213
529
|
|
214
|
-
|
530
|
+
before :each do
|
531
|
+
allow(sanctorale).to receive(:[]).and_return([])
|
532
|
+
end
|
215
533
|
|
216
|
-
|
217
|
-
expect(
|
534
|
+
it 'does not offer Saturday memorial' do
|
535
|
+
expect(celebrations)
|
536
|
+
.not_to include(an_object_eq_to(celfactory.saturday_memorial_bvm))
|
218
537
|
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
describe 'Vespers' do
|
542
|
+
let(:saturday) { Date.new(2014, 1, 4) }
|
543
|
+
let(:year) { 2013 }
|
544
|
+
let(:calendar) { described_class.new(year) }
|
545
|
+
|
546
|
+
describe 'not opted in' do
|
547
|
+
it 'does not fill Vespers' do
|
548
|
+
day = calendar.day saturday
|
549
|
+
expect(day.vespers).to be nil
|
550
|
+
end
|
551
|
+
end
|
219
552
|
|
220
|
-
|
221
|
-
|
222
|
-
expect(d.celebrations.size).to eq 1
|
553
|
+
describe 'opted in by constructor argument' do
|
554
|
+
let(:calendar) { described_class.new(year, nil, nil, vespers: true) }
|
223
555
|
|
224
|
-
|
556
|
+
it 'fills Vespers' do
|
557
|
+
day = calendar.day saturday
|
558
|
+
expect(day.vespers).to be_a CR::Celebration
|
225
559
|
end
|
226
560
|
|
227
|
-
|
228
|
-
|
561
|
+
describe 'but the day has not Vespers from following' do
|
562
|
+
it 'does not fill Vespers' do
|
563
|
+
friday = saturday - 1
|
564
|
+
day = calendar.day friday
|
565
|
+
expect(day.vespers).to be nil
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
229
569
|
|
230
|
-
|
231
|
-
|
232
|
-
|
570
|
+
describe 'opted in by argument' do
|
571
|
+
it 'fills Vespers' do
|
572
|
+
day = calendar.day saturday, vespers: true
|
573
|
+
expect(day.vespers).to be_a CR::Celebration
|
574
|
+
end
|
575
|
+
end
|
233
576
|
|
234
|
-
|
577
|
+
describe 'first Vespers of' do
|
578
|
+
let(:sanctorale) { CR::Data::GENERAL_ROMAN_ENGLISH.load }
|
579
|
+
let(:calendar) { described_class.new(year, sanctorale, nil, vespers: true) }
|
235
580
|
|
236
|
-
|
237
|
-
|
238
|
-
|
581
|
+
describe 'a Sunday' do
|
582
|
+
it 'has first Vespers' do
|
583
|
+
day = calendar.day saturday
|
584
|
+
expect(day.vespers.rank).to eq CR::Ranks::SUNDAY_UNPRIVILEGED
|
585
|
+
end
|
239
586
|
end
|
240
587
|
|
241
|
-
|
242
|
-
|
588
|
+
describe 'a solemnity' do
|
589
|
+
let(:testing_solemnity) do
|
590
|
+
CR::Celebration.new(
|
591
|
+
'Testing solemnity',
|
592
|
+
CR::Ranks::SOLEMNITY_GENERAL,
|
593
|
+
CR::Colours::WHITE,
|
594
|
+
:test
|
595
|
+
)
|
596
|
+
end
|
597
|
+
|
598
|
+
it 'has first Vespers' do
|
599
|
+
day = calendar.day(Date.new(2014, 11, 1) - 1)
|
600
|
+
expect(day.vespers.rank).to be CR::Ranks::SOLEMNITY_GENERAL
|
601
|
+
end
|
243
602
|
|
244
|
-
|
245
|
-
|
246
|
-
|
603
|
+
describe 'clash with Sunday Vespers' do
|
604
|
+
it 'wins over Sunday' do
|
605
|
+
sunday = Date.new(2014, 8, 17)
|
606
|
+
expect(sunday).to be_sunday # make sure
|
607
|
+
sanctorale.replace(8, 17, [testing_solemnity])
|
247
608
|
|
248
|
-
|
609
|
+
day = calendar.day(sunday - 1)
|
610
|
+
expect(day.vespers.symbol).to eq :test
|
611
|
+
end
|
612
|
+
end
|
249
613
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
expect(celebs[0].title).to eq 'Friday of the Passion of the Lord'
|
614
|
+
describe 'clash with second Vespers of another solemnity' do
|
615
|
+
it "the day's Vespers win" do
|
616
|
+
assumption = Date.new(2014, 8, 15)
|
617
|
+
sanctorale.replace(8, 16, [testing_solemnity])
|
255
618
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
619
|
+
day = calendar.day(assumption)
|
620
|
+
expect(day.celebrations.first.rank).to be CR::Ranks::SOLEMNITY_GENERAL
|
621
|
+
expect(day.vespers).to be nil
|
622
|
+
|
623
|
+
# make sure
|
624
|
+
next_day = calendar.day(assumption + 1)
|
625
|
+
expect(next_day.celebrations.first).to be testing_solemnity
|
626
|
+
end
|
627
|
+
end
|
261
628
|
end
|
262
629
|
|
263
|
-
|
264
|
-
|
630
|
+
describe 'feast of the Lord' do
|
631
|
+
describe 'not falling on a Sunday' do
|
632
|
+
it 'does not have first Vespers' do
|
633
|
+
calendar = described_class.new(2015, sanctorale, nil, vespers: true)
|
634
|
+
presentation = Date.new(2016, 2, 2)
|
635
|
+
expect(presentation).not_to be_sunday # make sure
|
265
636
|
|
266
|
-
|
637
|
+
day = calendar.day(presentation - 1)
|
638
|
+
expect(day.vespers).to be nil
|
639
|
+
end
|
640
|
+
end
|
267
641
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
expect(celebs[0].title).to eq 'Friday of the Passion of the Lord'
|
642
|
+
describe 'falling on a Sunday' do
|
643
|
+
it 'has first Vespers' do
|
644
|
+
presentation = Date.new(2014, 2, 2)
|
645
|
+
expect(presentation).to be_sunday # make sure
|
273
646
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
647
|
+
day = calendar.day(presentation - 1)
|
648
|
+
expect(day.vespers.rank).to be CR::Ranks::FEAST_LORD_GENERAL
|
649
|
+
end
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
# this group contains both days having and days not having
|
654
|
+
# first Vespers: special care must be taken
|
655
|
+
describe 'primary liturgical days' do
|
656
|
+
describe 'Ash Wednesday' do
|
657
|
+
it 'does not have first Vespers' do
|
658
|
+
aw = CR::Temporale::Dates.ash_wednesday year
|
659
|
+
day = calendar.day(aw - 1)
|
660
|
+
expect(day.vespers).to be nil
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
describe 'Nativity' do
|
665
|
+
it 'has first Vespers' do
|
666
|
+
day = calendar.day Date.new(2013, 12, 24)
|
667
|
+
expect(day.vespers).to eq celfactory.nativity
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
describe 'Epiphany' do
|
672
|
+
it 'has first Vespers' do
|
673
|
+
day = calendar.day Date.new(2014, 1, 5)
|
674
|
+
expect(day.vespers.rank).to be CR::Ranks::PRIMARY
|
675
|
+
expect(day.vespers.symbol).to be :epiphany
|
676
|
+
end
|
677
|
+
end
|
678
|
+
|
679
|
+
describe 'Palm Sunday' do
|
680
|
+
it 'has first Vespers' do
|
681
|
+
ps = CR::Temporale::Dates.palm_sunday year
|
682
|
+
day = calendar.day(ps - 1)
|
683
|
+
expect(day.vespers).to eq celfactory.palm_sunday
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
describe 'day in the Holy week' do
|
688
|
+
it 'does not have first Vespers' do
|
689
|
+
tuesday = CR::Temporale::Dates.palm_sunday(year) + 2
|
690
|
+
day = calendar.day(tuesday - 1)
|
691
|
+
expect(day.vespers).to be nil
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
describe 'Good Friday' do
|
696
|
+
it 'does not have first Vespers' do
|
697
|
+
gf = CR::Temporale::Dates.good_friday(year)
|
698
|
+
day = calendar.day(gf - 1)
|
699
|
+
expect(day.vespers).to be nil
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
describe 'Easter' do
|
704
|
+
it 'has first Vespers' do
|
705
|
+
es = CR::Temporale::Dates.easter_sunday year
|
706
|
+
day = calendar.day(es - 1)
|
707
|
+
expect(day.vespers).to eq celfactory.easter_sunday
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
711
|
+
describe 'day in Easter octave' do
|
712
|
+
it 'does not have first Vespers' do
|
713
|
+
tuesday = CR::Temporale::Dates.easter_sunday(year) + 2
|
714
|
+
day = calendar.day(tuesday - 1)
|
715
|
+
expect(day.vespers).to be nil
|
716
|
+
end
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
describe 'edge cases' do
|
721
|
+
describe 'First Sunday of Advent' do
|
722
|
+
it 'has first Vespers (and does not cause an exception)' do
|
723
|
+
sunday = CR::Temporale::Dates.first_advent_sunday(year + 1)
|
724
|
+
day = calendar.day(sunday - 1)
|
725
|
+
expect(day.vespers).to eq celfactory.first_advent_sunday
|
726
|
+
end
|
727
|
+
end
|
279
728
|
end
|
280
729
|
end
|
281
730
|
end
|
731
|
+
end
|
282
732
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
733
|
+
# only s small subset of the Sanctorale public interface
|
734
|
+
# is used by Calendar. These specs show how small it is.
|
735
|
+
describe 'required sanctorale interface' do
|
736
|
+
class HardcodedEmptySanctorale
|
737
|
+
def solemnities
|
738
|
+
{} # no solemnities
|
288
739
|
end
|
289
|
-
end
|
290
740
|
|
291
|
-
|
292
|
-
|
293
|
-
new_cal = @c.succ
|
294
|
-
expect(new_cal.year).to eq(@c.year + 1)
|
295
|
-
expect(new_cal.sanctorale).to eq (@c.sanctorale)
|
741
|
+
def [](date)
|
742
|
+
[] # no celebrations
|
296
743
|
end
|
297
744
|
end
|
745
|
+
|
746
|
+
let(:year) { 2000 }
|
747
|
+
let(:sanctorale) { HardcodedEmptySanctorale.new }
|
748
|
+
let(:calendar) { described_class.new(year, sanctorale) }
|
749
|
+
|
750
|
+
it 'comprises only two methods' do
|
751
|
+
expect do
|
752
|
+
calendar[Date.new(year, 12, 20)]
|
753
|
+
end.not_to raise_exception
|
754
|
+
end
|
298
755
|
end
|
299
756
|
end
|