Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Comment on

( #3333=superdoc: print w/replies, xml ) Need Help??
package MessageLibrary; $VERSION = "0.12"; # Copyright (C) 2002 John Clyman ( # Documentation and licensing details in POD below, or use perldoc Mes +sageLibrary =head1 NAME MessageLibrary - create objects with methods you can call to generate +both static and dynamic status, error, or other messages =head1 SYNOPSIS # create a MessageLibrary $error_messages = MessageLibrary->new({ bad_file_format => 'File format not recognized!', file_open_failed => sub{"Unable to open file $_[0]: $!"}, _default => sub{"Unknown message " . shift() . " with params " . (join ",",@_)}, }); # generate messages print $error_messages->bad_file_format; print $error_messages->file_open_failed('myfile'); print $error_messages->no_such_message; # falls back to _default # override default prefixes and suffixes $error_messages->set_prefix("myprogram: "); $error_messages->set_suffix("\n"); =head1 DESCRIPTION =head2 Overview With the MessageLibrary class, you can create objects that dynamically + construct status, error, or other messages on behalf of your programs. MessageLibrary is particularly useful in l +arger projects, where it can be used to create centralized collections of messages that are easier to maintain + than string literals scattered throughout the code. To create a MessageLibrary object, you'll need to create a hash contai +ning a set of keywords and a message associated with each keyword, then pass that hash to the C<new> constr +uctor. The keywords you choose are then exposed as methods of an individual M +essageLibrary object, so you can generate messages with this syntax: $messages->message_keyword(...with params too, if you want...) The messages themselves may be either literal strings or anonymous sub +routines that can perform arbitrarily complex operations. For instance, if you create an C<$error_messages> object l +ike this: $error_messages = MessageLibrary->new({ file_open_failed => sub{"Unable to open file $_[0]: $!\n"} }); You can then write this: open INPUT, "/no/such/file" or die $error_messages->file_open_failed('myfile'); And get this result: Unable to open file myfile: No such file or directory Notice that parameters to the method call are accessible to your subro +utine via C<@_>, and that the global C<$!> variable containing the error message from the last f +ile operation is available too. When you're using static error messages -- i.e., where interpolation a +t the moment of message generation is not required -- you can skip the anonymous subroutine and simply provide a + string literal: $status_messages = MessageLibrary->new( new_record => 'loading new record', all_done => 'processing complete', ); ... print $status_messages->new_record; ... print $status_messages->all_done; =head2 Prefixes and Suffixes Whether you're using static or dynamic messages, there's actually one +more thing that MessageLibrary objects do when constructing messages: They add a prefix and a suffix. By defa +ult, the prefix contains the name of the current executable (stripped of path information if you're running on a Window +s or Unix variant), and the suffix is simply a newline. So in practice you'll normally get messages that look more +like this: YourProgramName: Unable to open file myfile: No such file or directo +ry\n You can change this behavior by calling the C<set_prefix> and C<set_su +ffix> methods: $error_messages->set_prefix("Error: "); $error_messages->set_suffix("."); which would result instead in: Error: Unable to open file myfile: No such file or directory. The prefix and suffix that you set apply to all messages emitted by an + individual MessageLibrary object. (Incidentally, you can retrieve the current prefix and suffix by using + the C<get_prefix> and C<get_suffix> methods, but I can't think of a particularly compelling reason to actually do t +hat.) =head2 Defining Fallback Messages What happens if you try to call a method for which no message was defi +ned? MessageLibrary provides default behavior, so that: print $status_messages->no_such_message('nice try', 'dude'); results in: YourProgramName: message no_such_message(nice try,dude)\n You can override this behavior by specifying a C<_default> key (and as +sociated message) in your constructor: $error_messages = MessageLibrary->new({ bad_file_format => 'File format not recognized!', _default => sub{"Unknown message '$_[0]' received"}, }); With this C<_default> definition, the output would instead be: YourProgramName: Unknown message 'no_such_message' received\n =head2 Practical Uses If you have a fairly large, multi-module program, you may want to cent +ralize many of your messages in a single module somewhere. For example: package MyMessages; @ISA = qw(Exporter); @EXPORT = qw($error_messages $status_messages); use vars qw($error_messages $status_messages); use MessageLibrary; use strict; { my $verbose = 1; $error_messages = MessageLibrary->new( file_open => sub {return qq{file open failed on $_[0]: $!}}, _default => sub {return "unknown error $_[0] reported"}, ); $status_messages = MessageLibrary->new( starting_parser => ($verbose ? "Starting parser\n" : ""), starting_generator => ($verbose ? sub {"Starting generator $_[0] +\n"} : ""), ); $status_messages->set_prefix(); $status_messages->set_suffix(); 1; } Then your other modules can simply C<use MyMessages> and do things lik +e: print $status_messages->starting_parser; print $status_messages->starting_generator('alpha'); print $status_messages->starting_generator('omega'); print $error_messages->unexpected_end_of_file; Since all your messages are located in one module, it's a simple task +to change their wording -- or even language, though this package is not really intended as a substitute for somethi +ng like Locale::Maketext -- control their level of verbosity, and so on. Note that the methods generated are unique to each MessageLibrary obje +ct, so that given the definitions above, this statement: print $status_messages->file_open('my_file'); would end up calling the C<_default> message generator for the C<$stat +us_messages> object. (C<file_open> was defined only in the constructor for C<$error_messages>, so no C<file_open> met +hod exists for C<$status_messages>.) In effect, the method-call syntax is merely syntactic sugar for a hypothetical method call like t +his: print $status_messages->generate_message('file_open','my_file'); # + not for real On a separate note, if you wish to subclass MessageLibrary, you can ov +erride the default (empty) C<_init> function that the constructor calls and perform further initialization tasks there. =head2 Performance Considerations Not surprisingly, encapsulating your message generation within an obje +ct -- and, sometimes, an anonymous subroutine -- exacts a performance penalty. I've found in small-scale +experiments that the method call and anonymous-subroutine execution is roughly an order of magnitude slower than using literal strings and + Perl's native interpolation. But it's still I<pretty> fast in most cases, and the reduced speed may be an acceptab +le tradeoff for improved maintainability, particularly when it comes to things like error messages that are (we +hope!) generated only infrequently. =head2 Potential Enhancements There's currently no way to modify or add messages once you've constru +cted the object, nor a clone/copy method, but I haven't yet found a reason to implement either capability. =cut ######################################## CODE STARTS HERE ############ +############################ use vars qw($AUTOLOAD); use strict; use warnings; use Carp; =head1 PUBLIC METHODS =over 4 =item MessageLibrary->new(\%keyword_message_hash); Construct a new MessageLibrary object. The (key,value) pairs in C<%key +word_message_hash> are used to define the methods that the object will expose and the messages that will be +generated when those methods are called. The keys should be names that would pass muster as Perl subroutine nam +es, because you'll likely be calling them using the OO arrow syntax: $message_library->method_name; The values (messages) may be either literal strings or blocks of code +to be interpreted each time the method is invoked. Parameters passed to the method are accessible to the code + block in C<@_> as if it were a normal subroutine. For example: $status_message = MessageLibrary->new( {general => sub{"You said: $_[0], $_[1], $_[2]."}}; ); print $status_message->general('zero', 'one', 'two'); results in: You said: zero, one, two. The key C<_default> has a special significance: It defines a message t +hat is used if an unknown method is called. In this case, C<$_[0]> contains the name of the unknown method, and th +e rest of C<@_> contains the parameters. The object will provide standard C<_default> behavior if no such key i +s explicitly provided. =cut sub new { my ($class, @args) = @_; my $self = {}; bless $self, $class; $self->_init(@args); # do the real work return $self; } =item $messages->set_prefix($new_prefix) Set the prefix that is prepended onto any message returned by this obj +ect. By default, the prefix contains the name of the current executable (with path stripped out if you're r +unning under Windows and *nix OSs). Omitting C<$new_prefix> is equivalent to specifying a null string. =cut sub set_prefix { croak "set_prefix expects a single optional param" unless @_ <= 2; my ($self, $prefix) = @_; $prefix = '' unless defined($prefix); $self->{prefix} = $prefix; return 1; } =item $messages->set_suffix($new_suffix) Set the suffix that is appended onto any message returned by this obje +ct. By default, the suffix is a newline. Omitting C<$new_suffix> is equivalent to specifying a null string. =cut sub set_suffix { croak "set_suffix expects a single optional param" unless @_ <= 2; my ($self, $suffix) = @_; $suffix = '' unless defined($suffix); $self->{suffix} = $suffix; return 1; } =item $messages->get_prefix Return the currently defined prefix. =cut sub get_prefix { croak "get_prefix expects no params" unless @_ == 1; return $_[0]->{prefix}; } =item $messages->get_suffix Return the currently defined suffix. =cut sub get_suffix { croak "get_suffix expects no params" unless @_ == 1; return $_[0]->{suffix}; } =back =head1 PRIVATE METHODS (AND VARIABLES) =over 4 =item AUTOLOAD The AUTOLOAD method is called whenever a MessageLibrary object receive +s a method call to generate a message. It does not cache methods in the symbol table for future access, because +methods are unique to I<individual> MessageLibrary objects. (Remember that we're using method calls merely + as syntactic sugar to make the calling code more readable.) =cut sub AUTOLOAD { my $self = shift; $AUTOLOAD =~ /.*::(\w+)/; my $message_name = $1; # + get name of method return if $message_name eq 'DESTROY'; # + ignore destructor call my $message_generator = $self->{messages}->{$message_name}; # + look up generator for this method if (!defined($message_generator)) { # + doesn't exist? $message_generator = $self->{messages}->{_default}; # + use _default generator @_ = ($message_name, @_); # + push method name back onto params list } my $prefix = $self->get_prefix(); my $suffix = $self->get_suffix(); if (ref $message_generator eq 'CODE') { return $prefix . (&$message_generator) . $suffix; # + evaluate code... } else { return $prefix . $message_generator . $suffix; # + ...or just use static message text } } =item $messages->_init(@_) C<_init> does the actual initialization. =cut sub _init { my ($self, @params) = @_; my %message_hash = defined $_[1] ? %{$_[1]} : (); # dereference +hash or provide empty hash my %messages = ( _default => sub {return "message " . $_[0] . "(" . (join ",", @_[1 +..$#_]) . ")"}, # default value for _default %message_hash ); $self->{messages} = \%messages; my $prefix = $0; # get name of +executable if ($^O eq 'MSWin32') { # Windows? $0 =~ m{(\\|\A)([^\\]*)$}; # get stuff +after last backslash $prefix = $2; } elsif ($^O ne 'Mac' && $^O ne 'VMS' && $^O ne 'OS2') { # i.e., it' +s *nix $0 =~ m{(/|\A)([^/]*)$}; # get stuff a +fter last forward slash $prefix = $2; } $self->set_prefix("$prefix: "); $self->set_suffix("\n"); } =item Internal Data Structure A MessageLibrary is a blessed hash containing the following keys: =over 4 =item messages A reference to the hash containing message keywords and message text/c +ode that was passed into the constructor. =item prefix The current prefix, set with C<set_prefix>. =item suffix The current suffix, set with C<set_suffix>. =back =back =cut 1; =head1 REVISION HISTORY =over 4 =item Version 0.12 (2002-01-06) First public beta. Changed constructor to expect hash to be passed by +reference. Split C<_init> out from C<new>. =item Version 0.11 (2002-01-05) Removed method caching (which caused conflicts when instantiating mult +iple objects), rationalized code, completed POD. =item Version 0.10 (2001-12-17) Initial implementation. =back =head1 AUTHOR John Clyman ( =head1 COPYRIGHT AND LICENSE Copyright (c) 2002 John Clyman. Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Soft +ware"), to deal in the Software without restriction, including withou +t limitation the rights to use, copy, modify, merge, publish, distrib +ute, sublicense, and/or sell copies of the Software, and to permit pe +rsons to whom the Software is furnished to do so, subject to the foll +owing conditions: The above copyright notice and this permission notice shall be include +d in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRES +S OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANT +ABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT O +R OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =cut

In reply to MessageLibrary by seattlejohn

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    and the shadows deepen...

    How do I use this? | Other CB clients
    Other Users?
    Others romping around the Monastery: (5)
    As of 2017-04-27 09:53 GMT
    Find Nodes?
      Voting Booth?
      I'm a fool:

      Results (502 votes). Check out past polls.