File Coverage

blib/lib/Time/Duration/LocaleObject.pm
Criterion Covered Total %
statement 87 90 96.6
branch 30 38 78.9
condition 8 12 66.6
subroutine 19 20 95.0
pod 4 5 80.0
total 148 165 89.7


line stmt bran cond sub pod time code
1             # Copyright 2009, 2010, 2011, 2013, 2016, 2017 Kevin Ryde
2              
3             # This file is part of Time-Duration-Locale.
4             #
5             # Time-Duration-Locale is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU General Public License as published
7             # by the Free Software Foundation; either version 3, or (at your option) any
8             # later version.
9             #
10             # Time-Duration-Locale is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13             # Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Time-Duration-Locale. If not, see .
17              
18             package Time::Duration::LocaleObject;
19 4     4   1187 use 5.004;
  4         10  
20 4     4   19 use strict;
  4         5  
  4         104  
21 4     4   16 use Carp;
  4         3  
  4         282  
22 4     4   2147 use Module::Load;
  4         4564  
  4         23  
23 4     4   211 use vars qw($VERSION @ISA $AUTOLOAD);
  4         7  
  4         309  
24              
25 4     4   2094 use Class::Singleton;
  4         1226  
  4         3044  
26             @ISA = ('Class::Singleton');
27             *_new_instance = \&new;
28              
29             $VERSION = 12;
30              
31             # uncomment this to run the ### lines
32             #use Smart::Comments;
33              
34             sub new {
35             ### LocaleObject new(): @_
36 8     8 1 3628 my ($class, %self) = @_;
37 8         19 my $self = bless \%self, $class;
38              
39             # Load language module now, if given. You're not supposed to pass both
40             # 'module' and 'language', but for now the latter has precedence.
41             #
42 8 100       30 if (my $module = delete $self{'module'}) {
43 2         4 $self->module ($module);
44             }
45 6 50       27 if (my $lang = delete $self{'language'}) {
46 0         0 $self->language ($lang);
47             }
48              
49 6         21 return $self;
50             }
51              
52             # don't go through AUTOLOAD
53       0     sub DESTROY {}
54              
55             sub module {
56 65     65 1 776 my $self = shift;
57 65 100       205 ref $self or $self = $self->instance;
58 65 100       359 if (@_) {
59             # set
60 11         15 my ($module) = @_;
61 11 50       27 if (defined $module) {
62             # guard against infinite recursion on Time::Duration::Locale
63             # maybe should restrict to lower-case module names
64 11 100 100     61 if ($module eq 'Time::Duration::Locale'
65             || $module eq 'Time::Duration::LocaleObject') {
66 4         620 croak 'Don\'t set module to Locale or LocaleObject';
67             }
68 7         26 Module::Load::load ($module);
69             }
70 6         7126 $self->{'module'} = $module;
71             }
72             # get
73 60         145 return $self->{'module'};
74             }
75              
76             sub language {
77 8     8 1 17 my $self = shift;
78 8 100       59 ref $self or $self = $self->instance;
79 8 100       25 if (@_) {
80             # set
81 6         11 my ($lang) = @_;
82 6         19 $self->module (_language_to_module ($lang));
83             }
84             # get
85 8         15 my $module = $self->{'module'};
86 8 100       37 return (defined $module ? _module_to_language($module) : undef);
87             }
88              
89             # maybe it'd be easier to create a Time::Duration::en than mangle the names
90             sub _language_to_module {
91 6     6   7 my ($lang) = @_;
92 6 50       31 return ($lang eq 'en' ? 'Time::Duration' : "Time::Duration::$lang");
93             }
94             sub _module_to_language {
95 7     7   12 my ($module) = @_;
96 7 0       30 return ($module eq 'Time::Duration' ? 'en'
    50          
97             : $module =~ /^Time::Duration::(.*)/ ? $1
98             : $module);
99             }
100              
101             #------------------------------------------------------------------------------
102             # setlocale
103              
104             sub setlocale {
105 4     4 1 8 my ($self) = @_;
106 4 100       20 ref $self or $self = $self->instance;
107             ### TDLObj setlocale()
108              
109             # I18N::LangTags version 0.30 for implicate_supers_strictly(), don't worry
110             # about a I18N::LangTags->VERSION(0.30), it'll bomb
111             #
112 4         2042 require I18N::LangTags;
113 4         9746 require I18N::LangTags::Detect;
114              
115             # Prefer implicate_supers_strictly() over implicate_supers() since the
116             # latter loses territory preferences when it converts
117             #
118             # en-au, en-gb -> en-au, en, en-gb
119             #
120             # whereas implicate_supers_strictly() keeps gb ahead of generic en
121             #
122             # en-au, en-gb -> en-au, en-gb, en
123             #
124             # Not that it makes a difference as of July 2010 since there's no
125             # territory flavours (only the joke en_PIGLATIN).
126             #
127             # Chances are though that if you put in territory preferences in $LANGUAGE
128             # you'll want to include generics explicitly at the desired points, and in
129             # that case implicate_supers() and implicate_supers_strictly() come out
130             # the same.
131             #
132 4         5708 my %seen;
133             my $error;
134 4         15 foreach my $dashlang (I18N::LangTags::implicate_supers_strictly
135             (I18N::LangTags::Detect::detect()),
136             'en') {
137 4 50       1112 next if $seen{$dashlang}++;
138              
139 4         14 (my $lang = $dashlang) =~ s/-(.*)/_\U$1/g;
140             ### $dashlang
141             ### attempt lang: $lang
142              
143 4 50       6 if (eval { $self->language($lang); 1 }) {
  4         16  
  4         13  
144             # return value not documented ... don't use it yet
145 4         14 return $lang;
146             }
147 0         0 $error = $@;
148             ### $error
149             }
150 0         0 croak "Time::Duration not available -- $error";
151             }
152              
153             #------------------------------------------------------------------------------
154             # call-through
155             #
156             # ENHANCE-ME: Umm, like all AUTOLOAD for class methods this is slightly
157             # dangerous. If the base Class::Singleton already has a method the same
158             # name as the Time::Duration function/method which is supposed to be created
159             # here then the AUTOLOAD here doesn't run. Example in
160             # devel/autoload-singleton.pl.
161             #
162             # Should be ok in practice. The trick would be to stub up funcs for the
163             # possible methods in the target module, except that's not done immediately
164             # in new(), and later is too late. Maybe it'd be worth explicit stubs for
165             # the normal Time::Duration funcs at least ...
166             #
167              
168             sub can {
169 28     28 0 496 my ($self, $name) = @_;
170             ### print "TDLObj can(): $name
171 28   100     337 return $self->SUPER::can($name) || _make_dispatcher($self,$name);
172             }
173             sub AUTOLOAD {
174 1     1   887 my $name = $AUTOLOAD;
175             ### TDLObj AUTOLOAD(): $name
176 1         6 $name =~ s/.*://;
177 1   33     6 my $code = _make_dispatcher($_[0],$name)
178             || croak "No such function $name()";
179 1         5 goto $code;
180             }
181              
182 4     4   25 use vars '$_make_dispatcher';
  4         7  
  4         575  
183             sub _make_dispatcher {
184 26     26   42 my ($class_or_self, $name) = @_;
185             ### TDLObj _make_dispatcher(): $class_or_self, $name
186              
187             # $_make_dispatcher is recursion protection against bad
188             # language_preferences method, or any other undefined method module() or
189             # setlocale() might accidentally call here.
190 26 100 33     68 if ($_make_dispatcher
191             || do {
192 26         29 local $_make_dispatcher = 1;
193 26 100       54 $class_or_self->module || $class_or_self->setlocale;
194 26         53 my $module = $class_or_self->module;
195             ### module exists: $module
196             ### check can(): $name
197 26         189 ! $module->can($name) }) {
198 3         256 return undef;
199             }
200              
201             my $subr = sub {
202             #### TDLObj dispatch: $name
203              
204 39     39   855 my $self = shift;
205 39 100       128 ref $self or $self = $self->instance;
206 39 100       315 $self->{'module'} || $self->setlocale;
207              
208 39         88 my $target = "$self->{'module'}::$name";
209 4     4   20 no strict 'refs';
  4         6  
  4         267  
210 39         162 return &$target(@_);
211 23         125 };
212 4     4   14 { no strict 'refs'; *$name = $subr }
  4         7  
  4         200  
  23         29  
  23         60  
213 23         128 return $subr;
214             }
215              
216             1;
217             __END__