Subject: ANNOUNCE - Resources-1.00b available
From: [email-masked] (Francesco Callari)
Date: 20 May 1996 22:25:31 GMT
Newsgroups: comp.lang.perl.announce,comp.lang.perl.misc

Hello,

following my second RFC of may 9, and the favourable feedback received
(thanks, among the others,  to Tim Bunce, John C. Randolph, B. K. Oxley)
I am distributing the 1.00beta version of my Resources.pm package.

I uploaded it to ftp.cis.ufla.edu, and wrote to A. Koenig to register it.
It is immediately available at

	ftp://ftp.cim.mcgill.ca/pub/people/franco/Resources-1.00b.tar.gz

The package, for those who missed my RFCs, helps managing the definition of
configuration paramenters (a.k.a. resources) for applications through objects
trees. It offers an uniform way for defining class defaults, inheriting them
through subclassing, overriding them in subclasses, searching, loading from
files, wildcarding, etc etc, including dynamical viewing and editing.  A copy
of the package documentation (revised since the last RFC is attached).

>From an external point of view, it look prety much like the XWindows
default database idea. However, the idea of embedding parameter documentaion
with the parameters themselves is a new and, I think, valuable addition.

Please report your comments/bug reports to [email-masked].

Enjoy
Franco

===8<=======8<=======8<=======8<=======8<=======8<=======8<=======8<====

NAME
    Resources - handling application defaults in Perl.

SYNOPSIS
        use Resources;

        $res = new Resources;
        $res = new Resources "resfile";

DESCRIPTION
    Resources are a way to specify information of interest to
    program or packages.

    Applications use resource files to specify and document the
    values of quantities or attributes of interest.

    Resources can be loaded from or saved to resource files. Methods
    are provided to search, modify and create resources.

    Packages use resources to hardwire in their code the default
    values for their attributes, along with documentation for the
    attibutes themselves.

    Packages inherit resources when subclassed, and the resource
    names are updated dynamically to reflect a class hierarchy.

    Methods are provided for interactive resource inspection and
    editing.

  1. Resource Files.

    A resource specification in a (text) resource file is a line of
    the form:

            name: value

    There may be any number of whitespaces between the name and the
    colon character, and between the colon and the value.

    name    A word not containing whitespaces, colons (':'), dots ('.')
            or asterisks ('*'), nor starting with '_'.

            A sequence in one the following forms:

                    word.name   
                    word*name   
                    *name

            No distinction is made between uppercase and lowercase
            letters.

            The asterisks in a resource name act as wildcards,
            matching any sequence of characters.

    value   An unadorned word or a quoted sequence of whitespace-
            separated words. Both single (' ') and double quotes
            quotes (" ") are allowed, and they must be paired.

            Any *constant* scalar constructor in Perl, including
            anon references to constant arrays or hashes.

            The special words yes, true, no, false (case
            insensitive) are treated as boolean resources and
            converted 1 and 0, unless they are quoted.

    Examples of valid resource specifications:

         hotdog*pickles : plenty      # A word.
         hotdog.price   : 22          # Another word
         hotdog.recipe : 'Help me!'   # A quoted sentence
         hotdog*taste*sucks : yes     # A boolean, converted to 1.
         hotdog*smell*sucks : 'yes'   # yes, aken verbatim
         hotdog.size  : [1, [2, 3]]           # An anon array.
         hotdog.lett : {"P"=>1, "Q"=>[2, 3]}  # An anon hash.

    Examples of illegal resource names:

         hot dog    # Whitespace in the name.
         .hotdog    # Leading dot in name.
         hot._dog   # Leading underscore in _dog.
         hotdog*    # Trailing asterisk.
         hotdog.    # Trailing dot.

    A resource file may contain comments: anything from a hash ('#')
    character to the end of a line is ignored, unless the hash
    character appears inside a quoted value string.

  2. Resource hashes

    Resource hashes provide a way to specify the default values for
    the attributes of a package in its source code, along with
    documentation for the attributes themselves. The documentation
    itself is "dynamical" (as opposed to the static, pod-like
    variety) in that it follows a class hyerarchy and is suitable
    for interactive display and editing.

    A resource hash is just a hash of ($Name => $Value) or ($Name =>
    [$Value, $Doc]) things. Each hash key $Name is a resource name
    as above. Each hash value is either just the resource $Value, or
    a reference to an anon array [$Value, $Doc], with $Doc being the
    resource documentation.

    The resource $Name *cannot* contain wildcard ('*') or colon
    (':') characters, nor start or end with a dot ('.'). Also, it
    must *not* be prefixed with the package name (since this is
    automatically prepended by the merge method, see below). Names
    starting with an underscore ('_') character are special in that
    they define "hidden" resources. These may not be specified in
    resource files, nor dynamically viewed/edited: they come handy
    to specify global parameters when you dont want to use global
    application-wide variables, and/or want to take advantage of the
    inheritance mechanism.

    The resource $Value can be any *constant* scalar Perl
    constructor, including references to arrays and/or hashes of
    constants (or references thereof). Boolean values must be
    specified as 1 or 0.

    The resource documentation is a just string of any length: it
    will be appropriately broken into lines for visualization
    purposes. It can also be missing for resources defined in base
    classes, in which case the base class documentation is used (see
    the merge method below).

    The content of a resource hash is registered in a global
    Resource object using the merge method.

    Here is an example of deafults specification for a package.

            package HotDog;
            @ISA = qw( Food );

            %HotDogDefaults =  (
                taste       => ["Yuck!", "Tongue feeling"],
                meatcolor   => ["Corpse", "Freshness indicator"],
                stinks      => [1, "Does it smell?"],
                onions      => [ { on => 2, ions => [3, 5] }, "Rings" ],
                'food.good' => 0, # just a scalar, doc is in base class
                '_ghost'    => [0, "Hidden. Mr. Invisible"] 
            );

  3. Objects and resources

    The recommended way to use resources with Perl objects is to
    pass a Resource object to the "new" method of a package. The
    method itself will merge the passed resources with the package
    defaults, and the passed resource will override the defaults
    where needed.

    Resource inheritance via subclassing is then easily achieved,
    amounting to something like:

            package HotDog;
            @ISA = qw( Food );

            # Called as: $hotdog = new HotDog ($resources);
            sub new {
               my $package = shift;
               my $res = shift;

               $res = new Resources if (ref($res) !~ /Resources/o);
               $res->merge(\%HotDogDefaults); 

               my $hd = bless new Food ($res);
         
               # Whatever initialization code
               return $hd;
            }

  4. Methods in class Resources

  4.1. Creation and initialization

    new Resources ($resfile);
            Creates a new resource database, initialized with the
            defaults for class Resources (see below for a list of
            them).

            If a nonempty file name is specified in $resfile, it
            initializes the object with the content of the so named
            resource file. For safe (non overwriting) loading, see
            the load method below.

            If the special file name "_RES_NODEFAULTS" is specified,
            the object is created completely empty, with not even
            the Resources class defaults in it.

            Returns the new object, or undef in case of error.

    load($resfile, $mode, $nonew);
            Loads resources from a file named $resfile into a
            resource database.

            The loading is controlled by the $mode argument as
            follows.

            If $mode is omitted, or equal to "replace", the content
            of the file is loaded as it is, save for syntax checks.

            If $mode is equal to "merge", resource merging via merge
            is performed if the database is not empty. This
            effectively prevents loaded resources from overwriting
            those already defined in the database.

            The $nonew argument controls whether loading of non
            already defined resurces is allowed. If it is true, safe
            loading is performed: attempting to load non-wildcarded
            resource names that do not match those already present
            in the database causes an error. This can be useful if
            you want to make sure that only pre-defined resources
            (for which you presumably have hardwired defaults) are
            loaded. It can be a safety net against typos in a
            resource file.

            Use is made of Safe::reval to parse values specified
            through Perl constructors (only constants, anon hashes
            and anon arrays are allowed).

            Returns 1 if ok, 0 if error.

    merge(\%defaults);
            Merges the resources of $res with the content of the
            resource hash %defaults. Only non-wildcarded names are
            admissible in %defaults. Merging does *not* change the
            value of resources already defined in $res.

            Note that merging reflects subclassing in that the names
            of default resources of a base classe are prepended with
            the name of the caller package (lowercased and stripped
            of all foo::bar:: prefixes), followed by a dot. In the
            above example, the defaults of HotDog will be renamed,
            after merging as:

               hotdog.taste, hotdog.meatcolor, ...

            and, if Bell::Taco is a new blessed HotDog, then they
            will be translated again upon merging, to read:

               taco.hotdog.taste, taco.hotdog.meatcolor, ...

            And so on all the way up to the main package, but with
            the application name ($0, a.k.a $PROGRAM_NAME in
            English) prepended in place of "main". See also the
            documentation of the "byclass" method below for some
            conditions under which this will not work properly.

            The new names are the ones to use when accessing these
            resources by name.

            Again, if a resource is already defined when merge is
            entered, than its values overrides the defaults. See
            also the EXAMPLES section.

            Returns 1 if OK, 0 if error.

  4.2. Looking up resources

    The values and documentation strings stored in a Resource object
    can be accessed by specifying their names in three basic ways:

    directly ("byname" methods)
            As in "my.nice.cosy.couch" .

    by a pattern ("bypattern" methods)
            As in "m??nice.*" .

    hierarchically ("byclass" methods)
            If class My is a Nice, and Nice is a Cosy, and if the
            current stack of method calls goes from package My to
            Cosy, then asking for "couch" in package Cosy gets you
            the value/doc of "my.nice.cosy.couch". This behaviour is
            essential to proper resource subclassing, as explained
            in detail below.

    The "bypattern" methods must search through the Resource object,
    while the "byclass" methods must work through call frames and
    ISA lists. Both ways tend to be slow in case of large resource
    databases, so the "byvalue" methods should always be used where
    appropriate. See the EXAMPLE section for more.

    It is also possible to retrieve the whole content of a resource
    database ("names" and "each" methods)

    Note that all the resource lookup methods return named (non
    "wildcarded") resources only. Wildcarded resources (i.e. those
    specified in resource files, and whose names contain one or more
    '*') are best thought as placeholders, to be used when the value
    of an actual named resource is set.

    For example, a line in a resource file like

              *background : yellow

    fixes to yellow the color of all resources whose name ends with
    "background". However, your actual packages will never worry
    about unless they really need a background. In this case they
    either have a "background" resource in their defaults hash, or
    subclass a package that has one.

    valbyname($name);
            
    Retrieves the value of a named resource from a Resource database. The $name
    argument is a string containing a resource name with no wildcards.
            The resource is first looked up with the program name
            ($0) automatically prepended to the passed name. If this
            fails, a second search is done using the passed name
            verbatim.

            Returns the undefined value if no such resource is
            defined.

    docbyname($name);
            Retrieves the documentation string of a named resource
            from a Resource database. The $name argument is a string
            containing a resource name with no wildcards.

            The resource is first looked up with the program name
            ($0) automatically prepended to the passed name. If this
            fails, a second search is done using the passed name
            verbatim.

            Returns the undefined value if no such resource is
            defined.

    bypattern($pattern);
            Retrieves the full names, values and documentation
            strings of all the named (non wildcarded) resources
            whose name matches the given $pattern. The pattern
            itself is string containing a Perl regular expression,
            *not* enclosed in slashes.

            Returns a new Resource object containing only the
            matching resources, or the undefined value if no matches
            are found.

    valbypattern($pattern);
            Retrieves the full names and values of all named (non
            wildcarded) resources whose name matches the given
            pattern.

            Returns a new Resource object containing only names and
            values of the matching resources (i.e. with undefined
            doc strings), or the undefined value if no matches are
            found.

    docbypattern($pattern);
            Retrieves the full names and documentation strings of
            all named (non wildcarded) resources whose name matches
            the given pattern.

            Returns a new Resource object containing only names and
            docs of the matching resources (i.e. with undefined
            resource values), or the undefined value if no matches
            are found.

    byclass($suffix);
            Resources are naturally ordered with respect to a
            hierarchy of classes, in that if two resource names
            share a common suffix, then the resource defined in the
            subclass overrides the one of a base class (unless the
            former is wildcarded in a resource file).

            For example, if class HotDog has a default for a
            resource named "hotdog.pickles", and class Taco (a
            subclass of HotDog) has a default for
            "taco.hotdog.pickles", then we expect that the latter
            should override the former in a Taco object.

            Note however that we do *not* want
            "mc.donalds.hotdog.pickles" to override
            "taco.hotdog.pickles", for a Mc class unrelated to Taco.
            This means that finding the right default in the
            database is not just a matter of counting dots, but of
            following a class hierarchy,

            This method returns a 3 element list containing the
            name/value/doc tuple of the the most subclassed resource
            from the current class, among those having a common
            suffix. In the above example, a call like

                    package HotDog 
                    ...
                    ($name,$value,$doc) = $res->byclass("pickles");

            will set $name, $value and $doc equal to those of the
            "taco.hotdog.pickles" resource, if this HotDog is
            subclassed by Taco, and to those of
            "mc.donalds.hotdog.pickles", if it is subclassed by Mc
            via Donalds instead.

            To do this, the algorithm walks upward in the current
            subroutine call stack, and looks up the ISA list of each
            caller package to determine whether the callers are
            subclasses or not. Since it depends on the current call
            stack, it will work only when calls are really nested,
            i.e. with inherited methods or explicitly nested calls
            (e.g. in the "new" method of subclasses).

            The passed name suffix must not contain wildcards, nor
            start with a dot.

            Be careful not to confuse the "byclass" with the
            "byname" and "bypattern" retrieval methods: they are
            used for two radically different goals. See the EXAMPLES
            section for more.

            Returns the empty list if no resources are found for the
            given suffix, or if the suffix is incorrect.

    namebyclass($suffix);
            As the byclass method above, but returns just the
            resource name (i.e. the suffix with all the subclasses
            prepended).

    valbyclass($suffix);
            As the byclass method above, but returns just the
            resource value.

    docbyclass($suffix);
            As the byclass method above, but returns just the
            resource documentation.

    each;   Returns the next name/[value,doc] pair of the named (non
            wildcarded) resources in a resource database, exactly as
            the each Perl routine.

    names;  Returns a list of the names of all named (non-wildcarded)
            resources in a resource database, or undef if the
            databasee is empty.

  4.3. Assigning and removing Resources

    put($name, $value, $doc);
            Writes the value and doc of a resource in the database.
            It is possible to specify an empty documentation string,
            but name and value must be defined.

            Wildcards ('*' characters) are allowed in the $name, but
            the $doc is ignored in this case (documentation is
            intended for single resources, not for sets of them).

            The value is written unchanged unless the resource
            database already contains a wildcarded resource whose
            name includes $name (foo*bar includes foo.bar,
            foo.baz.bar, etc.). In this case the value of the
            wildcarded resource overrides the passed $value.

            Returns 1 if ok, 0 if error.

    removebyname($name);
            Removes the named (non wildcarded) resources from the
            database.

            Returns 1 if OK, 0 if the resource is not found in the
            database.

    removebypattern($name);
            Removes from the database all resources (both named
            *and* wildcarded) whose name mathes $pattern. An exactly
            matching name must be specified for wildcarded resources
            (foo*bar to remove foo*bar).

            Returns the number of removed resources.

  4.6. Viewing and editing resources.

    view;   Outputs the current content of a Resource object by piping
            to a pager program.

            The environment variable $ENV{RESPAGER}, the resource
            "resources.pager" and the environment variable
            $ENV{PAGER} are looked up, in this very order, to find
            the pager program. Defaults to /bin/more if none of them
            is found.

            The output format is the same of a resource file, with
            the resource names alphabetically ordered, and the
            resource documentation strings written as comments.

            Returns 1 if ok, 0 if error.

    edit($nonew);
            Provides dynamical resource editing of a Resource object
            via an external editor program. Only resource names and
            values can be edited (anyway, what is the point of
            editing a resource comment on the fly?).

            The environment variables $ENV{RESEDITOR}, the resource
            "resouces.editor", the environment variables
            $ENV{VISUAL} and $ENV{EDITOR} are looked up, in this
            very order, to find the editor program. Defaults to
            /bin/vi if none is found.

            The editor buffer is initialized in the same format of a
            resource file, with the resource names alphabetically
            ordered, and the resource documentation strings written
            as comments. The temporary file specified by the
            "resources.tmpfil" resource is used to initialize the
            editor, or '/tmp/resedit<pid>' if that resource is
            undefined.

            When the editor is exited (after saving the buffer) the
            method attempts to reload the edited resources. If an
            error is found the initial object is left unchanged, a
            warning with the first offending line in the file is
            printed, and the method returns with undef. Controlled
            resource loading is obtained by specifying a true value
            for the $nonew argument (see load).

            If the loading is successful, a new (edited) resource
            object is returned, which can be assigned to the old one
            for replacement.

            After a successful edit, the value of the resource
            "resources.updates" (which is always defined to 0
            whenever a new resource is created) is increased by one.
            This is meant to notify program the and/or packages of
            the resource change, so they can proceed accordingly if
            they wish.

  4.5. Miscellaneous methods

    write($filename);
            Outputs all resources of a resource database into a
            resource file (overwriting it).

            The resource documentation strings are normally written
            as comments, so the file itself is immediately available
            for resource loading. However, if the boolean resource
            "resources.writepod" is true, then the (non wildcarded)
            resources are output in POD format for your
            documentational pleasure.

            As usual in Perl, the filename can allo be of the form
            "|command", in which case the output is piped into
            "comma1nd".

            For resources whose value is a reference to an anon
            array or hash, it produces the appropriate constant Perl
            contructor by reverse parsing. The parser itself is
            available as a separate method named _parse (see package
            source for documentation).

            Returns 1 if ok, 0 if error.

  5. Resources of Resources

            As you may have guessed at this point, the default
            configuration of this package itself is defined by
            resources. The resource class is, of course, "resources"
            (meaning that all the names have a leading "resource.").

            To prevent chaos, however, these resources cannot be
            subclassed. This should not be a problem in normal
            applications, since the Resource package itself is not
            meant to be subclassed, but to help building a hierarchy
            of classes instead.

            The currently recognized resources, and their default
            values, are:

    resources.appname : ''
            The application name of this Resource object.

    resources.editor : /bin/vi
            Resource editor.

    resources.pager : /bin/more
            Resource pager.

    resources.separator : ':'
            Pattern separating names from values in resource files.

    resources.tmpfil : ''
            Editor temporary file.

    resources.updates : 0
            Number of resource updates.

    resources.verbosity : 1
            True to print warnings.

    resources.viewcols : 78
            Width of view/edit window.

    resources.writepod : false
            Boolean. True if the write method should output in POD
            format.

EXAMPLES
    Here is a more complex example of hyerarchical inheritance of
    resources. Package HotDog is subclassed from Junk and Food. The
    subclass has defaults for two resources defined by the base
    classes ("food.edible" and "junk.germs"), and their values will
    override the base class defaults.

    Remember that after merging all resources names are prefixed
    with the current class name.

       use Resources;
       package Food;
       %FoodDefaults = ( edible => "Sure" );
       
       sub new {
          my ($pack, $res) = @_;
          $res = new Resources unless $resources;
          $res->merge(\%FoodDefaults) || die ("can't merge defaults");
           
          my $food= bless {};
          $food->{Edible} = $res->valbyclass("edible");
          # Use valbyclass so a subclass like HotDog can change this by its
          # defaults.   
       }
     
       # A Food method to say if it can be eaten.
       sub eat { 
          my $food=shift; 
          return $food->{Edible}; 
       }

       package HotDog;
       use Junk;
       use Food;
       use Carp;
       @ISA = qw( Junk Food );

       %HotDogDefaults = 
          (
           taste         => "Yuck!",
           meatcolor     => "Cadaveric",
           stinks        => 1,
           'food.edible' => "Perhaps", # Quotes needed in these 
           'junk.germs'  => "Amoeba"   #    names because of the dots
          );

       sub new {
          my ($package, $res) = @_;
          
          $res = new Resources unless $resources;
          $res->merge(\%HotDogDefaults) || die ("can't merge defaults");
                   
          my $food   = new Food ($res); # Override Food's edible.
          my $junk   = new Junk ($res); # Override Junks's germs.
          my $hd = bless { %{food}, %{junk} }; # Merge and subclass.

          $hd->{Meatcolor} = $res->valbyclass("meatcolor");
          # Same reason as above

          return $hd;
       }

       # A HotDog method to get a taste via resources
       sub taste { 
          my ($hd, $res) = @_; 
          print $res->valbyname("hotdog.taste");
       }

       package main;
       # Whatever
       #
       $res = new Resources("AppDefltFile") || die;
       $snack = new HotDog($res);  
       $gnam = $snack->eat();  # hotdog.food.edible overridees food.edible, 
                               #   so here $gnam equals "Perhaps"

       $slurp = $snack->taste($res) # $slurp equals "Yuck!", unless 
                                    # this resource too was overridden 
                                    # by a subclass of HotDog , or
                                    # differently specified in 
                                    # "AppDefltFile"

    Note the different use of valbyclass and valbyname to access
    resources:

    valbyclass
    Is used for the resources like "edible" in Food, i.e. resources
    which are defined by the current class in its defaults. This
    because a subclass of it (e.g. HotDog) might have overridden
    them (by having an entry for "food.edible" in its defaults.

    Generally speaking, you should not use valbyclass outside the
    new (creation and initialization) method of a class.

    valbyname
    Is used after the initialization, when all subclassed resources
    have been resolved nicely. In any case, its arguments is the
    complete resource name, with all its dots.

SEE ALSO
    Safe(3).

BUGS
    The underlying idea is to use a centralized resource database
    for the whole application. This ensures uniformity of behaviour
    across kin objects, but allow special characterizations only at
    the cost of subclassing.

AUTHOR
            Francesco Callari <[email-masked]> 
            Artifical Perception Laboratory,
            Center for Intelligent Machines, 
            McGill University.

            WWW: http://www.cim.mcgill.ca/~franco/Home.html

COPYRIGHT
    Copyright 1996 Francesco Callari, McGill University

    Permission to use, copy, modify, and distribute this software
    and its documentation for any purpose without fee is hereby
    granted without fee, provided that the above copyright notice
    appear in all copies and that both that copyright notice and
    this permission notice appear in supporting documentation, and
    that the name of McGill not be used in advertising or publicity
    pertaining to distribution of the software without specific,
    written prior permission. McGill makes no representations about
    the suitability of this software for any purpose. It is provided
    "as is" without express or implied warranty.

    MCGILL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
    INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
    IN NO EVENT SHALL MCGILL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
    CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
    NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
    CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

--
----------------------------------------------------------------------------
Franco Callari                          
Centre for Intelligent Machines          "Beati coloro che hanno fame e sete
McGill University, 3480 University        di giustizia, perche` saranno
Montreal, Quebec, Canada, H3A 2A7         giustiziati."      (P. Bellocchio)
                                    
email: [email-masked] -- phone: +(514) 398 2185 -- fax: +(514) 398 7348
WWW: <a href="http://www.cim.mcgill.ca/~franco/Home.html> Franco </a>
-----------------------------------------------------------------------------