File Coverage

blib/lib/Date/Lectionary.pm
Criterion Covered Total %
statement 84 87 96.5
branch 2 2 100.0
condition n/a
subroutine 24 27 88.8
pod 1 1 100.0
total 111 117 94.8


line stmt bran cond sub pod time code
1             package Date::Lectionary;
2              
3 10     10   1084061 use v5.22;
  10         66  
4 10     10   53 use strict;
  10         20  
  10         183  
5 10     10   47 use warnings;
  10         16  
  10         233  
6              
7 10     10   5494 use Moose;
  10         4534150  
  10         68  
8 10     10   78809 use MooseX::StrictConstructor;
  10         302138  
  10         39  
9 10     10   94145 use Carp;
  10         21  
  10         698  
10 10     10   4704 use Try::Catch;
  10         7649  
  10         543  
11 10     10   6828 use XML::LibXML;
  10         525190  
  10         63  
12 10     10   5528 use File::Share ':all';
  10         208759  
  10         1339  
13 10     10   581 use Time::Piece;
  10         9386  
  10         123  
14 10     10   5284 use Date::Advent;
  10         921221  
  10         551  
15 10     10   6128 use Date::Lectionary::Year;
  10         30  
  10         340  
16 10     10   5552 use Date::Lectionary::Day;
  10         43  
  10         523  
17 10     10   117 use namespace::autoclean;
  10         22  
  10         102  
18 10     10   969 use Moose::Util::TypeConstraints;
  10         27  
  10         110  
19              
20             =head1 NAME
21              
22             Date::Lectionary - Readings for the Christian Lectionary
23              
24             =head1 VERSION
25              
26             Version 1.20200203
27              
28             =cut
29              
30 10     10   25059 use version; our $VERSION = version->declare("v1.20200203");
  10         25  
  10         87  
31              
32             =head1 SYNOPSIS
33              
34             use Time::Piece;
35             use Date::Lectionary;
36              
37             my $epiphany = Date::Lectionary->new('date'=>Time::Piece->strptime("2017-01-06", "%Y-%m-%d"));
38             say $epiphany->day->name; #String representation of the name of the day in the liturgical calendar; e.g. 'The Epiphany'
39             say $epiphany->year->name; #String representation of the name of the liturgical year; e.g. 'A'
40             say ${$epiphany->readings}[0] #String representation of the first reading for the day.
41              
42             =head1 DESCRIPTION
43              
44             Date::Lectionary takes a Time::Piece date and returns the liturgical day and associated readings for the day.
45              
46             =head2 ATTRIBUTES
47              
48             =head3 date
49              
50             The Time::Piece object date given at object construction.
51              
52             =head3 lectionary
53              
54             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.
55              
56             =head3 day
57              
58             A Date::Lectionary::Day object containing attributes related to the liturgical day.
59              
60             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.
61              
62             C<name>: The name of the day in the lectionary. For noLect days a String representation of the day is returned as the name.
63              
64             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.
65              
66             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.
67              
68             =head3 year
69              
70             A Date::Lectionary::Year object containing attributes related to the liturgical year the date given at object construction resides in.
71              
72             C<name>: Returns 'A', 'B', or 'C' depending on the liturgical year the date given at object construction resides in.
73              
74             =head3 readings
75              
76             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.
77              
78             my $singleReading = Date::Lectionary->new(
79             'date' => Time::Piece->strptime( "2016-11-13", "%Y-%m-%d" ),
80             'lectionary' => 'acna'
81             );
82              
83             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.
84             say $testReading->day->multiLect; #Will print 'no' because this day does not have multiple services in the lectionary.
85              
86             my $multiReading = Date::Lectionary->new(
87             'date' => Time::Piece->strptime( "2016-12-25", "%Y-%m-%d" ),
88             'lectionary' => 'rcl'
89             );
90              
91             say $multiReading->day->multiLect; #Will print 'yes' because this day does have multiple services in the lectionary.
92             say ${ $multiReading->readings }[0]{name}; #Will print 'Christmas, Proper I', the first services of Christmas Day in the RCL
93             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.
94              
95             =cut
96              
97             enum 'LectionaryType', [qw(acna rcl)];
98 10     10   1465 no Moose::Util::TypeConstraints;
  10         29  
  10         66  
99              
100             =head1 SUBROUTINES/METHODS
101              
102             =cut
103              
104             has 'date' => (
105             is => 'ro',
106             isa => 'Time::Piece',
107             required => 1,
108             );
109              
110             has 'day' => (
111             is => 'ro',
112             isa => 'Date::Lectionary::Day',
113             writer => '_setDay',
114             init_arg => undef,
115             );
116              
117             has 'year' => (
118             is => 'ro',
119             isa => 'Date::Lectionary::Year',
120             writer => '_setYear',
121             init_arg => undef,
122             );
123              
124             has 'lectionary' => (
125             is => 'ro',
126             isa => 'LectionaryType',
127             default => 'acna',
128             );
129              
130             has 'readings' => (
131             is => 'ro',
132             isa => 'ArrayRef',
133             writer => '_setReadings',
134             init_arg => undef,
135             );
136              
137             =head2 BUILD
138              
139             Constructor for the Date::Lectionary object. Takes a Time::Piect object, C<date>, to create the object.
140              
141             =cut
142              
143             sub BUILD {
144 615     615 1 1017 my $self = shift;
145              
146 615         17161 my $advent = _determineAdvent( $self->date );
147              
148 615         21560 $self->_setYear( Date::Lectionary::Year->new( 'year' => $advent->firstSunday->year ) );
149              
150 615         15521 $self->_setDay(
151             Date::Lectionary::Day->new(
152             'date' => $self->date,
153             'lectionary' => $self->lectionary
154             )
155             );
156              
157 615 100       15223 if ( $self->day->multiLect eq 'yes' ) {
158 28         688 $self->_setReadings( _buildMultiReadings( $self->day->subLects, $self->lectionary, $self->year->name ) );
159             }
160             else {
161 587         13624 $self->_setReadings( _buildReadings( $self->day->name, $self->lectionary, $self->year->name ) );
162             }
163             }
164              
165             =head2 _buildMultiReadings
166              
167             Private method that returns an ArrayRef of HashRefs for the multiple services and lectionary readings associated with the date.
168              
169             =cut
170              
171             sub _buildMultiReadings {
172 28     28   55 my $multiNames = shift;
173 28         50 my $lectionary = shift;
174 28         56 my $year = shift;
175              
176 28         41 my @multiReadings;
177 28         74 foreach my $name (@$multiNames) {
178              
179 83         210 my %lectPart = (
180             name => $name,
181             readings => _buildReadings( $name, $lectionary, $year )
182             );
183              
184 83         362 push( @multiReadings, \%lectPart );
185             }
186              
187 28         1191 return \@multiReadings;
188             }
189              
190             =head2 _buildReadings
191              
192             Private method that returns an ArrayRef of strings for the lectionary readings associated with the date.
193              
194             =cut
195              
196             sub _buildReadings {
197 670     670   1238 my $displayName = shift;
198 670         1017 my $lectionary = shift;
199 670         953 my $year = shift;
200              
201 670         2178 my $parser = XML::LibXML->new();
202 670         8741 my $data_location;
203             my $readings;
204              
205             try {
206 670     670   15203 $data_location = dist_file( 'Date-Lectionary', $lectionary . '_lect.xml' );
207 670         130935 $readings = $parser->parse_file($data_location);
208             }
209             catch {
210 0     0   0 carp "The readings database for the $lectionary lectionary could not be found or parsed.";
211 670         4306 };
212              
213 670         1283968 my $compiled_xpath = XML::LibXML::XPathExpression->new("/lectionary/year[\@name=\"$year\" or \@name=\"holidays\"]/day[\@name=\"$displayName\"]/lesson");
214              
215 670         1782 my @readings;
216             try {
217 670     670   15418 foreach my $lesson ( $readings->findnodes($compiled_xpath) ) {
218 2620         89257 push( @readings, $lesson->to_literal );
219             }
220             }
221             catch {
222 0     0   0 carp "Readings for $displayName in year $year could not be parsed from the database.";
223 670         4607 };
224              
225 670         286106 return \@readings;
226             }
227              
228             =head2 _determineAdvent
229              
230             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.
231              
232             =cut
233              
234             sub _determineAdvent {
235 615     615   1078 my $date = shift;
236              
237 615         1016 my $advent = undef;
238              
239             try {
240 615     615   27151 $advent = Date::Advent->new( date => $date );
241 615         608060 return $advent;
242             }
243             catch {
244 0     0     confess "Could not calculate Advent for the given date [" . $date->ymd . "].";
245 615         3899 };
246             }
247              
248             =head1 AUTHOR
249              
250             Michael Wayne Arnold, C<< <michael at rnold.info> >>
251              
252             =head1 BUGS
253              
254             =for html <a href="https://travis-ci.org/marmanold/Date-Lectionary"><img src="https://travis-ci.org/marmanold/Date-Lectionary.svg?branch=master"></a>
255              
256             =for html <a href='https://coveralls.io/github/marmanold/Date-Lectionary?branch=master'><img src='https://coveralls.io/repos/github/marmanold/Date-Lectionary/badge.svg?branch=master' alt='Coverage Status' /></a>
257              
258             Please report any bugs or feature requests to C<bug-date-lectionary at rt.cpan.org>, or through
259             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Date-Lectionary>. I will be notified, and then you'll
260             automatically be notified of progress on your bug as I make changes.
261              
262             =head1 SUPPORT
263              
264             You can find documentation for this module with the perldoc command.
265              
266             perldoc Date::Lectionary
267              
268              
269             You can also look for information at:
270              
271             =over 4
272              
273             =item * RT: CPAN's request tracker (report bugs here)
274              
275             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Date-Lectionary>
276              
277             =item * AnnoCPAN: Annotated CPAN documentation
278              
279             L<http://annocpan.org/dist/Date-Lectionary>
280              
281             =item * CPAN Ratings
282              
283             L<http://cpanratings.perl.org/d/Date-Lectionary>
284              
285             =item * Search CPAN
286              
287             L<http://search.cpan.org/dist/Date-Lectionary/>
288              
289             =back
290              
291              
292             =head1 ACKNOWLEDGEMENTS
293              
294             Many thanks to my beautiful wife, Jennifer, my amazing daughter, Rosemary, and my sweet son, Oliver. But, above all, SOLI DEO GLORIA!
295              
296             =head1 LICENSE
297              
298             Copyright 2016-2020 MICHAEL WAYNE ARNOLD
299              
300             Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
301              
302             1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
303              
304             2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
305              
306             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
307              
308              
309             =cut
310              
311             __PACKAGE__->meta->make_immutable;
312              
313             1; # End of Date::Lectionary