Hello.

My Name is Rani Pinchuk,
My Email address: [EMAIL PROTECTED]
My preferred user-ID on CPAN: RANI

Attached 5 pod files of 5 classes. Here in EM-TECH, Belgium, we use
those classes already for more then a year. It seems that they are
quite stable and extremely useful.

Now EM-TECH decided to publish those classes on CPAN (under the same 
terms as Perl itself).

I'm not sure yet about the name of those classes although I thought of
the following for the two first classes:

Class::PhraseBook
Class::PhraseBook::SQL

The PhraseBook classes implements the Phrasebook design pattern (see
http://jerry.cs.uiuc.edu/~plop/plop2k/proceedings/proceedings.html).
Because I found in Class other patterns (like Class::Singleton,
Class::Visitor or Class::Template), I though this is the best place.

It might be that we should write it as Phrasebook instead of PhraseBook -
please your comments.

About LockedFile - There are many flock classes on CPAN. So what so
special about this one? The answer is simple: It gives us interface that
is very similar to IO::File class. I could not find any class that
provides that. This is why I think this class should be:
IO::LockedFile

About Log and NullLog - Here it is not simple again: I'm sure these
classes have added values over the many Log classes on CPAN. 
But there are many other Log classes in CPAN. The uniqueness of the
Log class I try to submit is that it is very simple to use it (has 
only 6 method, although most of the time only 2 will be used), but 
gives very nice results, including logging from certain level, tracing 
the calls, and defining the look of the log messages using a template. 

Moreover, the NullLog class implements the Null Object pattern and let
us use NullLog object instead of Log object in cases where Log object 
should be used, but we don't want to have a log (for example if you don't 
want to generate log file from the PhraseBook class above, yet the call 
for Log is hard-coded there, we can use the NullLog instead, the code 
will
not break but no log will be written).

If I try to put those features to the other logging classes in CPAN, the 
whole interface of those classes will be totally different. And this is 
the reason I think they deserve to live their own life. 

What is the best name for Log and NullLog? Class::Log and Class::NullLog?
Log::XXX and Log::NullXXX ? I really cannot come with any idea right now.
Please your suggestions.

Thanks in advance,

Rani

-- 
Rani Pinchuk, E-mail: [EMAIL PROTECTED]
EM-TECH Group, Ockham, Olivetenvest 52, Mechelen,  B-2800
Tel: +32-15-28-18-20, Fax: +32-15-28-18-21

=head1 NAME

LockedFile Class - supply object methods for locking files 

=head1 SYNOPSIS

  use LockedFile;
              
  # create new locked file object. $file will hold a file handle.
  # if the file is already locked, the method will not return until the
  # file is unlocked (blocking method).
  my $file = new LockedFile(">locked1.txt");

  # when we close the file - it become unlocked.
  $file->close();

  # create new locked file object. if the file is already locked, return 
  # immediately with undef.
  $file = new LockedFile(">locked1.txt", 1);

  # if we delete the object, the file is automatically unlocked and 
  # closed.
  $file = undef;

=head1 DESCRIPTION

The LockedFile class gives us the same interface of the IO::File class 
to files with the unique difference that those files are locked using
the flock mechanism.   

If during the running of the process, the process hang up - the file will 
be automatically unlocked. Actually - if the LockedFile object goes
out of scope, the file is automatically closed and unlocked.

=head1 CONSTRUCTOR

=over 4

=item new ( FILENAME [,NO_BLOCKING_FLAG ] )

Creates a C<LockedFile>.  If it receives any parameters, they are passed to
the method C<open>; if the open fails, the object is destroyed.  Otherwise,
it is returned to the caller.

=back

=head1 METHODS

=over 4

=item open( FILENAME [,NO_BLOCKING_FLAG ] )

The file FILENAME will be opened as a locked file, and the object will be 
the file handle of that opened file. If the file that is opened is locked 
and the NO_BLOCKING_FLAG is flase, the method will not return until the 
file is unlocked. If NO_BLOCKING_FLAG is set to true, the method will return 
undef if the file is locked. 
FILENAME can be any string the the method C<open> of  C<IO::File> accepts 
(like ">file.txt" for example).

=item close()

The file will be closed and unlocked. The method does not return anything. 

=item is_locked( FILENAME )

Will return true if the file is locked. Will return false otherwise.


=head1 AUTHOR

Rani Pinchuk, EM-TECH, [EMAIL PROTECTED]

=head1 SEE ALSO

L<IO::File(3)>

=cut

=head1 NAME

Log - The C<Log> class helps us create simple logs for our application.   

=head1 SYNOPSIS

  use Log;
  my $LOG_DIRECTORY = "/where/ever/our/log/file/should/be"; 
  my $ERROR_LOG_LEVEL = 6; 
               
  # create new Log object
  my $log = new Log($LOG_DIRECTORY."/error.log", $ERROR_LOG_LEVEL);

  ...

  # we had an error
  $log->write("Could not open the file ".$file_name.": $!", 4);

=head1 DESCRIPTION

In order to have a log we have first to create a C<Log> object. The c<Log>
object is created with a logging level. The default logging level is 5. 
After the C<Log> object is created, each call to the C<write> method may 
write a new line in the log file. If the level of the message is lower or 
equal to the logging level, the message will be written to the log file.
The format of the logging messages can be controled by changing the template,
and by defining a default message.
The Log file uses the LockedFile class and the Devel::DumpStack module.

=head1 CONSTRUCTOR

=over 4

=item new ( FILEPATH [,LEVEL [,DEFAULT_MESSAGE ]] )

The constructor. FILEPATH is the path of the log file. LEVEL is the defined
logging level - the LEVEL data member. DEFAULT_MESSAGE will define the 
DEFAULT_MESSAGE data member - a message that will be added to the message 
of each entry in the log (according to the TEMPLATE data member, see below).

The levels can be any levels that the user chooses to use. There are, 
though, recommended levels:
      0  the application is unusable
      1  the application is going to be unusable
      2  critical conditions
      3  error conditions 
      4  warning conditions
      5  normal but significant condition
      6  informational
      7+ debug-level messages

The default value of LEVEL is 5.
The default value of DEFAULT_MESSAGE is "".
Returns the new object. 

=back

=head1 METHODS

=over 4

=item write( MESSAGE [, LEVEL ] ) 

If LEVEL is less or equal to the LEVEL data member, or if LEVEL is undefined, 
the string in MESSAGE will be written to the log file.
Does not return anything. 

=item level( [ LEVEL ] ) 

Access method to the LEVEL data member. If LEVEL is defined, the LEVEL data 
member will get its value. 
Returns the value of the LEVEL data member. 

=item default_message( [ MESSAGE ] ) 

Access method to the DEFAULT_MESSAGE data member. If MESSAGE is defined, the
DEFAULT_MESSAGE data member will get its value. 
Returns the value of the DEFAULT_MESSAGE data member. 

=item log_line_numbers( [ BOOLEAN ] )

If this flag is set to true, the <called_by> string will hold the file 
that calls the subroutine and the line where the call is issued. The default
value is zero. 

=item template( [ TEMPLATE ] ) 

Access method to the TEMPLATE data member. The TEMPLATE data member is a string
that defines how the log entries will look like. The default TEMPLATE is:

'[<date/>] <<level/>> <called_by/><default_message/><message/>'

We use here several tags in the XML syntax of single tags:

      <date/>           will be replaced by a string that represent 
                        the date. For example: 09/01/2000 17:00:13
      <level/>          will be replaced by the level of the entry.
      <called_by>       will be replaced by a call trace string. For 
                        example:
                        CGIDaemon::listen > MyCGIDaemon::accepted 
      <default_message> will be replaced by the value of the 
                        DEFAULT_MESSAGE data member.
      <message>         will be replaced by the message string that 
                        is sent to the C<write> method.

Returns the value of the TEMPLATE data member. 

=head1 AUTHOR

Rani Pinchuk, EM-TECH, [EMAIL PROTECTED]

=head1 SEE ALSO

L<Devel::DumpStack(3)>,
L<LockedFile(3)>

=cut
=head1 NAME

NullLog - The C<NullLog> class implements the Null Object pattern for the C<Log> class.

=head1 SYNOPSIS

  use Log;
               
  # create new NullLog object
  my $log = new NullLog();

  ...

  # we had an error (this entry will not be written to the log 
  # file because we use NullLog object).
  $log->write("Could not open the file ".$file_name.": $!", 4);

=head1 DESCRIPTION

The C<NullLog> class is derived from the C<Log> class and implement the 
Null object pattern to let us to use the C<Log> class with B<null> C<Log>
objects.
We might want to do that if we use a C<Log> object in our code, and we do 
not want always to actually define a C<Log> object (i.e. not always
we want to write to a log file). In such a case we will create a C<NullLog>
object instead of the C<Log> object, and will use that object instead.
The object has all the methods that the C<Log> object has, but those methods
do nothing. Thus our code will continue to run without any change, yet
we will not have to define a log file path for the C<Log> object, and no log
will be created.

=head1 CONSTRUCTOR

=over 4

=item new ( FILEPATH [,LEVEL [,DEFAULT_MESSAGE ]] )

The constructor. The parameters will not have any affect.
Returns the new NullLog object. 


=back

=head1 METHODS

=over 4

=item write( MESSAGE [, LEVEL ] ) 

Does nothing. The parameters will not have any affect.
Returns nothing. 

=item level( [ LEVEL ] ) 

Does nothing. The parameters will not have any affect.
Returns -1. 

=item default_message( [ MESSAGE ] ) 

Does nothing. The parameters will not have any affect.
Returns empty string (""). 

=head1 AUTHOR

Rani Pinchuk, EM-TECH, [EMAIL PROTECTED]

=head1 SEE ALSO

L<Log(3)>

=cut

=head1 NAME

PhraseBook - Implements the PhraseBook pattern

=head1 SYNOPSIS

  use PhraseBook;
  my $pb = new PhraseBook($log, "test.xml");
  $pb->load("NL"); # using Dutch as the language
  $phrase = $pb->get("ADDRESS", 
                     { street => "Chaim Levanon",
                       number => 88,
                       city   => "Tel Aviv" } );

=head1 DESCRIPTION

This class implements the PhraseBook pattern. It lets us create dictionaries 
of phrases. Each phrase can be accessed by a unique key. Each phrase may have
placeholders. Group of phrases are kept in a dictionary. The first dictionary 
is the default one - which means that it will always be read. One of the 
dictionaries might be used to override the default one. The phrases are kept
in an XML document.
  
The XML document type definition is as followed:

 <?xml version="1.0"?>
 <!DOCTYPE phrasebook [
               <!ELEMENT phrasebook (dictionary)*>              
               <!ELEMENT dictionary (phrase)*>
               <!ATTLIST dictionary name CDATA #REQUIRED>
               <!ELEMENT phrase (#PCDATA)>
               <!ATTLIST phrase name CDATA #REQUIRED>
 ]>

Example for XML file:

 <?xml version="1.0"?>
 <!DOCTYPE phrasebook [
               <!ELEMENT phrasebook (dictionary)*>              
               <!ELEMENT dictionary (phrase)*>
               <!ATTLIST dictionary name CDATA #REQUIRED>
               <!ELEMENT phrase (#PCDATA)>
               <!ATTLIST phrase name CDATA #REQUIRED>
 ]>
 <phrasebook>
 <dictionary name="EN">

 <phrase name="HELLO_WORLD">
            Hello World!!!
 </phrase>

 <phrase name="THE_HOUR">
            The time now is $hour. 
 </phrase>

 <phrase name="ADDITION">
            add $a and $b and you get $c
 </phrase>


 <!-- my name is the same in English Dutch and French. -->
 <phrase name="THE_AUTHOR">
            Rani Pinchuk
 </phrase>
 </dictionary>

 <dictionary name="FR">
 <phrase name="HELLO_WORLD">
            Bonjour le Monde!!!
 </phrase>

 <phrase name="THE_HOUR">
            Il est maintenant $hour. 
 </phrase>

 <phrase name="ADDITION">
            $a + $b = $c
 </phrase>

 </dictionary>

 <dictionary name="NL">
 <phrase name="HELLO_WORLD">
            Hallo Werld!!!
 </phrase>

 <phrase name="THE_HOUR">
            Het is nu $hour. 
 </phrase>

 <phrase name="ADDITION">
            $a + $b = $c
 </phrase>

 </dictionary>

 </phrasebook>

Each phrase should have a unique name. Within the phrase text we can 
place placeholders. When get method is called, those placeholders will be 
replaced by their value.

=head1 CONSTRUCTOR

=over 4

=item new ( [ LOG ], FILEPATH )

The constructor. FILEPATH can be the absolute path of the XML file. But it
can be also just a name of the file, or relative path to the file. In that
case, that file will be searched in the following places: 
   * The current directory.
   * The directory ./lib in the current directory.
   * The directory ../lib in the current directory.
   * The directories that are in @INC.

LOG is a Log object. If LOG is undef, NullLog object will be used. 
If it is provided, the class will use the Log facilities to log unusual events.
Returns the new object, or undef on failure.

=back

=head1 METHODS

=over 4

=item load( DICTIONARY_NAME )

Will load the phrases of certain dictionary from the file. If the dictionary
that is requested is not the first one (in the XML file), the first dictionary
will be loaded first, and then the requested dictionary phrases will be loaded.
That way, the first dictionary play the role of the default dictionary.

The DICTIONARY_NAME data member will be set to the parameter that is sent 
to this method. Yet, if nothing is sent to the method, the method will use 
the value of the DICTIONARY_NAME data member to load the right dictionary.
If the data member is not defined as well, the default dictionary will be 
loaded.
 
Returns 1 on success, 0 on failure.

=item get(KEY [, REFERENCE_TO_ANONYMOUS_HASH ])
Will return the phrase that fits to the KEY. If a reference to 
anonymous has is sent, it will be used to define the parameters in the 
phrase.

=item dictionary_name( DICTIONARY_NAME ) 

Access method to the DICTIONARY_NAME data member. See I<load> method above.

=item remove_new_lines ( BOOLEAN )

Access method to the data member REMOVE_NEW_LINES flag. If this data member 
is true (1), then new lines will be removed from the phrase that a is 
returned by the method I<get>.
Returns the value of the data member REMOVE_NEW_LINES flag.

=back

=head1 ACCESS METHODS

=over 4

=item get_xml_path ( FILE )

Will return the path of the xml file with that name. It will look for this
file in the current directory, in ./lib ../lib and in all the directories 
in @INC.
If it is not found, NULL will be returned.

=item file_path( FILEPATH ) 

Access method to the FILE_PATH data member. FILEPATH can be the absolute 
path of the XML file. But it can be also just a name of the file, or 
relative path to the file. In that case, that file will be searched in 
the following places: 
   * The current directory.
   * The directory ./lib below the current directory.
   * The directory ../lib below the current directory.
   * The directories that are in @INC.

=item log( LOG ) 

Access method to the LOG data member. 

=back

=head1 ENVIRONMENTS

=over 4

=item PHRASEBOOK_DEBUG_PRINTS

If this environment is set to "COLOR", the get method will print the 
phrases it gets, with some extra information in color screen output 
using ANSI escape sequences. If the environment is set to "HTML", the 
information will be printed in HTML format. If the environment is set to 
"TEXT" - the information will be printed as simple text. If the environment 
is not set, or empty - nothing will be printed. This feature comes to help
debugging the phrases that we get from the object of this class.

=back

=head1 AUTHOR

Rani Pinchuk, EM-TECH, [EMAIL PROTECTED]

=head1 SEE ALSO

L<XML::Parser(3)>,
L<Log(3)>,
L<NullLog(3)>

=cut

=head1 NAME

PhraseBook::SQL - Implements the PhraseBook pattern for SQL statements.

=head1 SYNOPSIS

  use PhraseBook::SQL;
  my $sql = new PhraseBook::SQL($log, "test.xml");
  $sql->load("Pg");
  $statement = $sql->get("INSERT_INTO_CONFIG_ROW", 
                       { id => 88,
                         parent => 77,
                         level => 5 });

=head1 DESCRIPTION

This class inherit from PhraseBook and let us manage all the SQL code we 
have in a project, in one file. The is done by placing all the SQL statements 
as phrases in the XML file of the PhraseBook. See I<PhraseBook> for details 
about that file format.

=head1 METHODS

=over 4

=item get(KEY [, REFERENCE_TO_ANONYMOUS_HASH ])

Will return the SQL statement that fits to the KEY. If a reference to 
anonymous has is sent, it will be used to define the parameters in the 
SQL statement.

For example, if the following statement is defined in the XML file:
   <statement name="INSERT_INTO_CONFIG_ROW">
               insert into t_config (id, parent_id, level)
                      values($id, $parent, $level)
   </statement>
We usually will call get method to get this statement in the following way:
   $statement = $sql->get("INSERT_INTO_CONFIG_ROW", 
                          { id => 88,
                            parent => 77,
                            level => 5 });

Special case are the SQL update instructions. Most of the time, when we 
call update, we would like to update only part of the columns in a row.
Yet, we usually prefer to avoid from writing all the possible update 
combinations. For example if we have the following update call:

   update t_account set
                         login = '$login',
                         description = '$description', 
                         dates_id = $dates_id, 
                         groups = $groups,
                         owners = $owners
                                     where id = $id

We do not want to write special update for each case like:

   update t_account set
                         owners = $owners
                                     where id = $id

or 

   update t_account set
                         login = '$login',
                         owners = $owners
                                     where id = $id

In order to solve this, the get method will delete the "set" lines of 
the update method where the were the parameter value is udefined.
Because of that we should write the update statements were the pairs of 
<column name> = <parameter> are in separate lines from the rest of the 
statement. Note that the get method will also fix comma problems between 
the pairs (so if the last pair is deleted we will not have extra comma).
The method returns the SQL statement, or undef if there is no SQL statement 
for the sent KEY.

=item escaped_quote ( STRING )

An access method to the data memeber ESCAPED_QUOTE. The default way to escape
a quote is to have two quotes (''). This will work on Postgres and on MSQL. 
Yet, if this default is not working with your database of choice, you can 
change it by seting the ESCAPE_QUOTE data member using this method. 

=item use_is_null( BOOLEAN ) 

Sometimes, when we have an argument in SQL statement, we will want to change 
the equal sign to 'is'. For example:
 
                   select * from my_table where my_id = $id

If $id is NULL, we sometimes want to have 'my_id is NULL'.
We can have that by sending to this method 1. This will promis that where 
ever we have the pattern '= NULL' it will become 'is NULL'. The default
is not to use the 'is' (thus 0).

=item save_statements_file_path ( [ FILE_PATH ] )

Access method to the SAVE_STATEMENTS_FILE_PATH data member. If this data
member is set, for each call to the I<get> method, the statement that 
is returned also will be appended to that file. This might be useful while
debugging big projects - it will let the user have a full log of all the 
statemnets that were generated by the I<get> method.

=back

=head1 ENVIRONMENTS

=over 4

=item PHRASEBOOK_SQL_DEBUG_PRINTS

If this environment is set to "COLOR", the get method will print the 
statements it gets, with some extra information in color screen output 
using ANSI escape sequences. If the environment is set to "HTML", the 
information will be printed in HTML format. If the environment is set to 
"TEXT" - the information will be printed as simple text. If the environment 
is not set, or empty - nothing will be printed. This feature comes to help
debugging the SQL statements that we get from the object of this class.

=item PHRASEBOOK_SQL_SAVE_STATEMENTS_FILE_PATH

Another way to set the SAVE_STATEMENTS_FILE_PATH data member is by setting 
this environment variable.

=back

=head1 AUTHOR

Rani Pinchuk, EM-TECH, [EMAIL PROTECTED]

=head1 SEE ALSO

L<XML::Parser(3)>,
L<PhraseBook(3)>,
L<Log(3)>,
L<NullLog(3)>

=cut

Reply via email to