Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??
package MessageLibrary; $VERSION = "0.12"; # Copyright (C) 2002 John Clyman (module-support@clyman.com) # 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 (module-support@clyman.com) =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

Title:
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!
  • 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?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others wandering the Monastery: (20)
    As of 2015-07-02 12:44 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









      Results (36 votes), past polls