calendarium-romanum 0.4.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) 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 +22 -0
  6. data/.yardopts +3 -0
  7. data/CHANGELOG.md +431 -0
  8. data/Gemfile +25 -0
  9. data/Gemfile.lock +86 -0
  10. data/README.md +598 -0
  11. data/Rakefile +16 -0
  12. data/bin/calendariumrom +4 -1
  13. data/calendarium-romanum.gemspec +31 -0
  14. data/config/locales/cs.yml +5 -0
  15. data/config/locales/en.yml +21 -14
  16. data/config/locales/es.yml +94 -0
  17. data/config/locales/fr.yml +7 -0
  18. data/config/locales/it.yml +7 -0
  19. data/config/locales/la.yml +7 -0
  20. data/data/README.md +70 -24
  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 +236 -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/easter_dates.txt +67 -0
  33. data/data/universal-1969-la.txt +234 -0
  34. data/data/universal-en.txt +214 -211
  35. data/data/universal-es.txt +243 -0
  36. data/data/universal-fr.txt +214 -210
  37. data/data/universal-it.txt +214 -211
  38. data/data/universal-la.txt +214 -210
  39. data/doc/data_readme.md +2 -0
  40. data/doc/images/class_diagram.png +0 -0
  41. data/doc/images/class_diagram.puml +44 -0
  42. data/doc/yard_readme.rdoc +76 -0
  43. data/lib/calendarium-romanum.rb +35 -22
  44. data/lib/calendarium-romanum/abstract_date.rb +15 -0
  45. data/lib/calendarium-romanum/calendar.rb +207 -42
  46. data/lib/calendarium-romanum/cli.rb +63 -80
  47. data/lib/calendarium-romanum/cli/comparator.rb +63 -0
  48. data/lib/calendarium-romanum/cli/date_parser.rb +30 -0
  49. data/lib/calendarium-romanum/cli/dumper.rb +68 -0
  50. data/lib/calendarium-romanum/cli/helper.rb +23 -0
  51. data/lib/calendarium-romanum/cli/querier.rb +73 -0
  52. data/lib/calendarium-romanum/cr.rb +16 -0
  53. data/lib/calendarium-romanum/data.rb +50 -20
  54. data/lib/calendarium-romanum/day.rb +208 -32
  55. data/lib/calendarium-romanum/enum.rb +42 -25
  56. data/lib/calendarium-romanum/enums.rb +124 -44
  57. data/lib/calendarium-romanum/errors.rb +4 -0
  58. data/lib/calendarium-romanum/ordinalizer.rb +23 -2
  59. data/lib/calendarium-romanum/perpetual_calendar.rb +58 -7
  60. data/lib/calendarium-romanum/rank.rb +43 -12
  61. data/lib/calendarium-romanum/rank_predicates.rb +43 -0
  62. data/lib/calendarium-romanum/sanctorale.rb +164 -24
  63. data/lib/calendarium-romanum/sanctorale_factory.rb +74 -3
  64. data/lib/calendarium-romanum/sanctorale_loader.rb +180 -0
  65. data/lib/calendarium-romanum/sanctorale_writer.rb +119 -0
  66. data/lib/calendarium-romanum/temporale.rb +226 -94
  67. data/lib/calendarium-romanum/temporale/celebration_factory.rb +107 -0
  68. data/lib/calendarium-romanum/temporale/dates.rb +84 -16
  69. data/lib/calendarium-romanum/temporale/easter_table.rb +27 -0
  70. data/lib/calendarium-romanum/temporale/extensions.rb +15 -0
  71. data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +16 -3
  72. data/lib/calendarium-romanum/temporale/extensions/dedication_before_all_saints.rb +73 -0
  73. data/lib/calendarium-romanum/transfers.rb +60 -15
  74. data/lib/calendarium-romanum/util.rb +22 -3
  75. data/lib/calendarium-romanum/version.rb +5 -1
  76. data/liturgical_law/1969_normae_universales.md +568 -0
  77. data/liturgical_law/1977_decretum_de_celebratione_baptismatis_domini.md +58 -0
  78. data/liturgical_law/1990_decretum_de_variatione_inducenda.md +67 -0
  79. data/liturgical_law/1998_notificatio_de_occurrentia.md +57 -0
  80. data/liturgical_law/2002_normae_universales.md +946 -0
  81. data/liturgical_law/2006_notification.md +37 -0
  82. data/liturgical_law/2012_declarationes.md +38 -0
  83. data/liturgical_law/README.md +74 -0
  84. metadata +50 -28
  85. data/lib/calendarium-romanum/sanctoraleloader.rb +0 -115
  86. data/spec/abstract_date_spec.rb +0 -62
  87. data/spec/calendar_spec.rb +0 -330
  88. data/spec/celebration_spec.rb +0 -23
  89. data/spec/cli_spec.rb +0 -26
  90. data/spec/colour_spec.rb +0 -17
  91. data/spec/data_spec.rb +0 -23
  92. data/spec/date_spec.rb +0 -61
  93. data/spec/dates_spec.rb +0 -45
  94. data/spec/day_spec.rb +0 -59
  95. data/spec/enum_spec.rb +0 -51
  96. data/spec/i18n_spec.rb +0 -59
  97. data/spec/ordinalizer_spec.rb +0 -22
  98. data/spec/perpetual_calendar_spec.rb +0 -91
  99. data/spec/rank_spec.rb +0 -57
  100. data/spec/readme_spec.rb +0 -52
  101. data/spec/sanctorale_factory_spec.rb +0 -42
  102. data/spec/sanctorale_spec.rb +0 -191
  103. data/spec/sanctoraleloader_spec.rb +0 -171
  104. data/spec/season_spec.rb +0 -17
  105. data/spec/spec_helper.rb +0 -35
  106. data/spec/temporale_spec.rb +0 -519
@@ -1,8 +1,17 @@
1
1
  module CalendariumRomanum
2
- # allows easy access to bundled data files
3
- class Data < Enum
2
+ # Allows easy access to bundled data files
3
+ #
4
+ # @example
5
+ # sanctorale = CalendariumRomanum::Data::GENERAL_ROMAN_LATIN.load
6
+ module Data
7
+ extend Enum
4
8
 
5
9
  class SanctoraleFile
10
+ # This class is not intended to be initialized by client code -
11
+ # it's sole purpose is to provide functionality for easy
12
+ # loading of the bundled sanctorale data files.
13
+ #
14
+ # @api private
6
15
  def initialize(base_name)
7
16
  @siglum = base_name.sub(/\.txt$/, '')
8
17
  @path = File.expand_path('../../data/' + base_name, File.dirname(__FILE__))
@@ -10,33 +19,54 @@ module CalendariumRomanum
10
19
 
11
20
  attr_reader :siglum, :path
12
21
 
22
+ # Load the data file
23
+ #
24
+ # @return [Sanctorale]
13
25
  def load
14
- SanctoraleLoader.new.load_from_file(@path)
26
+ SanctoraleLoader.new.load_from_file(path)
27
+ end
28
+
29
+ # Load the data file and all it's parents
30
+ #
31
+ # @return [Sanctorale]
32
+ # @since 0.7.0
33
+ def load_with_parents
34
+ SanctoraleFactory.load_with_parents(path)
15
35
  end
16
36
  end
17
37
 
38
+ GENERAL_ROMAN_LATIN = SanctoraleFile.new('universal-la.txt')
39
+ GENERAL_ROMAN_LATIN_1969 = SanctoraleFile.new('universal-1969-la.txt')
40
+ GENERAL_ROMAN_ENGLISH = SanctoraleFile.new('universal-en.txt')
41
+ GENERAL_ROMAN_FRENCH = SanctoraleFile.new('universal-fr.txt')
42
+ GENERAL_ROMAN_ITALIAN = SanctoraleFile.new('universal-it.txt')
43
+ GENERAL_ROMAN_SPANISH = SanctoraleFile.new('universal-es.txt')
44
+ CZECH = SanctoraleFile.new('czech-cs.txt')
45
+
18
46
  values(index_by: :siglum) do
19
47
  # only calendars of broader interest have constants defined
20
48
  [
21
- GENERAL_ROMAN_LATIN = SanctoraleFile.new('universal-la.txt'),
22
- GENERAL_ROMAN_ENGLISH = SanctoraleFile.new('universal-en.txt'),
23
- GENERAL_ROMAN_FRENCH = SanctoraleFile.new('universal-fr.txt'),
24
- GENERAL_ROMAN_ITALIAN = SanctoraleFile.new('universal-it.txt'),
25
- CZECH = SanctoraleFile.new('czech-cs.txt')
49
+ GENERAL_ROMAN_LATIN,
50
+ GENERAL_ROMAN_LATIN_1969,
51
+ GENERAL_ROMAN_ENGLISH,
52
+ GENERAL_ROMAN_FRENCH,
53
+ GENERAL_ROMAN_ITALIAN,
54
+ GENERAL_ROMAN_SPANISH,
55
+ CZECH,
26
56
  ] \
27
57
  +
28
- %w(
29
- czech-brno-cs.txt
30
- czech-budejovice-cs.txt
31
- czech-cechy-cs.txt
32
- czech-hradec-cs.txt
33
- czech-litomerice-cs.txt
34
- czech-morava-cs.txt
35
- czech-olomouc-cs.txt
36
- czech-ostrava-cs.txt
37
- czech-plzen-cs.txt
38
- czech-praha-cs.txt
39
- ).collect {|basename| SanctoraleFile.new(basename) }
58
+ %w(
59
+ czech-brno-cs.txt
60
+ czech-budejovice-cs.txt
61
+ czech-cechy-cs.txt
62
+ czech-hradec-cs.txt
63
+ czech-litomerice-cs.txt
64
+ czech-morava-cs.txt
65
+ czech-olomouc-cs.txt
66
+ czech-ostrava-cs.txt
67
+ czech-plzen-cs.txt
68
+ czech-praha-cs.txt
69
+ ).collect {|basename| SanctoraleFile.new(basename) }
40
70
  end
41
71
  end
42
72
  end
@@ -1,61 +1,172 @@
1
- require 'forwardable'
2
-
3
1
  module CalendariumRomanum
4
2
 
5
- # information on one particular day of the liturgical year
3
+ # Information on one particular day of the liturgical year
6
4
  class Day
7
- def initialize(args={})
8
- %i(date season season_week celebrations).each do |a|
9
- if args.include? a
10
- instance_variable_set "@#{a}", args.delete(a)
11
- end
12
- end
13
-
14
- unless args.empty?
15
- raise ArgumentError.new "Unexpected arguments #{args.keys.join(', ')}"
16
- end
5
+ # Note: despite of all constructor arguments being nullable,
6
+ # instances returned by {Calendar} always have all of them set,
7
+ # the only exception being +vespers+.
8
+ #
9
+ # @param date [Date, nil]
10
+ # @param season [Season, nil]
11
+ # @param season_week [Integer, nil]
12
+ # @param celebrations [Array<Celebration>, nil]
13
+ # @param vespers [Celebration, nil]
14
+ def initialize(date: nil, season: nil, season_week: nil, celebrations: nil, vespers: nil)
15
+ @date = date
16
+ @season = season
17
+ @season_week = season_week
18
+ @celebrations = celebrations ? celebrations.dup : []
19
+ @vespers = vespers
17
20
  end
18
21
 
22
+ # @return [Date]
19
23
  attr_reader :date
20
24
 
25
+ # Weekday as integer (Sunday is 0)
26
+ #
27
+ # @return [Integer]
21
28
  def weekday
22
29
  date.wday
23
30
  end
24
31
 
25
- # one of the Seasons (Symbol)
32
+ # Weekday as internationalized string
33
+ #
34
+ # @return [String]
35
+ # @since 0.7.0
36
+ def weekday_name
37
+ I18n.t(date.wday, scope: 'weekday')
38
+ end
39
+
40
+ # @return [Season]
26
41
  attr_reader :season
27
42
 
28
- # week of the season (Integer)
43
+ # Week of the season
44
+ #
45
+ # @return [Integer]
29
46
  attr_reader :season_week
30
47
 
31
- # an Array of Celebrations, possibly empty
48
+ # List of celebrations for the given day.
49
+ #
50
+ # In tests and other "less-standard" situations the array
51
+ # may be empty, but it's never empty for instances
52
+ # returned by {Calendar}.
53
+ #
54
+ # @return [Array<Celebration>]
32
55
  attr_reader :celebrations
33
56
 
57
+ # {Celebration} whose first Vespers are celebrated
58
+ # in place of Vespers of the day's {Celebration}(s).
59
+ # Please note that {Calendar} by default _doesn't_ populate
60
+ # Vespers, - it's an opt-in feature
61
+ # (see {Calendar#initialize}, {Calendar#populates_vespers?},
62
+ # {Calendar#day}).
63
+ #
64
+ # @return [Celebration, nil]
65
+ # @since 0.5.0
66
+ attr_reader :vespers
67
+
34
68
  def ==(other)
35
69
  self.class == other.class &&
36
70
  date == other.date &&
37
71
  season == other.season &&
38
72
  season_week == other.season_week &&
39
- celebrations == other.celebrations
73
+ celebrations == other.celebrations &&
74
+ vespers == other.vespers
75
+ end
76
+
77
+ # Are the day's Vespers suppressed in favour of first Vespers
78
+ # of a Sunday or solemnity?
79
+ #
80
+ # @return [Boolean]
81
+ def vespers_from_following?
82
+ !vespers.nil?
83
+ end
84
+
85
+ # String representation of the instance listing it's contents.
86
+ # Intended mostly for debugging purposes.
87
+ #
88
+ # @return [String]
89
+ # @since 0.7.0
90
+ def to_s
91
+ celebrations_string = '['
92
+ celebrations.each do |c|
93
+ celebrations_string << c.to_s + ', '
94
+ end
95
+ celebrations_string = celebrations_string.chomp(', ') << ']'
96
+ "#<#{self.class.name} @date=#{date} @season=#{season} @season_week=#{season_week} celebrations=#{celebrations_string} vespers=#{vespers.inspect}>"
40
97
  end
41
98
  end
42
99
 
43
- # information on one particular celebration of the liturgical year
100
+ # One particular celebration of the liturgical year
44
101
  # (like a Sunday, feast or memorial);
45
- # some days have no (ferial office is used), some have one,
46
- # some have more among which one may and may not be chosen
102
+ # some days have one,
103
+ # some have more among which one is to be chosen
47
104
  class Celebration
48
- extend Forwardable
105
+ include RankPredicates
106
+
107
+ # All arguments can be passed either as positional or keyword arguments.
108
+ # In case of conflict keyword arguments win.
109
+ # @example
110
+ # Celebration.new('Lost title', title: 'Winning title') # will have title 'Winning title'
111
+ #
112
+ # @param title [String|Proc]
113
+ # Celebration title/name.
114
+ # If a +Proc+ is passed, it is expected not to receive
115
+ # arguments and to return a +String+.
116
+ # (Used for celebration titles which have to be
117
+ # internationalizable - the +Proc+ is called whenever
118
+ # {#title} is invoked, which allows the value to vary
119
+ # depending e.g. on state of the +Proc+ or some
120
+ # global setting - like +I18n.locale+ - it may access.)
121
+ # @param rank [Rank] Celebration rank
122
+ # @param colour [Colour] Liturgical colour
123
+ # @param symbol [Symbol, nil]
124
+ # Unique machine-readable identifier of the celebration
125
+ # @param date [AbstractDate, nil]
126
+ # Normal fixed date of the celebration
127
+ # @param cycle [:sanctorale, :temporale]
128
+ # Cycle the celebration belongs to
129
+ def initialize(title = '', rank = Ranks::FERIAL, colour = Colours::GREEN, symbol = nil, date = nil, cycle = :sanctorale, sunday = false, **kwargs)
130
+ @title = kwargs.delete(:title) || title
131
+ @rank = kwargs.delete(:rank) || rank
132
+ @colour = kwargs.delete(:colour) || kwargs.delete(:color) || colour
133
+ @symbol = kwargs.delete(:symbol) || symbol
134
+ @date = kwargs.delete(:date) || date
135
+ @cycle = kwargs.delete(:cycle) || cycle
136
+ @sunday = kwargs.delete(:sunday) || sunday
137
+
138
+ unless kwargs.empty?
139
+ raise ArgumentError.new('Unexpected keyword arguments: ' + kwargs.keys.inspect)
140
+ end
141
+
142
+ if @sunday && ![Ranks::SUNDAY_UNPRIVILEGED, Ranks::PRIMARY].include?(@rank)
143
+ raise ArgumentError.new("Rank #{@rank} cannot be Sunday")
144
+ end
145
+ end
49
146
 
50
- def initialize(title='', rank=Ranks::FERIAL, colour=Colours::GREEN)
51
- @title = title
52
- @rank = rank
53
- @colour = colour
147
+ # Build a new instance using the receiver's attributes
148
+ # for all properties for which (a non-nil) value was not passed.
149
+ #
150
+ # @return [Celebration]
151
+ # @since 0.5.0
152
+ def change(title: nil, rank: nil, colour: nil, color: nil, symbol: nil, date: nil, cycle: nil, sunday: nil)
153
+ self.class.new(
154
+ title: title || self.title,
155
+ rank: rank || self.rank,
156
+ colour: colour || color || self.colour,
157
+ symbol: symbol || self.symbol,
158
+ date: date || self.date,
159
+ cycle: cycle || self.cycle,
160
+ sunday: sunday || @sunday
161
+ )
54
162
  end
55
163
 
164
+ # @return [Rank]
56
165
  attr_reader :rank
57
- def_delegators :@rank, :solemnity?, :feast?, :memorial?
58
166
 
167
+ # Feast title/name
168
+ #
169
+ # @return [String]
59
170
  def title
60
171
  if @title.respond_to? :call
61
172
  @title.call
@@ -64,14 +175,79 @@ module CalendariumRomanum
64
175
  end
65
176
  end
66
177
 
178
+ # Liturgical colour
179
+ #
180
+ # @return [Colour]
67
181
  attr_reader :colour
68
- alias_method :color, :colour
182
+ alias color colour
69
183
 
70
- def ==(other)
71
- self.class == other.class &&
72
- title == other.title &&
73
- rank == other.rank &&
74
- colour == other.colour
184
+ # Symbol uniquely identifying the celebration
185
+ #
186
+ # @return [Symbol, nil]
187
+ # @since 0.5.0
188
+ attr_reader :symbol
189
+
190
+ # Usual date of the celebration.
191
+ #
192
+ # Only set for celebrations with fixed date.
193
+ # (Only) In case of solemnities it may happen that
194
+ # {Celebration#date} differs from {Day#date} due to
195
+ # transfer of an impeded solemnity.
196
+ #
197
+ # @return [AbstractDate, nil]
198
+ # @since 0.6.0
199
+ attr_reader :date
200
+
201
+ # Describes the celebration as belonging either to the
202
+ # temporale or sanctorale cycle
203
+ #
204
+ # @return [:sanctorale, :temporale]
205
+ # @since 0.6.0
206
+ attr_reader :cycle
207
+
208
+ def ==(b)
209
+ self.class == b.class &&
210
+ title == b.title &&
211
+ rank == b.rank &&
212
+ colour == b.colour &&
213
+ symbol == b.symbol &&
214
+ date == b.date &&
215
+ cycle == b.cycle
216
+ end
217
+
218
+ # Does the celebration belong to the temporale cycle?
219
+ #
220
+ # @return [Boolean]
221
+ # @since 0.6.0
222
+ def temporale?
223
+ cycle == :temporale
224
+ end
225
+
226
+ # Does the celebration belong to the sanctorale cycle?
227
+ #
228
+ # @return [Boolean]
229
+ # @since 0.6.0
230
+ def sanctorale?
231
+ cycle == :sanctorale
232
+ end
233
+
234
+ # Is the celebration a Sunday?
235
+ #
236
+ # Please note that for "privileged Sundays" true is returned, while {Rank#sunday?}
237
+ # returns false (because not all celebrations of that rank are Sundays).
238
+ #
239
+ # @return [Boolean]
240
+ def sunday?
241
+ rank.sunday? || @sunday
242
+ end
243
+
244
+ # String representation of the object's contents
245
+ # (not very pretty, intended mostly for development inspections).
246
+ #
247
+ # @return [String]
248
+ # @since 0.7.0
249
+ def to_s
250
+ "#<#{self.class.name} @title=\"#{title}\" @rank=#{rank} @colour=#{colour} symbol=#{symbol.inspect} date=#{date.inspect} cycle=#{cycle.inspect}>"
75
251
  end
76
252
  end
77
253
  end
@@ -1,33 +1,50 @@
1
1
  require 'forwardable'
2
2
 
3
3
  module CalendariumRomanum
4
- # Utility class for definition of enumerated "types"
5
- class Enum
6
- class << self
7
- extend Forwardable
8
-
9
- def values(index_by: nil, &blk)
10
- defined?(@indexed) && raise(RuntimeError.new('initialized repeatedly'))
11
-
12
- @indexed = {}
13
- @all = yield.freeze
14
-
15
- @all.each_with_index do |val, i|
16
- val.freeze
17
-
18
- key = index_by ? val.public_send(index_by) : i
19
- @indexed[key] = val
20
- end
21
-
22
- @indexed.freeze
23
- end
24
-
25
- def all
26
- @all
4
+ # Each Enum encapsulates a finite set of value objects.
5
+ #
6
+ # @api private
7
+ module Enum
8
+ extend Forwardable
9
+
10
+ # @api private
11
+ # @param index_by
12
+ # specifies which value objects' property contains
13
+ # unique internal identifier for use with {.[]}
14
+ def values(index_by: nil)
15
+ defined?(@indexed) && raise(RuntimeError.new('initialized repeatedly'))
16
+
17
+ @indexed = {}
18
+ @all = yield.freeze
19
+
20
+ @all.each_with_index do |val, i|
21
+ val.freeze
22
+
23
+ key = index_by ? val.public_send(index_by) : i
24
+ @indexed[key] = val
27
25
  end
28
26
 
29
- def_delegators :@all, :each
30
- def_delegators :@indexed, :[]
27
+ @indexed.freeze
31
28
  end
29
+
30
+ # Returns all contained value objects
31
+ #
32
+ # @return [Array]
33
+ attr_reader :all
34
+
35
+ # Enumerates contained value objects
36
+ #
37
+ # @!method each
38
+ # @yield value object
39
+ # @return [void]
40
+ def_delegators :@all, :each
41
+
42
+ # Allows accessing contained value objects by their
43
+ # internal unique identifiers
44
+ #
45
+ # @!method [](identifier)
46
+ # @param identifier
47
+ # @return value object or nil
48
+ def_delegators :@indexed, :[]
32
49
  end
33
50
  end