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,87 +1,167 @@
1
1
  module CalendariumRomanum
2
+ # Methods shared by most value objects defined by the gem
3
+ module ValueObjectInterface
4
+ # Machine-readable internal representation of the value
5
+ #
6
+ # @return [Symbol]
7
+ attr_reader :symbol
8
+ alias to_sym symbol
9
+
10
+ # Internationalized, human-readable name
11
+ #
12
+ # @return [String]
13
+ def name
14
+ I18n.t @i18n_key
15
+ end
16
+
17
+ # String representation of the contents for debugging purposes
18
+ #
19
+ # @return [String]
20
+ def to_s
21
+ "#<#{self.class.name} #{symbol}>"
22
+ end
23
+ end
2
24
 
25
+ # Represents a liturgical colour
3
26
  class Colour
27
+ include ValueObjectInterface
28
+
4
29
  def initialize(symbol)
5
30
  @symbol = symbol
6
31
  @i18n_key = "colour.#{@symbol}"
7
32
  end
33
+ end
8
34
 
9
- attr_reader :symbol
10
- alias to_sym symbol
35
+ # Standard set of liturgical colours
36
+ module Colours
37
+ extend Enum
11
38
 
12
- def name
13
- I18n.t @i18n_key
14
- end
15
- end
39
+ GREEN = Colour.new(:green)
40
+ VIOLET = Colour.new(:violet)
41
+ WHITE = Colour.new(:white)
42
+ RED = Colour.new(:red)
16
43
 
17
- class Colours < Enum
18
44
  values(index_by: :symbol) do
19
45
  [
20
- GREEN = Colour.new(:green),
21
- VIOLET = Colour.new(:violet),
22
- WHITE = Colour.new(:white),
23
- RED = Colour.new(:red)
46
+ GREEN,
47
+ VIOLET,
48
+ WHITE,
49
+ RED
24
50
  ]
25
51
  end
26
52
  end
27
53
 
54
+ # Convenience alias (American English spelling)
28
55
  Colors = Colours
29
56
 
57
+ # Liturgical season
30
58
  class Season
59
+ include ValueObjectInterface
60
+
61
+ # @param symbol [Symbol] internal identifier
62
+ # @param colour [Colour]
63
+ # liturgical colour of the season's Sundays and ferials
31
64
  def initialize(symbol, colour)
32
65
  @symbol = symbol
33
66
  @colour = colour
34
67
  @i18n_key = "temporale.season.#{@symbol}"
35
68
  end
36
69
 
37
- attr_reader :symbol, :colour
38
- alias to_sym symbol
39
-
40
- def name
41
- I18n.t @i18n_key
42
- end
70
+ # Liturgical colour of the season's Sundays and ferials
71
+ #
72
+ # May be +nil+ if there is no single colour.
73
+ #
74
+ # @return [Colour, nil]
75
+ attr_reader :colour
43
76
  end
44
77
 
45
- class Seasons < Enum
78
+ # Standard set of liturgical seasons
79
+ module Seasons
80
+ extend Enum
81
+
82
+ ADVENT = Season.new(:advent, Colours::VIOLET)
83
+ CHRISTMAS = Season.new(:christmas, Colours::WHITE)
84
+ LENT = Season.new(:lent, Colours::VIOLET)
85
+ TRIDUUM = Season.new(:triduum, nil)
86
+ EASTER = Season.new(:easter, Colours::WHITE)
87
+ ORDINARY = Season.new(:ordinary, Colours::GREEN)
88
+
46
89
  values(index_by: :symbol) do
47
90
  [
48
- ADVENT = Season.new(:advent, Colours::VIOLET),
49
- CHRISTMAS = Season.new(:christmas, Colours::WHITE),
50
- LENT = Season.new(:lent, Colours::VIOLET),
51
- EASTER = Season.new(:easter, Colours::WHITE),
52
- ORDINARY = Season.new(:ordinary, Colours::GREEN)
91
+ ADVENT,
92
+ CHRISTMAS,
93
+ LENT,
94
+ TRIDUUM,
95
+ EASTER,
96
+ ORDINARY,
53
97
  ]
54
98
  end
55
99
  end
56
100
 
57
- LECTIONARY_CYCLES = [:A, :B, :C]
101
+ # Sunday lectionary cycles.
102
+ # Values returned by {Calendar#lectionary}
103
+ LECTIONARY_CYCLES = [:A, :B, :C].freeze
104
+
105
+ # Celebration ranks as specified in the Table of Liturgical Days
106
+ module Ranks
107
+ extend Enum
108
+
109
+ TRIDUUM = Rank.new(1.1, 'rank.1_1')
110
+ PRIMARY = Rank.new(1.2, 'rank.1_2') # description may not be exact
111
+ SOLEMNITY_GENERAL = Rank.new(1.3, 'rank.1_3', 'rank.short.solemnity') # description may not be exact
112
+ SOLEMNITY_PROPER = Rank.new(1.4, 'rank.1_4', 'rank.short.solemnity')
113
+
114
+ FEAST_LORD_GENERAL = Rank.new(2.5, 'rank.2_5', 'rank.short.feast')
115
+ SUNDAY_UNPRIVILEGED = Rank.new(2.6, 'rank.2_6', 'rank.short.sunday')
116
+ FEAST_GENERAL = Rank.new(2.7, 'rank.2_7', 'rank.short.feast')
117
+ FEAST_PROPER = Rank.new(2.8, 'rank.2_8', 'rank.short.feast')
118
+ FERIAL_PRIVILEGED = Rank.new(2.9, 'rank.2_9', 'rank.short.ferial')
119
+
120
+ MEMORIAL_GENERAL = Rank.new(3.10, 'rank.3_10', 'rank.short.memorial')
121
+ MEMORIAL_PROPER = Rank.new(3.11, 'rank.3_11', 'rank.short.memorial')
122
+ MEMORIAL_OPTIONAL = Rank.new(3.12, 'rank.3_12', 'rank.short.memorial_opt')
123
+ FERIAL = Rank.new(3.13, 'rank.3_13', 'rank.short.ferial')
124
+ # Not included as a celebration rank on it's own
125
+ # in the Table of Liturgical Days
126
+ COMMEMORATION = Rank.new(4.0, 'rank.4_0', 'rank.short.commemoration')
58
127
 
59
- # ranks of celebrations
60
- class Ranks < Enum
61
128
  values(index_by: :priority) do
62
129
  # Values are at the same time references to sections
63
130
  # of the Table of Liturgical Days.
64
131
  # The lower value, the higher rank.
65
132
  [
66
- TRIDUUM = Rank.new(1.1, 'rank.1_1'),
67
- PRIMARY = Rank.new(1.2, 'rank.1_2'), # description may not be exact
68
- SOLEMNITY_GENERAL = Rank.new(1.3, 'rank.1_3', 'rank.short.solemnity'), # description may not be exact
69
- SOLEMNITY_PROPER = Rank.new(1.4, 'rank.1_4', 'rank.short.solemnity'),
70
-
71
- FEAST_LORD_GENERAL = Rank.new(2.5, 'rank.2_5', 'rank.short.feast'),
72
- SUNDAY_UNPRIVILEGED = Rank.new(2.6, 'rank.2_6', 'rank.short.sunday'),
73
- FEAST_GENERAL = Rank.new(2.7, 'rank.2_7', 'rank.short.feast'),
74
- FEAST_PROPER = Rank.new(2.8, 'rank.2_8', 'rank.short.feast'),
75
- FERIAL_PRIVILEGED = Rank.new(2.9, 'rank.2_9', 'rank.short.ferial'),
76
-
77
- MEMORIAL_GENERAL = Rank.new(3.10, 'rank.3_10', 'rank.short.memorial'),
78
- MEMORIAL_PROPER = Rank.new(3.11, 'rank.3_11', 'rank.short.memorial'),
79
- MEMORIAL_OPTIONAL = Rank.new(3.12, 'rank.3_12', 'rank.short.memorial_opt'),
80
- FERIAL = Rank.new(3.13, 'rank.3_13', 'rank.short.ferial'),
81
- # not included as a celebration rank on it's own
82
- # in the Table of Liturgical Days
83
- COMMEMORATION = Rank.new(4.0, 'rank.4_0', 'rank.short.commemoration')
133
+ TRIDUUM,
134
+ PRIMARY,
135
+ SOLEMNITY_GENERAL,
136
+ SOLEMNITY_PROPER,
137
+
138
+ FEAST_LORD_GENERAL,
139
+ SUNDAY_UNPRIVILEGED,
140
+ FEAST_GENERAL,
141
+ FEAST_PROPER,
142
+ FERIAL_PRIVILEGED,
143
+
144
+ MEMORIAL_GENERAL,
145
+ MEMORIAL_PROPER,
146
+ MEMORIAL_OPTIONAL,
147
+ FERIAL,
148
+
149
+ COMMEMORATION,
84
150
  ]
85
151
  end
86
152
  end
153
+
154
+ # Convenience module containing all the colour, season and rank constants
155
+ # for easy including
156
+ #
157
+ # @example
158
+ # include CalendariumRomanum::Constants
159
+ # RED # now all the constants are available in current module
160
+ #
161
+ # @since 0.8.0
162
+ module Constants
163
+ include Colours
164
+ include Seasons
165
+ include Ranks
166
+ end
87
167
  end
@@ -0,0 +1,4 @@
1
+ module CalendariumRomanum
2
+ # Thrown by {SanctoraleLoader} on attempt to load invalid data
3
+ class InvalidDataError < RuntimeError; end
4
+ end
@@ -1,9 +1,18 @@
1
1
  require 'roman-numerals'
2
2
 
3
3
  module CalendariumRomanum
4
- # Knows how to produce localized ordinals
4
+ # Knows how to produce localized ordinals.
5
+ #
6
+ # Used by {Temporale} for building names of Sundays and ferials.
5
7
  class Ordinalizer
6
8
  class << self
9
+ # @param number [Integer] number to build ordinal for
10
+ # @param locale [Symbol,nil]
11
+ # locale; +I18n.locale+ (i.e. the `i18n` gem's current locale)
12
+ # is used if not provided
13
+ # @return [String, Integer]
14
+ # ordinal, or unchanged +number+ if +Ordinalizer+ cannot
15
+ # build ordinals for the given locale
7
16
  def ordinal(number, locale: nil)
8
17
  locale ||= I18n.locale
9
18
 
@@ -12,7 +21,8 @@ module CalendariumRomanum
12
21
  "#{number}."
13
22
  when :en
14
23
  english_ordinal(number)
15
- # when :it # TODO
24
+ when :fr
25
+ french_ordinal(number)
16
26
  when :la, :it
17
27
  RomanNumerals.to_roman number
18
28
  else
@@ -20,6 +30,8 @@ module CalendariumRomanum
20
30
  end
21
31
  end
22
32
 
33
+ private
34
+
23
35
  def english_ordinal(number)
24
36
  modulo = number % 10
25
37
  modulo = 9 if number / 10 == 1
@@ -35,6 +47,15 @@ module CalendariumRomanum
35
47
  "#{number}th"
36
48
  end
37
49
  end
50
+
51
+ def french_ordinal(number)
52
+ case number
53
+ when 1
54
+ '1er'
55
+ else
56
+ "#{number}ème"
57
+ end
58
+ end
38
59
  end
39
60
  end
40
61
  end
@@ -1,32 +1,83 @@
1
1
  module CalendariumRomanum
2
- # When you want to query a calendar without caring about
3
- # civil and liturgical years and Calendar instances
2
+ # Has mostly the same public interface as {Calendar},
3
+ # but represents a "perpetual" calendar, not a calendar
4
+ # for a single year, thus allowing the client code
5
+ # to query for liturgical data of any day, without bothering
6
+ # about boundaries of liturgical years.
7
+ #
8
+ # Internally builds {Calendar} instances as needed
9
+ # and delegates method calls to them.
10
+ #
11
+ # @since 0.4.0
4
12
  class PerpetualCalendar
5
- def initialize(sanctorale: nil, temporale_factory: nil, temporale_options: nil, cache: {})
13
+ # @param sanctorale [Sanctorale, nil]
14
+ # @param temporale_factory [Proc, nil]
15
+ # +Proc+ receiving a single parameter - year - and returning
16
+ # a {Temporale} instance.
17
+ # @param temporale_options [Hash, nil]
18
+ # +Hash+ of arguments for {Temporale#initialize}.
19
+ # +temporale_factory+ and +temporale_options+ are mutually
20
+ # exclusive - pass either (or none) of them, never both.
21
+ # @param vespers [Boolean]
22
+ # See argument of the same name to {Calendar#initialize}
23
+ # @param cache [Hash]
24
+ # object to be used as internal cache of {Calendar} instances -
25
+ # anything exposing +#[]=+ and +#[]+ and "behaving mostly like
26
+ # a +Hash+" will work.
27
+ # There's no need to pass it unless you want to have control
28
+ # over the cache. That may be sometimes useful
29
+ # in order to prevent a long-lived
30
+ # +PerpetualCalendar+ instance flooding the memory
31
+ # by huge amount of cached {Calendar} instances.
32
+ # (By default, once a {Calendar} for a certain year is built,
33
+ # it is cached for the +PerpetualCalendar+ instances' lifetime.)
34
+ def initialize(sanctorale: nil, temporale_factory: nil, temporale_options: nil, vespers: false, cache: {})
6
35
  if temporale_factory && temporale_options
7
36
  raise ArgumentError.new('Specify either temporale_factory or temporale_options, not both')
8
37
  end
9
38
 
10
39
  @sanctorale = sanctorale
11
40
  @temporale_factory = temporale_factory || build_temporale_factory(temporale_options)
41
+ @vespers = vespers
12
42
 
13
43
  @cache = cache
14
44
  end
15
45
 
16
- # returns a resolved Day
46
+ # @return [Day]
47
+ # @see Calendar#day
17
48
  def day(*args)
18
49
  calendar_for(*args).day(*args)
19
50
  end
20
51
 
21
- # returns a Calendar instance for the liturgical year containing
52
+ # @return [Day, Array<Day>]
53
+ # @see Calendar#[]
54
+ # @since 0.6.0
55
+ def [](arg)
56
+ if arg.is_a? Range
57
+ return arg.collect do |date|
58
+ calendar_for(date).day(date)
59
+ end
60
+ end
61
+
62
+ day(arg)
63
+ end
64
+
65
+ # Returns a {Calendar} instance for the liturgical year containing
22
66
  # the specified day
67
+ #
68
+ # Parameters like {Calendar#day}
69
+ #
70
+ # @return [Calendar]
23
71
  def calendar_for(*args)
24
72
  date = Calendar.mk_date(*args)
25
73
  year = Temporale.liturgical_year date
26
74
  calendar_instance year
27
75
  end
28
76
 
29
- # returns a Calendar instance for the specified liturgical year
77
+ # Returns a Calendar instance for the specified liturgical year
78
+ #
79
+ # @param year [Integer]
80
+ # @return [Calendar]
30
81
  def calendar_for_year(year)
31
82
  calendar_instance year
32
83
  end
@@ -42,7 +93,7 @@ module CalendariumRomanum
42
93
  if @cache.has_key? year
43
94
  @cache[year]
44
95
  else
45
- @cache[year] = Calendar.new(year, @sanctorale, @temporale_factory.call(year))
96
+ @cache[year] = Calendar.new(year, @sanctorale, @temporale_factory.call(year), vespers: @vespers)
46
97
  end
47
98
  end
48
99
  end
@@ -1,40 +1,71 @@
1
1
  module CalendariumRomanum
2
+ # Celebration rank
2
3
  class Rank
4
+ include RankPredicates
3
5
  include Comparable
4
6
 
5
- def initialize(priority=nil, desc=nil, short_desc=nil)
7
+ # @param priority [Float, nil] number in the Table of Liturgical Days
8
+ # @param desc [String, nil]
9
+ # full description (translation string identifier)
10
+ # @param short_desc [String, nil]
11
+ # short rank name (translation string identifier)
12
+ def initialize(priority = nil, desc = nil, short_desc = nil)
6
13
  @priority = priority
7
14
  @desc = desc
8
15
  @short_desc = short_desc
9
16
  end
10
17
 
18
+ # @return [Float, nil]
11
19
  attr_reader :priority
12
- alias_method :to_f, :priority
20
+ alias to_f priority
13
21
 
22
+ # Full description - internationalized human-readable string.
23
+ #
24
+ # @return [String, nil]
14
25
  def desc
15
26
  @desc && I18n.t(@desc)
16
27
  end
17
28
 
18
- alias_method :to_s, :desc
29
+ # String representation mostly for debugging purposes.
30
+ #
31
+ # @return [String]
32
+ def to_s
33
+ # 'desc' instead of '@desc' is intentional -
34
+ # for a good reason we don't present contents of an instance
35
+ # variable but result of an instance method
36
+ "#<#{self.class.name} @priority=#{priority} desc=#{desc.inspect}>"
37
+ end
19
38
 
39
+ # Short name - internationalized human-readable string.
40
+ #
41
+ # @return [String, nil]
20
42
  def short_desc
21
43
  @short_desc && I18n.t(@short_desc)
22
44
  end
23
45
 
24
- def <=>(b)
25
- b.priority <=> self.priority
46
+ def <=>(other)
47
+ other.priority <=> priority
26
48
  end
27
49
 
28
- def solemnity?
29
- priority.to_i == 1
30
- end
50
+ # Returns the next higher rank.
51
+ #
52
+ # Allows constructing ranges of ranks.
53
+ #
54
+ # @return [Rank]
55
+ # @since 0.8.0
56
+ def succ
57
+ all = CR::Ranks.all
58
+ index = all.index(self)
59
+ raise StopIteration.new if index == 0
31
60
 
32
- def feast?
33
- priority.to_i == 2
61
+ all[index - 1]
34
62
  end
35
63
 
36
- def memorial?
37
- priority.to_i == 3 && priority <= 3.12
64
+ private
65
+
66
+ # Required by the {RankPredicates} mixin
67
+ def rank
68
+ self
38
69
  end
39
70
  end
40
71
  end