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   427966 use strict;
  47         155  
  47         1384  
4              
5 47     47   301 use Carp;
  47         137  
  47         2514  
6              
7 47     47   302 use base 'Rose::HTML::Object::Exporter';
  47         105  
  47         22717  
8              
9             our $VERSION = '0.618';
10              
11             our $Debug = 0;
12              
13             use Rose::Class::MakeMethods::Generic
14             (
15 47         261 inheritable_scalar =>
16             [
17             '_message_names',
18             'message_id_to_name_map',
19             'message_name_to_id_map',
20             ],
21 47     47   344 );
  47         114  
22              
23             BEGIN
24             {
25 47     47   14794 __PACKAGE__->_message_names([]);
26 47         555 __PACKAGE__->message_id_to_name_map({});
27 47         403 __PACKAGE__->message_name_to_id_map({});
28             }
29              
30             sub init_export_tags
31             {
32 139     139 0 339 my($class) = shift;
33              
34 139         475 my $list = $class->message_names;
35              
36             $class->export_tags
37             (
38             all => $list,
39 6283         13094 field => [ grep { /^FIELD_/ } @$list ],
40 6283         10644 form => [ grep { /^FORM_/ } @$list ],
41 6283         22784 date => [ grep { /^DATE_|_(?:YEAR|MONTH|DAY)$/ } @$list ],
42 6283         20789 time => [ grep { /^TIME_|_(?:HOUR|MINUTE|SECOND)$/ } @$list ],
43 6283         10237 email => [ grep { /^EMAIL_/ } @$list ],
44 6283         10240 phone => [ grep { /^PHONE_/ } @$list ],
45 6283         10990 number => [ grep { /^NUM_/ } @$list ],
46 6283         10463 set => [ grep { /^SET_/ } @$list ],
47 139         1642 string => [ grep { /^STRING_/ } @$list ],
  6283         10856  
48             );
49             }
50              
51             sub import
52             {
53 332     332   80542 my($class) = shift;
54              
55 332         1273 $class->use_private_messages;
56 332         1343 $class->init_export_tags;
57              
58 332 100       20203 if($Rose::HTML::Object::Exporter::Target_Class)
59             {
60 3         14 $class->SUPER::import(@_);
61             }
62             else
63             {
64 329         1285 local $Rose::HTML::Object::Exporter::Target_Class = (caller)[0];
65 329         1554 $class->SUPER::import(@_);
66             }
67             }
68              
69             our %Private;
70              
71             sub use_private_messages
72             {
73 4220     4220 0 6837 my($class) = shift;
74              
75 4220 100       10150 unless($Private{$class})
76             {
77 107         267 $Private{$class} = 1;
78              
79             # Make private copies of inherited data structures
80             # (shallow copy is sufficient)
81 107         430 $class->message_names([ $class->message_names ]);
82 107         846 $class->message_id_to_name_map({ %{$class->message_id_to_name_map} });
  107         384  
83 107         2426 $class->message_name_to_id_map({ %{$class->message_name_to_id_map} });
  107         391  
84             }
85             }
86              
87 69     69 1 2926 sub message_id_exists { defined $_[0]->message_id_to_name_map->{$_[1]} }
88 4     4 1 1303 sub message_name_exists { defined $_[0]->message_name_to_id_map->{$_[1]} }
89              
90             sub message_names
91             {
92 593     593 1 3951 my($class) = shift;
93              
94 593 100       1968 $class->_message_names(@_) if(@_);
95              
96 593 100       3063 wantarray ? @{$class->_message_names} :
  109         448  
97             $class->_message_names;
98             }
99              
100             sub get_message_id
101             {
102 113     113 1 1614 my($class, $symbol) = @_;
103 47     47   31087 no strict 'refs';
  47         168  
  47         8679  
104 113         378 my $const = "${class}::$symbol";
105 113 100       1371 return &$const if(defined &$const);
106 2         11 return undef;
107             }
108              
109             sub message_ids
110             {
111 6     6 1 8967 my($class) = shift;
112 6         29 my $map = $class->message_id_to_name_map;
113              
114             return wantarray ?
115 580         761 (sort { $a <=> $b } keys %$map) :
116 6 100       127 [ sort { $a <=> $b } keys %$map ];
  272         367  
117             }
118              
119             sub get_message_name
120             {
121 47     47   436 no warnings 'uninitialized';
  47         160  
  47         10208  
122 471     471 1 5056 return $_[0]->message_id_to_name_map->{$_[1]};
123             }
124              
125             sub add_message
126             {
127 3779     3779 1 8215 my($class, $name, $id) = @_;
128              
129 3779         8154 $class->use_private_messages;
130              
131 3779 100       9027 unless($class->imported($name))
132             {
133 3282 50 33     6900 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     25482 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         39 '(', $class->message_id_to_name_map->{$id}, ')';
145             }
146             }
147              
148             MAKE_CONSTANT:
149             {
150 47     47   433 no strict 'refs';
  47         126  
  47         9334  
  3777         23857  
151 3777         7366 my $const = "${class}::$name";
152 3777 100 66     13676 unless($class->can($name) || defined &$const)
153             {
154 19     0   218 *{"${class}::$name"} = sub() { $id };
  19         148  
  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 3777 100       8186 unless(exists $class->message_name_to_id_map->{$name})
170             {
171 3280         21879 push(@{$class->_message_names}, $name);
  3280         6176  
172             }
173              
174 3777         28064 $class->message_id_to_name_map->{$id} = $name;
175 3777         28693 $class->message_name_to_id_map->{$name} = $id;
176              
177 3777         29646 return;
178             }
179              
180             sub add_messages
181             {
182 109     109 1 4375 my($class) = shift;
183              
184 109         691 $class->use_private_messages;
185              
186 47     47   428 no strict 'refs';
  47         150  
  47         14944  
187              
188 109 100       2215 if(@_)
189             {
190 2         9 foreach my $name (@_)
191             {
192 4         29 $class->add_message($name, "${class}::$name"->());
193             }
194             }
195             else
196             {
197 107         204 foreach my $name (keys %{"${class}::"})
  107         1820  
198             {
199 6111         11778 my $fq_name = "${class}::$name";
200              
201 6111 100 100     7899 next unless(defined *{$fq_name}{'CODE'} && $name =~ /^[A-Z0-9_]+$/);
  6111         51128  
202              
203 3754         15290 my $code = $class->can($name);
204              
205             # Skip it if it's not a constant
206 3754 50 33     14784 next unless(defined prototype($code) && !length(prototype($code)));
207              
208             # Should not need this check?
209 3754 50       10889 next if($name =~ /^(BEGIN|DESTROY|AUTOLOAD|TIE.*)$/);
210              
211 3754 50       6972 $Debug && warn "$class ADD $name = ", $code->(), "\n";
212 3754         8355 $class->add_message($name, $code->());
213             }
214             }
215             }
216              
217             #
218             # Messages
219             #
220              
221 47     47   398 use constant CUSTOM_MESSAGE => -1;
  47         316  
  47         3320  
222              
223             # Fields and labels
224 47     47   394 use constant FIELD_LABEL => 1;
  47         128  
  47         3062  
225 47     47   351 use constant FIELD_DESCRIPTION => 2;
  47         152  
  47         2508  
226 47     47   308 use constant FIELD_REQUIRED_GENERIC => 4;
  47         227  
  47         3226  
227 47     47   343 use constant FIELD_REQUIRED_LABELLED => 5;
  47         137  
  47         2724  
228 47     47   328 use constant FIELD_REQUIRED_SUBFIELD => 6;
  47         109  
  47         2530  
229 47     47   313 use constant FIELD_REQUIRED_SUBFIELDS => 7;
  47         118  
  47         2580  
230 47     47   346 use constant FIELD_PARTIAL_VALUE => 8;
  47         97  
  47         2487  
231 47     47   291 use constant FIELD_INVALID_GENERIC => 10;
  47         179  
  47         2540  
232 47     47   346 use constant FIELD_INVALID_LABELLED => 11;
  47         101  
  47         2499  
233              
234 47     47   306 use constant FIELD_LABEL_YEAR => 10_000;
  47         107  
  47         2373  
235 47     47   360 use constant FIELD_LABEL_MONTH => 10_001;
  47         132  
  47         2436  
236 47     47   303 use constant FIELD_LABEL_DAY => 10_002;
  47         280  
  47         2467  
237 47     47   322 use constant FIELD_LABEL_HOUR => 10_003;
  47         154  
  47         2409  
238 47     47   296 use constant FIELD_LABEL_MINUTE => 10_004;
  47         137  
  47         2500  
239 47     47   295 use constant FIELD_LABEL_SECOND => 10_005;
  47         169  
  47         2373  
240              
241 47     47   302 use constant FIELD_ERROR_LABEL_YEAR => 11_000;
  47         142  
  47         2534  
242 47     47   319 use constant FIELD_ERROR_LABEL_MONTH => 11_001;
  47         111  
  47         2556  
243 47     47   314 use constant FIELD_ERROR_LABEL_DAY => 11_002;
  47         108  
  47         2418  
244 47     47   303 use constant FIELD_ERROR_LABEL_HOUR => 11_003;
  47         166  
  47         2429  
245 47     47   342 use constant FIELD_ERROR_LABEL_MINUTE => 11_004;
  47         157  
  47         2456  
246 47     47   335 use constant FIELD_ERROR_LABEL_SECOND => 11_005;
  47         133  
  47         2590  
247              
248 47     47   357 use constant FIELD_ERROR_LABEL_MINIMUM_DATE => 11_006;
  47         108  
  47         2347  
249 47     47   289 use constant FIELD_ERROR_LABEL_MAXIMUM_DATE => 11_007;
  47         102  
  47         2583  
250              
251             # Forms
252 47     47   301 use constant FORM_HAS_ERRORS => 100;
  47         106  
  47         2610  
253              
254             # Numerical messages
255 47     47   328 use constant NUM_INVALID_INTEGER => 1300;
  47         112  
  47         2426  
256 47     47   311 use constant NUM_INVALID_INTEGER_POSITIVE => 1301;
  47         129  
  47         2498  
257 47     47   286 use constant NUM_NOT_POSITIVE_INTEGER => 1302;
  47         129  
  47         2596  
258 47     47   326 use constant NUM_BELOW_MIN => 1303;
  47         104  
  47         2345  
259 47     47   296 use constant NUM_ABOVE_MAX => 1304;
  47         296  
  47         2431  
260 47     47   281 use constant NUM_INVALID_NUMBER => 1305;
  47         145  
  47         2551  
261 47     47   332 use constant NUM_INVALID_NUMBER_POSITIVE => 1306;
  47         117  
  47         2353  
262 47     47   277 use constant NUM_NOT_POSITIVE_NUMBER => 1307;
  47         232  
  47         3089  
263              
264             # String messages
265 47     47   311 use constant STRING_OVERFLOW => 1400;
  47         132  
  47         2509  
266              
267             # Date messages
268 47     47   315 use constant DATE_INVALID => 1500;
  47         288  
  47         2380  
269 47     47   303 use constant DATE_MIN_GREATER_THAN_MAX => 1501;
  47         158  
  47         2662  
270              
271             # Time messages
272 47     47   320 use constant TIME_INVALID => 1550;
  47         101  
  47         2646  
273 47     47   317 use constant TIME_INVALID_HOUR => 1551;
  47         136  
  47         2436  
274 47     47   340 use constant TIME_INVALID_MINUTE => 1552;
  47         138  
  47         2701  
275 47     47   325 use constant TIME_INVALID_SECONDS => 1553;
  47         120  
  47         2406  
276 47     47   336 use constant TIME_INVALID_AMPM => 1554;
  47         174  
  47         2701  
277              
278             # Email messages
279 47     47   314 use constant EMAIL_INVALID => 1600;
  47         121  
  47         2451  
280              
281             # Phone messages
282 47     47   344 use constant PHONE_INVALID => 1650;
  47         118  
  47         2457  
283              
284             # Set messages
285 47     47   527 use constant SET_INVALID_QUOTED_STRING => 1700;
  47         136  
  47         2499  
286 47     47   318 use constant SET_PARSE_ERROR => 1701;
  47         101  
  47         2512  
287              
288 47     47   274 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.