calendarium-romanum 0.6.0 → 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 +3 -0
- data/config/locales/es.yml +90 -0
- data/data/README.md +43 -1
- data/data/czech-brno-cs.txt +4 -6
- data/data/czech-budejovice-cs.txt +4 -6
- data/data/czech-cechy-cs.txt +4 -5
- data/data/czech-cs.txt +237 -234
- data/data/czech-hradec-cs.txt +3 -5
- data/data/czech-litomerice-cs.txt +5 -7
- data/data/czech-morava-cs.txt +4 -5
- data/data/czech-olomouc-cs.txt +2 -4
- data/data/czech-ostrava-cs.txt +3 -5
- data/data/czech-plzen-cs.txt +3 -5
- data/data/czech-praha-cs.txt +3 -4
- data/data/universal-en.txt +214 -211
- data/data/universal-es.txt +243 -0
- data/data/universal-fr.txt +214 -210
- data/data/universal-it.txt +214 -211
- data/data/universal-la.txt +214 -212
- data/lib/calendarium-romanum.rb +6 -0
- data/lib/calendarium-romanum/abstract_date.rb +12 -0
- data/lib/calendarium-romanum/calendar.rb +93 -13
- data/lib/calendarium-romanum/cli.rb +11 -4
- data/lib/calendarium-romanum/cr.rb +16 -0
- data/lib/calendarium-romanum/data.rb +34 -7
- data/lib/calendarium-romanum/day.rb +132 -14
- data/lib/calendarium-romanum/enum.rb +22 -1
- data/lib/calendarium-romanum/enums.rb +80 -28
- data/lib/calendarium-romanum/errors.rb +1 -1
- data/lib/calendarium-romanum/ordinalizer.rb +10 -1
- data/lib/calendarium-romanum/perpetual_calendar.rb +43 -5
- data/lib/calendarium-romanum/rank.rb +23 -0
- data/lib/calendarium-romanum/sanctorale.rb +119 -20
- data/lib/calendarium-romanum/sanctorale_factory.rb +74 -3
- data/lib/calendarium-romanum/sanctorale_loader.rb +60 -19
- data/lib/calendarium-romanum/temporale.rb +110 -9
- data/lib/calendarium-romanum/temporale/celebration_factory.rb +45 -2
- data/lib/calendarium-romanum/temporale/dates.rb +65 -1
- data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +14 -2
- data/lib/calendarium-romanum/transfers.rb +20 -1
- data/lib/calendarium-romanum/util.rb +15 -3
- data/lib/calendarium-romanum/version.rb +3 -2
- data/spec/calendar_spec.rb +48 -4
- data/spec/celebration_factory_spec.rb +4 -0
- data/spec/celebration_spec.rb +9 -0
- data/spec/cli_spec.rb +7 -0
- data/spec/data_spec.rb +23 -0
- data/spec/day_spec.rb +26 -0
- data/spec/i18n_spec.rb +10 -0
- data/spec/sanctorale_factory_spec.rb +113 -9
- data/spec/sanctorale_loader_spec.rb +49 -24
- data/spec/sanctorale_spec.rb +72 -9
- data/spec/temporale_spec.rb +52 -53
- data/spec/year_spec.rb +25 -0
- metadata +7 -3
@@ -1,15 +1,50 @@
|
|
1
1
|
module CalendariumRomanum
|
2
|
-
#
|
2
|
+
# Utility loading {Sanctorale} from several sources
|
3
|
+
# and building a single {Sanctorale} by layering them
|
4
|
+
# over each other.
|
3
5
|
class SanctoraleFactory
|
4
6
|
class << self
|
5
|
-
#
|
7
|
+
# Takes several {Sanctorale} instances, returns a new one,
|
8
|
+
# resulting by merging them all together
|
9
|
+
# (using {Sanctorale#update})
|
10
|
+
#
|
11
|
+
# @return [Sanctorale]
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# include CalendariumRomanum
|
15
|
+
#
|
16
|
+
# prague_sanctorale = SanctoraleFactory.create_layered(
|
17
|
+
# Data['czech-cs'].load, # Czech Republic
|
18
|
+
# Data['czech-cechy-cs'].load, # Province of Bohemia
|
19
|
+
# Data['czech-praha-cs'].load, # Archdiocese of Prague
|
20
|
+
# )
|
6
21
|
def create_layered(*instances)
|
7
22
|
r = Sanctorale.new
|
8
23
|
instances.each {|i| r.update i }
|
24
|
+
|
25
|
+
metadata = instances
|
26
|
+
.collect(&:metadata)
|
27
|
+
.select {|i| i.is_a? Hash }
|
28
|
+
r.metadata = metadata.inject((metadata.first || {}).dup) {|merged,i| merged.update i }
|
29
|
+
r.metadata.delete 'extends'
|
30
|
+
r.metadata['components'] = instances.collect(&:metadata)
|
31
|
+
|
9
32
|
r
|
10
33
|
end
|
11
34
|
|
12
|
-
#
|
35
|
+
# Takes several filesystem paths, loads a {Sanctorale}
|
36
|
+
# from each of them (using {SanctoraleLoader})
|
37
|
+
# and then merges them (using {.create_layered})
|
38
|
+
#
|
39
|
+
# @return [Sanctorale]
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# include CalendariumRomanum
|
43
|
+
#
|
44
|
+
# my_sanctorale = SanctoraleFactory.load_layered_from_files(
|
45
|
+
# 'my_data/general_calendar.txt',
|
46
|
+
# 'my_data/particular_calendar.txt'
|
47
|
+
# )
|
13
48
|
def load_layered_from_files(*paths)
|
14
49
|
loader = SanctoraleLoader.new
|
15
50
|
instances = paths.collect do |p|
|
@@ -17,6 +52,42 @@ module CalendariumRomanum
|
|
17
52
|
end
|
18
53
|
create_layered(*instances)
|
19
54
|
end
|
55
|
+
|
56
|
+
# Takes a single filesystem path. If the file's YAML front
|
57
|
+
# matter references any parent data files using the
|
58
|
+
# 'extends' key, it loads all the parents and assembles
|
59
|
+
# the resulting {Sanctorale}.
|
60
|
+
# If the data file doesn't reference any parents,
|
61
|
+
# result is the same as {SanctoraleLoader#load_from_file}.
|
62
|
+
#
|
63
|
+
# @return [Sanctorale]
|
64
|
+
# @since 0.7.0
|
65
|
+
def load_with_parents(path)
|
66
|
+
loader = SanctoraleLoader.new
|
67
|
+
|
68
|
+
hierarchy = load_parent_hierarchy(path, loader)
|
69
|
+
return hierarchy.first if hierarchy.size == 1
|
70
|
+
|
71
|
+
create_layered *hierarchy
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def load_parent_hierarchy(path, loader)
|
77
|
+
main = loader.load_from_file path
|
78
|
+
return [main] unless main.metadata.has_key? 'extends'
|
79
|
+
|
80
|
+
to_merge = [main]
|
81
|
+
parents = main.metadata['extends']
|
82
|
+
parents = [parents] unless parents.is_a? Array
|
83
|
+
parents.reverse.each do |parent_path|
|
84
|
+
expanded_path = File.expand_path parent_path, File.dirname(path)
|
85
|
+
subtree = load_parent_hierarchy(expanded_path, loader)
|
86
|
+
to_merge = subtree + to_merge
|
87
|
+
end
|
88
|
+
|
89
|
+
to_merge
|
90
|
+
end
|
20
91
|
end
|
21
92
|
end
|
22
93
|
end
|
@@ -1,47 +1,62 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
module CalendariumRomanum
|
2
4
|
|
3
|
-
#
|
4
|
-
# and knows how to transform it to
|
5
|
-
# and fill them in a Sanctorale
|
6
|
-
#
|
7
|
-
# Format of the file:
|
8
|
-
# 1/31 m : S. Ioannis Bosco, presbyteri
|
5
|
+
# Understands a custom plaintext calendar format
|
6
|
+
# and knows how to transform it to {Celebration}s
|
7
|
+
# and fill them in a {Sanctorale}.
|
9
8
|
#
|
10
|
-
#
|
11
|
-
#
|
9
|
+
# For specification of the data format see {file:data/README.md},
|
10
|
+
# for a complete example see the file describing General Roman Calendar:
|
11
|
+
# {file:data/universal-en.txt}
|
12
12
|
class SanctoraleLoader
|
13
13
|
|
14
|
+
# @api private
|
14
15
|
RANK_CODES = {
|
15
|
-
nil => Ranks::MEMORIAL_OPTIONAL,
|
16
|
+
nil => Ranks::MEMORIAL_OPTIONAL, # default
|
16
17
|
'm' => Ranks::MEMORIAL_GENERAL,
|
17
18
|
'f' => Ranks::FEAST_GENERAL,
|
18
19
|
's' => Ranks::SOLEMNITY_GENERAL
|
19
20
|
}.freeze
|
21
|
+
|
22
|
+
# @api private
|
20
23
|
COLOUR_CODES = {
|
21
|
-
nil => Colours::WHITE,
|
24
|
+
nil => Colours::WHITE, # default
|
22
25
|
'w' => Colours::WHITE,
|
23
26
|
'v' => Colours::VIOLET,
|
24
27
|
'g' => Colours::GREEN,
|
25
28
|
'r' => Colours::RED
|
26
29
|
}.freeze
|
27
30
|
|
28
|
-
#
|
29
|
-
#
|
31
|
+
# Load from an object which understands +#each_line+
|
32
|
+
#
|
33
|
+
# @param src [String, File, #each_line]
|
34
|
+
# source of the loaded data
|
35
|
+
# @param dest [Sanctorale, nil]
|
36
|
+
# objects to populate. If not provided, a new {Sanctorale}
|
37
|
+
# instance will be created
|
38
|
+
# @return [Sanctorale]
|
39
|
+
# @raise [InvalidDataError]
|
30
40
|
def load(src, dest = nil)
|
31
41
|
dest ||= Sanctorale.new
|
32
42
|
|
33
43
|
in_front_matter = false
|
44
|
+
front_matter = ''
|
34
45
|
month_section = nil
|
35
46
|
src.each_line.with_index(1) do |l, line_num|
|
36
47
|
# skip YAML front matter
|
37
48
|
if line_num == 1 && l.start_with?('---')
|
38
49
|
in_front_matter = true
|
50
|
+
front_matter += l
|
39
51
|
next
|
40
52
|
elsif in_front_matter
|
41
53
|
if l.start_with?('---')
|
42
54
|
in_front_matter = false
|
55
|
+
dest.metadata = YAML.load(front_matter).freeze
|
43
56
|
end
|
44
57
|
|
58
|
+
front_matter += l
|
59
|
+
|
45
60
|
next
|
46
61
|
end
|
47
62
|
|
@@ -78,26 +93,52 @@ module CalendariumRomanum
|
|
78
93
|
|
79
94
|
alias load_from_string load
|
80
95
|
|
96
|
+
# Load from a filesystem path
|
97
|
+
#
|
98
|
+
# @param filename [String]
|
99
|
+
# @param dest [Sanctorale, nil]
|
100
|
+
# @param encoding [String]
|
101
|
+
# @return (see #load)
|
102
|
+
# @raise (see #load)
|
81
103
|
def load_from_file(filename, dest = nil, encoding = 'utf-8')
|
82
104
|
load File.open(filename, 'r', encoding: encoding), dest
|
83
105
|
end
|
84
106
|
|
85
107
|
private
|
86
108
|
|
109
|
+
def line_regexp
|
110
|
+
@line_regexp ||=
|
111
|
+
begin
|
112
|
+
rank_letters = RANK_CODES.keys.compact.join('')
|
113
|
+
colour_letters = COLOUR_CODES.keys.compact.join('')
|
114
|
+
|
115
|
+
Regexp.new(
|
116
|
+
'^((?<month>\d+)\/)?(?<day>\d+)' + # date
|
117
|
+
'(\s+(?<rank_char>[' + rank_letters + '])?(?<rank_num>\d\.\d{1,2})?)?' + # rank (optional)
|
118
|
+
'(\s+(?<colour>[' + colour_letters + ']))?' + # colour (optional)
|
119
|
+
'(\s+(?<symbol>[\w]{2,}))?' + # symbol (optional)
|
120
|
+
'\s*:(?<title>.*)$', # title
|
121
|
+
Regexp::IGNORECASE
|
122
|
+
)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
87
126
|
# parses a line containing celebration record,
|
88
127
|
# returns a single Celebration
|
89
128
|
def load_line(line, month_section = nil)
|
90
129
|
# celebration record
|
91
|
-
|
92
|
-
m = line.match(/^((\d+)\/)?(\d+)\s*(([#{rank_letters}])?(\d\.\d{1,2})?)?\s*([WVRG])?\s*(:[\w\d_]+)?\s*:(.*)$/i)
|
130
|
+
m = line.match(line_regexp)
|
93
131
|
if m.nil?
|
94
132
|
raise RuntimeError.new("Syntax error, line skipped '#{line}'")
|
95
133
|
end
|
96
134
|
|
97
|
-
month
|
98
|
-
|
99
|
-
|
100
|
-
|
135
|
+
month = (m[:month] || month_section).to_i
|
136
|
+
day = m[:day].to_i
|
137
|
+
rank_char = m[:rank_char]
|
138
|
+
rank_num = m[:rank_num]
|
139
|
+
colour = m[:colour]
|
140
|
+
symbol_str = m[:symbol]
|
141
|
+
title = m[:title]
|
101
142
|
|
102
143
|
rank = RANK_CODES[rank_char && rank_char.downcase]
|
103
144
|
|
@@ -116,7 +157,7 @@ module CalendariumRomanum
|
|
116
157
|
|
117
158
|
symbol = nil
|
118
159
|
if symbol_str
|
119
|
-
symbol = symbol_str
|
160
|
+
symbol = symbol_str.to_sym
|
120
161
|
end
|
121
162
|
|
122
163
|
Celebration.new(
|
@@ -2,15 +2,26 @@ require 'date'
|
|
2
2
|
|
3
3
|
module CalendariumRomanum
|
4
4
|
|
5
|
-
#
|
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
|
|
13
|
+
# Which solemnities can be transferred to Sunday
|
10
14
|
SUNDAY_TRANSFERABLE_SOLEMNITIES =
|
11
15
|
%i(epiphany ascension corpus_christi).freeze
|
12
16
|
|
13
|
-
#
|
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
|
14
25
|
def initialize(year, extensions: [], transfer_to_sunday: [])
|
15
26
|
@year = year
|
16
27
|
|
@@ -21,10 +32,14 @@ module CalendariumRomanum
|
|
21
32
|
prepare_solemnities
|
22
33
|
end
|
23
34
|
|
35
|
+
# @return [Fixnum]
|
24
36
|
attr_reader :year
|
25
37
|
|
26
38
|
class << self
|
27
39
|
# Determines liturgical year for the given date
|
40
|
+
#
|
41
|
+
# @param date [Date]
|
42
|
+
# @return [Fixnum]
|
28
43
|
def liturgical_year(date)
|
29
44
|
year = date.year
|
30
45
|
temporale = Temporale.new year
|
@@ -36,14 +51,19 @@ module CalendariumRomanum
|
|
36
51
|
year
|
37
52
|
end
|
38
53
|
|
39
|
-
#
|
54
|
+
# Creates an instance for the liturgical year including given
|
40
55
|
# date
|
56
|
+
#
|
57
|
+
# @param date [Date]
|
58
|
+
# @return [Temporale]
|
41
59
|
def for_day(date)
|
42
60
|
new(liturgical_year(date))
|
43
61
|
end
|
44
62
|
|
45
|
-
#
|
63
|
+
# Factory method creating temporale {Celebration}s
|
46
64
|
# with sensible defaults
|
65
|
+
#
|
66
|
+
# See {Celebration#initialize} for argument description.
|
47
67
|
def create_celebration(title, rank, colour, symbol: nil, date: nil)
|
48
68
|
Celebration.new(title, rank, colour, symbol, date, :temporale)
|
49
69
|
end
|
@@ -51,7 +71,7 @@ module CalendariumRomanum
|
|
51
71
|
C = Struct.new(:date_method, :celebration)
|
52
72
|
private_constant :C
|
53
73
|
|
54
|
-
#
|
74
|
+
# @api private
|
55
75
|
def celebrations
|
56
76
|
@celebrations ||=
|
57
77
|
begin
|
@@ -90,22 +110,41 @@ module CalendariumRomanum
|
|
90
110
|
end
|
91
111
|
end
|
92
112
|
|
113
|
+
# Does this instance transfer the specified solemnity to Sunday?
|
114
|
+
#
|
115
|
+
# @param solemnity [Symbol]
|
116
|
+
# @return [Boolean]
|
93
117
|
def transferred_to_sunday?(solemnity)
|
94
118
|
@transfer_to_sunday.include?(solemnity)
|
95
119
|
end
|
96
120
|
|
121
|
+
# First day of the liturgical year
|
122
|
+
#
|
123
|
+
# @return [Date]
|
97
124
|
def start_date
|
98
125
|
first_advent_sunday
|
99
126
|
end
|
100
127
|
|
128
|
+
# Last day of the liturgical year
|
129
|
+
#
|
130
|
+
# @return [Date]
|
101
131
|
def end_date
|
102
132
|
Dates.first_advent_sunday(year + 1) - 1
|
103
133
|
end
|
104
134
|
|
135
|
+
# Date range of the liturgical year
|
136
|
+
#
|
137
|
+
# @return [Range<Date>]
|
105
138
|
def date_range
|
106
139
|
start_date .. end_date
|
107
140
|
end
|
108
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]
|
109
148
|
def range_check(date)
|
110
149
|
# necessary in order to handle Date correctly
|
111
150
|
date = date.to_date if date.class != Date
|
@@ -115,6 +154,44 @@ module CalendariumRomanum
|
|
115
154
|
end
|
116
155
|
end
|
117
156
|
|
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]
|
118
195
|
(celebrations.collect(&:date_method) + [:first_advent_sunday])
|
119
196
|
.each do |feast|
|
120
197
|
if SUNDAY_TRANSFERABLE_SOLEMNITIES.include? feast
|
@@ -132,7 +209,12 @@ module CalendariumRomanum
|
|
132
209
|
end
|
133
210
|
end
|
134
211
|
|
135
|
-
#
|
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
|
136
218
|
def season(date)
|
137
219
|
range_check date
|
138
220
|
|
@@ -157,6 +239,10 @@ module CalendariumRomanum
|
|
157
239
|
end
|
158
240
|
end
|
159
241
|
|
242
|
+
# When the specified liturgical season begins
|
243
|
+
#
|
244
|
+
# @param s [Season]
|
245
|
+
# @return [Date]
|
160
246
|
def season_beginning(s)
|
161
247
|
case s
|
162
248
|
when Seasons::ADVENT
|
@@ -174,6 +260,10 @@ module CalendariumRomanum
|
|
174
260
|
end
|
175
261
|
end
|
176
262
|
|
263
|
+
# Determine week of a season for a given date
|
264
|
+
#
|
265
|
+
# @param seasonn [Season]
|
266
|
+
# @param date [Date]
|
177
267
|
def season_week(seasonn, date)
|
178
268
|
week1_beginning = season_beginning = season_beginning(seasonn)
|
179
269
|
unless season_beginning.sunday?
|
@@ -197,14 +287,23 @@ module CalendariumRomanum
|
|
197
287
|
week
|
198
288
|
end
|
199
289
|
|
290
|
+
# Retrieve temporale celebration for the given day
|
291
|
+
#
|
292
|
+
# @param date [Date]
|
293
|
+
# @return [Celebration]
|
294
|
+
# @since 0.6.0
|
200
295
|
def [](date)
|
201
296
|
@solemnities[date] || @feasts[date] || sunday(date) || @memorials[date] || ferial(date)
|
202
297
|
end
|
203
298
|
|
204
|
-
#
|
205
|
-
# scheduled for the given day
|
299
|
+
# Retrieve temporale celebration for the given day
|
206
300
|
#
|
207
|
-
#
|
301
|
+
# @overload get(date)
|
302
|
+
# @param date [Date]
|
303
|
+
# @overload get(month, day)
|
304
|
+
# @param month [Fixnum]
|
305
|
+
# @param day [Fixnum]
|
306
|
+
# @return (see #[])
|
208
307
|
def get(*args)
|
209
308
|
if args.size == 1 && args[0].is_a?(Date)
|
210
309
|
date = args[0]
|
@@ -219,6 +318,8 @@ module CalendariumRomanum
|
|
219
318
|
self[date]
|
220
319
|
end
|
221
320
|
|
321
|
+
# @return [Boolean]
|
322
|
+
# @since 0.6.0
|
222
323
|
def ==(b)
|
223
324
|
self.class == b.class &&
|
224
325
|
year == b.year &&
|