File Coverage

blib/lib/Locale/KeyedText/Translator.pm
Criterion Covered Total %
statement 140 152 92.1
branch 34 50 68.0
condition 5 11 45.4
subroutine 28 30 93.3
pod 0 14 0.0
total 207 257 80.5


line stmt bran cond sub pod time code
1 4     4   100 use 5.008000;
  4         60  
2 4     4   68 use utf8;
  4         9  
  4         19  
3 4     4   291 use strict;
  4         22  
  4         177  
4 4     4   69 use warnings;
  4         9  
  4         263  
5              
6 4     4   19 use Locale::KeyedText::Message 2.001001;
  4         144  
  4         334  
7              
8             ###########################################################################
9             ###########################################################################
10              
11             { package Locale::KeyedText::Translator; # class
12             BEGIN {
13 4     4   19 our $VERSION = '2.001001';
14 4         220 $VERSION = eval $VERSION;
15             }
16              
17 4     4   19 use Scalar::Util 'blessed';
  4         6  
  4         2618  
18              
19             # has _set_names
20             # isa ArrayRef
21             # One elem per set name:
22             # elem is Str
23             # default []
24             # List of Template module Set Names to search.
25             sub _set_names {
26 99     99   129 my $self = shift;
27 99 100       245 $self->{_set_names} = $_[0] if scalar @_;
28 99         176 return $self->{_set_names};
29             }
30              
31             # has _member_names
32             # isa ArrayRef
33             # One elem per member name:
34             # elem is Str
35             # default []
36             # List of Template module Member Names to search.
37             sub _member_names {
38 87     87   102 my $self = shift;
39 87 100       144 $self->{_member_names} = $_[0] if scalar @_;
40 87         176 return $self->{_member_names};
41             }
42              
43             ###########################################################################
44              
45             sub new {
46 25     25 0 7697 my ($class, @args) = @_;
47 25   33     104 $class = (blessed $class) || $class;
48              
49 25         51 my $params = $class->BUILDARGS( @args );
50              
51 25         48 my $self = bless {}, $class;
52              
53             # Set attribute default values.
54 25         65 $self->_set_names( [] );
55 25         53 $self->_member_names( [] );
56              
57 25         53 $self->BUILD( $params );
58              
59 21         68 return $self;
60             }
61              
62             ###########################################################################
63              
64             sub BUILDARGS {
65 25     25 0 43 my ($class, @args) = @_;
66 25 50 33     91 if (@args == 1 and ref $args[0] eq 'HASH') {
    0          
67             # Constructor was called with (possibly zero) named arguments.
68 25         35 return { %{$args[0]} };
  25         106  
69             }
70             elsif ((scalar @args % 2) == 0) {
71             # Constructor was called with (possibly zero) named arguments.
72 0         0 return { @args };
73             }
74             else {
75             # Constructor was called with odd number positional arguments.
76 0         0 $class->_die_with_msg( 'LKT_ARGS_BAD_PSEUDO_NAMED' );
77             }
78             }
79              
80             ###########################################################################
81              
82             sub BUILD {
83 25     25 0 35 my ($self, $args) = @_;
84             my ($set_names_ref, $member_names_ref)
85 25         43 = @{$args}{'set_names', 'member_names'};
  25         42  
86              
87 25 100       54 if (ref $set_names_ref ne 'ARRAY') {
88 5         8 $set_names_ref = [$set_names_ref];
89             }
90 25 100       45 if (ref $member_names_ref ne 'ARRAY') {
91 5         8 $member_names_ref = [$member_names_ref];
92             }
93              
94 25         56 $self->_assert_arg_ary( 'new', ':@set_names!', $set_names_ref );
95 23         46 $self->_assert_arg_ary( 'new', ':@member_names!', $member_names_ref );
96              
97 21         27 $self->_set_names( [@{$set_names_ref}] );
  21         52  
98 21         28 $self->_member_names( [@{$member_names_ref}] );
  21         44  
99              
100 21         32 return;
101             }
102              
103             ###########################################################################
104              
105             sub export_as_hash {
106 0     0 0 0 my ($self) = @_;
107             return {
108 0         0 'set_names' => [@{$self->_set_names()}],
109 0         0 'member_names' => [@{$self->_member_names()}],
  0         0  
110             };
111             }
112              
113             ###########################################################################
114              
115             sub get_set_names {
116 1     1 0 414 my ($self) = @_;
117 1         2 return [@{$self->_set_names()}];
  1         3  
118             }
119              
120             sub get_member_names {
121 1     1 0 572 my ($self) = @_;
122 1         2 return [@{$self->_member_names()}];
  1         3  
123             }
124              
125             ###########################################################################
126              
127             sub as_debug_string {
128 0     0 0 0 my ($self) = @_;
129 0         0 my $set_names = $self->_set_names();
130 0         0 my $member_names = $self->_member_names();
131             return ' Debug String of a Locale::KeyedText::Translator object:'
132             . "\n"
133 0         0 . ' @set_names: ["' . (join q{", "}, @{$set_names}) . '"]'
134             . "\n"
135             . ' @member_names: ["'
136 0         0 . (join q{", "}, @{$member_names}) . '"]'
  0         0  
137             . "\n";
138             }
139              
140             use overload (
141 4         21 '""' => \&as_debug_string,
142             fallback => 1,
143 4     4   26 );
  4         7  
144              
145             sub as_debug_str {
146 21     21 0 4645 my ($self) = @_;
147 21         37 my $set_names = $self->_set_names();
148 21         32 my $member_names = $self->_member_names();
149 21         90 return 'SETS: ' . (join ', ', @{$set_names}) . '; '
150 21         26 . 'MEMBERS: ' . (join ', ', @{$member_names});
  21         84  
151             }
152              
153             ###########################################################################
154              
155             sub get_set_member_combinations {
156 19     19 0 28 my ($self) = @_;
157 19         28 my @combinations = ();
158 19         20 for my $member_name (@{$self->_member_names()}) {
  19         30  
159 31         36 for my $set_name (@{$self->_set_names()}) {
  31         51  
160 55         116 push @combinations, $set_name . $member_name;
161             }
162             }
163 19         39 return \@combinations;
164             }
165              
166             ###########################################################################
167              
168             sub translate_message {
169 21     21 0 9748 my ($self, $message) = @_;
170              
171 21         47 $self->_assert_arg_msg( 'translate_message', '$message!', $message );
172              
173 19         28 my $text = undef;
174             SET_MEMBER:
175 19         23 for my $module_name (@{$self->get_set_member_combinations()}) {
  19         31  
176             # Determine if requested template module is already loaded.
177             # It may have been embedded in a core program file and hence
178             # should never be loaded by translate_message().
179 23         41 my $module_is_loaded
180             = $self->template_module_is_loaded( $module_name );
181              
182             # Try to load an external Perl template module; on a require
183             # failure, we assume that module intentionally doesn't exist,
184             # and so skip to the next candidate module name.
185 23 100       35 if (!$module_is_loaded) {
186 4         6 eval {
187 4         8 $self->load_template_module( $module_name );
188             };
189             next SET_MEMBER
190 4 50       10 if $@;
191             }
192              
193             # Try to fetch template text for the given message key from the
194             # successfully loaded template module; on a function call
195             # death, assume module is damaged and say so; an undefined
196             # ret val means module doesn't define key, skip to next module.
197 23         56 $text = $self->get_template_text_from_loaded_module( $module_name,
198             $message->get_msg_key() ); # let escape any thrown exception
199             next SET_MEMBER
200 23 100       89 if !defined $text;
201              
202             # We successfully got template text for the message key, so
203             # interpolate the message vars into it and return that.
204 18         39 $text = $self->interpolate_vars_into_template_text(
205             $text, $message->get_msg_vars() );
206 18         104 last SET_MEMBER;
207             }
208              
209 19         72 return $text;
210             }
211              
212             ###########################################################################
213              
214             sub template_module_is_loaded {
215 23     23 0 35 my ($self, $module_name) = @_;
216 23         43 $self->_assert_arg_str( 'template_module_is_loaded',
217             '$module_name!', $module_name );
218 4     4   1565 no strict 'refs';
  4         7  
  4         3195  
219 23         27 return scalar keys %{$module_name . '::'};
  23         72  
220             }
221              
222             sub load_template_module {
223 4     4 0 8 my ($self, $module_name) = @_;
224              
225 4         8 $self->_assert_arg_str( 'load_template_module',
226             '$module_name!', $module_name );
227              
228             # Note: We have to invoke this 'require' in an eval string
229             # because we need the bareword semantics, where 'require'
230             # will munge the package name into file system paths.
231 4         239 eval "require $module_name;";
232 4 50       763 $self->_die_with_msg( 'LKT_T_FAIL_LOAD_TMPL_MOD',
233             { 'METH' => 'load_template_module',
234             'TMPL_MOD_NAME' => $module_name, 'REASON' => $@ } )
235             if $@;
236              
237 4         8 return;
238             }
239              
240             sub get_template_text_from_loaded_module {
241 23     23 0 38 my ($self, $module_name, $msg_key) = @_;
242              
243 23         44 $self->_assert_arg_str( 'get_template_text_from_loaded_module',
244             '$module_name!', $module_name );
245 23         40 $self->_assert_arg_str( 'get_template_text_from_loaded_module',
246             '$msg_key!', $msg_key );
247              
248             # TODO: Use a "can" test to suss out whether a call would work before trying it.
249              
250 23         29 my $text = undef;
251 23         28 eval {
252 23         56 $text = $module_name->get_text_by_key( $msg_key );
253             };
254 23 50       106 $self->_die_with_msg( 'LKT_T_FAIL_GET_TMPL_TEXT',
255             { 'METH' => 'get_template_text_from_loaded_module',
256             'TMPL_MOD_NAME' => $module_name, 'REASON' => $@ } )
257             if $@;
258              
259 23         36 return $text;
260             }
261              
262             sub interpolate_vars_into_template_text {
263 18     18 0 26 my ($self, $text, $msg_vars_ref) = @_;
264              
265 18 50       32 $self->_die_with_msg( 'LKT_ARG_UNDEF',
266             { 'METH' => 'interpolate_vars_into_template_text',
267             'ARG' => '$text!' } )
268             if !defined $text;
269 18         38 $self->_assert_arg_hash( 'interpolate_vars_into_template_text',
270             '%msg_vars!', $msg_vars_ref );
271              
272 18         19 while (my ($var_name, $var_value) = each %{$msg_vars_ref}) {
  37         135  
273 19 100       40 my $var_value_as_str
274             = defined $var_value ? "$var_value"
275             : q{}
276             ;
277 19         228 $text =~ s/ \< $var_name \> /$var_value_as_str/xg;
278             }
279              
280 18         36 return $text;
281             }
282              
283             ###########################################################################
284              
285             sub _die_with_msg {
286 6     6   13 my ($self, $msg_key, $msg_vars_ref) = @_;
287 6   50     10 $msg_vars_ref ||= {};
288 6         11 $msg_vars_ref->{'CLASS'} = 'Locale::KeyedText::Translator';
289 6         24 die Locale::KeyedText::Message->new({
290             'msg_key' => $msg_key, 'msg_vars' => $msg_vars_ref });
291             }
292              
293             sub _assert_arg_str {
294 73     73   104 my ($self, $meth, $arg, $val) = @_;
295 73 50       100 $self->_die_with_msg( 'LKT_ARG_UNDEF',
296             { 'METH' => $meth, 'ARG' => $arg } )
297             if !defined $val;
298 73 50       127 $self->_die_with_msg( 'LKT_ARG_EMP_STR',
299             { 'METH' => $meth, 'ARG' => $arg } )
300             if $val eq q{};
301             }
302              
303             sub _assert_arg_ary {
304 48     48   78 my ($self, $meth, $arg, $val) = @_;
305 48 50       81 $self->_die_with_msg( 'LKT_ARG_UNDEF',
306             { 'METH' => $meth, 'ARG' => $arg } )
307             if !defined $val;
308 48 50       74 $self->_die_with_msg( 'LKT_ARG_NO_ARY',
309             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
310             if ref $val ne 'ARRAY';
311             $self->_die_with_msg( 'LKT_ARG_ARY_NO_ELEMS',
312             { 'METH' => $meth, 'ARG' => $arg } )
313 48 100       52 if @{$val} == 0;
  48         86  
314 46         54 for my $val_elem (@{$val}) {
  46         68  
315 60 100       92 $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_UNDEF',
316             { 'METH' => $meth, 'ARG' => $arg } )
317             if !defined $val_elem;
318 58 50       102 $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_EMP_STR',
319             { 'METH' => $meth, 'ARG' => $arg } )
320             if $val_elem eq q{};
321             }
322             }
323              
324             sub _assert_arg_hash {
325 18     18   26 my ($self, $meth, $arg, $val) = @_;
326 18 50       28 $self->_die_with_msg( 'LKT_ARG_UNDEF',
327             { 'METH' => $meth, 'ARG' => $arg } )
328             if !defined $val;
329 18 50       34 $self->_die_with_msg( 'LKT_ARG_NO_HASH',
330             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
331             if ref $val ne 'HASH';
332             $self->_die_with_msg( 'LKT_ARG_HASH_KEY_EMP_STR',
333             { 'METH' => $meth, 'ARG' => $arg } )
334 18 50       37 if exists $val->{q{}};
335             }
336              
337             sub _assert_arg_msg {
338 21     21   39 my ($self, $meth, $arg, $val) = @_;
339 21 50       76 $self->_die_with_msg( 'LKT_ARG_UNDEF',
340             { 'METH' => $meth, 'ARG' => $arg } )
341             if !defined $val;
342 21 100 66     143 $self->_die_with_msg( 'LKT_ARG_NO_EXP_TYPE', { 'METH' => $meth,
343             'ARG' => $arg, 'EXP_TYPE' => 'Locale::KeyedText::Message',
344             'VAL' => $val } )
345             if !blessed $val or !$val->isa( 'Locale::KeyedText::Message' );
346             }
347              
348             ###########################################################################
349              
350             } # class Locale::KeyedText::Translator
351              
352             ###########################################################################
353             ###########################################################################
354              
355             1;