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   428625 use strict;
  47         149  
  47         1408  
4              
5 47     47   285 use Carp;
  47         114  
  47         2448  
6              
7 47     47   318 use base 'Rose::HTML::Object::Exporter';
  47         118  
  47         22254  
8              
9             our $VERSION = '0.618';
10              
11             our $Debug = 0;
12              
13             use Rose::Class::MakeMethods::Generic
14             (
15 47         264 inheritable_scalar =>
16             [
17             '_message_names',
18             'message_id_to_name_map',
19             'message_name_to_id_map',
20             ],
21 47     47   347 );
  47         111  
22              
23             BEGIN
24             {
25 47     47   13688 __PACKAGE__->_message_names([]);
26 47         551 __PACKAGE__->message_id_to_name_map({});
27 47         431 __PACKAGE__->message_name_to_id_map({});
28             }
29              
30             sub init_export_tags
31             {
32 139     139 0 369 my($class) = shift;
33              
34 139         447 my $list = $class->message_names;
35              
36             $class->export_tags
37             (
38             all => $list,
39 6283         13588 field => [ grep { /^FIELD_/ } @$list ],
40 6283         10804 form => [ grep { /^FORM_/ } @$list ],
41 6283         22404 date => [ grep { /^DATE_|_(?:YEAR|MONTH|DAY)$/ } @$list ],
42 6283         21192 time => [ grep { /^TIME_|_(?:HOUR|MINUTE|SECOND)$/ } @$list ],
43 6283         10414 email => [ grep { /^EMAIL_/ } @$list ],
44 6283         10299 phone => [ grep { /^PHONE_/ } @$list ],
45 6283         10772 number => [ grep { /^NUM_/ } @$list ],
46 6283         10496 set => [ grep { /^SET_/ } @$list ],
47 139         1721 string => [ grep { /^STRING_/ } @$list ],
  6283         10858  
48             );
49             }
50              
51             sub import
52             {
53 332     332   70909 my($class) = shift;
54              
55 332         1651 $class->use_private_messages;
56 332         1443 $class->init_export_tags;
57              
58 332 100       20762 if($Rose::HTML::Object::Exporter::Target_Class)
59             {
60 3         16 $class->SUPER::import(@_);
61             }
62             else
63             {
64 329         1252 local $Rose::HTML::Object::Exporter::Target_Class = (caller)[0];
65 329         1701 $class->SUPER::import(@_);
66             }
67             }
68              
69             our %Private;
70              
71             sub use_private_messages
72             {
73 4255     4255 0 6807 my($class) = shift;
74              
75 4255 100       10187 unless($Private{$class})
76             {
77 107         279 $Private{$class} = 1;
78              
79             # Make private copies of inherited data structures
80             # (shallow copy is sufficient)
81 107         441 $class->message_names([ $class->message_names ]);
82 107         764 $class->message_id_to_name_map({ %{$class->message_id_to_name_map} });
  107         341  
83 107         2093 $class->message_name_to_id_map({ %{$class->message_name_to_id_map} });
  107         359  
84             }
85             }
86              
87 69     69 1 3154 sub message_id_exists { defined $_[0]->message_id_to_name_map->{$_[1]} }
88 4     4 1 1724 sub message_name_exists { defined $_[0]->message_name_to_id_map->{$_[1]} }
89              
90             sub message_names
91             {
92 593     593 1 4177 my($class) = shift;
93              
94 593 100       1971 $class->_message_names(@_) if(@_);
95              
96 593 100       3097 wantarray ? @{$class->_message_names} :
  109         431  
97             $class->_message_names;
98             }
99              
100             sub get_message_id
101             {
102 113     113 1 1815 my($class, $symbol) = @_;
103 47     47   30302 no strict 'refs';
  47         144  
  47         8248  
104 113         327 my $const = "${class}::$symbol";
105 113 100       1242 return &$const if(defined &$const);
106 2         13 return undef;
107             }
108              
109             sub message_ids
110             {
111 6     6 1 10928 my($class) = shift;
112 6         34 my $map = $class->message_id_to_name_map;
113              
114             return wantarray ?
115 585         770 (sort { $a <=> $b } keys %$map) :
116 6 100       156 [ sort { $a <=> $b } keys %$map ];
  278         376  
117             }
118              
119             sub get_message_name
120             {
121 47     47   434 no warnings 'uninitialized';
  47         123  
  47         9854  
122 471     471 1 5770 return $_[0]->message_id_to_name_map->{$_[1]};
123             }
124              
125             sub add_message
126             {
127 3814     3814 1 8366 my($class, $name, $id) = @_;
128              
129 3814         8256 $class->use_private_messages;
130              
131 3814 100       9048 unless($class->imported($name))
132             {
133 3282 50 33     6648 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     25052 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         37 '(', $class->message_id_to_name_map->{$id}, ')';
145             }
146             }
147              
148             MAKE_CONSTANT:
149             {
150 47     47   448 no strict 'refs';
  47         105  
  47         9055  
  3812         23584  
151 3812         7392 my $const = "${class}::$name";
152 3812 100 66     13742 unless($class->can($name) || defined &$const)
153             {
154 19     0   206 *{"${class}::$name"} = sub() { $id };
  19         166  
  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 3812 100       8115 unless(exists $class->message_name_to_id_map->{$name})
170             {
171 3280         21518 push(@{$class->_message_names}, $name);
  3280         6019  
172             }
173              
174 3812         28232 $class->message_id_to_name_map->{$id} = $name;
175 3812         28554 $class->message_name_to_id_map->{$name} = $id;
176              
177 3812         29917 return;
178             }
179              
180             sub add_messages
181             {
182 109     109 1 4600 my($class) = shift;
183              
184 109         637 $class->use_private_messages;
185              
186 47     47   439 no strict 'refs';
  47         153  
  47         14440  
187              
188 109 100       2184 if(@_)
189             {
190 2         9 foreach my $name (@_)
191             {
192 4         27 $class->add_message($name, "${class}::$name"->());
193             }
194             }
195             else
196             {
197 107         203 foreach my $name (keys %{"${class}::"})
  107         1785  
198             {
199 6153         11809 my $fq_name = "${class}::$name";
200              
201 6153 100 100     8115 next unless(defined *{$fq_name}{'CODE'} && $name =~ /^[A-Z0-9_]+$/);
  6153         50879  
202              
203 3789         13727 my $code = $class->can($name);
204              
205             # Skip it if it's not a constant
206 3789 50 33     14947 next unless(defined prototype($code) && !length(prototype($code)));
207              
208             # Should not need this check?
209 3789 50       10976 next if($name =~ /^(BEGIN|DESTROY|AUTOLOAD|TIE.*)$/);
210              
211 3789 50       6938 $Debug && warn "$class ADD $name = ", $code->(), "\n";
212 3789         8301 $class->add_message($name, $code->());
213             }
214             }
215             }
216              
217             #
218             # Messages
219             #
220              
221 47     47   361 use constant CUSTOM_MESSAGE => -1;
  47         263  
  47         3325  
222              
223             # Fields and labels
224 47     47   364 use constant FIELD_LABEL => 1;
  47         123  
  47         3396  
225 47     47   353 use constant FIELD_DESCRIPTION => 2;
  47         138  
  47         2542  
226 47     47   316 use constant FIELD_REQUIRED_GENERIC => 4;
  47         231  
  47         3021  
227 47     47   330 use constant FIELD_REQUIRED_LABELLED => 5;
  47         100  
  47         2615  
228 47     47   318 use constant FIELD_REQUIRED_SUBFIELD => 6;
  47         119  
  47         2511  
229 47     47   321 use constant FIELD_REQUIRED_SUBFIELDS => 7;
  47         118  
  47         2558  
230 47     47   344 use constant FIELD_PARTIAL_VALUE => 8;
  47         99  
  47         2394  
231 47     47   300 use constant FIELD_INVALID_GENERIC => 10;
  47         156  
  47         2500  
232 47     47   341 use constant FIELD_INVALID_LABELLED => 11;
  47         100  
  47         2572  
233              
234 47     47   305 use constant FIELD_LABEL_YEAR => 10_000;
  47         94  
  47         2363  
235 47     47   349 use constant FIELD_LABEL_MONTH => 10_001;
  47         131  
  47         2520  
236 47     47   298 use constant FIELD_LABEL_DAY => 10_002;
  47         243  
  47         2467  
237 47     47   316 use constant FIELD_LABEL_HOUR => 10_003;
  47         140  
  47         2531  
238 47     47   372 use constant FIELD_LABEL_MINUTE => 10_004;
  47         135  
  47         2474  
239 47     47   300 use constant FIELD_LABEL_SECOND => 10_005;
  47         165  
  47         2362  
240              
241 47     47   314 use constant FIELD_ERROR_LABEL_YEAR => 11_000;
  47         163  
  47         2509  
242 47     47   328 use constant FIELD_ERROR_LABEL_MONTH => 11_001;
  47         88  
  47         2534  
243 47     47   314 use constant FIELD_ERROR_LABEL_DAY => 11_002;
  47         114  
  47         2390  
244 47     47   293 use constant FIELD_ERROR_LABEL_HOUR => 11_003;
  47         183  
  47         2570  
245 47     47   338 use constant FIELD_ERROR_LABEL_MINUTE => 11_004;
  47         161  
  47         2439  
246 47     47   318 use constant FIELD_ERROR_LABEL_SECOND => 11_005;
  47         136  
  47         2544  
247              
248 47     47   359 use constant FIELD_ERROR_LABEL_MINIMUM_DATE => 11_006;
  47         107  
  47         2449  
249 47     47   294 use constant FIELD_ERROR_LABEL_MAXIMUM_DATE => 11_007;
  47         96  
  47         2551  
250              
251             # Forms
252 47     47   311 use constant FORM_HAS_ERRORS => 100;
  47         98  
  47         2528  
253              
254             # Numerical messages
255 47     47   323 use constant NUM_INVALID_INTEGER => 1300;
  47         98  
  47         2370  
256 47     47   292 use constant NUM_INVALID_INTEGER_POSITIVE => 1301;
  47         126  
  47         2503  
257 47     47   318 use constant NUM_NOT_POSITIVE_INTEGER => 1302;
  47         118  
  47         2597  
258 47     47   324 use constant NUM_BELOW_MIN => 1303;
  47         92  
  47         2394  
259 47     47   311 use constant NUM_ABOVE_MAX => 1304;
  47         272  
  47         2407  
260 47     47   300 use constant NUM_INVALID_NUMBER => 1305;
  47         111  
  47         2519  
261 47     47   328 use constant NUM_INVALID_NUMBER_POSITIVE => 1306;
  47         107  
  47         2313  
262 47     47   281 use constant NUM_NOT_POSITIVE_NUMBER => 1307;
  47         237  
  47         2948  
263              
264             # String messages
265 47     47   336 use constant STRING_OVERFLOW => 1400;
  47         131  
  47         2372  
266              
267             # Date messages
268 47     47   310 use constant DATE_INVALID => 1500;
  47         240  
  47         2303  
269 47     47   387 use constant DATE_MIN_GREATER_THAN_MAX => 1501;
  47         109  
  47         2602  
270              
271             # Time messages
272 47     47   329 use constant TIME_INVALID => 1550;
  47         104  
  47         2710  
273 47     47   341 use constant TIME_INVALID_HOUR => 1551;
  47         116  
  47         2344  
274 47     47   331 use constant TIME_INVALID_MINUTE => 1552;
  47         116  
  47         2624  
275 47     47   305 use constant TIME_INVALID_SECONDS => 1553;
  47         135  
  47         2335  
276 47     47   325 use constant TIME_INVALID_AMPM => 1554;
  47         178  
  47         2607  
277              
278             # Email messages
279 47     47   310 use constant EMAIL_INVALID => 1600;
  47         122  
  47         2405  
280              
281             # Phone messages
282 47     47   328 use constant PHONE_INVALID => 1650;
  47         92  
  47         2424  
283              
284             # Set messages
285 47     47   525 use constant SET_INVALID_QUOTED_STRING => 1700;
  47         108  
  47         2454  
286 47     47   307 use constant SET_PARSE_ERROR => 1701;
  47         97  
  47         2429  
287              
288 47     47   221 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.