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   949514 use v5.22;
  10         59  
4 10     10   46 use strict;
  10         17  
  10         176  
5 10     10   41 use warnings;
  10         16  
  10         217  
6              
7 10     10   4723 use Moose;
  10         4044044  
  10         61  
8 10     10   69209 use MooseX::StrictConstructor;
  10         270263  
  10         35  
9 10     10   84064 use Carp;
  10         20  
  10         698  
10 10     10   4740 use Try::Catch;
  10         6891  
  10         507  
11 10     10   6033 use XML::LibXML;
  10         464986  
  10         56  
12 10     10   4877 use File::Share ':all';
  10         185583  
  10         1443  
13 10     10   556 use Time::Piece;
  10         9412  
  10         85  
14 10     10   4774 use Date::Advent;
  10         825966  
  10         546  
15 10     10   5545 use Date::Lectionary::Year;
  10         31  
  10         366  
16 10     10   5712 use Date::Lectionary::Day;
  10         41  
  10         515  
17 10     10   82 use namespace::autoclean;
  10         21  
  10         97  
18 10     10   897 use Moose::Util::TypeConstraints;
  10         18  
  10         100  
19              
20             =head1 NAME
21              
22             Date::Lectionary - Readings for the Christian Lectionary
23              
24             =head1 VERSION
25              
26             Version 1.20190120
27              
28             =cut
29              
30 10     10   22190 use version; our $VERSION = version->declare("v1.20190120");
  10         22  
  10         82  
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   1355 no Moose::Util::TypeConstraints;
  10         22  
  10         47  
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 614     614 1 1076 my $self = shift;
145              
146 614         15602 my $advent = _determineAdvent( $self->date );
147              
148 614         19331 $self->_setYear( Date::Lectionary::Year->new( 'year' => $advent->firstSunday->year ) );
149              
150 614         13950 $self->_setDay(
151             Date::Lectionary::Day->new(
152             'date' => $self->date,
153             'lectionary' => $self->lectionary
154             )
155             );
156              
157 614 100       13784 if ( $self->day->multiLect eq 'yes' ) {
158 28         593 $self->_setReadings( _buildMultiReadings( $self->day->subLects, $self->lectionary, $self->year->name ) );
159             }
160             else {
161 586         12479 $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   53 my $multiNames = shift;
173 28         98 my $lectionary = shift;
174 28         40 my $year = shift;
175              
176 28         43 my @multiReadings;
177 28         64 foreach my $name (@$multiNames) {
178              
179 83         249 my %lectPart = (
180             name => $name,
181             readings => _buildReadings( $name, $lectionary, $year )
182             );
183              
184 83         305 push( @multiReadings, \%lectPart );
185             }
186              
187 28         1226 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 669     669   1237 my $displayName = shift;
198 669         970 my $lectionary = shift;
199 669         885 my $year = shift;
200              
201 669         2021 my $parser = XML::LibXML->new();
202 669         7803 my $data_location;
203             my $readings;
204              
205             try {
206 669     669   13546 $data_location = dist_file( 'Date-Lectionary', $lectionary . '_lect.xml' );
207 669         120474 $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 669         4269 };
212              
213 669         1112775 my $compiled_xpath = XML::LibXML::XPathExpression->new("/lectionary/year[\@name=\"$year\" or \@name=\"holidays\"]/day[\@name=\"$displayName\"]/lesson");
214              
215 669         1659 my @readings;
216             try {
217 669     669   14063 foreach my $lesson ( $readings->findnodes($compiled_xpath) ) {
218 2616         79795 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 669         4589 };
224              
225 669         253942 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 614     614   1035 my $date = shift;
236              
237 614         928 my $advent = undef;
238              
239             try {
240 614     614   24538 $advent = Date::Advent->new( date => $date );
241 614         548427 return $advent;
242             }
243             catch {
244 0     0     confess "Could not calculate Advent for the given date [" . $date->ymd . "].";
245 614         3881 };
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-2018 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