Revision history for Perl extension SPOPS.



   * SPOPS/

     - Modify the determine_limit() to always explicitly return a 0
     when appropriate.

   * t/07_utility.t:

     - Modify the list_process() tests to not depend on the order of
     the items returned. (Test would work on 5.6.x a-ok, but not on
     5.8.x -- thanks for report from the CPAN testers.)

0.67  Tue Aug 27 21:19:14 EDT 2002


   * Fix module versions to eliminate conflicts with CPAN --
     everything has been upgraded in CVS to a version 3.0 and with a
     proper VERSION so we can start from a known good version and have
     no danger of retrograding. Thanks to merlyn
     <> for the friendly notice. (Again.)


   * t/07_utility.t:

     - Add tests for functions in SPOPS::Utility (lesson learned!)

0.66  Mon Aug 26 08:24:45 EDT 2002


   * Quick bugfix release



     - Move 'use SPOPS::Utility' to 'require SPOPS::Utility', after
     the exporting stuff defined

   * SPOPS/

     - Import the DEBUG and _w constants

   * doc/Manual/Relationships.pod:

     - Fixed POD error, thanks to Mike Castle <>
     for the report.

0.65  Wed Aug 21 17:12:34 EDT 2002


   - Build process will now run without intervention; DBI and LDAP
   tests require environment variables being set or running the
   process manually (perl Makefile.PL MANUAL=1). See 'README' for the
   environment variables used.

   - Fixed and added lots of documentation on relationships and
   multifield primary keys, Thanks to Gert Thiel
   ( for pointing out deficiencies and

   - Added lots of tests

   - Cleaned up some items from the base SPOPS class


   * doc/Manual/Error.pod:

     - Deleted all content with pointer to
     SPOPS::Manual::Exception. This doc will be removed in a near
     future release.

   * doc/Manual/Object.pod:

     - Clarified what to use with multifield primary keys.

   * doc/Manual/Relationships.pod:

     - Added tons of documentation and examples. Some of it was pulled
     from other locations, but most is new.

   * Makefile.PL:

     - Lots of changes so the build process will run without
     intervention. You can skip the DBI/LDAP tests or specify the
     relevant information in environment variables. (See 'README' for
     the environment variables to use.) This should make acme happy :-)


     - Removed the timestamp methods. They never did anything, were
     marked for removal, and the functionality can be added as a
     pre_save rule.

     - Added a set_global_debug() method so we can programmatically
     set debugging.

     - Added documentation for set_global_debug() as well as the _w()
     and _wm() debugging methods.

     - Remove Storable from ISA and instead create methods 'store',
     'retrieve' and 'nstore' which just act as delegates for the
     Storable methods.

     - Rename fetch_determine_limit() to
     SPOPS::Utility->determine_limit() and modify all uses in the

     - All objects returned from new() now have the 'changed' flag
     explicitly set and the and the 'saved' flag explicitly cleared.

     - Be able to turn on strict field checking for individual objects
     (pass a 'strict_field => true' to new())

   * SPOPS/ClassFactory/

     - Enable fetch() to use either an arrayref or a string with
     multifield primary keys. Thanks to Gert Thiel
     <> for prompting the change.

   * SPOPS/ClassFactory/

     - Add brief documentation for the different methods.

   * SPOPS/

     - Update field_update() so it can operate as a class method, and
     add documentation to match. Thanks to Ray Zimmerman for noting
     the tangential reference to class method usage in previous docs.

     - Add SET_DEBUG_FETCH() and SET_DEBUG_SAVE() to programmatically
     set debugging levels.

   * SPOPS/

     and SET_DEBUG_DELETE() to programmatically set debugging levels.

   * SPOPS/Tool/

     - Issue a die() rather than a warn() on a save/update.

   * SPOPS/Tool/

     - Issue a die() rather than a warn() on a save() or remove()

   * t/00_base.t:

     - Add tests for the base SPOPS methods (metadata, constructor,
     cloning, etc.)

   * t/20_gdbm.t:

     - Separate the tests that were combining different tests in one.

   * t/30_dbi.t:

     - Add tests for field_update() as a class method

   * t/32_dbi_inline_config.t:

     - Add tests for SPOPS::Tool::DBI::Datasource

   * t/33_dbi_discover_field.t:

     - Add tests for SPOPS::Tool::DBI::DiscoverField

   * t/34_dbi_find_defaults.t;

     - Add tests for SPOPS::Tool::DBI::FindDefaults

   * t/50_rule_create_only.t:

     - Add tests for SPOPS::Tool::CreateOnly

   * t/51_rule_read_only.t:

     - Add tests for SPOPS::Tool::ReadOnly

0.64  Sun Aug 11 23:08:05 EDT 2002



     - Modified standalone script (we moved the tools to SPOPS::Tool)
     and added Interbase/Firebird to the list of supported databases.

   * SPOPS/

     - Update fetch() to check to see if the ID passed in is defined
     *and* non-empty. Thanks to Hal Finkel for pointing this out. (SF
     Bug #572661)

   * t/30_dbi.t:

     - Add tests to ensure that trying to fetch an object with a blank
     ID and with an undef ID returns undef.

0.63  Sat Aug 10 09:30:43 EDT 2002


   * Small fix to t/40_ldap.t to ensure it doesn't run when it's not
     supposed to

0.62  Fri Aug  9 23:21:08 EDT 2002


   * A few small but useful changes, mostly to SPOPS::DBI and mostly
     written by someone else.


   * SPOPS/

     - Added method 'refetch()' to class. This fetches the values for
     a limited number of fields for a particular object and updates
     the object. Thanks to Ray Zimmerman for the full implementation
     and documentation!

     - Added method 'field_update()' to class. This updates the values
     for a limited number of fields, or performs an update across
     several objects at once. Thanks to Ray Zimmerman for the full
     implementation and documentation!

     - Added method 'filter_fields()' to class. This ensures that a
     list of fields or the keys in a hashref are not private and are
     translated properly using the field map. (This is likely only to
     be used within the class or subclasses.)

     - Allow call to 'save()' to pass per-object definitions for
     'skip_undef', 'no_insert' and 'no_update' and ensure that
     'no_insert' fields are refetched post-insert just like
     'sql_defaults' fields are.

   * SPOPS/

     - Ensure that 'skip_undef' is implemented for both inserts and
     updates, and allow call to 'save()' to pass per-object
     definitions for that and for 'no_insert' and 'no_update'.

   * SPOPS/Tool/

     - Simple behavior to ensure that objects can only be created, not

   * t/30_dbi.t:

     - Add tests for refetch() and field_update()

     - Add tests for 'no_insert' and 'no_update' passed along with save()

0.61  Mon May  6 21:44:57 EDT 2002


   * Quick bugfix release: SPOPS::DBI::MySQL quoting fix and warning
     removed for incorrect EXPORT_OK declaration for



     - Override sql_quote() to use the two-argument form of
     $dbh->quote(). (Thanks to Ray Zimmerman for the pointer.)

   * SPOPS/Exception/

     - Fix typo: $...::EXPORT_OK -> @...::EXPORT_OK. (Thanks to Ray
     Zimmerman for the spot.)

0.60  Mon May  6 08:45:34 EDT 2002


   * New feature: Initial support for InterBase family of databases
     (InterBase and FirebirdSQL), using the DBD::InterBase
     driver. There is one major caveat: if you're using security with
     your objects, you cannot use the same database handle for
     security as other objects. (See other notes below.)

   * New feature: SPOPS::DBI classes can modify the value inserted
     into the database when calling save() on a new object. See
     'insert_alter' documentation in SPOPS::DBI.

   * Added tools previously in 'eg/' to the main distribution, now
     found in SPOPS::Tool

   * Small bugfixes and enhancements.


   * Makefile.PL:

     - Registered InterBase as a valid DBD

   * doc/Manual/Configuration.pod:

     - Document new 'insert_alter' configuration item for SPOPS::DBI

   * doc/Manual/Exceptions.pod:

     - Document exception shortcuts.

   * SPOPS/

     - Fix bug in perform_lazy_load() so we allow field-mapped fields
     to be lazy-loaded as well. (Thanks to Bill Heckel
     <> for the bug report.)

     - Fix fetch_count() so it uses a fully-qualified column to select
     the count. (Thanks to Ray Zimmerman for patch.)

     - Add id_field_select() to provide a list of fields suitable for
     a SELECT statement.

     - Add configuration callback for 'insert_alter' that holds
     information to transform values on a save() for a new object.

     - Add apply_insert_alter() that is called on an insert to process
     the new 'insert_alter' directive. Note that the fieldname entries
     in the configuration must be in lower-case.

     - If lazy-loading is turned on, don't try to lazy-load fields
     beginning with a '_'. Add note and example about this in docs.

   * SPOPS/

     - Fixed error messages (SF Bug #533134)

   * SPOPS/ClassFactory/

     - Add id_field_select() to be generated for multifield primary

     - Fixed error messages (SF Bug #533134)


     - Add support for InterBase family of databases. This was only
     tested using FirebirdSQL 1.0 running in SuperServer mode on Linux
     using DBD::InterBase 0.30. This seems to work but is still
     considered experimental until I learn more about this database or
     someone who is knowledgable can set me straight. (If you're one
     of those people, email me at

   * SPOPS/

     - In shortcut 'spops_error' changed normal subroutine call to a
     goto &sub to keep caller stack intact.

   * SPOPS/Exception/

     - Added shortcut 'spops_dbi_error'

   * SPOPS/Exception/

     - Added shortcut 'spops_ldap_error'

   * SPOPS/Exception/

     - Added shortcut 'spops_security_error'

   * SPOPS/Import/DBI/

     - Add mappings for 'interbase' and 'firebird'

   * SPOPS/Import/DBI/TableTransform/

     - Add table transformation strings for InterBase family

   * SPOPS/

     - Modify db_discover_types() to try both $dbh->{Name} and
     $dbh->{name} for the database name. Thanks to Florian Hellberg
     <> for the bug report.

     - Use 'spops_error' and 'spops_dbi_error' shortcuts for all

   * SPOPS/Tool/

     - Add tool to make objects read-only.

   * SPOPS/Tool/

     - Add tool to do UTF8 translation to-from a datasource. (See

   * SPOPS/Tool/DBI/

     - Add tool to do automatic field discovery for DBI-based objects.

   * SPOPS/Tool/DBI/

     - Add tool to embed DBI datasource connection information in
     the object configuration.

   * SPOPS/Tool/DBI/

     - Add tool to get field defaults from a particular record.

   * SPOPS/Tool/LDAP/

     - Add tool to embed LDAP datasource connection information in
     the object configuration.

   * t/dbi_config.t:

     - Registered InterBase type

   * t/30_dbi.t, t/31_dbi_multifield.t:

     - Add test for output of new methods id_field_select()

     - Add more tests (id_clause)

     - Add test for 'insert_alter'

   * t/06_ruleset.t, t/

     - Add files for testing rulesets -- very sparse for the moment.

0.59  Mon Mar 18 23:33:06 EST 2002


   * Fix module versions to eliminate conflicts with CPAN --
     everything has been upgraded in CVS to a version 2.0 so we can
     start from a known good version and have no danger of
     retrograding. Thanks to Merlyn <> for the
     friendly notice.

0.58  Thu Mar 14 09:03:35 EST 2002


   * Modified DBI id_clause() quoting (in both single and multifield keys)
     so it will call ->sql_quote() (in SPOPS::SQLInterface or
     overridden by the SPOPS DBI driver class) instead of ->quote()
     directly on the db handle, since some drivers don't know what to
     do with the extra type information. (Found with latest version --
     0.11 -- of DBD::SQLite.)


   * SPOPS/

     - Updated id_clause() to call ->sql_quote(). See 'Overall' for

   * SPOPS/ClassFactory/

     - Updated the generated id_clause() for multifield primary keys
     to call ->sql_quote(). See 'Overall' for notes.

0.57  Sat Feb 23 14:02:22 EST 2002


   * SPOPS now works with Oracle! All tests pass and all the scripts
     under eg/ work ok as well, but you might consider this support
     beta until someone who actually knows Oracle takes a look.

   * SPOPS now works with SQLite! SQLite is an embeddable database,
     and DBD::SQLite comes with the database source ready to
     compile. You can use it instead of DBD::Sprite or DBD::CSV -- it
     is much faster and behaves like a real database. Kudos to the
     indefatigable Matt Sergant!

   * Fix tests and (hopefully) Makefile.PL to follow all dependencies


   * Makefile.PL:

     - Add 'Class::Factory' as a dependency

   * eg/

     - Display all known objects in eg/ modules

   * eg/users_groups_oracle.sql, eg/users_groups_clear_oracle.sql:

     - Data create/clear scripts for Oracle

   * eg/

     - Data create script for SQLite

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

     - Use named sequences versus relying on default behavior


     - Modified clone() to respect false-but-defined values passed in
     for the new object. Thanks to Ray Zimmerman <>
     for the patch.

   * SPOPS/

     - Add 'sql_case_insensitive' to allow drivers to specify a
     case-insensitive operator. (Default: 'LIKE', which may or may not
     work for all databases but at least it will not break anything.)

     - Add docs for the pre_fetch_id() and post_fetch_id() callbacks
     to get/find ID values on an insert.

     - The return value from pre_fetch_id() is by default set to
     no_quote -- the implementer of pre_fetch_id() is responsible for
     either quoting it or for telling SPOPS::DBI that it is ok to
     quote it using the normal means.


     - Specify the '~*' case-insensitive operator

     - Modify how we fetch/retrieve sequence values.


     - Hey, we support Oracle now! (See above)


     - Hey, we support SQLite now! (See above)

   * SPOPS/

     - You can now pass as many arguments as you want to throw() --
     they will all get concatenated into the exception message unless
     the last one is a hashref, which will be set to the optional
     parameter hashref.


     - Modify to use Class::Factory

   * SPOPS/Import/TableTransform/(Oracle|SQLite).pm:

     - Add table transforms for Oracle and SQLite

   * SPOPS/Key/(Random|UUID).pm:

     - Quote the return value from pre_fetch_id()

   * SPOPS/

     - Add a 'require SPOPS::Exception::Security' to bring in the
     module, but add it after all the constants have been defined so
     we don't get into a deadlock

   * t/05_exception.t:

     - Modify stringification comparison to work properly all the time

   * t/30_dbi.t, t/31_dbi_multifield.t, t/40_ldap.t:

     - Only run the END {} if we're running the tests

   * t/

     - Add Oracle-specific info

0.56  Tue Jan 15 07:17:02 EST 2002


   * Overhauled the error reporting scheme to use exceptions instead
     of package variables; as a result, modified all files that used
     'die' to indicate error to throw an exception object
     instead. (Backwards compatibility is maintained by echoing the
     error information to SPOPS::Error as before.)

   * Added examples for specifying DBI/LDAP connection information in
     an object configuration, useful for one-off scripts


   * Makefile.PL:

     - Check to see whether Data::UUID is installed. (Thanks to brian
     moseley <> for pointing out the test failure.)

     - Add Devel::StackTrace as a dependency, as well as the
     Class::Accessor dependencies

   * eg/

     - Added example of specifying DBI configuration in object

   * eg/

     - Added example of specifying LDAP configuration in object

   * eg/My/DBI/

     - Added sample behavior to create a 'global_datasource_handle()'
     for a class to return a DBI handle based on the configuration
     information in the object.

   * eg/My/LDAP/

     - Added sample behavior to create a 'global_datasource_handle()'
     for a class to return a LDAP handle based on the configuration
     information in the object.

   * SPOPS/

     - Fixed fetch() to deal with: fetch( 0 ) properly (use:
     'if defined $id' rather than 'if $id') and remove the default set
     of $id to 0 as well (SF Bug: #500514)

   * SPOPS/

    - Added base class for exceptions in SPOPS (to replace
    SPOPS::Error with backwards compatibility)

   * SPOPS/Exception/

    - Create exception class for DBI-specific errors

   * SPOPS/Exception/

    - Create exception class for LDAP-specific errors

   * SPOPS/Exception/

    - Create exception class for security errors

   * SPOPS/Iterator/

    - Ensure we can handle ID field intialization properly, add
    helpful debugging messages and get rid of redundant offset check

   * SPOPS/Iterator/

    - Add helpful debugging messages and get rid of redundant offset

   * SPOPS/Manual/Exceptions.pod:

    - Add manual section on exceptions.

   * t/03_uuid_key.t:

     - Check the build configuration to see if the test should run

   * t/05_exception.t:

     - Added tests for exceptions

0.55  Mon Dec 31 00:49:15 EST 2001


 - Added data import and export capabilities to SPOPS

 - You can now use a UUID (Universally Unique Identifier) as a key


   * eg/

     - Added sample for SPOPS::Export::Object

   * eg/

     - Added sample for SPOPS::Import::Object


     - Fixed item to allow specification of the sequence name in
     object configuration

   * SPOPS/

     - Added factory class and parent to exporting routines

   * SPOPS/Export/

     - Added simple class to take existing data and export it to a
     portable format.

   * SPOPS/Export/

     - Added simple class to export SPOPS objects to a raw perl data

   * SPOPS/Export/

     - Added simplistic class to export SPOPS objects to a series of
     SQL INSERT statements.

   * SPOPS/Export/

     - Added simplistic class to export SPOPS objects to XML format

   * SPOPS/

     - Added factory class and parent to importing routines

   * SPOPS/Import/DBI/

     - Added class for importing/translating DBI tables. This should
     allow you to create a database-neutral schema and have SPOPS
     translate it dynamically.

   * SPOPS/Import/DBI/

     - Added parent factory class for creating transformation objects

   * SPOPS/Import/DBI/TableTransform/

     - Added transformer for generic tables to MySQL

   * SPOPS/Import/DBI/TableTransform/

     - Added transformer for generic tables to PostgreSQL

   * SPOPS/Import/DBI/TableTransform/

     - Added transformer for generic tables to Sybase (ASE, ASA and MS

   * SPOPS/Import/

     - Added a simple class to bring into an SPOPS datastore data
     formatted by something like SPOPS::Export::Object.

   * SPOPS/Key/

     - Wrapped the call to $class->CONFIG in an eval {} in case we're not
     calling this from an SPOPS class/object

   * SPOPS/Key/

     - Created new method for generating keys. The Universally Unique
     Identifier is a fairly long string whose uniqueness is (more or
     less) guaranteed -- the docs for Data::UUID say that it's good
     for millions of keys per second, which is good enough for me :-)
     (Note: this apprently does not work on Win32 systems; pointers
     are welcome for either a Perl module or an external program that
     can do this.)

   * SPOPS/

     - Created a dummy SPOPS class useful for testing. It runs all the
     pre/post actions, so you can test out your rules on it as well.

   * SPOPS/Manual/ImportExport.pod

     - Added manual section for importing and exporting data.

   * t/03_uuid_key.t:

     - Created test for SPOPS::Key::UUID

   * t/04_random_key.t:

     - Created test for SPOPS::Key::Random

0.54  Tue Nov 27 07:42:59 EST 2001


   - Fix bug introduced by 0.53 in the clone() method. (Thanks to Ray
   Zimmerman for spotting.)



     - Modify clone() method to behave as previous -- the
     multifield-specific behavior is now in a separate method

  * SPOPS/ClassFactory/

     - Generate clone() method for classes using multifield primary

  * t/31_dbi_multifield.t:

    - Modify clone save() test (#12) to specify one value of the ID in
    the clone() call and set the other one afterward.

0.53  Sat Nov 24 20:50:32 EST 2001


   - SPOPS::DBI now supports multiple-field primary keys

   - Small bugfixes


   * eg/My/

     - Example of how you can create read-only objects in SPOPS.

   * eg/My/

     - Example of converting to/from unicode values for datasources
     that store unicode (WARNING: This is a demo only, and only works
     for those 5.6.0 and earlier versions of perl that support 'use


     - Modified how the ID value gets set in initialize() if { id =>
     'blah' } is passed in -- this modification enables multifield
     keys to work properly.

   * SPOPS/ClassFactory/

     - Add id_clause() and id() methods to classes using
     multiple-field primary keys (see below).

   * SPOPS/

     - Fixed the function that gathered up fields to select to ignore
     putting the table name in front of a field unless it has the
     'table.' in front, not just 'table'

     - Enable multi-field primary keys. All you need to do is specify
     multiple fields for your id_field:

       Single field:

         id_field => 'object_id',

       Multiple field:

         id_field => [ 'creation_time', 'created_by' ]

      SPOPS::DBI will take care of the rest for you. To retrieve a
      multi-field record, you just need to specify the ID values

         # '1005082576' is the creation_time, '45' is the ID of the
         # user who created it

         my $obj = My::Object->fetch( '1005082576,45' );

   * SPOPS/ 

     - Always make a backup before doing anything, and keep it around
     rather than deleting it.

   * t/

     - Moved certain information from t/30_dbi.t here and generalized
     for other DBI tests.

   * t/30_dbi.t:

     - Modified to use for certain behaviors

   * t/31_dbi_multifield.t:

     - Added tests for multi-field primary keys.

0.52  Thu Oct 25 23:19:43 EDT 2001


   - You can now specify defaults for objects so when you instantiate
   one via new() the values get automatically filled in. There are
   different ways to set these defaults -- see
   SPOPS::Manual::Configuration and SPOPS::Manual::CodeGeneration for
   examples and ideas.

   - Enhancements to SPOPS::LDAP::Multidatasource

   - Bugfixes, including one in SPOPS::ClassFactory::DefaultBehavior
   that broke old apps using rules


   * eg/

     - Self-contained script used to demonstrate how
     My::DBI::FindDefaults works.

   * eg/

     - Added script to demonstrate (along with eg/My/
     how multiple datasources work with SPOPS::LDAP::MultiDatasource

   * eg/My/DBI/

     - Example of a SPOPS::ClassFactory behavior that retrieves the
     information from a particular record to use as defaults.

   * eg/My/

     - Example of an LDAP connection manager that works with multiple


     - Enable default values to be set via the object configuration
     (or dynamically, via ClassFactory) that are used to fill up an
     object created with new(). You can also use the key 'NOW' to
     specify the current date/time or a class and method to deal with
     more complicated tasks.

     (Accompanying this, modified all implementations -- DBI, LDAP and
     GDBM -- plus the iterators -- DBI and GDBM -- to pass a
     'skip_default_values => 1' when creating a new object to be
     filled with a fetch() or fetch_group().)


     - Added SPOPS::ClassFactory behavior so we can tell MySQL to find
     defaults for fields.

   * SPOPS/ClassFactory/

     - Bugfix: rulesets used to be generated using the method
     'ruleset_add'. We now use 'ruleset_factory' since it's more
     descriptive and consistent with the behavior generation
     code. However, we forgot to keep the old method enabled for a few
     versions. Fixed. (Thanks to Peter Beardsley
     <> for the report.)


    - Make datasources sticky -- each object should be able to find
    out where it came from (modified fetch() and fetch_group_all()
    with this).

    - Override save() and remove() so that we first grab the right
    handle (from the datasource put into the object) and pass it along
    to the method that does the real work.

    - Create fetch_by_dn() so you can actually store in a group the
    DNs for objects that reside on multiple servers. (Cool!) Thanks to
    Andreas Nolte <> for testing this out.

0.51  Sun Oct 14 20:23:59 EDT 2001


   - Centralized SPOPS documentation started

   - LDAP bugfixes and additional tests

   - Other fixes and goodies


   * SPOPS/Manual*:

     - Added first draft of SPOPS manual. Note that much of the
     documentation was removed from other classes, reworked and placed
     here. For example, all discussion of configuration keys is now in
     one place, SPOPS::Manual::Configuration. Some pieces of the
     manual are fairly complete while others are barren. To start,
     just do 'perldoc SPOPS::Manual'.

   * eg/(items from 0.50):

     - Actually added these to the distribution (doh!)

   * eg/My/

     - Added sample ClassFactory behavior for auto-discovering fields
     in a DBI datasource and using all of them as object properties.

   * SPOPS/

     - Allow rules-only classes to be specified in the configuration
     field 'rules_from' in addition to in the 'isa' (used for
     ClassFactory behaviors).

     - change require_isa() to require_config_classes(), and enable it
     to require the 'rules_from' classes as well

   * SPOPS/ClassFactory/

     - Change 'warn' calls when we get an error to 'SPOPS::_w' calls,
     to fit with everything else

     - Change generated 'links_to' methods so that we get the database
     handle from the correct class. (This hasn't been a problem for
     people because most people use the same datasource to hold
     related objects. But SPOPS doesn't care!)

   * SPOPS/ClassFactory/

     - Allow rules-only classes to be specified in the configuration
     field 'rules_from' rather than in the 'isa' (used for rulesets)

   * SPOPS/ClassFactory/

     - Add %alias%_add() and %alias%_remove() calls to the 'has_a'

     - Move certain parameter-parsing and object-getting functionality
     from separate generated methods and into a method that gets
     installed via the 'read_code' slot in the ClassFactory process.

   * SPOPS/Iterator/(DBI|LDAP).pm:

     - Fixed off-by-one error when using 'limit'

   * SPOPS/Iterator/

     - Added simple implementation of SPOPS::Iterator to wrap around an
     already-existing list of objects.

   * SPOPS/

     - Modified the save procedures to first fetch the existing object
     then implement a 'replace' on only the items that SPOPS knows

     - Modified 'fetch()' to work properly with a secured object that
     is fetched with a filter rather than an ID.

     - 'fetch_by_dn()' is much more sensible about how it works (it
     now does a search for all objects below the particular DN and
     returns the only one that should be there)

     - Pulled out pre- and post- actions from fetch() so that other
     methods can use them.

     - You can force a filter to not be used by passing a true value
     for the 'no_filter' parameter key to 'fetch'

     - ensure that _perform_postfetch() looks at the {skip_security}
     setting before checking security on a 'delay_security_check'
     (Thanks to Andreas Nolte <>)

   * SPOPS/

     - Move security error reporting (putting error info into
     SPOPS::Error plus the 'die' call) into a separate method so it
     can be called from elsewhere

   * t/40_ldap.t:

     - Add tests for new feature signified by ldap_update_only_changed
     configuration flag

     - Add tests for 'has_a' and 'links_to' linking functionality

0.50  Tue Aug 28 16:59:39 EDT 2001


     - 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

     - Added multivalue and name mapping functions tie interface


   * 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/

     - 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

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

     - Define sample user/group/security objects.


     - 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/ 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/ 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/

     - 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/

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

   * SPOPS/ClassFactory/

     - Holds implementation-specific behaviors for subclasses of

   * SPOPS/ClassFactory/

     - Holds implementation-specific behaviors for subclasses for

   * SPOPS/

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

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

     - Moved '_determine_limits()' to as

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

   * SPOPS/

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

     - Modified to work with SPOPS::ClassFactory

   * SPOPS/Iterator/

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

   * SPOPS/

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


     - 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

   * SPOPS/

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

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

   * SPOPS/

     - 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/

     - 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


     - 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


     - 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:


     Or even:

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


     - 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/

     - Ray Zimmerman <> 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 so a subclass won't get a
     superclass's ID.

   * SPOPS/Configure/

     - Cleaned up docs.

   * SPOPS/Configure/

     - Cleaned up docs.

   * SPOPS/

     - 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/

     - 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/

     - Added SPOPS::DBI implementation of iterator.

   * SPOPS/

     - 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


   * 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 <> 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 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.


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


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

     - Better documented rulesets (thanks to Ray Zimmerman for

   * SPOPS/

     - 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/ Thanks to Ray Zimmerman
     <> 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

   * SPOPS/Configure/

     - Fixed error so that an inheritance diamond won't load the same
     rule into the ruleset twice, spotted by Ray Zimmerman

   * SPOPS/

     - 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 <>.

     - 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/ 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

   * SPOPS/DBI/ 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
     <> for pointing it out.)

   * SPOPS/Impl/ Lots of refactoring.

   * SPOPS/Key/DBI/ Added first draft of a sequence key
     generator (found in many databases, including PostgreSQL and

   * SPOPS/, SPOPS/Secure/

     - 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/ 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/ Documented the value 'sth' for the
     'return' parameter of db_select(). 

0.39 Sun Feb 25 13:50:56 EST 2001

   *, SPOPS/, SPOPS/ 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/ Prompted by Christian Lemburg
     <>: 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
     Also added documentation to the save() method.

   * SPOPS/, SPOPS/Key/, 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/ was trying to use a DBI
     database handle as the width for the random code to
     generate. Ouch!)

   * SPOPS/ Thanks to questions and notes from Leon
     Brocard <>, 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/ 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.


     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/ 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/ _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

   * 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,


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


     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/ Functionality for doing field consistency checking
     is now in the new SPOPS/Tie/, 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/, SPOPS/Key/DBI/,
     SPOPS/Key/DBI/, SPOPS/Key/DBI/ Moved
     all key-generation functions to the SPOPS::Key hierarchy. The and came out of SPOPS/DBI/
     and SPOPS/DBI/, respectively, and is a rewritten
     SPOPS/DBI/ SPOPS/DBI/ was just renamed to (Hope that makes sense!) This might cause a little
     pain but we'll see if we can keep backward compatibility.

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

   * SPOPS/, t/gdbm.t: Applied patches from Rick Myers
     <> 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 exportable methods _w (warn) and
     _wm (warn multiple) which provides centralized debugging -- just
     change the DEBUG value in to a number (higher for more
     warnings) and all the packages in the modules will spill the

     Applied patch from Christian Lemburg ( to make
     'parse_into_hash' manipulations work as intended in

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/ 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/ 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/ for finding security in a
     parent-child relationship, like filesystems. This involved
     exploding out some functionality from the 'get_security()' method
     in SPOPS/ 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 and into new file
     'SPOPS/' -- 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/ 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 to

0.34  Nov 10, 2000

     Switched some silly documentation stuff in all the files. Changed
     SPOPS/ 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 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/,
     SPOPS/Configure/ and SPOPS/; added some
     abstract methods in (such as CONFIG) so if subclasses do
     not define them we don't get an error (duh). Add a new feature to
     SPOPS/ 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/
     and SPOPS/Configure/; SPOPS/Configure/ 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/ 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

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

0.31  Sep 26, 2000

     Removed the html encoding/decoding stuff from Cleaned
     up the initial_security stuff in SPOPS/ -- 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/ 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/ 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/ 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/ and for SPOPS/ DBI tests are going to
     be a little more difficult...

0.28  Sep 11, 2000

     Added the 'object_description' method to, 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/
     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/ 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/ This involved moving the package
     variable and accessor from SPOPS/ and moving the
     ruleset constructors from SPOPS/Configure/ Added
     documentation about rulesets into; 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/ 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/ and SPOPS/Configure/ as we modified
      configuration stuff in OpenInteract. Modified SPOPS/ to
      track 'internal' variables -- first use will be for SPOPS/
      methods to be able to validate data, particularly for 'Grouped'
      objects. Split up 'save()' in SPOPS/ into internal methods
      '_save_insert()' and '_save_update()' to make it more

      Moved the 'check_action_security()' method from SPOPS/
      to SPOPS/ 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 so
      non-SPOPS::Secure classes will inherit the 'everything goes'

      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/ in the eval'd
      routines (including one where we were fetching the linked
      objects but not returning them -- duh!); changed how
      SPOPS/ gets the caller information. Added the ability of
      SPOPS/ to put any coderefs where they
      belong. Modified SPOPS/ 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/ 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/ 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/ 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/ 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/ _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/ 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/ 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/ made by renaming a hash. <blush>

0.19  Jun 18, 2000

       Added and reworked a ton of documentation to SPOPS/; 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/ 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/, SPOPS/ and SPOPS/ Added
       documentation to SPOPS/ 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/ to take an arrayref
       (versus a hashref) argument for the list of fields it will

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/, 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/ 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/ that wasn't differentiating properly between a
       class and an object in ->create_initial_security; made a fairly
       simple patch to SPOPS/ 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/ 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/; 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/ 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/ to allow it to work with class-only and data
        object security transparently (not a lot of testing yet,
        however); SPOPS/Impl/ also had some changes to
        enable it to work with classes

0.13  Jun 5, 2000

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

0.12  May 29, 2000

        Added list_process() method to 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;
        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.