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.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/bin/calendariumrom +3 -0
  3. data/config/locales/es.yml +90 -0
  4. data/data/README.md +43 -1
  5. data/data/czech-brno-cs.txt +4 -6
  6. data/data/czech-budejovice-cs.txt +4 -6
  7. data/data/czech-cechy-cs.txt +4 -5
  8. data/data/czech-cs.txt +237 -234
  9. data/data/czech-hradec-cs.txt +3 -5
  10. data/data/czech-litomerice-cs.txt +5 -7
  11. data/data/czech-morava-cs.txt +4 -5
  12. data/data/czech-olomouc-cs.txt +2 -4
  13. data/data/czech-ostrava-cs.txt +3 -5
  14. data/data/czech-plzen-cs.txt +3 -5
  15. data/data/czech-praha-cs.txt +3 -4
  16. data/data/universal-en.txt +214 -211
  17. data/data/universal-es.txt +243 -0
  18. data/data/universal-fr.txt +214 -210
  19. data/data/universal-it.txt +214 -211
  20. data/data/universal-la.txt +214 -212
  21. data/lib/calendarium-romanum.rb +6 -0
  22. data/lib/calendarium-romanum/abstract_date.rb +12 -0
  23. data/lib/calendarium-romanum/calendar.rb +93 -13
  24. data/lib/calendarium-romanum/cli.rb +11 -4
  25. data/lib/calendarium-romanum/cr.rb +16 -0
  26. data/lib/calendarium-romanum/data.rb +34 -7
  27. data/lib/calendarium-romanum/day.rb +132 -14
  28. data/lib/calendarium-romanum/enum.rb +22 -1
  29. data/lib/calendarium-romanum/enums.rb +80 -28
  30. data/lib/calendarium-romanum/errors.rb +1 -1
  31. data/lib/calendarium-romanum/ordinalizer.rb +10 -1
  32. data/lib/calendarium-romanum/perpetual_calendar.rb +43 -5
  33. data/lib/calendarium-romanum/rank.rb +23 -0
  34. data/lib/calendarium-romanum/sanctorale.rb +119 -20
  35. data/lib/calendarium-romanum/sanctorale_factory.rb +74 -3
  36. data/lib/calendarium-romanum/sanctorale_loader.rb +60 -19
  37. data/lib/calendarium-romanum/temporale.rb +110 -9
  38. data/lib/calendarium-romanum/temporale/celebration_factory.rb +45 -2
  39. data/lib/calendarium-romanum/temporale/dates.rb +65 -1
  40. data/lib/calendarium-romanum/temporale/extensions/christ_eternal_priest.rb +14 -2
  41. data/lib/calendarium-romanum/transfers.rb +20 -1
  42. data/lib/calendarium-romanum/util.rb +15 -3
  43. data/lib/calendarium-romanum/version.rb +3 -2
  44. data/spec/calendar_spec.rb +48 -4
  45. data/spec/celebration_factory_spec.rb +4 -0
  46. data/spec/celebration_spec.rb +9 -0
  47. data/spec/cli_spec.rb +7 -0
  48. data/spec/data_spec.rb +23 -0
  49. data/spec/day_spec.rb +26 -0
  50. data/spec/i18n_spec.rb +10 -0
  51. data/spec/sanctorale_factory_spec.rb +113 -9
  52. data/spec/sanctorale_loader_spec.rb +49 -24
  53. data/spec/sanctorale_spec.rb +72 -9
  54. data/spec/temporale_spec.rb +52 -53
  55. data/spec/year_spec.rb +25 -0
  56. metadata +7 -3
@@ -1,3 +1,9 @@
1
+ # Module wrapping the gem's classes
2
+ #
3
+ # If you hate typing the long module name, see {CR}
4
+ module CalendariumRomanum
5
+ end
6
+
1
7
  %w(
2
8
  version
3
9
  i18n_setup
@@ -4,12 +4,20 @@ module CalendariumRomanum
4
4
  class AbstractDate
5
5
  include Comparable
6
6
 
7
+ # @param month [Fixnum]
8
+ # @param day [Fixnum]
9
+ # @raise [RangeError] on invalid +month+/+day+ value
7
10
  def initialize(month, day)
8
11
  validate! month, day
9
12
  @month = month
10
13
  @day = day
11
14
  end
12
15
 
16
+ # Build a new instance from a +Date+ (or an object with
17
+ # similar public interface).
18
+ #
19
+ # @param date [Date]
20
+ # @return [AbstractDate]
13
21
  def self.from_date(date)
14
22
  new(date.month, date.day)
15
23
  end
@@ -32,6 +40,10 @@ module CalendariumRomanum
32
40
  month == other.month && day == other.day
33
41
  end
34
42
 
43
+ # Produce a +Date+ by providing a year to an +AbstractDate+
44
+ #
45
+ # @param year [Fixnum]
46
+ # @return [Date]
35
47
  def concretize(year)
36
48
  Date.new(year, month, day)
37
49
  end
@@ -5,15 +5,30 @@ module CalendariumRomanum
5
5
 
6
6
  # Provides complete information concerning a liturgical year,
7
7
  # it's days and celebrations occurring on them.
8
+ #
9
+ # {Calendar}'s business logic is mostly about correctly combining
10
+ # information from {Temporale} and {Sanctorale}.
8
11
  class Calendar
9
12
  extend Forwardable
10
13
 
11
14
  # Day when the implemented calendar system became effective
12
15
  EFFECTIVE_FROM = Date.new(1970, 1, 1).freeze
13
16
 
14
- # year: Integer
15
- # returns a calendar for the liturgical year beginning with
17
+ # Returns a calendar for the liturgical year beginning with
16
18
  # Advent of the specified civil year.
19
+ #
20
+ # @param year [Fixnum]
21
+ # Civil year when the liturgical year begins.
22
+ # @param sanctorale [Sanctorale, nil]
23
+ # If not provided, the +Calendar+ will only know celebrations
24
+ # of the temporale cycle, no feasts of the saints!
25
+ # @param temporale [Temporale, nil]
26
+ # If not provided, +Temporale+ for the given year with default
27
+ # configuration will built.
28
+ # @param vespers [Boolean] Set to true if you want the +Calendar+ to populate {Day#vespers}
29
+ # @raise [RangeError]
30
+ # if +year+ is specified for which the implemented calendar
31
+ # system wasn't in force
17
32
  def initialize(year, sanctorale = nil, temporale = nil, vespers: false)
18
33
  if year < (EFFECTIVE_FROM.year - 1)
19
34
  raise system_not_effective
@@ -32,6 +47,7 @@ module CalendariumRomanum
32
47
  end
33
48
 
34
49
  class << self
50
+ # @api private
35
51
  def mk_date(*args)
36
52
  ex = TypeError.new('Date, DateTime or three Integers expected')
37
53
 
@@ -51,23 +67,46 @@ module CalendariumRomanum
51
67
  end
52
68
  end
53
69
 
54
- # creates a Calendar for the liturgical year including given
55
- # date
70
+ # Creates a new instance for the liturgical year which includes
71
+ # given date
72
+ #
73
+ # @param date [Date]
74
+ # @param constructor_args
75
+ # arguments that will be passed to {initialize}
76
+ # @return [Calendar]
56
77
  def for_day(date, *constructor_args)
57
78
  new(Temporale.liturgical_year(date), *constructor_args)
58
79
  end
59
80
  end # class << self
60
81
 
82
+ # @!method range_check(date)
83
+ # @see Temporale#range_check
84
+ # @param date
85
+ # @return [void]
86
+ # @!method season(date)
87
+ # @see Temporale#season
88
+ # @param date
89
+ # @return [Season]
61
90
  def_delegators :@temporale, :range_check, :season
91
+
92
+ # @return [Fixnum]
62
93
  attr_reader :year
94
+
95
+ # @return [Temporale]
63
96
  attr_reader :temporale
97
+
98
+ # @return [Sanctorale]
64
99
  attr_reader :sanctorale
65
100
 
101
+ # Do {Day} instances returned by this +Calendar+
102
+ # have {Day#vespers} populated?
103
+ # @return [Boolean]
104
+ # @since 0.6.0
66
105
  def populates_vespers?
67
106
  @populate_vespers
68
107
  end
69
108
 
70
- # Calendars are equal if they have equal settings
109
+ # Two +Calendar+s are equal if they have equal settings
71
110
  # (which means that to equal input they return equal data)
72
111
  def ==(b)
73
112
  b.class == self.class &&
@@ -77,18 +116,39 @@ module CalendariumRomanum
77
116
  sanctorale == b.sanctorale
78
117
  end
79
118
 
119
+ # Retrieve liturgical calendar information for the specified day
120
+ # or range of days.
121
+ #
122
+ # @overload [](date)
123
+ # @param date [Date]
124
+ # @return [Day]
125
+ # @overload [](range)
126
+ # @param range [Range<Date>]
127
+ # @return [Array<Day>]
80
128
  def [](args)
81
- if(args.is_a?(Range))
82
- args.map{|date| day(date)}
129
+ if args.is_a?(Range)
130
+ args.map {|date| day(date) }
83
131
  else
84
132
  day(args)
85
133
  end
86
134
  end
87
135
 
88
- # accepts date information represented as
89
- # Date, DateTime, or two to three integers
90
- # (month - day or year - month - day);
91
- # returns filled Day for the specified day
136
+ # Retrieve liturgical calendar information for the specified day
137
+ #
138
+ # @overload day(date, vespers: false)
139
+ # @param date [Date]
140
+ # @overload day(year, month, day, vespers: false)
141
+ # @param year [Fixnum]
142
+ # @param month [Fixnum]
143
+ # @param day [Fixnum]
144
+ # @param vespers [Boolean]
145
+ # Set to +true+ in order to get {Day} with {Day#vespers}
146
+ # populated (overrides instance-wide setting {#populates_vespers?}).
147
+ # @return [Day]
148
+ # @raise [RangeError]
149
+ # If a date is specified on which the implemented calendar
150
+ # system was not yet in force (it became effective during
151
+ # the liturgical year 1969/1970)
92
152
  def day(*args, vespers: false)
93
153
  if args.size == 2
94
154
  date = Date.new(@year, *args)
@@ -126,21 +186,41 @@ module CalendariumRomanum
126
186
  )
127
187
  end
128
188
 
189
+ # Iterate over the whole liturgical year, day by day,
190
+ # for each day yield calendar data.
191
+ # If called without a block, returns +Enumerator+.
192
+ #
193
+ # @yield [Day]
194
+ # @return [void, Enumerator]
195
+ # @since 0.6.0
129
196
  def each
130
- (temporale.start_date..temporale.end_date)
131
- .each { |date| yield(day(date)) }
197
+ return to_enum(__method__) unless block_given?
198
+
199
+ temporale.date_range
200
+ .each {|date| yield(day(date)) }
132
201
  end
133
202
 
134
203
  # Sunday lectionary cycle
204
+ #
205
+ # @return [Symbol]
206
+ # For possible values see {LECTIONARY_CYCLES}
135
207
  def lectionary
136
208
  LECTIONARY_CYCLES[@year % 3]
137
209
  end
138
210
 
139
211
  # Ferial lectionary cycle
212
+ #
213
+ # @return [1, 2]
140
214
  def ferial_lectionary
141
215
  @year % 2 + 1
142
216
  end
143
217
 
218
+ # Freezes the instance.
219
+ #
220
+ # *WARNING*: {Temporale} and {Sanctorale} instances passed
221
+ # to the +Calendar+ on initialization will be frozen, too!
222
+ # This is necessary, because a +Calendar+ would not really be
223
+ # frozen were it possible to mutate it's key components.
144
224
  def freeze
145
225
  @temporale.freeze
146
226
  @sanctorale.freeze
@@ -2,6 +2,10 @@ require 'thor'
2
2
 
3
3
  module CalendariumRomanum
4
4
 
5
+ # Implementation of the +calendariumrom+ executable.
6
+ # _Not_ loaded by default when you +require+ the gem.
7
+ #
8
+ # @api private
5
9
  class CLI < Thor
6
10
  include CalendariumRomanum::Util
7
11
 
@@ -131,10 +135,13 @@ module CalendariumRomanum
131
135
 
132
136
  rank_length = day.celebrations.collect {|c| c.rank.short_desc.nil? ? 0 : c.rank.short_desc.size }.max
133
137
  day.celebrations.each do |c|
134
- next if c.rank.short_desc.nil?
135
- print c.rank.short_desc.rjust(rank_length)
136
- print ' : '
137
- puts c.title
138
+ if [Ranks::PRIMARY, Ranks::TRIDUUM].include? c.rank
139
+ puts c.title
140
+ elsif !c.rank.short_desc.nil?
141
+ print c.rank.short_desc.rjust(rank_length)
142
+ print ' : '
143
+ puts c.title
144
+ end
138
145
  end
139
146
  end
140
147
 
@@ -0,0 +1,16 @@
1
+ require 'calendarium-romanum'
2
+
3
+ # The module name {CalendariumRomanum} is quite long,
4
+ # hence constant +CR+ is provided as a convenient shortcut.
5
+ # It is _not_ loaded by +require 'calendarium-romanum'+,
6
+ # must be required explicitly +require 'calendarium-romanum/cr'+ -
7
+ # because there's a good chance
8
+ # that the short constant name clashes with a constant
9
+ # defined by some other code.
10
+ #
11
+ # @example
12
+ # require 'calendarium-romanum/cr'
13
+ #
14
+ # calendar = CR::Calendar.new 2000
15
+ # @since 0.7.0
16
+ CR = CalendariumRomanum
@@ -1,8 +1,16 @@
1
1
  module CalendariumRomanum
2
- # allows easy access to bundled data files
2
+ # Allows easy access to bundled data files
3
+ #
4
+ # @example
5
+ # sanctorale = CalendariumRomanum::Data::GENERAL_ROMAN_LATIN.load
3
6
  class Data < Enum
4
7
 
5
8
  class SanctoraleFile
9
+ # This class is not intended to be initialized by client code -
10
+ # it's sole purpose is to provide functionality for easy
11
+ # loading of the bundled sanctorale data files.
12
+ #
13
+ # @api private
6
14
  def initialize(base_name)
7
15
  @siglum = base_name.sub(/\.txt$/, '')
8
16
  @path = File.expand_path('../../data/' + base_name, File.dirname(__FILE__))
@@ -10,19 +18,38 @@ module CalendariumRomanum
10
18
 
11
19
  attr_reader :siglum, :path
12
20
 
21
+ # Load the data file
22
+ #
23
+ # @return [Sanctorale]
13
24
  def load
14
- SanctoraleLoader.new.load_from_file(@path)
25
+ SanctoraleLoader.new.load_from_file(path)
26
+ end
27
+
28
+ # Load the data file and all it's parents
29
+ #
30
+ # @return [Sanctorale]
31
+ # @since 0.7.0
32
+ def load_with_parents
33
+ SanctoraleFactory.load_with_parents(path)
15
34
  end
16
35
  end
17
36
 
37
+ GENERAL_ROMAN_LATIN = SanctoraleFile.new('universal-la.txt')
38
+ GENERAL_ROMAN_ENGLISH = SanctoraleFile.new('universal-en.txt')
39
+ GENERAL_ROMAN_FRENCH = SanctoraleFile.new('universal-fr.txt')
40
+ GENERAL_ROMAN_ITALIAN = SanctoraleFile.new('universal-it.txt')
41
+ GENERAL_ROMAN_SPANISH = SanctoraleFile.new('universal-es.txt')
42
+ CZECH = SanctoraleFile.new('czech-cs.txt')
43
+
18
44
  values(index_by: :siglum) do
19
45
  # only calendars of broader interest have constants defined
20
46
  [
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')
47
+ GENERAL_ROMAN_LATIN,
48
+ GENERAL_ROMAN_ENGLISH,
49
+ GENERAL_ROMAN_FRENCH,
50
+ GENERAL_ROMAN_ITALIAN,
51
+ GENERAL_ROMAN_SPANISH,
52
+ CZECH,
26
53
  ] \
27
54
  +
28
55
  %w(
@@ -2,8 +2,17 @@ require 'forwardable'
2
2
 
3
3
  module CalendariumRomanum
4
4
 
5
- # information on one particular day of the liturgical year
5
+ # Information on one particular day of the liturgical year
6
6
  class Day
7
+ # Note: despite of all constructor arguments being nullable,
8
+ # instances returned by {Calendar} always have all of them set,
9
+ # the only exception being +vespers+.
10
+ #
11
+ # @param date [Date, nil]
12
+ # @param season [Season, nil]
13
+ # @param season_week [Fixnum, nil]
14
+ # @param celebrations [Array<Celebration>, nil]
15
+ # @param vespers [Celebration, nil]
7
16
  def initialize(date: nil, season: nil, season_week: nil, celebrations: nil, vespers: nil)
8
17
  @date = date
9
18
  @season = season
@@ -12,25 +21,50 @@ module CalendariumRomanum
12
21
  @vespers = vespers
13
22
  end
14
23
 
24
+ # @return [Date]
15
25
  attr_reader :date
16
26
 
27
+ # Weekday as integer (Sunday is 0)
28
+ #
29
+ # @return [Fixnum]
17
30
  def weekday
18
31
  date.wday
19
32
  end
20
33
 
21
- # one of the Seasons
34
+ # Weekday as internationalized string
35
+ #
36
+ # @return [String]
37
+ # @since 0.7.0
38
+ def weekday_name
39
+ I18n.t(date.wday, scope: 'weekday')
40
+ end
41
+
42
+ # @return [Season]
22
43
  attr_reader :season
23
44
 
24
- # week of the season (Integer)
45
+ # Week of the season
46
+ #
47
+ # @return [Fixnum]
25
48
  attr_reader :season_week
26
49
 
27
- # an Array of Celebrations, possibly empty
50
+ # List of celebrations for the given day.
51
+ #
52
+ # In tests and other "less-standard" situations the array
53
+ # may be empty, but it's never empty for instances
54
+ # returned by {Calendar}.
55
+ #
56
+ # @return [Array<Celebration>]
28
57
  attr_reader :celebrations
29
58
 
30
- # nil or Celebration from which first Vespers are celebrated
31
- # instead of Vespers of the day's other Celebrations.
32
- # Please note that Calendar by default *doesn't* populate
33
- # Vespers, - it's an opt-in feature.
59
+ # {Celebration} whose first Vespers are celebrated
60
+ # in place of Vespers of the day's {Celebration}(s).
61
+ # Please note that {Calendar} by default _doesn't_ populate
62
+ # Vespers, - it's an opt-in feature
63
+ # (see {Calendar#initialize}, {Calendar#populates_vespers?},
64
+ # {Calendar#day}).
65
+ #
66
+ # @return [Celebration, nil]
67
+ # @since 0.5.0
34
68
  attr_reader :vespers
35
69
 
36
70
  def ==(other)
@@ -44,18 +78,51 @@ module CalendariumRomanum
44
78
 
45
79
  # Are the day's Vespers suppressed in favour of first Vespers
46
80
  # of a Sunday or solemnity?
81
+ #
82
+ # @return [Boolean]
47
83
  def vespers_from_following?
48
84
  !vespers.nil?
49
85
  end
86
+
87
+ # String representation of the instance listing it's contents.
88
+ # Intended mostly for debugging purposes.
89
+ #
90
+ # @return [String]
91
+ # @since 0.7.0
92
+ def to_s
93
+ celebrations_string = '['
94
+ celebrations.each do |c|
95
+ celebrations_string << c.to_s + ', '
96
+ end
97
+ celebrations_string = celebrations_string.chomp(', ') << ']'
98
+ "#<#{self.class.name} @date=#{date} @season=#{season} @season_week=#{season_week} celebrations=#{celebrations_string} vespers=#{vespers.inspect}>"
99
+ end
50
100
  end
51
101
 
52
- # information on one particular celebration of the liturgical year
102
+ # One particular celebration of the liturgical year
53
103
  # (like a Sunday, feast or memorial);
54
104
  # some days have one,
55
105
  # some have more among which one is to be chosen
56
106
  class Celebration
57
107
  extend Forwardable
58
108
 
109
+ # @param title [String|Proc]
110
+ # Celebration title/name.
111
+ # If a +Proc+ is passed, it is expected not to receive
112
+ # arguments and to return a +String+.
113
+ # (Used for celebration titles which have to be
114
+ # internationalizable - the +Proc+ is called whenever
115
+ # {#title} is invoked, which allows the value to vary
116
+ # depending e.g. on state of the +Proc+ or some
117
+ # global setting - like +I18n.locale+ - it may access.)
118
+ # @param rank [Rank] Celebration rank
119
+ # @param colour [Colour] Liturgical colour
120
+ # @param symbol [Symbol, nil]
121
+ # Unique machine-readable identifier of the celebration
122
+ # @param date [AbstractDate, nil]
123
+ # Normal fixed date of the celebration
124
+ # @param cycle [:sanctorale, :temporale]
125
+ # Cycle the celebration belongs to
59
126
  def initialize(title = '', rank = Ranks::FERIAL, colour = Colours::GREEN, symbol = nil, date = nil, cycle = :sanctorale)
60
127
  @title = title
61
128
  @rank = rank
@@ -65,11 +132,26 @@ module CalendariumRomanum
65
132
  @cycle = cycle
66
133
  end
67
134
 
68
- # Rank instance
135
+ # @return [Rank]
69
136
  attr_reader :rank
70
137
 
138
+ # @!method solemnity?
139
+ # @return [Boolean]
140
+ # @!method feast?
141
+ # @return [Boolean]
142
+ # @!method memorial?
143
+ # @return [Boolean]
144
+ # @!method sunday?
145
+ # @return [Boolean]
146
+ # @since 0.6.0
147
+ # @!method ferial?
148
+ # @return [Boolean]
149
+ # @since 0.6.0
71
150
  def_delegators :@rank, :solemnity?, :feast?, :memorial?, :sunday?, :ferial?
72
151
 
152
+ # Feast title/name
153
+ #
154
+ # @return [String]
73
155
  def title
74
156
  if @title.respond_to? :call
75
157
  @title.call
@@ -78,20 +160,34 @@ module CalendariumRomanum
78
160
  end
79
161
  end
80
162
 
81
- # Colour instance (always set) - liturgical colour
163
+ # Liturgical colour
164
+ #
165
+ # @return [Colour]
82
166
  attr_reader :colour
83
167
  alias color colour
84
168
 
85
- # Symbol uniquely identifying the celebration (may be nil)
169
+ # Symbol uniquely identifying the celebration
170
+ #
171
+ # @return [Symbol, nil]
172
+ # @since 0.5.0
86
173
  attr_reader :symbol
87
174
 
88
- # AbstractDate instance - usual date of the celebration.
175
+ # Usual date of the celebration.
176
+ #
89
177
  # Only set for celebrations with fixed date.
178
+ # (Only) In case of solemnities it may happen that
179
+ # {Celebration#date} differs from {Day#date} due to
180
+ # transfer of an impeded solemnity.
181
+ #
182
+ # @return [AbstractDate, nil]
183
+ # @since 0.6.0
90
184
  attr_reader :date
91
185
 
92
- # Symbol :temporale|:sanctorale
93
186
  # Describes the celebration as belonging either to the
94
187
  # temporale or sanctorale cycle
188
+ #
189
+ # @return [:sanctorale, :temporale]
190
+ # @since 0.6.0
95
191
  attr_reader :cycle
96
192
 
97
193
  def ==(b)
@@ -104,14 +200,27 @@ module CalendariumRomanum
104
200
  cycle == b.cycle
105
201
  end
106
202
 
203
+ # Does the celebration belong to the temporale cycle?
204
+ #
205
+ # @return [Boolean]
206
+ # @since 0.6.0
107
207
  def temporale?
108
208
  cycle == :temporale
109
209
  end
110
210
 
211
+ # Does the celebration belong to the sanctorale cycle?
212
+ #
213
+ # @return [Boolean]
214
+ # @since 0.6.0
111
215
  def sanctorale?
112
216
  cycle == :sanctorale
113
217
  end
114
218
 
219
+ # Build a new instance using the receiver's attributes
220
+ # for all properties for which (a non-nil) value was not passed.
221
+ #
222
+ # @return [Celebration]
223
+ # @since 0.5.0
115
224
  def change(title: nil, rank: nil, colour: nil, color: nil, symbol: nil, date: nil, cycle: nil)
116
225
  self.class.new(
117
226
  title || self.title,
@@ -122,5 +231,14 @@ module CalendariumRomanum
122
231
  cycle || self.cycle,
123
232
  )
124
233
  end
234
+
235
+ # String representation of the object's contents
236
+ # (not very pretty, intended mostly for development inspections).
237
+ #
238
+ # @return [String]
239
+ # @since 0.7.0
240
+ def to_s
241
+ "#<#{self.class.name} @title=\"#{title}\" @rank=#{rank} @colour=#{colour} symbol=#{symbol.inspect} date=#{date.inspect} cycle=#{cycle.inspect}>"
242
+ end
125
243
  end
126
244
  end