Revision history for Perl extension SPOPS.

0.50  Tue Aug 28 16:59:39 EDT 2001

Overall:

     - You can now use LDAP to store your objects! Also included is
     SPOPS::LDAP::MultiDatastore, which lets you specify multiple
     directories to query for objects. Thanks to MSN Marketing Service
     Nordwest, GmbH, for funding development!

     - Configuration process has been totally rewritten and renamed
     (SPOPS::ClassFactory)

     - Added multivalue and name mapping functions tie interface


Individual:

   * Makefile.PL:

     - Added support for non-interactive execution 
       ('perl Makefile.PL AUTO' if you have a 'spops_test.conf' file
       around from a previous test)

     - Added support for LDAP test definitions

   * eg/README:

     - Information about the examples now here

   * eg/stock_user_group.pl:

     - Added script to stock the sample user/group/security objects.

   * eg/users_groups_(mysql|psql|tsql).sql

     - Added SQL structure files for the sample user/group/security
     objects.

   * eg/My/(Common|Group|Security|User).pm:

     - Define sample user/group/security objects.

   * SPOPS.pm:

     - Added (and documented) a few keys to the information returned
     by the 'object_description' method.

     - Documented information that is used in object configuration for
     the tie interface by the 'new()' method.

     - Modified 'initialize()' to set object values passed to new()

     - Removed 'global_config()' method and the need for it -- it was
     only being used for caching and when we tackle that we'll figure
     out what's necessary.

     - Moved a number of cache methods from SPOPS/DBI.pm here

     - Removed the '_idx_data()' method

     - The 'clone()' method can now clone an object from one class to
     another given the parameter '_class'. This could be an extremely
     easy way to copy objects having the same interface from one
     datastore to another. (See POD for an example.)

     - Moved the method '_determine_limits()' from SPOPS/DBI.pm here,
     renaming it 'fetch_determine_limit()'

     - Added 'behavior_factory' which installs the default behaviors
     (found in SPOPS::ClassFactory::DefaultBehavior) to subclasses

     - Added 'as_html' to represent an object as HTML. (Note that the
     default just wraps 'as_string' in '<pre>' tags. U-G-L-Y. Override
     if you value aesthetics.)

   * SPOPS/ClassFactory.pm:

     - Added new configuration process. Instead of a monolithic
     process this class simply gathers behaviors for a class based on
     its inheritance tree. These behaviors are put into a particular
     slot (type) and run in turn. This is quite extensible and easy to
     customize. See POD for more.

   * SPOPS/ClassFactory/DefaultBehavior.pm:

     - Holds default behaviors run (potentially) by every SPOPS subclass

   * SPOPS/ClassFactory/DBI.pm:

     - Holds implementation-specific behaviors for subclasses of
     SPOPS::DBI

   * SPOPS/ClassFactory/LDAP.pm:

     - Holds implementation-specific behaviors for subclasses for
     SPOPS::LDAP

   * SPOPS/DBI.pm:

     - Moved 'check_action_security()' to SPOPS::Secure

     - Removed 'initialize()' (now inherits from SPOPS.pm); caching
     methods (moved to SPOPS.pm)

     - Moved '_determine_limits()' to SPOPS.pm as
     'fetch_determine_limit()'

     - Added 'behavior_factory' which installs the
     implementation-specific behaviors (found in
     SPOPS::ClassFactory::DBI) to subclasses

   * SPOPS/Initialize.pm:

     - Better documented the process() method and added the ability to
     pass multiple already-formed SPOPS configuration hashrefs at
     once.

     - Modified to work with SPOPS::ClassFactory

   * SPOPS/Iterator/LDAP.pm:

     - Added implementation of SPOPS::Iterator for SPOPS::LDAP

   * SPOPS/LDAP.pm:

     - Added new datastore for SPOPS. You can now save, fetch and
     remove objects from an LDAP datastore as you would a DBI
     datastore.

   * SPOPS/LDAP/MultiDatastore.pm:

     - Add subclass to SPOPS::LDAP - be able to query multiple
     directories at once by using this (instead of SPOPS::LDAP) as a
     parent class and specifying multiple datasources in the object
     configuration.

   * SPOPS/Secure.pm

     - Updated docs and added information about subclassing to create
     custom security.

     - Added method 'check_action_security()' from SPOPS/DBI.pm so
     that other implementation classes can use it and
     non-SPOPS::Secure objects can just call the stub in SPOPS.pm.

   * SPOPS/SQLInterface.pm

     - Patch from Ray Zimmerman to actually return data when you
     specify a return value of 'hash' for db_select().

     - Patch from Ray Zimmerman to return from 'db_insert()',
     'db_update()' and 'db_delete()' the return value from the
     '$sth->execute()' statement. This is an API change but a very
     small one -- if successful the return value should still be true,
     since even if (for instance) an update affects no rows DBI
     returns '0E0' which is numerically zero but evaluates to
     true. Also added note regarding various return values to
     'db_update()' docs.

   * SPOPS/Tie.pm

     - Added functionality for multivalued properties. A single
     property can have non-duplicate, unordered multiple values. This
     is necessary for SPOPS::LDAP. (Documentation included!)

     - Added functionality for field mapping. This allows you to
     retrofit an existing object interface to a table scheme with
     different field names. (Documentation included!)

   * t/*

     - Renamed all tests from 'name.t' to '##_name.t' so we can have
     some control over the order in which they're executed.

     - Added LDAP test suite (40_ldap.t).


0.41  Fri Jul 20 07:00:38 EDT 2001

   Overall:

     - Added iterators to scroll sensibly through resultsets

     - Handful of bugfixes and changes.

     - Removed the previously deprecated SPOPS::DBI::RandomCode; use
     SPOPS::Key::Random instead

     - Added more tests and new testing scheme

   API CHANGES:

     - The return value of 'save()' is now the object itself rather
     than the object's ID. This allows you to perform chained method
     calls like:

       $obj->save()->perform_other_method();

     Or even:

       $film->add_director( 
                 Director->new({ name => 'Peter Jackson' })->save );

   * SPOPS.pm:

     - Document special lazy loading column group '_id_field'

     - Added 'is_saved()' which returns true if the object has been
     saved. (Old method 'saved()' is still there but may be removed at
     a later date.)

     - Moved 'id()' method to be in the auto-generated class.

     - Moved all credits to this file.

   * SPOPS/Configure.pm:

     - Ray Zimmerman <rz10@cornell.edu> caught a little bug in
     'find_config_class()' that might have prevented
     'SPOPS::Configure::DBI' from getting invoked properly, which
     would have almost certainly led to mass hysteria.

     - Ray Zimmerman also caught a typo in the hasa processing.

     - Cleaned up docs.

     - Moved 'id()' method from SPOPS.pm so a subclass won't get a
     superclass's ID.

   * SPOPS/Configure/DBI.pm:

     - Cleaned up docs.

   * SPOPS/Configure/Ruleset.pm:

     - Cleaned up docs.

   * SPOPS/DBI.pm:

     - Implement special lazy loading column group '_id_field'

     - Added 'fetch_iterator()' method which sets up the information
     for the iterator (runs the query and gets the statement handle)
     and returns it.

     - Fixed wrong call in 'remove()' that was supposed to check
     whether the object had been saved. (Thanks to Ray Zimmerman.)

   * SPOPS/Iterator.pm:

     - Added new class for an Iterator, or an object that allows you
     to step through a resultset one at a time rather than forcing you
     to retrieve the entire thing into memory at once. This class
     defines the interface as well as the state variables and
     methods. Implementing classes only need to implement three
     methods to make it work. See docs (which are pretty complete) for
     more info.

   * SPOPS/Iterator/DBI.pm:

     - Added SPOPS::DBI implementation of iterator.

   * SPOPS/SQLInterface.pm:

     - Allow 'from' to be a scalar in 'db_select()'

     - Add 'group' -- mapping to 'GROUP BY' clause -- as a parameter
     for 'db_select()'


0.40  Sun Jun 10 14:50:20 EDT 2001

Overall: 

   * Modified how debugging works (again). This should be a
     performance gain -- thanks to posts and pointers on a recent
     thread on the mod_perl mailing list (April 2001, "Cutting down on
     the DEBUG bloat...")

   * Initialization is easier: SPOPS::Initialize should take care of
     the details for you. (Thanks to David Boone <dave@bis.bc.ca> for
     asking about this.)

   * PostgreSQL now supported, as are sequences as a key generation
     tool which should open the door for Oracle support.

   * Lazy loading of objects now supported. You can specify a logical
     group of properties and ask that only those be fetched. Your
     database will thank you, as will your memory in a persistent
     environment. See 'LAZY LOADING' in SPOPS.pm for details. (Please
     note that this is a quite new feature and if you have any
     problems and/or feedback, share!) (Tip 'o the hat to Class::DBI
     and Master Schwern for inspiration.)

   * Note: To use strict field checking, you need to set
     'strict_field' in your object configuration to a true value.

Specifics:

   * All modules: (hopefully) fixed version numbering CPAN issues.

   * SPOPS.pm:

     - Implemented (and documented!) the lazy-loading interface.

     - Better documented rulesets (thanks to Ray Zimmerman for
     prodding).

   * SPOPS/Configure.pm

     - The primary method in this class ('process_config()' now uses
     whatever configuration subclass is appropriate. For instance, if
     an SPOPS object has 'SPOPS::DBI' as a parent it will use
     'SPOPS::Configure::DBI'. Generally this just ensures that extra
     capabilities of the configuration subclass get used (like
     'create_relationships()') but it's open-ended enough to allow
     interesting things...

     - Added some inline and POD documentation to clarify matters.

   * SPOPS/Configure/DBI.pm: Thanks to Ray Zimmerman
     <rz10@cornell.edu> for spotting an error in the generated
     '_remove()' subroutine -- it wasn't passing the parameters
     (namely a database handle) through to the 'id_clause()' method
     call.

   * SPOPS/Configure/Ruleset.pm:

     - Fixed error so that an inheritance diamond won't load the same
     rule into the ruleset twice, spotted by Ray Zimmerman
     <rz10@cornell.edu>.

   * SPOPS/DBI.pm

     - Modified fetch_group() to accommodate a 'limit' parameter,
     which allows limit the number of returned objects as well as
     slice that number offset from the beginning. Patch by Rusty
     Foster <rusty@kuro5hin.org>.

     - Also (inspired by Rusty) modified fetch_group() to retrieve
     entire records rather than just the ID field and submitting that
     to a fetch() call -- security is still checked just like before,
     but since we refactored that action into its own routine we can
     call it from both 'fetch()' and 'fetch_group()'.  This should
     immediately reduce the number of database calls.

     - Added (and documented) a new method fetch_count() which
     executes a query and counts up the number of records it would
     have fetched. This **includes** security restrictions. (Otherwise
     it would be misleading...)

     - Refactored a number of actions from fetch() and fetch_group()
     into separate routines, including _fetch_select_fields() which
     returns the raw fields plus the formatted fields.

     - Added lazy-loading routines meeting the SPOPS interface.

     - fetch_group() will now only prepend the table name to columns
     that aren't formatting select statements (e.g., not to
     "DATE_FORMAT( fieldname, '%a...' )"

     - added docs regarding database handles -- how to create a
     'global_db_handle()' method, etc.

   * SPOPS/DBI/Pg.pm: Added first draft of PostgreSQL DBI
     adapter. It's been tested and seems to work pretty well with
     OpenInteract, which itself is a decent SPOPS test. Uses the
     'SPOPS::Key::DBI::Sequence' class to implement auto-incrementing
     fields.

   * SPOPS/DBI/Sybase.pm: Use the now-standard 'increment_field'
     boolean to see whether we're using an IDENTITY field for key
     generation or not. (Thanks to Harry Danilevsky
     <hdanilevsky@DeerfieldCapital.com> for pointing it out.)

   * SPOPS/Impl/SecurityObj.pm: Lots of refactoring.

   * SPOPS/Key/DBI/Sequence.pm: Added first draft of a sequence key
     generator (found in many databases, including PostgreSQL and
     Oracle). 

   * SPOPS/Secure.pm, SPOPS/Secure/Hierarchy.pm

     - Renamed the field 'oid' to 'object_id' to get around databases
     that use 'oid' as a reserved word (like PostgreSQL).

     - Renamed the field 'level' to 'security_level' to get around
     databases that have 'level' as a reserved word (like
     Sybase). While this is a fairly fundamental change from the
     database schema side of things, people simply using SPOPS as it
     was intended shouldn't feel any changes except for altering the
     table in which the security information is kept. (But that's an
     implementation issue :-)

   * SPOPS/Secure/Hierarchy.pm: Fixed the 'create_root_object' call to
     SPOPS::Secure->set_security() to call the right method with the
     right parameter types. Added some docs.

   * SPOPS/SQLInterface.pm: Documented the value 'sth' for the
     'return' parameter of db_select(). 


0.39 Sun Feb 25 13:50:56 EST 2001

   * SPOPS.pm, SPOPS/Tie.pm, SPOPS/DBI.pm: Created a 'saved' flag
     within the tied hash so that we always know whether a record has
     been retrieved from a data store or not.

   * SPOPS/DBI.pm: Prompted by Christian Lemburg
     <clemburg@aixonix.de>: modify check_action_security() to see
     whether the action is an add or not, and if so pass back
     SEC_LEVEL_WRITE since SPOPS relies on your application to ensure
     that a user can create an object. (Should SPOPS implement this?
     Let us know on the openinteract-dev mailing list at
     http://lists.sourceforge.net/lists/listinfo/openinteract-dev)
     Also added documentation to the save() method.

   * SPOPS/DBI.pm, SPOPS/Key/Random.pm, SPOPS/Key/DBI/*.pm: Modified
     how the pre_fetch_id() and post_fetch_id() methods are called --
     instead of passing a db handle/statement handle as the arguments,
     instead put everything in a hashref and read from that. (Prompted
     by the fact that SPOPS/Key/Random.pm was trying to use a DBI
     database handle as the width for the random code to
     generate. Ouch!)

   * SPOPS/Configure.pm: Thanks to questions and notes from Leon
     Brocard <acme@astray.com>, we added documentation for the 'has_a'
     configuration item for an SPOPS object as well as gave developers
     the ability to define a custom alias for the returned object
     alias. For instance, you can do something like:

      user => {
        field    => [ 'user_id', 'group_id', 'email' ],
        id_field => 'user_id',
        class    => 'My::User',
        has_a    => { 'My::Group' => [ { group_id => 'zanybrainy' } ]
        ...,
      },

     and call the 'zanybrainy' method of a C<My::User> object to get
     back the associated C<My::Group> object. (This is a silly example
     but it means we don't have to rely solely on the clunky
     automatic naming anymore, although it's still there if you want.)

   * SPOPS/DBI/MySQL.pm: post_fetch_id() now first checks to see if
     the 'increment_field' of the class configuration is set to a true
     value and if so retrieves the value from the handle.

     **THIS REQUIRES A CHANGE!!**

     From now on to use AUTO_INCREMENT fields in MySQL and SPOPS, you
     must set:

       'increment_field' => 1

     in the SPOPS configuration file for that class.

   * SPOPS/DBI/Sybase.pm: post_fetch_id() now first checks to see if
     the 'syb_identity' field of the class configuration is set to a
     true value and if so retrieves the value from the handle. This is
     no retrofit issue at all since we already required this.

   * SPOPS/DBI.pm: _save_insert() now retrieves a db handle and
     passes it to both the pre_fetch_id() and post_fetch_id()
     methods as well as the appropriate (insert/update) SQLInterface
     method.

   * SPOPS.pm: added 'Storable' to the ISA list of SPOPS, which means
     that every SPOPS object can now call store(), retrieve() and
     nstore() get serialized in a network-transportable
     format. Cool. Implications of this have yet to shake out,
     however.

     **POSSIBLE INTERFACE CONFLICT!!**

     If you are using a routine with any of the following names:

        store()
        retrieve()
        nstore()

     then you will not be able to use the methods from Storable. If
     you don't plan to use Storable then this is no big deal, but if
     you do then you'll have to rename your class methods.

   * SPOPS/Tie.pm: Functionality for doing field consistency checking
     is now in the new SPOPS/Tie/StrictField.pm, which allows you to
     create an SPOPS object without field restrictions and serialize
     it to a datasource. This might not seem useful at first -- why
     not just serialize a hashref? -- but we can treat all these
     objects the same which will (trust me) be important to you at
     some time or other. You also get builtin serialization and other
     fun stuff for free.

   * SPOPS/Key/Random.pm, SPOPS/Key/DBI/Pool.pm,
     SPOPS/Key/DBI/Identity.pm, SPOPS/Key/DBI/HandleField.pm: Moved
     all key-generation functions to the SPOPS::Key hierarchy. The
     Identity.pm and HandleField.pm came out of SPOPS/DBI/Sybase.pm
     and SPOPS/DBI/MySQL.pm, respectively, and Pool.pm is a rewritten
     SPOPS/DBI/Keypool.pm. SPOPS/DBI/RandomCode.pm was just renamed to
     Random.pm. (Hope that makes sense!) This might cause a little
     pain but we'll see if we can keep backward compatibility.

   * SPOPS.pm, SPOPS/DBI.pm, t/dbi.t: Applied patches from Rick Myers
     <rik@sumthin.nu> to eliminate warnings regarding undefined items
     under -w.

   * SPOPS/GDBM.pm, t/gdbm.t: Applied patches from Rick Myers
     <rik@sumthin.nu> to properly deal with GDBM_WRCREAT permissions
     on new GDBM databases.


0.38  Thu Feb  1 00:54:03 EST 2001

     Modified format of all files in the module to be more
     readable. Also added to SPOPS.pm exportable methods _w (warn) and
     _wm (warn multiple) which provides centralized debugging -- just
     change the DEBUG value in SPOPS.pm to a number (higher for more
     warnings) and all the packages in the modules will spill the
     beans.

     Applied patch from Christian Lemburg (lemburg@aixonix.de) to make
     'parse_into_hash' manipulations work as intended in
     SPOPS/Configure.pm.


0.37  Wed Jan  3 22:53:46 EST 2001

     One small (but important) bugfix: an error in SPOPS::Secure was
     using an object as a classname and instead of putting
     'NS::MyObject' into the 'class' column of the 'sys_security'
     table, it put something like 'NS::MyObject=HASH(0x8b7bb12)'.


0.36  Dec 20, 2000

     SMALL API CHANGE: the return value from the 'fetch_by_object'
     method in the security object class is now undef if there are no
     security objects found and a hashref of security information if
     there are security objects found. Unless you've been subclassing
     SPOPS::Secure (or doing other funky stuff with it) you won't have
     to worry about this.

     Add the option to pass in a 'security_object_class' to any method
     that neeeds it.

     SPOPS/Secure/Hierarchy.pm has now been tested and works with some
     simple cases. More work is needed to make the whole parsing thing
     more flexible (e.g., if you pass a URL you want to only parse
     everything after the hostname:port). Added lots of documentation
     to this as well.

     Updated SPOPS/Impl/SecurityObj.pm sample security object with
     updates from OpenInteract usage. We don't require the thing being
     protected to be an SPOPS object -- you can protect (and check
     on) anything now, as long as you have a consistent categorical
     identifier and a unique identifier for each thing.


0.35  Nov 18, 2000

     Added SPOPS/Secure/Hierarchy.pm for finding security in a
     parent-child relationship, like filesystems. This involved
     exploding out some functionality from the 'get_security()' method
     in SPOPS/Secure.pm so we could create an effective subclass. This
     new security checking style (with the hierarchy) hasn't been
     tested yet, but it's pretty straightforward and should work ok.

     Moved certain functions out of SPOPS.pm and into new file
     'SPOPS/Utility.pm' -- these are ones not really related to data
     persistence but just helpful utilities.

     NOTE: If you're updating to this version you need to put
     'SPOPS::Utility' in your class 'isa' so that you can use the
     methods now(), now_between_dates(), crypt_it(),
     generate_random_code() and list_process().

     COMPATIBILITY WARNING: Renamed the 'fetch_by_obj' method in the
     security object (used in SPOPS/Secure.pm) to
     'fetch_by_object'. This WILL NOT affect you if you're
     just using the interface for SPOPS::Secure. (If you don't know
     what that means, then you don't need to worry about it :)

     (minor note) Changed all instances of RCS keyword '$Header' to
     '$Id' and modified my email from chris@cwinters.com to
     chris@cwinters.com.


0.34  Nov 10, 2000

     Switched some silly documentation stuff in all the files. Changed
     SPOPS/DBI.pm to pass $p to the pre/post action rulesets so that
     the caller can affect the ruleset (e.g., 'full_text_skip =>
     1'). Modified SPOPS::Configure::_read_code_class to be able to
     read in multiple code classes when creating an SPOPS object and
     added some documentation to that file for the 'fetch_by'
     relationship. Modify SPOPS.pm to only respond to AUTOLOAD
     requests when the thing doing the requesting is an object.


0.33  Oct 16, 2000

     Added lots of documentation to individual files; transition to
     'use constant' for debugging values (much gets optimized away at
     compiletime). Ensure we're passing the database handle around in
     all necessary operations using SPOPS/DBI.pm,
     SPOPS/Configure/DBI.pm and SPOPS/SQLInterface.pm; added some
     abstract methods in SPOPS.pm (such as CONFIG) so if subclasses do
     not define them we don't get an error (duh). Add a new feature to
     SPOPS/Configure.pm: define a list of fieldnames in your config
     under the 'fetch_by' key and the system will create a subroutine
     for you to retrieve objects by data contained in that
     field. Updated the README file with some helpful installation
     tips, including a pointer to the bundle file. Also modified most
     (all?) places where we 'eval' code into existence to use a
     localized $SIG{__WARN__} handler to throw out any messages passed
     to STDERR so we can rely on our own error reporting


0.32  Sep 27, 2000

     Added lots of documentation (including a 'README' file) and made
     lots of changes to the configuration classes (SPOPS/Configure.pm
     and SPOPS/Configure/DBI.pm; SPOPS/Configure/Ruleset.pm was
     changed little) -- goal was to make it easier for some (but not
     all) of the methods to be run, if necessary.and modified the
     'create_initial_security' method of SPOPS/Secure.pm to have the
     'code' method of generating initial security be responsible for
     *all* the scopes. Also documented that method.

     Added the 'no_security' configuration setting so that a class can
     decide not to use security even if 'SPOPS::Secure' is in its
     'isa'.

     Added a couple of examples into the 'eg/' directory.


0.31  Sep 26, 2000

     Removed the html encoding/decoding stuff from SPOPS.pm. Cleaned
     up the initial_security stuff in SPOPS/Secure.pm -- better
     comments with examples, better flow, etc. -- cleaned up the
     documentation (formatting), and added a couple of TO DO items.

     Added the necessary information to license SPOPS under the
     Artistic/GPL licenses. Woo-hoo!


0.30  Sep 15, 2000

     More tests, now for DBI. These are pretty simple and don't go
     into security, but they should actually be quite useful to see if
     the database (or rather, the DBD driver) supports the DBI
     name/type inspection. I suspect that most 'modern' databases do
     (or maybe that's just a hope). Also modified Makefile.PL to
     collect information for testing. 

     The hope, was of course, immediately dashed. Current versions of 
     DBD::ASAny (Sybase Adaptive Server Anywhere) do not support
     $sth->{TYPE}. So the planned modifications happened sooner than
     expected. If your database does not support dynamic type
     inspection, you can give simple type names in your configuration
     ('int', 'num', 'char', etc.) and SPOPS will translate them to the
     DBI types for you.


0.29  Sep 14, 2000

     Added the second implementation for storing SPOPS objects:
     SPOPS/GDBM.pm. Tested with Interact::Package objects and so far
     it's working well. Note that we don't deal with security or cache
     checking yet -- I don't think those will be implemented until we
     actually need them. (XP rubs off...). Also added the third
     implementation for storing SPOPS objects: SPOPS/HashFile.pm. Use
     it for reading in a perl hashref dumped (via Data::Dumper) to a
     file. This simply reads in the file and provides key/value access
     for any values you want to access and store. This implementation
     actually uses its own tiehash implementation (packaged in the
     same file) rather than SPOPS/Tie.pm. The XP stuff is definitely
     rubbing off, because I'm starting to test everything. (Gasp!)
     Added the t/ directory for testing and a testing suite for both
     SPOPS/GDBM.pm and for SPOPS/HashFile.pm. DBI tests are going to
     be a little more difficult...


0.28  Sep 11, 2000

     Added the 'object_description' method to SPOPS.pm, since we
     really do not have a generic way to query an object for a general
     description, including a URL where we can get more information, a
     name (general descriptive class of object) and a title
     (information about a specific object). Modify SPOPS/Configure.pm
     so we do not have to specify every single configuration key
     needed for the class -- just those that we have to massage in
     some way (e.g., an arrayref into a hashref) and let the rest just
     get assigned. Let calls to $dbi_obj->save skip the logging and
     security phases if they want. Also modify SPOPS/Configure.pm so
     that we now create every class on the fly and read in the
     subroutines if the configuration states that there is code behind
     the class.


0.27  Aug 28, 2000

     Moved all the ruleset initialization configuration actions to
     SPOPS/Configure/Ruleset.pm. This involved moving the package
     variable and accessor from SPOPS/Configure.pm and moving the
     ruleset constructors from SPOPS/Configure/DBI.pm. Added
     documentation about rulesets into SPOPS.pm; we should probably
     take a lot of that general documentation and put it in its own
     pod. Modified the '$item_remove' eval'd subroutine in
     SPOPS/Configure/DBI.pm to use linking tables by default; it
     should also work with one-to-many relationships that do not use
     linking tables, but we might need to create a different set of
     eval'd subroutines for such relationships. This probably doesn't
     deserve its own 'version', but we need to get this out for
     something :)


0.26  Aug 16, 2000

      Minor changes to reflect that we never need to call a
      global_error_class, and that the cache return has been named to
      'global_cache' from 'global_cache_class'; also ducmented what
      exactly the cache needs to do. Minor shifting around in
      SPOPS/Configure.pm and SPOPS/Configure/DBI.pm as we modified
      configuration stuff in OpenInteract. Modified SPOPS/Tie.pm to
      track 'internal' variables -- first use will be for SPOPS/DBI.pm
      methods to be able to validate data, particularly for 'Grouped'
      objects. Split up 'save()' in SPOPS/DBI.pm into internal methods
      '_save_insert()' and '_save_update()' to make it more
      understandable.

      Moved the 'check_action_security()' method from SPOPS/Secure.pm
      to SPOPS/DBI.pm and put additional functionality in. Now we
      simply pass an object and a required level -- do not wrap it in
      an eval{} and it will throw any necessary errors for you. Added
      the default 'check_security' method to SPOPS.pm so
      non-SPOPS::Secure classes will inherit the 'everything goes'
      security.

      Finally, added some cool functionality to ensure that pre/post
      actions actually mean something. At initialization time, we
      scour the parents of a SPOPS object class for rules to execute
      during these phases. These rules can be simple or complex, and
      they must be exclusive of one another and execute in any order.

      This will allow us to do interesting things with layers of
      meta-information on top of objects. For example, any class
      inheriting from 'SPOPS::Searchable' will submit its information
      to a separate class, which processes it and adds/modifies a
      full-text search engine. Cool stuff :)


0.25  Aug 2, 2000

      Fixed a long-dormant buglet: this entire time, the
      SPOPS/DBI/save method was trying to retrieve the just-set values
      of fields with DEFAULT settings from the database so the object
      could be in sync at all times; turns out that we didn't tell
      SQLInterface to return a single row so we were assigning a
      stringified arrayref (eg: ARRAY(0xADFE21)) to the first value
      and nothing to the others...  Good thing caching isn't activated
      yet :)


0.24  Jul 26, 2000

      Fixed some typos in SPOPS/Configure/DBI.pm in the eval'd
      routines (including one where we were fetching the linked
      objects but not returning them -- duh!); changed how
      SPOPS/Error.pm gets the caller information. Added the ability of
      SPOPS/Configure.pm to put any coderefs where they
      belong. Modified SPOPS/DBI.pm to take the old ID to update by in
      the save() method, so you can update the primary key field and
      still refer to the old ID.


0.23  Jul 15, 2000

      Radically changed how errors are thrown. Every error generated
      by SPOPS now sets the package variables user_msg, system_msg,
      type and (optionally) extra in the brand new module,
      SPOPS::Error. It then dies with the simple scalar user_msg,
      which is what the caller catches. The caller is then responsible
      for retrieving as much information as it needs from the
      SPOPS::Error package. The main reason we're doing this is to
      eliminate problems arising from overriding the CORE::die
      function in perl. Also added some documentation to lots of
      modules. Added the module SPOPS/Impl/User.pm as an example of
      what a SPOPS user object looks like and should do.


0.22  Jul 14, 2000

      Modified a couple items in SPOPS/DBI.pm: the fetch_group()
      method can now take multiple tables and do a join but still
      return a group of objects that match the criteria. To do this we
      also changed the id_clause() method to fully-qualify ID fields
      unless otherwise told. Also changed the alias subroutine in
      SPOPS/Configure/DBI.pm created for links_to relationships to
      *not* qualify the ID field, since we frequently use link tables
      to accomplish this. Also flipped some items around in the has_a
      alias created in SPOPS/Configure.pm. And in a minor item, change
      the die {} idiom to use a preceding "if ( $@ )" clause rather
      than a trailing one, since the latter is too difficult to pick
      out easily. Modify the SPOPS/Configure/DBI.pm _add and _remove
      aliases to take either a single ID or an arrayref of IDs.


0.21  Jun 27, 2000

      Updated to put all internal dependencies (like @ISA) into
      explicit 'use' statements rather than depend on mod_perl or
      something else loading everything else up for us. Modified
      SPOPS/Configure/DBI.pm to wrap the fetch() call for a links_to
      relationship in an eval {} so we don't catch any errors thrown
      with die up top. SPOPS/generate_random_code can now return
      mixed-case codes. Also changed SPOPS/DBI.pm to return undef if a
      particular record simply isn't found -- for some reason, I
      thought this had been in there since the beginning.


0.20  Jun 18, 2000

      Paper bag fix in SPOPS/Secure.pm made by renaming a hash. <blush>


0.19  Jun 18, 2000

       Added and reworked a ton of documentation to SPOPS/DBI.pm; also
       added a super-easy and super-cool feature to its fetch()
       method: every time you ask for an object, the system will also
       give its security level to you, free of charge. Just look in
       the {tmp_security_level} property. Made a tiny change to
       SPOPS/Impl/SecurityObj.pm to ensure that users not logged in do
       not automatically belong to all existing groups :) Put
       modification into SPOPS/DBI/fetch and fetch_group to allow
       method calls to skip security checks to get around a
       chicken-and-egg problem with logging in as the superuser. (Only
       the superuser object can see the superuser object, so how are
       we going to fetch the object to login before we've logged in
       yet? :)


0.18  Jun 14, 2000

       Modified how errors are handled within all SPOPS modules. This
       forced changes throughout a number of modules, but particularly
       SPOPS/DBI.pm, SPOPS/Secure.pm and SPOPS/SQLInterface.pm. Added
       documentation to SPOPS/Tie.pm and modified it to use Carp::carp
       when something tries to access/modify a property that is not in
       the field list. Modified other modules to use Carp::carp when
       reporting nonfatal errors that require some modification from
       the method call. Also modified SPOPS/Tie.pm to take an arrayref
       (versus a hashref) argument for the list of fields it will
       accept.


0.17  Jun 13, 2000

       Modified all parameters to be passed as hashrefs rather than
       hashes. Passing-by-hashref was implemented in more recent
       modules (such as SPOPS/Secure.pm), but was not retrofitted to
       older modules. Finally bit the bullet and did this. More
       testing for object security stuff -- nothing amiss so
       far. Added some configuration options for as_string method to
       make the output look a little nicer and modified SPOPS/AUTOLOAD
       (again) to not warn with an error if it cannot find a value for
       a 'tmp_' variable. Modified the SPOPS/clone() method to force
       the ID field of the new object to be blank.


0.16  Jun 9, 2000

       Fixed SPOPS/Configure.pm to have the alias of the 'has_a'
       subroutine created to match the type of object it 'has' only if
       the ID field is exactly the same; fixed dumb mistake in
       SPOPS/Secure.pm that wasn't differentiating properly between a
       class and an object in ->create_initial_security; made a fairly
       simple patch to SPOPS/Tie.pm which allows you to store
       temporary attributes for the life of the object -- when the
       object is DESTROYed, there go the attribs, and they're not
       saved when the object is cached. Very useful for tracking
       access rights on a per-object basis. We also had to make a
       change to SPOPS/AUTOLOAD so we could call the temporary key as
       a subroutine reference (Template Toolkit does this, somewhat
       annoyingly...). Also tested out object security stuff -- works
       pretty well.


0.15  Jun 8, 2000

        Modified the SQL-generation in SPOPS/Impl/SecurityObj.pm to
        properly fetch WORLD-only requests; if there is no user object
        existing and $class->check_security; is called, we want to
        return only the WORLD permissions (SPOPS/Secure.pm); modified
        the API for object security checking so that only one method
        is called (SPOPS/Secure/check_action_security) and the action
        is passed as the first argument; created the
        SPOPS/Secure/create_initial_security method that allows us to
        initialize an object with security information. Tested
        check_action_security and fixed related API boo-boos in
        SPOPS/DBI.pm. Individual object as well as class-based
        security now seems to be working well, although more testing
        is certainly required :)
        

0.14  Jun 7, 2000

        Allow user to pass information to fetch() method (via both fetch()
        and fetch_group() that overrides configuration for the field_alter
        key (refresher: allows you to set a format on a returned value
        from the database, such as DATE_FORMAT( mydate, '%b %e' )); massive
        changes to SPOPS/Secure.pm to allow it to work with class-only and data
        object security transparently (not a lot of testing yet,
        however); SPOPS/Impl/SecurityObj.pm also had some changes to
        enable it to work with classes


0.13  Jun 5, 2000

        Added SPOPS/Impl/SecurityObj.pm to the distribution as a DBI-based
        SPOPS object representing security; added methods and documentation
        to SPOPS/Secure.pm; modified SPOPS/Configure.pm to allow multiple
        aliases per type in the 'has_a' descriptor.


0.12  May 29, 2000

        Added list_process() method to SPOPS.pm and added the related
        methods to SPOPS::Configure::DBI %alias_to%_remove and 
        %alias_to%_add, which allow us to link together objects without
        actually modifying the objects. For instance, if the alias 
        'group' has defined: links_to => [ 'user' ] the methods would
        be named 'user_add' and 'user_remove' and would add or remove
        one or more users to the group. Also modified all the errors
        thrown in SQLInterface to have the proper type and at least
        some semblance of informative messages.


0.11  May 24, 2000

        Paper bag fix in SPOPS::Tie (changes weren't being tracked and
        therefore updates not saved); paper bag fix in SPOPS::Configure::DBI
        (bad definition for eval'd subroutine created *evil* bug of
        super-rapid memory consumption); added data() method to SPOPS.pm;
        routine created in SPOPS::Configure and eval'd for has_a now
        only tries a fetch if the ID value exists


0.10  May 17, 2000 

        Polishing; things basically work


0.01  May 12, 2000

	    Original version - much of the packages were brought in from elsewhere,
        but a lot of work is needed to extricate them from their
        previous application and to make everything work together and
        feel cohesive.

