File Coverage

blib/lib/Rose/HTML/Object/Messages.pm
Criterion Covered Total %
statement 238 240 99.1
branch 28 32 87.5
condition 9 15 60.0
subroutine 66 67 98.5
pod 8 10 80.0
total 349 364 95.8


line stmt bran cond sub pod time code
1             package Rose::HTML::Object::Messages;
2              
3 47     47   487884 use strict;
  47         107  
  47         1785  
4              
5 47     47   242 use Carp;
  47         106  
  47         3290  
6              
7 47     47   298 use base 'Rose::HTML::Object::Exporter';
  47         170  
  47         26409  
8              
9             our $VERSION = '0.618';
10              
11             our $Debug = 0;
12              
13             use Rose::Class::MakeMethods::Generic
14             (
15 47         280 inheritable_scalar =>
16             [
17             '_message_names',
18             'message_id_to_name_map',
19             'message_name_to_id_map',
20             ],
21 47     47   363 );
  47         111  
22              
23             BEGIN
24             {
25 47     47   16540 __PACKAGE__->_message_names([]);
26 47         651 __PACKAGE__->message_id_to_name_map({});
27 47         462 __PACKAGE__->message_name_to_id_map({});
28             }
29              
30             sub init_export_tags
31             {
32 139     139 0 387 my($class) = shift;
33              
34 139         1630 my $list = $class->message_names;
35              
36             $class->export_tags
37             (
38             all => $list,
39 6283         13270 field => [ grep { /^FIELD_/ } @$list ],
40 6283         11505 form => [ grep { /^FORM_/ } @$list ],
41 6283         29020 date => [ grep { /^DATE_|_(?:YEAR|MONTH|DAY)$/ } @$list ],
42 6283         28296 time => [ grep { /^TIME_|_(?:HOUR|MINUTE|SECOND)$/ } @$list ],
43 6283         10950 email => [ grep { /^EMAIL_/ } @$list ],
44 6283         11130 phone => [ grep { /^PHONE_/ } @$list ],
45 6283         11194 number => [ grep { /^NUM_/ } @$list ],
46 6283         10916 set => [ grep { /^SET_/ } @$list ],
47 139         1923 string => [ grep { /^STRING_/ } @$list ],
  6283         12377  
48             );
49             }
50              
51             sub import
52             {
53 332     332   97444 my($class) = shift;
54              
55 332         1500 $class->use_private_messages;
56 332         1640 $class->init_export_tags;
57              
58 332 100       24353 if($Rose::HTML::Object::Exporter::Target_Class)
59             {
60 3         16 $class->SUPER::import(@_);
61             }
62             else
63             {
64 329         1436 local $Rose::HTML::Object::Exporter::Target_Class = (caller)[0];
65 329         2025 $class->SUPER::import(@_);
66             }
67             }
68              
69             our %Private;
70              
71             sub use_private_messages
72             {
73 4247     4247 0 7025 my($class) = shift;
74              
75 4247 100       10674 unless($Private{$class})
76             {
77 107         305 $Private{$class} = 1;
78              
79             # Make private copies of inherited data structures
80             # (shallow copy is sufficient)
81 107         487 $class->message_names([ $class->message_names ]);
82 107         845 $class->message_id_to_name_map({ %{$class->message_id_to_name_map} });
  107         360  
83 107         2054 $class->message_name_to_id_map({ %{$class->message_name_to_id_map} });
  107         400  
84             }
85             }
86              
87 69     69 1 3780 sub message_id_exists { defined $_[0]->message_id_to_name_map->{$_[1]} }
88 4     4 1 3342 sub message_name_exists { defined $_[0]->message_name_to_id_map->{$_[1]} }
89              
90             sub message_names
91             {
92 593     593 1 4774 my($class) = shift;
93              
94 593 100       2007 $class->_message_names(@_) if(@_);
95              
96 593 100       3760 wantarray ? @{$class->_message_names} :
  109         501  
97             $class->_message_names;
98             }
99              
100             sub get_message_id
101             {
102 113     113 1 1917 my($class, $symbol) = @_;
103 47     47   37025 no strict 'refs';
  47         178  
  47         11366  
104 113         298 my $const = "${class}::$symbol";
105 113 100       1397 return &$const if(defined &$const);
106 2         12 return undef;
107             }
108              
109             sub message_ids
110             {
111 6     6 1 464565 my($class) = shift;
112 6         33 my $map = $class->message_id_to_name_map;
113              
114             return wantarray ?
115 601         884 (sort { $a <=> $b } keys %$map) :
116 6 100       169 [ sort { $a <=> $b } keys %$map ];
  287         436  
117             }
118              
119             sub get_message_name
120             {
121 47     47   343 no warnings 'uninitialized';
  47         102  
  47         11040  
122 471     471 1 6871 return $_[0]->message_id_to_name_map->{$_[1]};
123             }
124              
125             sub add_message
126             {
127 3806     3806 1 8433 my($class, $name, $id) = @_;
128              
129 3806         9202 $class->use_private_messages;
130              
131 3806 100       9988 unless($class->imported($name))
132             {
133 3282 50 33     7221 if(exists $class->message_name_to_id_map->{$name} &&
134             $class->message_name_to_id_map->{$name} != $id)
135             {
136             croak "Could not add message '$name' - a message with that name already exists ",
137 0         0 '(', $class->message_name_to_id_map->{$name}, ')';
138             }
139              
140 3282 100 66     26772 if(exists $class->message_id_to_name_map->{$id} &&
141             $class->message_id_to_name_map->{$id} ne $name)
142             {
143             croak "Could not add message '$name' - a message with the id $id already exists ",
144 2         52 '(', $class->message_id_to_name_map->{$id}, ')';
145             }
146             }
147              
148             MAKE_CONSTANT:
149             {
150 47     47   323 no strict 'refs';
  47         146  
  47         12803  
  3804         24466  
151 3804         6065 my $const = "${class}::$name";
152 3804 100 66     15391 unless($class->can($name) || defined &$const)
153             {
154 19     0   324 *{"${class}::$name"} = sub() { $id };
  19         136  
  0         0  
155              
156             #my $error;
157             #
158             #TRY:
159             #{
160             # local $@;
161             # eval "package $class; use constant $name => $id;";
162             # $error = $@;
163             #}
164             #
165             #croak "Could not create constant '$name' in $class - $error" if($error);
166             }
167             }
168              
169 3804 100       8411 unless(exists $class->message_name_to_id_map->{$name})
170             {
171 3280         21548 push(@{$class->_message_names}, $name);
  3280         6702  
172             }
173              
174 3804         29824 $class->message_id_to_name_map->{$id} = $name;
175 3804         30107 $class->message_name_to_id_map->{$name} = $id;
176              
177 3804         32611 return;
178             }
179              
180             sub add_messages
181             {
182 109     109 1 5431 my($class) = shift;
183              
184 109         716 $class->use_private_messages;
185              
186 47     47   359 no strict 'refs';
  47         200  
  47         21354  
187              
188 109 100       2231 if(@_)
189             {
190 2         7 foreach my $name (@_)
191             {
192 4         30 $class->add_message($name, "${class}::$name"->());
193             }
194             }
195             else
196             {
197 107         202 foreach my $name (keys %{"${class}::"})
  107         2659  
198             {
199 6144         9988 my $fq_name = "${class}::$name";
200              
201 6144 100 100     8121 next unless(defined *{$fq_name}{'CODE'} && $name =~ /^[A-Z0-9_]+$/);
  6144         57546  
202              
203 3781         13992 my $code = $class->can($name);
204              
205             # Skip it if it's not a constant
206 3781 50 33     15590 next unless(defined prototype($code) && !length(prototype($code)));
207              
208             # Should not need this check?
209 3781 50       10730 next if($name =~ /^(BEGIN|DESTROY|AUTOLOAD|TIE.*)$/);
210              
211 3781 50       7314 $Debug && warn "$class ADD $name = ", $code->(), "\n";
212 3781         9047 $class->add_message($name, $code->());
213             }
214             }
215             }
216              
217             #
218             # Messages
219             #
220              
221 47     47   360 use constant CUSTOM_MESSAGE => -1;
  47         92  
  47         6725  
222              
223             # Fields and labels
224 47     47   322 use constant FIELD_LABEL => 1;
  47         118  
  47         2915  
225 47     47   260 use constant FIELD_DESCRIPTION => 2;
  47         119  
  47         6450  
226 47     47   2608 use constant FIELD_REQUIRED_GENERIC => 4;
  47         2568  
  47         2609  
227 47     47   350 use constant FIELD_REQUIRED_LABELLED => 5;
  47         2167  
  47         3086  
228 47     47   349 use constant FIELD_REQUIRED_SUBFIELD => 6;
  47         91  
  47         8966  
229 47     47   276 use constant FIELD_REQUIRED_SUBFIELDS => 7;
  47         136  
  47         2349  
230 47     47   346 use constant FIELD_PARTIAL_VALUE => 8;
  47         113  
  47         4481  
231 47     47   310 use constant FIELD_INVALID_GENERIC => 10;
  47         122  
  47         4726  
232 47     47   303 use constant FIELD_INVALID_LABELLED => 11;
  47         99  
  47         2407  
233              
234 47     47   263 use constant FIELD_LABEL_YEAR => 10_000;
  47         89  
  47         2442  
235 47     47   240 use constant FIELD_LABEL_MONTH => 10_001;
  47         106  
  47         2096  
236 47     47   232 use constant FIELD_LABEL_DAY => 10_002;
  47         77  
  47         2487  
237 47     47   293 use constant FIELD_LABEL_HOUR => 10_003;
  47         141  
  47         2372  
238 47     47   265 use constant FIELD_LABEL_MINUTE => 10_004;
  47         78  
  47         2427  
239 47     47   250 use constant FIELD_LABEL_SECOND => 10_005;
  47         87  
  47         2230  
240              
241 47     47   363 use constant FIELD_ERROR_LABEL_YEAR => 11_000;
  47         100  
  47         2261  
242 47     47   336 use constant FIELD_ERROR_LABEL_MONTH => 11_001;
  47         82  
  47         2491  
243 47     47   241 use constant FIELD_ERROR_LABEL_DAY => 11_002;
  47         113  
  47         2458  
244 47     47   289 use constant FIELD_ERROR_LABEL_HOUR => 11_003;
  47         88  
  47         2193  
245 47     47   229 use constant FIELD_ERROR_LABEL_MINUTE => 11_004;
  47         85  
  47         2733  
246 47     47   289 use constant FIELD_ERROR_LABEL_SECOND => 11_005;
  47         95  
  47         2540  
247              
248 47     47   253 use constant FIELD_ERROR_LABEL_MINIMUM_DATE => 11_006;
  47         106  
  47         2806  
249 47     47   285 use constant FIELD_ERROR_LABEL_MAXIMUM_DATE => 11_007;
  47         106  
  47         2342  
250              
251             # Forms
252 47     47   229 use constant FORM_HAS_ERRORS => 100;
  47         94  
  47         2545  
253              
254             # Numerical messages
255 47     47   264 use constant NUM_INVALID_INTEGER => 1300;
  47         153  
  47         2262  
256 47     47   271 use constant NUM_INVALID_INTEGER_POSITIVE => 1301;
  47         87  
  47         2418  
257 47     47   326 use constant NUM_NOT_POSITIVE_INTEGER => 1302;
  47         91  
  47         2547  
258 47     47   252 use constant NUM_BELOW_MIN => 1303;
  47         115  
  47         2366  
259 47     47   315 use constant NUM_ABOVE_MAX => 1304;
  47         101  
  47         4129  
260 47     47   258 use constant NUM_INVALID_NUMBER => 1305;
  47         82  
  47         3180  
261 47     47   285 use constant NUM_INVALID_NUMBER_POSITIVE => 1306;
  47         118  
  47         2419  
262 47     47   285 use constant NUM_NOT_POSITIVE_NUMBER => 1307;
  47         88  
  47         2483  
263              
264             # String messages
265 47     47   308 use constant STRING_OVERFLOW => 1400;
  47         100  
  47         2248  
266              
267             # Date messages
268 47     47   265 use constant DATE_INVALID => 1500;
  47         114  
  47         2368  
269 47     47   268 use constant DATE_MIN_GREATER_THAN_MAX => 1501;
  47         89  
  47         2587  
270              
271             # Time messages
272 47     47   314 use constant TIME_INVALID => 1550;
  47         124  
  47         2149  
273 47     47   307 use constant TIME_INVALID_HOUR => 1551;
  47         147  
  47         2348  
274 47     47   262 use constant TIME_INVALID_MINUTE => 1552;
  47         169  
  47         2600  
275 47     47   310 use constant TIME_INVALID_SECONDS => 1553;
  47         125  
  47         2224  
276 47     47   263 use constant TIME_INVALID_AMPM => 1554;
  47         82  
  47         2218  
277              
278             # Email messages
279 47     47   236 use constant EMAIL_INVALID => 1600;
  47         99  
  47         2271  
280              
281             # Phone messages
282 47     47   244 use constant PHONE_INVALID => 1650;
  47         182  
  47         2304  
283              
284             # Set messages
285 47     47   223 use constant SET_INVALID_QUOTED_STRING => 1700;
  47         103  
  47         2314  
286 47     47   262 use constant SET_PARSE_ERROR => 1701;
  47         106  
  47         2502  
287              
288 47     47   257 BEGIN { __PACKAGE__->add_messages }
289              
290             1;
291              
292             __END__
293              
294             =head1 NAME
295              
296             Rose::HTML::Object::Messages - Message ids and named constants for use with HTML objects.
297              
298             =head1 SYNOPSIS
299              
300             package My::HTML::Object::Messages;
301              
302             use strict;
303              
304             # Import the standard set of message ids
305             use Rose::HTML::Object::Messages qw(:all);
306             use base qw(Rose::HTML::Object::Messages);
307              
308             ##
309             ## Define your new message ids below
310             ##
311              
312             # Message ids from 0 to 29,999 are reserved for built-in messages.
313             # Negative message ids are reserved for internal use. Please use
314             # message ids 30,000 or higher for your messages. Suggested message
315             # id ranges and naming conventions for various message types are
316             # shown below.
317              
318             # Field labels
319              
320             use constant FIELD_LABEL_LOGIN_NAME => 100_000;
321             use constant FIELD_LABEL_PASSWORD => 100_001;
322             ...
323              
324             # Field error messages
325              
326             use constant FIELD_ERROR_PASSWORD_TOO_SHORT => 101_000;
327             use constant FIELD_ERROR_USERNAME_INVALID => 101_001;
328             ...
329              
330             # Generic messages
331              
332             use constant LOGIN_NO_SUCH_USER => 200_000;
333             use constant LOGIN_USER_EXISTS_ERROR => 200_001;
334             ...
335              
336             # This line must be below all the "use constant ..." declarations
337             BEGIN { __PACKAGE__->add_messages }
338              
339             1;
340              
341             =head1 DESCRIPTION
342              
343             L<Rose::HTML::Object::Messages> stores message ids and names. The message ids are defined as Perl L<constants|constant> with integer values. The constants themselves as well as the mapping between the symbolic constant names and their values are stored as class data.
344              
345             If you merely want to import one of the standard message id constants, you may use this module as-is (see the L<EXPORTS|/EXPORTS> section for details). If you want to define your own messages, you must subclass this module exactly as shown in the synopsis. The order of the statements is important!
346              
347             When adding your own messages, you are free to choose any integer message id values, subject to the following constraints:
348              
349             =over 4
350              
351             =item * Message ids from 0 to 29,999 are reserved for built-in messages.
352              
353             =item * Negative message ids are reserved for internal use.
354              
355             =back
356              
357             Please use ids 30,000 or higher for your messages. Constant names may contain only the characters C<[A-Z0-9_]> and must be unique among all message constant names.
358              
359             =head1 EXPORTS
360              
361             L<Rose::HTML::Object::Messages> does not export any symbols by default.
362              
363             The 'all' tag:
364              
365             use Rose::HTML::Object::Messages qw(:all);
366              
367             will cause all message name constant to be imported.
368              
369             The following tags will cause all messages whose names match the regular expression to the right of the tag name to be imported.
370              
371             TAG NAME REGEX
372             ----- -----------------
373             field ^FIELD_
374             form ^FORM_
375             date ^DATE_|_(?:YEAR|MONTH|DAY)$
376             time ^TIME_|_(?:HOUR|MINUTE|SECOND)$
377             email ^EMAIL_
378             phone ^PHONE_
379             number ^NUM_
380             set ^SET_
381             string ^STRING_
382              
383             For example, this will import all the message constants whose names begin with "FIELD_"
384              
385             use Rose::HTML::Object::Messages qw(:field);
386              
387             Finally, you can import individual message constant names as well:
388              
389             use Rose::HTML::Object::Messages qw(FIELD_LABEL_YEAR TIME_INVALID);
390              
391             A complete listing of the default set of message constant names appears in the next section.
392              
393             =head1 BUILT-IN MESSAGES
394              
395             The list of built-in messages constant names appears below. You should not rely on the actual numeric values of these constants. Import and refer to them only by their symbolic names.
396              
397             FIELD_LABEL
398             FIELD_DESCRIPTION
399             FIELD_REQUIRED_GENERIC
400             FIELD_REQUIRED_LABELLED
401             FIELD_REQUIRED_SUBFIELD
402             FIELD_REQUIRED_SUBFIELDS
403             FIELD_PARTIAL_VALUE
404             FIELD_INVALID_GENERIC
405             FIELD_INVALID_LABELLED
406              
407             FIELD_LABEL_YEAR
408             FIELD_LABEL_MONTH
409             FIELD_LABEL_DAY
410             FIELD_LABEL_HOUR
411             FIELD_LABEL_MINUTE
412             FIELD_LABEL_SECOND
413              
414             FIELD_ERROR_LABEL_YEAR
415             FIELD_ERROR_LABEL_MONTH
416             FIELD_ERROR_LABEL_DAY
417             FIELD_ERROR_LABEL_HOUR
418             FIELD_ERROR_LABEL_MINUTE
419             FIELD_ERROR_LABEL_SECOND
420              
421             FIELD_ERROR_LABEL_MINIMUM_DATE
422             FIELD_ERROR_LABEL_MAXIMUM_DATE
423              
424             FORM_HAS_ERRORS
425              
426             NUM_INVALID_INTEGER
427             NUM_INVALID_INTEGER_POSITIVE
428             NUM_NOT_POSITIVE_INTEGER
429             NUM_BELOW_MIN
430             NUM_ABOVE_MAX
431             NUM_INVALID_NUMBER
432             NUM_INVALID_NUMBER_POSITIVE
433             NUM_NOT_POSITIVE_NUMBER
434              
435             STRING_OVERFLOW
436              
437             DATE_INVALID
438             DATE_MIN_GREATER_THAN_MAX
439              
440             TIME_INVALID
441             TIME_INVALID_HOUR
442             TIME_INVALID_MINUTE
443             TIME_INVALID_SECONDS
444             TIME_INVALID_AMPM
445              
446             EMAIL_INVALID
447              
448             PHONE_INVALID
449              
450             SET_INVALID_QUOTED_STRING
451             SET_PARSE_ERROR
452              
453             =head1 CLASS METHODS
454              
455             =over 4
456              
457             =item B<add_message NAME, ID>
458              
459             Add a new message constant with NAME and an integer ID value. Message ids from 0 to 29,999 are reserved for built-in messages. Negative message ids are reserved for internal use. Please use message ids 30,000 or higher for your messages. Constant names may contain only the characters C<[A-Z0-9_]> and must be unique among all message names.
460              
461             =item B<add_messages [NAME1, NAME2, ...]>
462              
463             If called with no arguments, this method L<adds|/add_message> all message L<constants|constant> defined in the calling class. Example:
464              
465             __PACKAGE__->add_messages;
466              
467             If called with a list of constant names, add each named constant to the list of messages. These L<constants|constant> must already exist in the calling class. Example:
468              
469             use constant MY_MESSAGE1 => 123456;
470             use constant MY_MESSAGE2 => 123457;
471             ...
472             __PACKAGE__->add_messages('MY_MESSAGE1', 'MY_MESSAGE2');
473              
474             =item B<get_message_id NAME>
475              
476             Returns the integer message id corresponding to the symbolic constant NAME, or undef if no such name exists.
477              
478             =item B<get_message_name ID>
479              
480             Returns the symbolic message constant name corresponding to the integer message ID, or undef if no such message ID exists.
481              
482             =item B<message_id_exists ID>
483              
484             Return true if the integer message ID exists, false otherwise.
485              
486             =item B<message_name_exists NAME>
487              
488             Return true if the symbolic message constant NAME exists, false otherwise.
489              
490             =item B<message_ids>
491              
492             Returns a list (in list context) or reference to an array (in scalar context) of integer message ids.
493              
494             =item B<message_names>
495              
496             Returns a list (in list context) or reference to an array (in scalar context) of message names.
497              
498             =back
499              
500             =head1 AUTHOR
501              
502             John C. Siracusa (siracusa@gmail.com)
503              
504             =head1 LICENSE
505              
506             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.