File Coverage

blib/lib/App/CELL/Message.pm
Criterion Covered Total %
statement 62 80 77.5
branch 15 20 75.0
condition 18 21 85.7
subroutine 13 18 72.2
pod 9 9 100.0
total 117 148 79.0


line stmt bran cond sub pod time code
1             # *************************************************************************
2             # Copyright (c) 2014-2020, SUSE LLC
3             #
4             # All rights reserved.
5             #
6             # Redistribution and use in source and binary forms, with or without
7             # modification, are permitted provided that the following conditions are met:
8             #
9             # 1. Redistributions of source code must retain the above copyright notice,
10             # this list of conditions and the following disclaimer.
11             #
12             # 2. Redistributions in binary form must reproduce the above copyright
13             # notice, this list of conditions and the following disclaimer in the
14             # documentation and/or other materials provided with the distribution.
15             #
16             # 3. Neither the name of SUSE LLC nor the names of its contributors may be
17             # used to endorse or promote products derived from this software without
18             # specific prior written permission.
19             #
20             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30             # POSSIBILITY OF SUCH DAMAGE.
31             # *************************************************************************
32              
33             package App::CELL::Message;
34              
35 17     17   7636 use strict;
  17         33  
  17         442  
36 17     17   76 use warnings;
  17         28  
  17         351  
37 17     17   260 use 5.012;
  17         60  
38              
39 17     17   89 use App::CELL::Log qw( $log );
  17         36  
  17         1364  
40 17     17   109 use App::CELL::Util qw( stringify_args );
  17         57  
  17         701  
41 17     17   95 use Data::Dumper;
  17         30  
  17         650  
42 17     17   96 use Try::Tiny;
  17         35  
  17         16075  
43              
44              
45             =head1 NAME
46              
47             App::CELL::Message - handle messages the user might see
48              
49              
50              
51             =head1 SYNOPSIS
52              
53             use App::CELL::Message;
54              
55             # server messages: pass message code only, message text
56             # will be localized to the site default language, if
57             # assertainable, or, failing that, in English
58             my $message = App::CELL::Message->new( code => 'FOOBAR' )
59             # and then we pass $message as an argument to
60             # App::CELL::Status->new
61              
62             # client messages: pass message code and session id,
63             # message text will be localized according to the user's language
64             # preference setting
65             my $message = App::CELL::Message->new( code => 'BARBAZ',
66             session => $s_obj );
67             $msg_to_display = $message->App::CELL::Message->text;
68              
69             # a message may call for one or more arguments. If so,
70             # include an 'args' hash element in the call to 'new':
71             args => [ 'FOO', 'BAR' ]
72             # they will be included in the message text via a call to
73             # sprintf
74              
75              
76              
77             =head1 EXPORTS AND PUBLIC METHODS
78              
79             This module provides the following public functions and methods:
80              
81             =over
82              
83             =item C - construct a C object
84              
85             =item C - get text of an existing object
86              
87             =item C - get maximum size of a given message code
88              
89             =back
90              
91             =cut
92              
93              
94              
95             =head1 DESCRIPTION
96              
97             An App::CELL::Message object is a reference to a hash containing some or
98             all of the following keys (attributes):
99              
100             =over
101              
102             =item C - message code (see below)
103              
104             =item C - message text
105              
106             =item C - error (if any) related to this message
107              
108             =item C - message language (e.g., English)
109              
110             =item C - maximum number of characters this message is
111             guaranteed not to exceed (and will be truncated to fit into)
112              
113             =item C - boolean value: text has been truncated or not
114              
115             =back
116              
117             The information in the hash is sourced from two places: the
118             C<$mesg> hashref in this module (see L) and the SQL
119             database. The former is reserved for "system critical" messages, while
120             the latter contains messages that users will come into contact with on
121             a daily basis. System messages are English-only; only user messages
122             are localizable.
123              
124              
125              
126             =head1 PACKAGE VARIABLES
127              
128              
129             =head2 C<$mesg>
130              
131             The C module stores messages in a package variable, C<$mesg>
132             (which is a hashref).
133              
134             =head2 C<@supp_lang>
135              
136             List of supported languages. Set by C<< $CELL->load >> from the value of
137             CELL_SUPP_LANG
138              
139             =head2 C<$def_lang>
140              
141             The defined, or default, language. Set by C<< $CELL->load >> from the value
142             of CELL_DEF_LANG
143              
144             =cut
145              
146             our $mesg = {};
147             our $supp_lang;
148             our $def_lang;
149              
150              
151              
152             =head1 FUNCTIONS AND METHODS
153              
154              
155             =head2 supported_languages
156              
157             Get reference to list of supported languages.
158              
159             =cut
160              
161             sub supported_languages {
162 10   100 10 1 48 my $sl = $supp_lang || [ 'en' ];
163 10         144 return $sl;
164             }
165              
166              
167             =head2 language_supported
168              
169             Determine if a given language is supported.
170              
171             =cut
172              
173             sub language_supported {
174 6     6 1 15 my ( $lang ) = @_;
175 6 100       10 return 1 if grep( /$lang/, @{ supported_languages() } );
  6         13  
176 2         20 return 0;
177             }
178              
179              
180             =head2 default_language
181              
182             Return the default language.
183              
184             =cut
185              
186             sub default_language {
187 0   0 0 1 0 my $dl = $def_lang || 'en';
188 0         0 return $dl;
189             }
190              
191              
192             =head2 new
193            
194             Construct a message object. Takes a PARAMHASH containing, at least,
195             a 'code' attribute as well as, optionally, other attributes such as
196             'args' (a reference to an array of arguments). Returns a status object. If
197             the status is ok, then the message object will be in the payload. See
198             L.
199              
200             =cut
201              
202             sub new {
203              
204 79     79 1 281 my ( $class, %ARGS ) = @_;
205 79         223 my $stringified_args = stringify_args( \%ARGS );
206 79         165 my $my_caller;
207 79         130 my $msgobj = {};
208              
209             #$log->debug( "Entering Message->new called from " . (caller)[1] . " line " . (caller)[2]);
210 79 100       182 if ( $ARGS{called_from_status} ) {
211 69         122 $my_caller = $ARGS{caller};
212             } else {
213 10         31 $my_caller = [ caller ];
214             }
215            
216 79 100       175 if ( not exists( $ARGS{'code'} ) ) {
217 1         7 return App::CELL::Status->new( level => 'ERR',
218             code => 'CELL_MESSAGE_NO_CODE',
219             caller => $my_caller,
220             );
221             }
222 78 100       174 if ( not $ARGS{'code'} ) {
223 1         4 return App::CELL::Status->new( level => 'ERR',
224             code => 'CELL_MESSAGE_CODE_UNDEFINED',
225             caller => $my_caller,
226             );
227             }
228 77         160 $msgobj->{'code'} = $ARGS{code};
229              
230 77 50       157 if ( $ARGS{lang} ) {
231 0         0 $log->debug( $ARGS{code} . ": " . $mesg->{ $ARGS{code} }->{ $ARGS{lang} }->{ 'Text' },
232             cell => 1 );
233             }
234 77   100     379 $msgobj->{'lang'} = $ARGS{lang} || $def_lang || 'en';
235             $msgobj->{'file'} = $mesg->
236             { $msgobj->{code} }->
237             { $msgobj->{lang} }->
238 77   100     319 { 'File' } || '';
239             $msgobj->{'line'} = $mesg->
240             { $msgobj->{code} }->
241             { $msgobj->{lang} }->
242 77   100     280 { 'Line' } || '';
243              
244             # This next line is important: it may happen that the developer wants
245             # to quickly code some messages/statuses without formally assigning
246             # codes in the site configuration. In these cases, the $mesg lookup
247             # will fail. Instead of throwing an error, we just generate a message
248             # text from the value of 'code'.
249             my $text = $mesg->
250             { $msgobj->{code} }->
251             { $msgobj->{lang} }->
252             { 'Text' }
253 77   66     237 || $msgobj->{code};
254              
255             # strip out anything that resembles a newline
256 77         173 $text =~ s/\n//g;
257 77         117 $text =~ s/\012/ -- /g;
258              
259 77   100     178 my $stringy = stringify_args( $ARGS{args} ) || '';
260 77 100 100     228 if ( defined $ARGS{args} and @{ $ARGS{args} } and not $text =~ m/%s/ ) {
  74   100     340  
261 1         4 $ARGS{text} = $text . " ARGS: $stringy";
262             } else {
263              
264             # insert the arguments into the message text -- needs to be in an eval
265             # block because we have no control over what crap the application
266             # programmer might send us
267             try {
268             local $SIG{__WARN__} = sub {
269 0         0 die @_;
270 76     76   3262 };
271 76 100       156 $ARGS{text} = sprintf( $text, @{ $ARGS{args} || [] } );
  76         618  
272             }
273             catch {
274 0     0   0 my $errmsg = $_;
275 0         0 $errmsg =~ s/\012/ -- /g;
276 0         0 $ARGS{text} = "CELL_MESSAGE_ARGUMENT_MISMATCH on $ARGS{code}, error was: $errmsg";
277 0         0 $log->err( $ARGS{text}, cell => 1);
278 76         492 };
279              
280             }
281 77         1190 $msgobj->{'text'} = $ARGS{text};
282              
283             # uncomment if needed
284             #$log->debug( "Creating message object ->" . $ARGS{code} .
285             # "<- with args ->$stringified_args<-",
286             # caller => $my_caller, cell => 1);
287              
288             # bless into objecthood
289 77         148 my $self = bless $msgobj, __PACKAGE__;
290              
291             # return ok status with created object in payload
292 77         255 return App::CELL::Status->new( level => 'OK',
293             payload => $self,
294             );
295             }
296              
297              
298             =head2 lang
299              
300             Clones the message into another language. Returns a status object. On
301             success, the new message object will be in the payload.
302              
303             =cut
304              
305             sub lang {
306 0     0 1 0 my ( $self, $lang ) = @_;
307 0         0 my $status = __PACKAGE__->new(
308             code => $self->code,
309             lang => $lang,
310             args => $self->args,
311             );
312 0         0 return $status;
313             }
314              
315              
316             =head2 stringify
317              
318             Generate a string representation of a message object using Data::Dumper.
319              
320             =cut
321              
322             sub stringify {
323 0     0 1 0 local $Data::Dumper::Terse = 1;
324 0         0 my $self = shift;
325 0         0 my %u_self = %$self;
326 0         0 return Dumper( \%u_self );
327             }
328              
329              
330             =head2 code
331              
332             Accessor method for the 'code' attribute.
333              
334             =cut
335              
336             sub code {
337 70     70 1 104 my $self = shift;
338 70 50       171 return if not $self->{code}; # returns undef in scalar context
339 70         143 return $self->{code};
340             }
341              
342              
343             =head2 args
344              
345             Accessor method for the 'args' attribute.
346              
347             =cut
348              
349             sub args {
350 0     0 1 0 my $self = $_[0];
351 0 0       0 return [] if not $self->{args};
352 0         0 return $self->{args};
353             }
354              
355              
356             =head2 text
357            
358             Accessor method for the 'text' attribute. Returns content of 'text'
359             attribute, or "" if it can't find any content.
360              
361             =cut
362              
363             sub text {
364 79     79 1 667 my $self = $_[0];
365 79 50       175 return "" if not $self->{text};
366 79         318 return $self->{text};
367             }
368              
369             1;