File Coverage

blib/lib/Date/Lectionary.pm
Criterion Covered Total %
statement 18 20 90.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 25 27 92.5


line stmt bran cond sub pod time code
1             package Date::Lectionary;
2              
3 1     1   44463 use v5.22;
  1         3  
4 1     1   4 use strict;
  1         2  
  1         16  
5 1     1   4 use warnings;
  1         5  
  1         26  
6              
7 1     1   323 use Moose;
  1         370395  
  1         6  
8 1     1   6146 use Carp;
  1         2  
  1         56  
9 1     1   6 use Try::Tiny;
  1         2  
  1         42  
10 1     1   145 use XML::LibXML;
  0            
  0            
11             use File::Share ':all';
12             use Time::Piece;
13             use Date::Advent;
14             use Date::Lectionary::Year;
15             use Date::Lectionary::Day;
16             use namespace::autoclean;
17             use Moose::Util::TypeConstraints;
18              
19             =head1 NAME
20              
21             Date::Lectionary - Readings for the Christian Lectionary
22              
23             =head1 VERSION
24              
25             Version 1.20170809
26              
27             =cut
28              
29             our $VERSION = '1.20170809';
30              
31             =head1 SYNOPSIS
32              
33             use Time::Piece;
34             use Date::Lectionary;
35              
36             my $epiphany = Date::Lectionary->new('date'=>Time::Piece->strptime("2017-01-06", "%Y-%m-%d"));
37             say $epiphany->day->name; #String representation of the name of the day in the liturgical calendar; e.g. 'The Epiphany'
38             say $epiphany->year->name; #String representation of the name of the liturgical year; e.g. 'A'
39             say ${$epiphany->readings}[0] #String representation of the first reading for the day.
40              
41             =head1 DESCRIPTION
42              
43             Date::Lectionary takes a Time::Piece date and returns the liturgical day and associated readings for the day.
44              
45             =head2 ATTRIBUTES
46              
47             =head3 date
48              
49             The Time::Piece object date given at object construction.
50              
51             =head3 lectionary
52              
53             An optional attribute given at object creation time. Valid values are 'acna' for the Anglican Church of North America lectionary and 'rcl' for the Revised Common Lectionary with complementary readings in ordinary time. This attribute defaults to 'acna' if no value is given.
54              
55             =head3 day
56              
57             A Date::Lectionary::Day object containing attributes related to the liturgical day.
58              
59             C<type>: Stores the type of liturgical day. 'fixedFeast' is returned for non-moveable feast days such as Christmas Day. 'moveableFeast' is returned for moveable feast days. Moveable feasts move to a Monday when they occure on a Sunday. 'Sunday' is returned for non-fixed feast Sundays of the liturgical year. 'noLect' is returned for days with no feast day or Sunday readings.
60              
61             C<name>: The name of the day in the lectionary. For noLect days a String representation of the day is returned as the name.
62              
63             C<alt>: The alternative name --- if one is given --- of the day in the lectionary. If there is no alternative name for the day, then the empty string will be returned.
64              
65             C<multiLect>: Returns 'yes' if the day has multiple services with readings associated with it. (E.g. Christmas Day, Easter, etc.) Returns 'no' if the day is a normal lectioanry day with only one service and one set of readings.
66              
67             =head3 year
68              
69             A Date::Lectionary::Year object containing attributes related to the liturgical year the date given at object construction resides in.
70              
71             C<name>: Returns 'A', 'B', or 'C' depending on the liturgical year the date given at object construction resides in.
72              
73             =head3 readings
74              
75             Return an ArrayRef of the String representation of the day's readings if there are any. Readings in the ArrayRef are ordered in the array according to the order the readings are given in the lectionary. If mutliple readings exist for the day, an ArrayRef of HashRefs will be given.
76              
77             my $singleReading = Date::Lectionary->new(
78             'date' => Time::Piece->strptime( "2016-11-13", "%Y-%m-%d" ),
79             'lectionary' => 'acna'
80             );
81              
82             say ${ $testReading->readings }[1]; #Will print 'Ps 98', the second reading for the Sunday closest to November 16 in the default ACNA lectionary for year C.
83             say $testReading->day->multiLect; #Will print 'no' because this day does not have multiple services in the lectionary.
84              
85             my $multiReading = Date::Lectionary->new(
86             'date' => Time::Piece->strptime( "2016-12-25", "%Y-%m-%d" ),
87             'lectionary' => 'rcl'
88             );
89              
90             say $multiReading->day->multiLect; #Will print 'yes' because this day does have multiple services in the lectionary.
91             say ${ $multiReading->readings }[0]{name}; #Will print 'Christmas, Proper I', the first services of Christmas Day in the RCL
92             say ${ $multiReading->readings }[1]{readings}[0]; #Will print 'Isaiah 62:6-12', the first reading of the second service 'Christmas, Proper II' on Christmas Day in the RCL.
93              
94             =cut
95              
96             enum 'LectionaryType', [qw(acna rcl)];
97             no Moose::Util::TypeConstraints;
98              
99             =head1 SUBROUTINES/METHODS
100              
101             =cut
102              
103             has 'date' => (
104             is => 'ro',
105             isa => 'Time::Piece',
106             required => 1,
107             );
108              
109             has 'day' => (
110             is => 'ro',
111             isa => 'Date::Lectionary::Day',
112             writer => '_setDay',
113             init_arg => undef,
114             );
115              
116             has 'year' => (
117             is => 'ro',
118             isa => 'Date::Lectionary::Year',
119             writer => '_setYear',
120             init_arg => undef,
121             );
122              
123             has 'lectionary' => (
124             is => 'ro',
125             isa => 'LectionaryType',
126             default => 'acna',
127             );
128              
129             has 'readings' => (
130             is => 'ro',
131             isa => 'ArrayRef',
132             writer => '_setReadings',
133             init_arg => undef,
134             );
135              
136             =head2 BUILD
137              
138             Constructor for the Date::Lectionary object. Takes a Time::Piect object, C<date>, to create the object.
139              
140             =cut
141              
142             sub BUILD {
143             my $self = shift;
144              
145             my $advent = _determineAdvent( $self->date );
146              
147             $self->_setYear(
148             Date::Lectionary::Year->new( 'year' => $advent->firstSunday->year ) );
149              
150             $self->_setDay(
151             Date::Lectionary::Day->new(
152             'date' => $self->date,
153             'lectionary' => $self->lectionary
154             )
155             );
156              
157             if ( $self->day->multiLect eq 'yes' ) {
158             $self->_setReadings(
159             _buildMultiReadings(
160             $self->day->subLects, $self->lectionary, $self->year->name
161             )
162             );
163             }
164             else {
165             $self->_setReadings(
166             _buildReadings(
167             $self->day->name, $self->lectionary, $self->year->name
168             )
169             );
170             }
171             }
172              
173             =head2 _buildMultiReadings
174              
175             Private method that returns an ArrayRef of HashRefs for the multiple services and lectionary readings associated with the date.
176              
177             =cut
178              
179             sub _buildMultiReadings {
180             my $multiNames = shift;
181             my $lectionary = shift;
182             my $year = shift;
183              
184             my @multiReadings;
185             foreach my $name (@$multiNames) {
186              
187             my %lectPart = (
188             name => $name,
189             readings => _buildReadings( $name, $lectionary, $year )
190             );
191              
192             push( @multiReadings, \%lectPart );
193             }
194              
195             return \@multiReadings;
196             }
197              
198             =head2 _buildReadings
199              
200             Private method that returns an ArrayRef of strings for the lectionary readings associated with the date.
201              
202             =cut
203              
204             sub _buildReadings {
205             my $displayName = shift;
206             my $lectionary = shift;
207             my $year = shift;
208              
209             my $parser = XML::LibXML->new();
210             my $data_location;
211             my $readings;
212              
213             try {
214             $data_location =
215             dist_file( 'Date-Lectionary', $lectionary . '_lect.xml' );
216             $readings = $parser->parse_file($data_location);
217             }
218             catch {
219             carp
220             "The readings database for the $lectionary lectionary could not be found or parsed.";
221             };
222              
223             my $compiled_xpath = XML::LibXML::XPathExpression->new(
224             "/lectionary/year[\@name=\"$year\" or \@name=\"holidays\"]/day[\@name=\"$displayName\"]/lesson"
225             );
226              
227             my @readings;
228             try {
229             foreach my $lesson ( $readings->findnodes($compiled_xpath) ) {
230             push( @readings, $lesson->to_literal );
231             }
232             }
233             catch {
234             carp
235             "Readings for $displayName in year $year could not be parsed from the database.";
236             };
237              
238             return \@readings;
239             }
240              
241             =head2 _determineAdvent
242              
243             Private method that takes a Time::Piece date object to returns a Date::Advent object containing the dates for Advent of the current liturgical year.
244              
245             =cut
246              
247             sub _determineAdvent {
248             my $date = shift;
249              
250             my $advent = undef;
251              
252             try {
253             $advent = Date::Advent->new( date => $date );
254             return $advent;
255             }
256             catch {
257             confess "Could not calculate Advent for the given date ["
258             . $date->ymd . "].";
259             };
260             }
261              
262             =head1 AUTHOR
263              
264             Michael Wayne Arnold, C<< <marmanold at cpan.org> >>
265              
266             =head1 BUGS
267              
268             Please report any bugs or feature requests to C<bug-date-lectionary at rt.cpan.org>, or through
269             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Date-Lectionary>. I will be notified, and then you'll
270             automatically be notified of progress on your bug as I make changes.
271              
272             =head1 SUPPORT
273              
274             You can find documentation for this module with the perldoc command.
275              
276             perldoc Date::Lectionary
277              
278              
279             You can also look for information at:
280              
281             =over 4
282              
283             =item * RT: CPAN's request tracker (report bugs here)
284              
285             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Date-Lectionary>
286              
287             =item * AnnoCPAN: Annotated CPAN documentation
288              
289             L<http://annocpan.org/dist/Date-Lectionary>
290              
291             =item * CPAN Ratings
292              
293             L<http://cpanratings.perl.org/d/Date-Lectionary>
294              
295             =item * Search CPAN
296              
297             L<http://search.cpan.org/dist/Date-Lectionary/>
298              
299             =back
300              
301              
302             =head1 ACKNOWLEDGEMENTS
303              
304             Many thanks to my beautiful wife, Jennifer, and my amazing daughter, Rosemary. But, above all, SOLI DEO GLORIA!
305              
306             =head1 LICENSE AND COPYRIGHT
307              
308             Copyright 2017 Michael Wayne Arnold.
309              
310             This program is free software; you can redistribute it and/or modify it
311             under the terms of either: the GNU General Public License as published
312             by the Free Software Foundation; or the Artistic License.
313              
314             See L<http://dev.perl.org/licenses/> for more information.
315              
316              
317             =cut
318              
319             __PACKAGE__->meta->make_immutable;
320              
321             1; # End of Date::Lectionary