File Coverage

blib/lib/Locale/KeyedText.pm
Criterion Covered Total %
statement 207 224 92.4
branch 52 74 70.2
condition 8 19 42.1
subroutine 44 47 93.6
pod n/a
total 311 364 85.4


line stmt bran cond sub pod time code
1 4     4   43500 use 5.008001;
  4         9  
2 4     4   11 use utf8;
  4         4  
  4         12  
3 4     4   63 use strict;
  4         4  
  4         48  
4 4     4   11 use warnings;
  4         7  
  4         223  
5              
6             ###########################################################################
7             ###########################################################################
8              
9             # Constant values used by packages in this file:
10             my $EMPTY_STR = q{};
11              
12             ###########################################################################
13             ###########################################################################
14              
15             { package Locale::KeyedText; # package
16             BEGIN {
17 4     4   5 our $VERSION = '2.000000';
18 4         153 $VERSION = eval $VERSION;
19             }
20             } # package Locale::KeyedText
21              
22             ###########################################################################
23             ###########################################################################
24              
25             { package Locale::KeyedText::Message; # class
26             BEGIN {
27 4     4   6 our $VERSION = '2.000000';
28 4         140 $VERSION = eval $VERSION;
29             }
30              
31 4     4   15 use Scalar::Util 'blessed';
  4         5  
  4         2049  
32              
33             # has _msg_key
34             # isa Str
35             # default ''
36             # The machine-readable key that uniquely ident this message.
37             sub _msg_key {
38 96     96   71 my $self = shift;
39 96 100       204 $self->{_msg_key} = $_[0] if scalar @_;
40 96         106 return $self->{_msg_key};
41             }
42              
43             # has _msg_vars
44             # isa HashRef
45             # One elem per var:
46             # hkey is Str - var name
47             # hval is Any - current value for var
48             # default {}
49             # Named variables for messages, if any, go here.
50             sub _msg_vars {
51 94     94   70 my $self = shift;
52 94 100       122 $self->{_msg_vars} = $_[0] if scalar @_;
53 94         122 return $self->{_msg_vars};
54             }
55              
56             ###########################################################################
57              
58             sub new {
59 26     26   2225 my ($class, @args) = @_;
60 26   33     108 $class = (blessed $class) || $class;
61              
62 26         41 my $params = $class->BUILDARGS( @args );
63              
64 26         33 my $self = bless {}, $class;
65              
66             # Set attribute default values.
67 26         40 $self->_msg_key( '' );
68 26         33 $self->_msg_vars( {} );
69              
70 26         36 $self->BUILD( $params );
71              
72 23         88 return $self;
73             }
74              
75             ###########################################################################
76              
77             sub BUILDARGS {
78 26     26   28 my ($class, @args) = @_;
79 26 50 33     93 if (@args == 1 and ref $args[0] eq 'HASH') {
    0          
80             # Constructor was called with (possibly zero) named arguments.
81 26         20 return { %{$args[0]} };
  26         83  
82             }
83             elsif ((scalar @args % 2) == 0) {
84             # Constructor was called with (possibly zero) named arguments.
85 0         0 return { @args };
86             }
87             else {
88             # Constructor was called with odd number positional arguments.
89 0         0 $class->_die_with_msg( 'LKT_ARGS_BAD_PSEUDO_NAMED' );
90             }
91             }
92              
93             ###########################################################################
94              
95             sub BUILD {
96 26     26   45 my ($self, $args) = @_;
97 26         22 my ($msg_key, $msg_vars_ref) = @{$args}{'msg_key', 'msg_vars'};
  26         28  
98              
99 26 100       40 if (!defined $msg_vars_ref) {
100 7         14 $msg_vars_ref = {};
101             }
102              
103 26         38 $self->_assert_arg_str( 'new', ':$msg_key!', $msg_key );
104 24         32 $self->_assert_arg_hash( 'new', ':%msg_vars?', $msg_vars_ref );
105              
106 23         25 $self->_msg_key( $msg_key );
107 23         16 $self->_msg_vars( {%{$msg_vars_ref}} );
  23         70  
108              
109 23         25 return;
110             }
111              
112             ###########################################################################
113              
114             sub export_as_hash {
115 0     0   0 my ($self) = @_;
116             return {
117             'msg_key' => $self->_msg_key(),
118 0         0 'msg_vars' => {%{$self->_msg_vars()}},
  0         0  
119             };
120             }
121              
122             ###########################################################################
123              
124             sub get_msg_key {
125 24     24   252 my ($self) = @_;
126 24         32 return $self->_msg_key();
127             }
128              
129             sub get_msg_var {
130 5     5   1336 my ($self, $var_name) = @_;
131 5         9 $self->_assert_arg_str( 'get_msg_var', '$var_name!', $var_name );
132 3         4 return $self->_msg_vars()->{$var_name};
133             }
134              
135             sub get_msg_vars {
136 19     19   245 my ($self) = @_;
137 19         15 return {%{$self->_msg_vars()}};
  19         21  
138             }
139              
140             ###########################################################################
141              
142             sub as_debug_string {
143 11     11   179 my ($self) = @_;
144 11         13 my $msg_key = $self->_msg_key();
145 11         14 my $msg_vars = $self->_msg_vars();
146             return ' Debug String of a Locale::KeyedText::Message object:'
147             . "\n"
148             . ' $msg_key: "' . $msg_key . '"'
149             . "\n"
150             . ' %msg_vars: {' . (join q{, }, map {
151             '"' . $_ . '"="' . (defined $msg_vars->{$_}
152 37 50       106 ? $msg_vars->{$_} : $EMPTY_STR) . '"'
153 11         21 } sort keys %{$msg_vars}) . '}'
  11         39  
154             . "\n";
155             }
156              
157             use overload (
158 4         22 '""' => \&as_debug_string,
159             fallback => 1,
160 4     4   2563 );
  4         2100  
161              
162             sub as_debug_str {
163 12     12   1173 my ($self) = @_;
164 12         17 my $msg_key = $self->_msg_key();
165 12         19 my $msg_vars = $self->_msg_vars();
166             return $msg_key . ': ' . join ', ', map {
167             $_ . '='
168 13 100       48 . (defined $msg_vars->{$_} ? $msg_vars->{$_} : $EMPTY_STR)
169 12         16 } sort keys %{$msg_vars};
  12         41  
170             }
171              
172             ###########################################################################
173              
174             sub _die_with_msg {
175 5     5   4 my ($self, $msg_key, $msg_vars_ref) = @_;
176 5   50     8 $msg_vars_ref ||= {};
177 5         6 $msg_vars_ref->{'CLASS'} = 'Locale::KeyedText::Message';
178 5         11 die Locale::KeyedText::Message->new({
179             'msg_key' => $msg_key, 'msg_vars' => $msg_vars_ref });
180             }
181              
182             sub _assert_arg_str {
183 31     31   30 my ($self, $meth, $arg, $val) = @_;
184 31 100       43 $self->_die_with_msg( 'LKT_ARG_UNDEF',
185             { 'METH' => $meth, 'ARG' => $arg } )
186             if !defined $val;
187 29 100       52 $self->_die_with_msg( 'LKT_ARG_EMP_STR',
188             { 'METH' => $meth, 'ARG' => $arg } )
189             if $val eq $EMPTY_STR;
190             }
191              
192             sub _assert_arg_hash {
193 24     24   25 my ($self, $meth, $arg, $val) = @_;
194 24 50       35 $self->_die_with_msg( 'LKT_ARG_UNDEF',
195             { 'METH' => $meth, 'ARG' => $arg } )
196             if !defined $val;
197 24 50       38 $self->_die_with_msg( 'LKT_ARG_NO_HASH',
198             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
199             if ref $val ne 'HASH';
200             $self->_die_with_msg( 'LKT_ARG_HASH_KEY_EMP_STR',
201             { 'METH' => $meth, 'ARG' => $arg } )
202 24 100       37 if exists $val->{$EMPTY_STR};
203             }
204              
205             ###########################################################################
206              
207             } # class Locale::KeyedText::Message
208              
209             ###########################################################################
210             ###########################################################################
211              
212             { package Locale::KeyedText::Translator; # class
213             BEGIN {
214 4     4   1145 our $VERSION = '2.000000';
215 4         156 $VERSION = eval $VERSION;
216             }
217              
218 4     4   27 use Scalar::Util 'blessed';
  4         4  
  4         1771  
219              
220             # has _set_names
221             # isa ArrayRef
222             # One elem per set name:
223             # elem is Str
224             # default []
225             # List of Template module Set Names to search.
226             sub _set_names {
227 99     99   71 my $self = shift;
228 99 100       219 $self->{_set_names} = $_[0] if scalar @_;
229 99         95 return $self->{_set_names};
230             }
231              
232             # has _member_names
233             # isa ArrayRef
234             # One elem per member name:
235             # elem is Str
236             # default []
237             # List of Template module Member Names to search.
238             sub _member_names {
239 87     87   67 my $self = shift;
240 87 100       119 $self->{_member_names} = $_[0] if scalar @_;
241 87         141 return $self->{_member_names};
242             }
243              
244             ###########################################################################
245              
246             sub new {
247 25     25   4309 my ($class, @args) = @_;
248 25   33     103 $class = (blessed $class) || $class;
249              
250 25         37 my $params = $class->BUILDARGS( @args );
251              
252 25         34 my $self = bless {}, $class;
253              
254             # Set attribute default values.
255 25         40 $self->_set_names( [] );
256 25         39 $self->_member_names( [] );
257              
258 25         34 $self->BUILD( $params );
259              
260 21         44 return $self;
261             }
262              
263             ###########################################################################
264              
265             sub BUILDARGS {
266 25     25   26 my ($class, @args) = @_;
267 25 50 33     94 if (@args == 1 and ref $args[0] eq 'HASH') {
    0          
268             # Constructor was called with (possibly zero) named arguments.
269 25         15 return { %{$args[0]} };
  25         101  
270             }
271             elsif ((scalar @args % 2) == 0) {
272             # Constructor was called with (possibly zero) named arguments.
273 0         0 return { @args };
274             }
275             else {
276             # Constructor was called with odd number positional arguments.
277 0         0 $class->_die_with_msg( 'LKT_ARGS_BAD_PSEUDO_NAMED' );
278             }
279             }
280              
281             ###########################################################################
282              
283             sub BUILD {
284 25     25   31 my ($self, $args) = @_;
285             my ($set_names_ref, $member_names_ref)
286 25         19 = @{$args}{'set_names', 'member_names'};
  25         32  
287              
288 25 100       43 if (ref $set_names_ref ne 'ARRAY') {
289 5         6 $set_names_ref = [$set_names_ref];
290             }
291 25 100       33 if (ref $member_names_ref ne 'ARRAY') {
292 5         7 $member_names_ref = [$member_names_ref];
293             }
294              
295 25         43 $self->_assert_arg_ary( 'new', ':@set_names!', $set_names_ref );
296 23         27 $self->_assert_arg_ary( 'new', ':@member_names!', $member_names_ref );
297              
298 21         15 $self->_set_names( [@{$set_names_ref}] );
  21         34  
299 21         16 $self->_member_names( [@{$member_names_ref}] );
  21         27  
300              
301 21         20 return;
302             }
303              
304             ###########################################################################
305              
306             sub export_as_hash {
307 0     0   0 my ($self) = @_;
308             return {
309 0         0 'set_names' => [@{$self->_set_names()}],
310 0         0 'member_names' => [@{$self->_member_names()}],
  0         0  
311             };
312             }
313              
314             ###########################################################################
315              
316             sub get_set_names {
317 1     1   230 my ($self) = @_;
318 1         1 return [@{$self->_set_names()}];
  1         2  
319             }
320              
321             sub get_member_names {
322 1     1   258 my ($self) = @_;
323 1         1 return [@{$self->_member_names()}];
  1         3  
324             }
325              
326             ###########################################################################
327              
328             sub as_debug_string {
329 0     0   0 my ($self) = @_;
330 0         0 my $set_names = $self->_set_names();
331 0         0 my $member_names = $self->_member_names();
332             return ' Debug String of a Locale::KeyedText::Translator object:'
333             . "\n"
334 0         0 . ' @set_names: ["' . (join q{", "}, @{$set_names}) . '"]'
335             . "\n"
336             . ' @member_names: ["'
337 0         0 . (join q{", "}, @{$member_names}) . '"]'
  0         0  
338             . "\n";
339             }
340              
341             use overload (
342 4         14 '""' => \&as_debug_string,
343             fallback => 1,
344 4     4   16 );
  4         4  
345              
346             sub as_debug_str {
347 21     21   2682 my ($self) = @_;
348 21         28 my $set_names = $self->_set_names();
349 21         26 my $member_names = $self->_member_names();
350 21         36 return 'SETS: ' . (join ', ', @{$set_names}) . '; '
351 21         18 . 'MEMBERS: ' . (join ', ', @{$member_names});
  21         52  
352             }
353              
354             ###########################################################################
355              
356             sub get_set_member_combinations {
357 19     19   15 my ($self) = @_;
358 19         16 my @combinations = ();
359 19         14 for my $member_name (@{$self->_member_names()}) {
  19         24  
360 31         15 for my $set_name (@{$self->_set_names()}) {
  31         35  
361 55         82 push @combinations, $set_name . $member_name;
362             }
363             }
364 19         28 return \@combinations;
365             }
366              
367             ###########################################################################
368              
369             sub translate_message {
370 21     21   4323 my ($self, $message) = @_;
371              
372 21         31 $self->_assert_arg_msg( 'translate_message', '$message!', $message );
373              
374 19         15 my $text = undef;
375             SET_MEMBER:
376 19         12 for my $module_name (@{$self->get_set_member_combinations()}) {
  19         23  
377             # Determine if requested template module is already loaded.
378             # It may have been embedded in a core program file and hence
379             # should never be loaded by translate_message().
380 23         27 my $module_is_loaded
381             = $self->template_module_is_loaded( $module_name );
382              
383             # Try to load an external Perl template module; on a require
384             # failure, we assume that module intentionally doesn't exist,
385             # and so skip to the next candidate module name.
386 23 100       35 if (!$module_is_loaded) {
387 4         2 eval {
388 4         8 $self->load_template_module( $module_name );
389             };
390             next SET_MEMBER
391 4 50       7 if $@;
392             }
393              
394             # Try to fetch template text for the given message key from the
395             # successfully loaded template module; on a function call
396             # death, assume module is damaged and say so; an undefined
397             # ret val means module doesn't define key, skip to next module.
398 23         37 $text = $self->get_template_text_from_loaded_module( $module_name,
399             $message->get_msg_key() ); # let escape any thrown exception
400             next SET_MEMBER
401 23 100       36 if !defined $text;
402              
403             # We successfully got template text for the message key, so
404             # interpolate the message vars into it and return that.
405 18         22 $text = $self->interpolate_vars_into_template_text(
406             $text, $message->get_msg_vars() );
407 18         35 last SET_MEMBER;
408             }
409              
410 19         58 return $text;
411             }
412              
413             ###########################################################################
414              
415             sub template_module_is_loaded {
416 23     23   20 my ($self, $module_name) = @_;
417 23         28 $self->_assert_arg_str( 'template_module_is_loaded',
418             '$module_name!', $module_name );
419 4     4   1139 no strict 'refs';
  4         4  
  4         2104  
420 23         17 return scalar keys %{$module_name . '::'};
  23         49  
421             }
422              
423             sub load_template_module {
424 4     4   3 my ($self, $module_name) = @_;
425              
426 4         5 $self->_assert_arg_str( 'load_template_module',
427             '$module_name!', $module_name );
428              
429             # Note: We have to invoke this 'require' in an eval string
430             # because we need the bareword semantics, where 'require'
431             # will munge the package name into file system paths.
432 4         175 eval "require $module_name;";
433 4 50       628 $self->_die_with_msg( 'LKT_T_FAIL_LOAD_TMPL_MOD',
434             { 'METH' => 'load_template_module',
435             'TMPL_MOD_NAME' => $module_name, 'REASON' => $@ } )
436             if $@;
437              
438 4         7 return;
439             }
440              
441             sub get_template_text_from_loaded_module {
442 23     23   19 my ($self, $module_name, $msg_key) = @_;
443              
444 23         23 $self->_assert_arg_str( 'get_template_text_from_loaded_module',
445             '$module_name!', $module_name );
446 23         24 $self->_assert_arg_str( 'get_template_text_from_loaded_module',
447             '$msg_key!', $msg_key );
448              
449             # TODO: Use a "can" test to suss out whether a call would work before trying it.
450              
451 23         16 my $text = undef;
452 23         19 eval {
453 23         56 $text = $module_name->get_text_by_key( $msg_key );
454             };
455 23 50       85 $self->_die_with_msg( 'LKT_T_FAIL_GET_TMPL_TEXT',
456             { 'METH' => 'get_template_text_from_loaded_module',
457             'TMPL_MOD_NAME' => $module_name, 'REASON' => $@ } )
458             if $@;
459              
460 23         24 return $text;
461             }
462              
463             sub interpolate_vars_into_template_text {
464 18     18   16 my ($self, $text, $msg_vars_ref) = @_;
465              
466 18 50       24 $self->_die_with_msg( 'LKT_ARG_UNDEF',
467             { 'METH' => 'interpolate_vars_into_template_text',
468             'ARG' => '$text!' } )
469             if !defined $text;
470 18         22 $self->_assert_arg_hash( 'interpolate_vars_into_template_text',
471             '%msg_vars!', $msg_vars_ref );
472              
473 18         10 while (my ($var_name, $var_value) = each %{$msg_vars_ref}) {
  37         83  
474 19 100       23 my $var_value_as_str
475             = defined $var_value ? "$var_value"
476             : $EMPTY_STR
477             ;
478 19         205 $text =~ s/ \< $var_name \> /$var_value_as_str/xg;
479             }
480              
481 18         26 return $text;
482             }
483              
484             ###########################################################################
485              
486             sub _die_with_msg {
487 6     6   7 my ($self, $msg_key, $msg_vars_ref) = @_;
488 6   50     10 $msg_vars_ref ||= {};
489 6         8 $msg_vars_ref->{'CLASS'} = 'Locale::KeyedText::Translator';
490 6         21 die Locale::KeyedText::Message->new({
491             'msg_key' => $msg_key, 'msg_vars' => $msg_vars_ref });
492             }
493              
494             sub _assert_arg_str {
495 73     73   64 my ($self, $meth, $arg, $val) = @_;
496 73 50       85 $self->_die_with_msg( 'LKT_ARG_UNDEF',
497             { 'METH' => $meth, 'ARG' => $arg } )
498             if !defined $val;
499 73 50       89 $self->_die_with_msg( 'LKT_ARG_EMP_STR',
500             { 'METH' => $meth, 'ARG' => $arg } )
501             if $val eq $EMPTY_STR;
502             }
503              
504             sub _assert_arg_ary {
505 48     48   41 my ($self, $meth, $arg, $val) = @_;
506 48 50       59 $self->_die_with_msg( 'LKT_ARG_UNDEF',
507             { 'METH' => $meth, 'ARG' => $arg } )
508             if !defined $val;
509 48 50       54 $self->_die_with_msg( 'LKT_ARG_NO_ARY',
510             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
511             if ref $val ne 'ARRAY';
512             $self->_die_with_msg( 'LKT_ARG_ARY_NO_ELEMS',
513             { 'METH' => $meth, 'ARG' => $arg } )
514 48 100       32 if @{$val} == 0;
  48         70  
515 46         33 for my $val_elem (@{$val}) {
  46         51  
516 60 100       74 $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_UNDEF',
517             { 'METH' => $meth, 'ARG' => $arg } )
518             if !defined $val_elem;
519 58 50       81 $self->_die_with_msg( 'LKT_ARG_ARY_ELEM_EMP_STR',
520             { 'METH' => $meth, 'ARG' => $arg } )
521             if $val_elem eq $EMPTY_STR;
522             }
523             }
524              
525             sub _assert_arg_hash {
526 18     18   14 my ($self, $meth, $arg, $val) = @_;
527 18 50       21 $self->_die_with_msg( 'LKT_ARG_UNDEF',
528             { 'METH' => $meth, 'ARG' => $arg } )
529             if !defined $val;
530 18 50       28 $self->_die_with_msg( 'LKT_ARG_NO_HASH',
531             { 'METH' => $meth, 'ARG' => $arg, 'VAL' => $val } )
532             if ref $val ne 'HASH';
533             $self->_die_with_msg( 'LKT_ARG_HASH_KEY_EMP_STR',
534             { 'METH' => $meth, 'ARG' => $arg } )
535 18 50       29 if exists $val->{$EMPTY_STR};
536             }
537              
538             sub _assert_arg_msg {
539 21     21   18 my ($self, $meth, $arg, $val) = @_;
540 21 50       32 $self->_die_with_msg( 'LKT_ARG_UNDEF',
541             { 'METH' => $meth, 'ARG' => $arg } )
542             if !defined $val;
543 21 100 66     137 $self->_die_with_msg( 'LKT_ARG_NO_EXP_TYPE', { 'METH' => $meth,
544             'ARG' => $arg, 'EXP_TYPE' => 'Locale::KeyedText::Message',
545             'VAL' => $val } )
546             if !blessed $val or !$val->isa( 'Locale::KeyedText::Message' );
547             }
548              
549             ###########################################################################
550              
551             } # class Locale::KeyedText::Translator
552              
553             ###########################################################################
554             ###########################################################################
555              
556             1;
557             __END__