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
@@ -0,0 +1,106 @@
1
+ module CalendariumRomanum
2
+ class Temporale
3
+ # Provides factory methods building {Celebration}s
4
+ # for temporale feasts
5
+ class CelebrationFactory
6
+ class << self
7
+ # @yield [Symbol]
8
+ # @return [void, Enumerator]
9
+ def each
10
+ return to_enum(__method__) unless block_given?
11
+
12
+ celebrations.each do |symbol|
13
+ yield public_send(symbol)
14
+ end
15
+ end
16
+
17
+ # @return [Celebration]
18
+ def first_advent_sunday
19
+ Temporale.create_celebration(
20
+ I18n.t('temporale.advent.sunday', week: Ordinalizer.ordinal(1)),
21
+ Ranks::PRIMARY,
22
+ Colours::VIOLET
23
+ )
24
+ end
25
+
26
+ private
27
+
28
+ def celebrations
29
+ @celebrations ||= [:first_advent_sunday]
30
+ end
31
+
32
+ def celebration(symbol, rank, colour = Colours::WHITE, fixed_date: false)
33
+ define_singleton_method(symbol) do
34
+ Temporale.create_celebration(
35
+ proc { I18n.t("temporale.solemnity.#{symbol}") },
36
+ rank,
37
+ colour,
38
+ symbol: symbol,
39
+ date: fixed_date
40
+ )
41
+ end
42
+
43
+ celebrations << symbol
44
+ end
45
+ end
46
+
47
+ # @return [Celebration]
48
+ # @!scope class
49
+ celebration(:nativity, Ranks::PRIMARY, fixed_date: AbstractDate.new(12, 25))
50
+ # @return [Celebration]
51
+ # @!scope class
52
+ celebration(:holy_family, Ranks::FEAST_LORD_GENERAL)
53
+ # @return [Celebration]
54
+ # @!scope class
55
+ celebration(:mother_of_god, Ranks::SOLEMNITY_GENERAL, fixed_date: AbstractDate.new(1, 1))
56
+ # @return [Celebration]
57
+ # @!scope class
58
+ celebration(:epiphany, Ranks::PRIMARY)
59
+ # @return [Celebration]
60
+ # @!scope class
61
+ celebration(:baptism_of_lord, Ranks::FEAST_LORD_GENERAL)
62
+ # @return [Celebration]
63
+ # @!scope class
64
+ celebration(:ash_wednesday, Ranks::PRIMARY, Colours::VIOLET)
65
+ # @return [Celebration]
66
+ # @!scope class
67
+ celebration(:good_friday, Ranks::TRIDUUM, Colours::RED)
68
+ # @return [Celebration]
69
+ # @!scope class
70
+ celebration(:holy_saturday, Ranks::TRIDUUM, Colours::VIOLET)
71
+ # @return [Celebration]
72
+ # @!scope class
73
+ celebration(:palm_sunday, Ranks::PRIMARY, Colours::RED)
74
+ # @return [Celebration]
75
+ # @!scope class
76
+ celebration(:easter_sunday, Ranks::TRIDUUM)
77
+ # @return [Celebration]
78
+ # @!scope class
79
+ celebration(:ascension, Ranks::PRIMARY)
80
+ # @return [Celebration]
81
+ # @!scope class
82
+ celebration(:pentecost, Ranks::PRIMARY, Colours::RED)
83
+ # @return [Celebration]
84
+ # @!scope class
85
+ celebration(:holy_trinity, Ranks::SOLEMNITY_GENERAL)
86
+ # @return [Celebration]
87
+ # @!scope class
88
+ celebration(:corpus_christi, Ranks::SOLEMNITY_GENERAL)
89
+ # @return [Celebration]
90
+ # @!scope class
91
+ celebration(:sacred_heart, Ranks::SOLEMNITY_GENERAL)
92
+ # @return [Celebration]
93
+ # @!scope class
94
+ celebration(:christ_king, Ranks::SOLEMNITY_GENERAL)
95
+ # @return [Celebration]
96
+ # @!scope class
97
+ celebration(:mother_of_church, Ranks::MEMORIAL_GENERAL)
98
+ # @return [Celebration]
99
+ # @!scope class
100
+ celebration(:immaculate_heart, Ranks::MEMORIAL_GENERAL)
101
+ # @return [Celebration]
102
+ # @!scope class
103
+ celebration(:saturday_memorial_bvm, Ranks::MEMORIAL_OPTIONAL)
104
+ end
105
+ end
106
+ end
@@ -1,15 +1,20 @@
1
1
  module CalendariumRomanum
2
2
  class Temporale
3
- # dates of movable feasts
3
+ # Provides methods computing dates of movable feasts
4
+ # and utilities for common computations of relative dates
4
5
  module Dates
6
+ # (see .nativity)
5
7
  def self.first_advent_sunday(year)
6
- sunday_before(nativity(year)) - 3 * Temporale::WEEK
8
+ sunday_before(nativity(year)) - 3 * WEEK
7
9
  end
8
10
 
11
+ # @param year [Fixnum] liturgical year
12
+ # @return [Date]
9
13
  def self.nativity(year)
10
14
  Date.new(year, 12, 25)
11
15
  end
12
16
 
17
+ # (see .nativity)
13
18
  def self.holy_family(year)
14
19
  xmas = nativity(year)
15
20
  if xmas.sunday?
@@ -19,22 +24,41 @@ module CalendariumRomanum
19
24
  end
20
25
  end
21
26
 
27
+ # (see .nativity)
22
28
  def self.mother_of_god(year)
23
29
  octave_of(nativity(year))
24
30
  end
25
31
 
26
- def self.epiphany(year)
27
- Date.new(year+1, 1, 6)
32
+ # @param year [Fixnum] liturgical year
33
+ # @param sunday [Boolean] transfer to Sunday?
34
+ # @return [Date]
35
+ def self.epiphany(year, sunday: false)
36
+ if sunday
37
+ # GNLYC 7 a)
38
+ return sunday_after(Date.new(year + 1, 1, 1))
39
+ end
40
+
41
+ Date.new(year + 1, 1, 6)
28
42
  end
29
43
 
30
- def self.baptism_of_lord(year)
31
- sunday_after epiphany(year)
44
+ # @param year [Fixnum] liturgical year
45
+ # @param epiphany_on_sunday [Boolean] was Epiphany transferred to Sunday?
46
+ # @return [Date]
47
+ def self.baptism_of_lord(year, epiphany_on_sunday: false)
48
+ e = epiphany(year, sunday: epiphany_on_sunday)
49
+ if epiphany_on_sunday
50
+ e + 1
51
+ else
52
+ sunday_after e
53
+ end
32
54
  end
33
55
 
56
+ # (see .nativity)
34
57
  def self.ash_wednesday(year)
35
- easter_sunday(year) - (6 * Temporale::WEEK + 4)
58
+ easter_sunday(year) - (6 * WEEK + 4)
36
59
  end
37
60
 
61
+ # (see .nativity)
38
62
  def self.easter_sunday(year)
39
63
  year += 1
40
64
 
@@ -42,100 +66,157 @@ module CalendariumRomanum
42
66
  # https://github.com/jrobertson/easter
43
67
 
44
68
  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
69
+ dominical_number = (year + (year / 4) - (year / 100) + (year / 400)) % 7
70
+ solar_correction = (year - 1600) / 100 - (year - 1600) / 400
71
+ lunar_correction = (((year - 1400) / 100) * 8) / 25
72
+ paschal_full_moon = (3 - 11 * golden_number + solar_correction - lunar_correction) % 30
56
73
  dominical_number += 7 until dominical_number > 0
57
74
  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)
75
+ paschal_full_moon -= 1 if (paschal_full_moon == 29) || ((paschal_full_moon == 28) && golden_number > 11)
59
76
  difference = (4 - paschal_full_moon - dominical_number) % 7
60
77
  difference += 7 if difference < 0
61
78
  day_easter = paschal_full_moon + difference + 1
62
- if day_easter < 11 then
79
+ if day_easter < 11
63
80
  # Easter occurs in March.
64
- return Date.new(y=year, m=3, d=day_easter + 21)
81
+ return Date.new(year, 3, day_easter + 21)
65
82
  else
66
83
  # Easter occurs in April.
67
- return Date.new(y=year, m=4, d=day_easter - 10)
84
+ return Date.new(year, 4, day_easter - 10)
68
85
  end
69
86
  end
70
87
 
88
+ # (see .nativity)
71
89
  def self.palm_sunday(year)
72
90
  easter_sunday(year) - 7
73
91
  end
74
92
 
93
+ # (see .nativity)
75
94
  def self.good_friday(year)
76
95
  easter_sunday(year) - 2
77
96
  end
78
97
 
98
+ # (see .nativity)
79
99
  def self.holy_saturday(year)
80
100
  easter_sunday(year) - 1
81
101
  end
82
102
 
83
- def self.ascension(year)
103
+ # (see .epiphany)
104
+ def self.ascension(year, sunday: false)
105
+ if sunday
106
+ # GNLYC 7 b)
107
+ return easter_sunday(year) + 6 * WEEK
108
+ end
109
+
84
110
  pentecost(year) - 10
85
111
  end
86
112
 
113
+ # (see .nativity)
87
114
  def self.pentecost(year)
88
- easter_sunday(year) + 7 * Temporale::WEEK
115
+ easter_sunday(year) + 7 * WEEK
89
116
  end
90
117
 
118
+ # (see .nativity)
91
119
  def self.holy_trinity(year)
92
120
  octave_of(pentecost(year))
93
121
  end
94
122
 
95
- def self.body_blood(year)
123
+ # (see .epiphany)
124
+ def self.corpus_christi(year, sunday: false)
125
+ if sunday
126
+ # GNLYC 7 c)
127
+ return holy_trinity(year) + WEEK
128
+ end
129
+
96
130
  holy_trinity(year) + 4
97
131
  end
98
132
 
133
+ # (see .nativity)
99
134
  def self.sacred_heart(year)
100
- body_blood(year) + 8
135
+ corpus_christi(year) + 8
136
+ end
137
+
138
+ # (see .nativity)
139
+ def self.mother_of_church(year)
140
+ pentecost(year) + 1
101
141
  end
102
142
 
143
+ # (see .nativity)
103
144
  def self.immaculate_heart(year)
104
145
  pentecost(year) + 20
105
146
  end
106
147
 
148
+ # (see .nativity)
107
149
  def self.christ_king(year)
108
150
  first_advent_sunday(year + 1) - 7
109
151
  end
110
152
 
111
153
  # utility methods
112
154
 
155
+ # @param weekday [Fixnum]
156
+ # @param date [Date]
157
+ # @return [Date]
113
158
  def self.weekday_before(weekday, date)
114
- if date.wday == weekday then
115
- return date - Temporale::WEEK
159
+ if date.wday == weekday
160
+ date - WEEK
116
161
  elsif weekday < date.wday
117
- return date - (date.wday - weekday)
162
+ date - (date.wday - weekday)
118
163
  else
119
- return date - (date.wday + Temporale::WEEK - weekday)
164
+ date - (date.wday + WEEK - weekday)
120
165
  end
121
166
  end
122
167
 
168
+ # (see .weekday_before)
123
169
  def self.weekday_after(weekday, date)
124
- if date.wday == weekday then
125
- return date + Temporale::WEEK
170
+ if date.wday == weekday
171
+ date + WEEK
126
172
  elsif weekday > date.wday
127
- return date + (weekday - date.wday)
173
+ date + (weekday - date.wday)
128
174
  else
129
- return date + (Temporale::WEEK - date.wday + weekday)
175
+ date + (WEEK - date.wday + weekday)
130
176
  end
131
177
  end
132
178
 
179
+ # @param date [Date]
180
+ # @return [Date]
133
181
  def self.octave_of(date)
134
- date + Temporale::WEEK
182
+ date + WEEK
135
183
  end
136
184
 
137
185
  class << self
138
- WEEKDAYS = %w{sunday monday tuesday wednesday thursday friday saturday}
186
+ # @!method sunday_before(date)
187
+ # @param date [Date]
188
+ # @return [Date]
189
+ # @!method monday_before(date)
190
+ # (see .sunday_before)
191
+ # @!method tuesday_before(date)
192
+ # (see .sunday_before)
193
+ # @!method wednesday_before(date)
194
+ # (see .sunday_before)
195
+ # @!method thursday_before(date)
196
+ # (see .sunday_before)
197
+ # @!method friday_before(date)
198
+ # (see .sunday_before)
199
+ # @!method saturday_before(date)
200
+ # (see .sunday_before)
201
+
202
+ # @!method sunday_after(date)
203
+ # @param date [Date]
204
+ # @return [Date]
205
+ # @!method monday_after(date)
206
+ # (see .sunday_after)
207
+ # @!method tuesday_after(date)
208
+ # (see .sunday_after)
209
+ # @!method wednesday_after(date)
210
+ # (see .sunday_after)
211
+ # @!method thursday_after(date)
212
+ # (see .sunday_after)
213
+ # @!method friday_after(date)
214
+ # (see .sunday_after)
215
+ # @!method saturday_after(date)
216
+ # (see .sunday_after)
217
+
218
+ # @api private
219
+ WEEKDAYS = %w(sunday monday tuesday wednesday thursday friday saturday).freeze
139
220
  WEEKDAYS.each_with_index do |weekday, weekday_i|
140
221
  define_method "#{weekday}_before" do |date|
141
222
  send('weekday_before', weekday_i, date)
@@ -1,11 +1,20 @@
1
1
  module CalendariumRomanum
2
2
  class Temporale
3
3
  module Extensions
4
- # Temporale extension adding feast of Christ Eternal Priests,
4
+ # {Temporale} extension adding the movable feast
5
+ # of "Christ Eternal Priests",
5
6
  # included in some local calendars
7
+ #
8
+ # @example
9
+ # temporale = Temporale.new(2015, extensions: [
10
+ # Temporale::Extensions::ChristEternalPriest
11
+ # ])
6
12
  module ChristEternalPriest
7
- def self.included(mod)
8
- mod.add_celebration(
13
+ # @yield [Symbol, Celebration]
14
+ # @return [void]
15
+ def self.each_celebration
16
+ yield(
17
+ # symbol refers to the date-computing method
9
18
  :christ_eternal_priest,
10
19
  Celebration.new(
11
20
  proc { I18n.t('temporale.extension.christ_eternal_priest') },
@@ -15,9 +24,12 @@ module CalendariumRomanum
15
24
  )
16
25
  end
17
26
 
18
- # method computing date
19
- def christ_eternal_priest
20
- pentecost + 4
27
+ # Computes the feast's date
28
+ #
29
+ # @param year [Fixnum] liturgical year
30
+ # @return [Date]
31
+ def self.christ_eternal_priest(year)
32
+ Dates.pentecost(year) + 4
21
33
  end
22
34
  end
23
35
  end
@@ -1,7 +1,22 @@
1
1
  module CalendariumRomanum
2
2
 
3
- # Resolves transfers of solemnities.
3
+ # Internal {Calendar} component.
4
+ # Resolves transfers of conflicting solemnities.
5
+ #
6
+ # For any day {Temporale} has a {Celebration}.
7
+ # Often {Sanctorale} has one (or more), too.
8
+ # {Calendar} handles these conflicts, in most cases
9
+ # by throwing away all the proposed {Celebration}s except
10
+ # of the one of highest rank.
11
+ # But when there are two conflicting _solemnities_,
12
+ # one is celebrated on the given day and the less lucky one
13
+ # must be transferred to another day.
14
+ # However, not all days are valid as targets of solemnity transfer.
15
+ #
16
+ # @api private
4
17
  class Transfers
18
+ # @param temporale [Temporale]
19
+ # @param sanctorale [Sanctorale]
5
20
  def initialize(temporale, sanctorale)
6
21
  @transferred = {}
7
22
  @temporale = temporale
@@ -28,6 +43,10 @@ module CalendariumRomanum
28
43
  end
29
44
  end
30
45
 
46
+ # Retrieve solemnity for the specified day
47
+ #
48
+ # @param date [Date]
49
+ # @return [Celebration, nil]
31
50
  def get(date)
32
51
  @transferred[date]
33
52
  end
@@ -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
- alias_method :each_day, :each
30
+ alias each_day each
22
31
  end
23
32
 
24
- # enumerates days of a year
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
- # enumerates days of a month
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