File Coverage

blib/lib/Time/Duration/LocaleObject.pm
Criterion Covered Total %
statement 88 92 95.6
branch 30 38 78.9
condition 8 12 66.6
subroutine 19 20 95.0
pod 4 5 80.0
total 149 167 89.2


line stmt bran cond sub pod time code
1             # Copyright 2009, 2010, 2011, 2013 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   1808 use 5.004;
  4         14  
  4         162  
20 4     4   22 use strict;
  4         7  
  4         188  
21 4     4   20 use Carp;
  4         189  
  4         664  
22 4     4   4995 use Module::Load;
  4         5389  
  4         685  
23 4     4   247 use vars qw($VERSION @ISA $AUTOLOAD);
  4         9  
  4         356  
24              
25 4     4   4320 use Class::Singleton;
  4         1937  
  4         3909  
26             @ISA = ('Class::Singleton');
27             *_new_instance = \&new;
28              
29             $VERSION = 10;
30              
31             # uncomment this to run the ### lines
32             #use Smart::Comments;
33              
34             sub new {
35             ### LocaleObject new(): @_
36 8     8 1 7245 my ($class, %self) = @_;
37 8         28 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       32 if (my $module = delete $self{'module'}) {
43 2         9 $self->module ($module);
44             }
45 6 50       20 if (my $lang = delete $self{'language'}) {
46 0         0 $self->language ($lang);
47             }
48              
49 6         20 return $self;
50             }
51              
52             # don't go through AUTOLOAD
53 0     0   0 sub DESTROY {}
54              
55             sub module {
56 65     65 1 1297 my $self = shift;
57 65 100       263 ref $self or $self = $self->instance;
58 65 100       677 if (@_) {
59             # set
60 11         24 my ($module) = @_;
61 11 50       32 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     69 if ($module eq 'Time::Duration::Locale'
65             || $module eq 'Time::Duration::LocaleObject') {
66 4         792 croak 'Don\'t set module to Locale or LocaleObject';
67             }
68 7         34 Module::Load::load ($module);
69             }
70 6         9344 $self->{'module'} = $module;
71             }
72             # get
73 60         186 return $self->{'module'};
74             }
75              
76             sub language {
77 8     8 1 24 my $self = shift;
78 8 100       49 ref $self or $self = $self->instance;
79 8 100       33 if (@_) {
80             # set
81 6         11 my ($lang) = @_;
82 6         20 $self->module (_language_to_module ($lang));
83             }
84             # get
85 8         18 my $module = $self->{'module'};
86 8 100       70 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   13 my ($lang) = @_;
92 6 50       43 return ($lang eq 'en' ? 'Time::Duration' : "Time::Duration::$lang");
93             }
94             sub _module_to_language {
95 7     7   14 my ($module) = @_;
96 7 0       35 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 11 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         3394 require I18N::LangTags;
113 4         13537 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         6916 my %seen;
133             my $error;
134 4         16 foreach my $dashlang (I18N::LangTags::implicate_supers_strictly
135             (I18N::LangTags::Detect::detect()),
136             'en') {
137 4 50       1542 next if $seen{$dashlang}++;
138              
139 4         15 (my $lang = $dashlang) =~ s/-(.*)/_\U$1/g;
140             ### $dashlang
141             ### attempt lang: $lang
142              
143 4 50       11 if (eval { $self->language($lang); 1 }) {
  4         20  
  4         17  
144             # return value not documented ... don't use it yet
145 4         17 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 629 my ($self, $name) = @_;
170             ### print "TDLObj can(): $name
171 28   100     397 return $self->SUPER::can($name) || _make_dispatcher($self,$name);
172             }
173             sub AUTOLOAD {
174 1     1   1334 my $name = $AUTOLOAD;
175             ### TDLObj AUTOLOAD(): $name
176 1         5 $name =~ s/.*://;
177 1   33     5 my $code = _make_dispatcher($_[0],$name)
178             || croak "No such function $name()";
179 1         3 goto $code;
180             }
181              
182 4     4   34 use vars '$_make_dispatcher';
  4         12  
  4         768  
183             sub _make_dispatcher {
184 26     26   52 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     90 if ($_make_dispatcher
191             || do {
192 26         39 local $_make_dispatcher = 1;
193 26 100       79 $class_or_self->module || $class_or_self->setlocale;
194 26         78 my $module = $class_or_self->module;
195             ### module exists: $module
196             ### check can(): $name
197 26         225 ! $module->can($name) }) {
198 3         219 return undef;
199             }
200              
201             my $subr = sub {
202             #### TDLObj dispatch: $name
203              
204 39     39   2095 my $self = shift;
205 39 100       163 ref $self or $self = $self->instance;
206 39 100       487 $self->{'module'} || $self->setlocale;
207              
208 39         117 my $target = "$self->{'module'}::$name";
209 4     4   20 no strict 'refs';
  4         8  
  4         248  
210 39         173 return &$target(@_);
211 23         118 };
212 4     4   20 { no strict 'refs'; *$name = $subr }
  4         6  
  4         222  
  23         36  
  23         74  
213 23         145 return $subr;
214             }
215              
216             1;
217             __END__