File Coverage

lib/Date/Holidays/Adapter.pm
Criterion Covered Total %
statement 45 108 41.6
branch 4 40 10.0
condition 3 18 16.6
subroutine 12 15 80.0
pod 3 3 100.0
total 67 184 36.4


line stmt bran cond sub pod time code
1             package Date::Holidays::Adapter;
2              
3 1     1   22 use strict;
  1         3  
  1         29  
4 1     1   5 use warnings;
  1         3  
  1         22  
5 1     1   6 use Carp; # croak
  1         10  
  1         52  
6 1     1   5 use Try::Tiny;
  1         2  
  1         44  
7 1     1   5 use Module::Load qw(load);
  1         2  
  1         8  
8 1     1   70 use Locale::Country;
  1         12  
  1         130  
9 1     1   7 use Scalar::Util qw(blessed);
  1         2  
  1         41  
10              
11 1     1   5 use vars qw($VERSION);
  1         2  
  1         1145  
12              
13             $VERSION = '1.35';
14              
15             sub new {
16 2     2 1 8 my ($class, %params) = @_;
17              
18             my $self = bless {
19             _countrycode => $params{countrycode},
20 2   33     9 _adaptee => undef,
21             }, $class || ref $class;
22              
23 2         12 my $adaptee = $self->_fetch(\%params);
24              
25 2 50       5 if ($adaptee) {
26 2         5 $self->{_adaptee} = $adaptee;
27             } else {
28 0         0 die 'Unable to initialize adaptee class';
29             }
30              
31 2         6 return $self;
32             }
33              
34             sub holidays {
35 0     0 1 0 my ($self, %params) = @_;
36              
37 0         0 my $r;
38             my $adaptee;
39              
40             # Adaptee has a constructor
41 0 0 0     0 if ( $self->{_adaptee}->can('new')
42             and $self->isa('Date::Holidays::Adapter')) {
43              
44 0         0 $adaptee = $self->{_adaptee}->new();
45              
46             # Adaptee has no constructor
47             } else {
48 0         0 $adaptee = $self->{'_adaptee'};
49             }
50              
51 0 0       0 if (blessed $adaptee) {
52              
53             # Adapting non-polymorphic interface
54 0         0 my $method = "$self->{'_countrycode'}_holidays";
55 0         0 my $sub = $adaptee->can($method);
56              
57             # Adapting polymorphic interface
58 0 0       0 if (! $sub) {
59 0         0 $method = 'holidays';
60 0         0 $sub = $adaptee->can($method);
61             }
62              
63 0 0       0 if ($sub) {
64 0         0 $r = $adaptee->$method($params{'year'});
65             }
66              
67 0         0 return $r;
68              
69             } else {
70              
71             # Adapting non-polymorphic interface
72 0         0 my $method = "$self->{'_countrycode'}_holidays";
73 0         0 my $sub = $adaptee->can($method);
74              
75             # Adapting polymorphic interface
76 0 0       0 if (! $sub) {
77 0         0 $sub = $adaptee->can('holidays');
78             }
79              
80 0 0       0 if ($sub) {
81 0         0 $r = &{$sub}($params{'year'});
  0         0  
82             }
83              
84 0         0 return $r;
85             }
86             }
87              
88             sub is_holiday {
89 0     0 1 0 my ($self, %params) = @_;
90              
91 0         0 my $r;
92             my $adaptee;
93              
94 0 0 0     0 if ( $self->{'_adaptee'}->can('new')
95             and $self->isa('Date::Holidays::Adapter')) {
96              
97 0         0 $adaptee = $self->{'_adaptee'}->new();
98              
99             } else {
100 0         0 $adaptee = $self->{'_adaptee'};
101             }
102              
103 0 0       0 if (blessed $adaptee) {
104              
105             # Adapting non-polymorphic interface
106 0         0 my $method = "is_$self->{'_countrycode'}_holiday";
107              
108 0 0       0 if ($adaptee->can($method)) {
109              
110             $r = $adaptee->$method(
111             $params{'year'},
112             $params{'month'},
113 0         0 $params{'day'}
114             );
115              
116 0         0 return $r;
117              
118             # Adapting polymorphic interface
119             } else {
120              
121 0 0       0 if ($adaptee->can('is_holiday')) {
122             $r = $adaptee->is_holiday(
123             $params{'year'},
124             $params{'month'},
125 0         0 $params{'day'}
126             );
127             }
128              
129 0         0 return $r;
130             }
131              
132             } else {
133             # Adapting non-polymorphic interface
134 0         0 my $method = "is_$self->{_countrycode}_holiday";
135 0         0 my $sub = $adaptee->can($method);
136              
137             # We have an interface
138 0 0       0 if ($sub) {
139              
140 0         0 $r = &{$sub}(
141             $params{'year'},
142             $params{'month'},
143 0         0 $params{'day'}
144             );
145              
146 0         0 return $r;
147             }
148              
149             # Adapting polymorphic interface
150 0         0 $sub = $adaptee->can('is_holiday');
151              
152 0 0       0 if ($sub) {
153 0         0 $r = &{$sub}(
154             $params{'year'},
155             $params{'month'},
156 0         0 $params{'day'}
157             );
158              
159 0         0 return $r;
160             }
161             }
162              
163 0         0 return $r;
164             }
165              
166             sub _load {
167 5     5   11 my ( $self, $module ) = @_;
168              
169             # Trying to load module
170 5         8 eval { load $module; }; # From Module::Load
  5         18  
171              
172             # Asserting success of load
173 5 50       2563 if ($@) {
174 0         0 die "Unable to load: $module - $@\n";
175             }
176              
177             # Returning name of loaded module upon success
178 5         18 return $module;
179             }
180              
181             sub _fetch {
182 2     2   4 my ( $self, $params ) = @_;
183              
184             # Do we have a country code?
185 2 0 33     13 if ( not $self->{'_countrycode'} and not $params->{countrycode} ) {
186 0         0 croak 'No country code specified';
187             }
188              
189 2   33     6 my $countrycode = $params->{countrycode} || $self->{'_countrycode'};
190              
191             # Do we do country code assertion?
192 2 50       5 if ( !$params->{'nocheck'} ) {
193              
194             # Is our country code valid or local?
195 0 0 0     0 if ( $countrycode !~ m/local/i and !code2country( $countrycode ) ) { #from Locale::Country
196 0         0 die "$countrycode is not a valid country code";
197             }
198             }
199              
200             # Trying to load adapter module for country code
201 2         3 my $module;
202              
203             try {
204             # We load an adapter implementation
205 2 50   2   70 if ( code2country( $countrycode ) ) {
206 0         0 $module = 'Date::Holidays::' . uc $countrycode;
207             } else {
208 2         92 $module = 'Date::Holidays::' . $countrycode;
209             }
210              
211 2         9 $module = $self->_load($module);
212              
213             } catch {
214 0     0   0 warn "Unable to load module: $module - $_";
215              
216             try {
217             #$countrycode = uc $countrycode;
218              
219 0 0       0 if ($countrycode =~ m/local/i) {
220 0         0 $module = 'Date::Holidays::Local';
221             } else {
222 0         0 $module = 'Date::Holidays::' . $countrycode;
223             }
224              
225             # We load an adapter implementation
226              
227 0 0       0 if ($module = $self->_load($module)) {
228 0         0 warn "we got a module and we return\n";
229             }
230              
231             } catch {
232 0         0 warn "Unable to load module: $module - $_";
233              
234 0         0 $module = 'Date::Holidays::Adapter';
235 0         0 $module = $self->_load($module);
236 0         0 };
237 2         12 };
238              
239             # Returning name of loaded module upon success
240 2         33 return $module;
241             }
242              
243             1;
244              
245             __END__
246              
247             =pod
248              
249             =encoding UTF-8
250              
251             =head1 NAME
252              
253             Date::Holidays::Adapter - an adapter class for Date::Holidays::* modules
254              
255             =head1 VERSION
256              
257             This POD describes version 1.35 of Date::Holidays::Adapter
258              
259             =head1 SYNOPSIS
260              
261             my $adapter = Date::Holidays::Adapter->new(countrycode => 'NO');
262              
263             my ($year, $month, $day) = (localtime)[ 5, 4, 3 ];
264             $year += 1900;
265             $month += 1;
266              
267             print "Woohoo" if $adapter->is_holiday( year => $year, month => $month, day => $day );
268              
269             my $hashref = $adapter->holidays(year => $year);
270             printf "Dec. 24th is named '%s'\n", $hashref->{'1224'}; #christmas I hope
271              
272             =head1 DESCRIPTION
273              
274             The is the SUPER adapter class. All of the adapters in the distribution of
275             Date::Holidays are subclasses of this class. (SEE also L<Date::Holidays>).
276              
277             The SUPER adapter class is at the same time a generic adapter. It attempts to
278             adapt to the most used API for modules in the Date::Holidays::* namespace. So
279             it should only be necessary to implement adapters to the exceptions to modules
280             not following the the defacto standard or suffering from other local
281             implementations.
282              
283             =head1 SUBROUTINES/METHODS
284              
285             The public methods in this class are all expected from the adapter, so it
286             actually corresponds with the abstract is outlined in L<Date::Holidays::Abstract>.
287              
288             Not all methods/subroutines may be implemented in the adaptee classes, the
289             adapters attempt to make the adaptee APIs adaptable where possible. This is
290             afterall the whole idea of the Adapter Pattern, but apart from making the
291             single Date::Holidays::* modules uniform towards the clients and
292             L<Date::Holidays> it is attempted to make the multitude of modules uniform in
293             the extent possible.
294              
295             =head2 new
296              
297             The constructor, takes a single named argument, B<countrycode>
298              
299             =head2 is_holiday
300              
301             The B<holidays> method, takes 3 named arguments, B<year>, B<month> and B<day>
302              
303             returns an indication of whether the day is a holiday in the calendar of the
304             country referenced by B<countrycode> in the call to the constructor B<new>.
305              
306             =head2 holidays
307              
308             The B<holidays> method, takes a single named argument, B<year>
309              
310             returns a reference to a hash holding the calendar of the country referenced by
311             B<countrycode> in the call to the constructor B<new>.
312              
313             The calendar will spand for a year and the keys consist of B<month> and B<day>
314             concatenated.
315              
316             =head1 DEVELOPING A DATE::HOLIDAYS::* ADAPTER
317              
318             If you want to develop an adapter compatible with interface specified in this
319             class. You have to implement the following 3 methods:
320              
321             =over
322              
323             =item new
324              
325             A constructor, taking a single argument a two-letter countrycode
326             (SEE: L<Locale::Country>)
327              
328             You can also inherit the one implemented and offered by this class
329              
330             B<NB>If inheritance is used, please remember to overwrite the two following
331             methods, if applicable.
332              
333             =item holidays
334              
335             This has to follow the API outlined in SUBROUTINES/METHODS.
336              
337             For the adaptee class anything goes, hence the use of an adapter.
338              
339             Please refer to the DEVELOPER section in L<Date::Holidays> about contributing to
340             the Date::Holidays::* namespace or attempting for adaptability with
341             L<Date::Holidays>.
342              
343             =item is_holiday
344              
345             This has to follow the API outlined in SUBROUTINES/METHODS.
346              
347             For the adaptee class anything goes, hence the use of an adapter.
348              
349             Please refer to the DEVELOPER section in L<Date::Holidays> about contributing to
350             the Date::Holidays::* namespace or attempting for adaptability with
351             L<Date::Holidays>.
352              
353             =back
354              
355             Apart from the methods described above you can also overwrite the _fetch method
356             in this class, This is used if your module is not a part of the
357             Date::Holidays::* namespace or the module bears a name which is not ISO3166
358             compliant.
359              
360             See also:
361              
362             =over
363              
364             =item * L<Date::Holidays::UK>
365              
366             =item * L<Date::Japanese::Holiday>
367              
368             =back
369              
370             =head1 DIAGNOSTICS
371              
372             =over
373              
374             =item * L<Date::Holidays::Exception::AdapterLoad>
375              
376             Exception thrown in the case where the B<_load> method is unable to load a
377             requested adapter module.
378              
379             The exception is however handled internally.
380              
381             =item * L<Date::Holidays::Exception::AdapterInitialization>
382              
383             Exception thrown in the case where the B<_new> method is unable to
384             initialize a requested adapter module.
385              
386             =item * L<Date::Holidays::Exception::UnsupportedMethod>
387              
388             Exception thrown in the case where the loaded and initialized module does not
389             support the called method. (SEE: METHODS/SUBROUTINES).
390              
391             =back
392              
393             =head1 DEPENDENCIES
394              
395             =over
396              
397             =item * L<Carp>
398              
399             =item * L<Module::Load>
400              
401             =item * L<TryCatch>
402              
403             =item * L<Locale::Country>
404              
405             =item * L<Scalar::Util>
406              
407             =back
408              
409             =head1 INCOMPATIBILITIES
410              
411             Please refer to INCOMPATIBILITIES in L<Date::Holidays>
412              
413             =head1 BUGS AND LIMITATIONS
414              
415             Please refer to BUGS AND LIMITATIONS in L<Date::Holidays>
416              
417             =head1 BUG REPORTING
418              
419             Please refer to BUG REPORTING in L<Date::Holidays>
420              
421             =head1 AUTHOR
422              
423             Jonas Brømsø, (jonasbn) - C<< <jonasbn@cpan.org> >>
424              
425             =head1 LICENSE AND COPYRIGHT
426              
427             L<Date::Holidays> and related modules are (C) by Jonas Brømsø, (jonasbn)
428             2004-2022
429              
430             Date-Holidays and related modules are released under the Artistic License 2.0
431              
432              
433             =cut