The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

OP::Persistence - Serialization mix-in

DESCRIPTION

Configurable class mix-in for storable OP objects.

Provides transparent support for various serialization methods.

SYNOPSIS

This package should not typically be used directly. Subclasses created with OP's create function will respond to these methods by default.

See __use<Feature>() and __dbiType(), under the Class Callback Methods section, for directions on how to disable or augment backing store options when subclassing.

PUBLIC CLASS METHODS

General

  • $class->load($id)

    Retrieve an object by ID from the backing store.

    This method will delegate to the appropriate private backend method. It returns the requested object, or throws an exception if the object was not found.

      my $object;
    
      try {
        $object = $class->load($id);
      } catch Error with {
        # ...
    
      };

Database I/O

  • $class->query($query)

    Runs the received query, and returns a statement handle which may be used to iterate through the query's result set.

    Reconnects to database, if necessary.

      sub($) {
        my $class = shift;
    
        my $query = sprintf('select * from %s', $class->tableName());
    
        my $sth = $class->query($query);
    
        while ( my $object = $sth->fetchrow_hashref() ) {
          $class->__marshal($object);
    
          # Stuff ...
        }
    
        $sth->finish();
      }
  • $class->write()

    Runs the received query against the reporting database, using the DBI do() method. Returns number of rows updated.

    Reconnects to database, if necessary.

      sub($$) {
        my $class = shift;
        my $value = shift;
    
        my $query = sprintf('update %s set foo = %s',
          $class->tableName(), $class->quote($value)
        )
    
        return $class->write($query);
      }
  • $class->allIds()

    Returns an OP::Array of all object ids in the receiving class.

      my $ids = $class->allIds();
    
      #
      # for loop way
      #
      for my $id ( @{ $ids } ) {
        my $object = $class->load($id);
    
        # Stuff ...
      }
    
      #
      # collector way
      #
      $ids->each( sub {
        my $object = $class->load($_);
    
        # Stuff ...
      } );
  • $class->memberClass($attribute)

    Only applicable for attributes which were asserted as OP::ExtID().

    Returns the class of object referenced by the named attribute.

    Example A:

      #
      # In a class prototype, there was an ExtID assertion:
      #
      create "OP::Example" => {
        userId => OP::ExtID->assert( "OP::Example::User" ),
    
        # Stuff ...
      };
    
      #
      # Hypothetical object "Foo" has a "userId" attribute,
      # which specifies an ID in a user table:
      #
      my $exa = OP::Example->spawn("Foo");
    
      #
      # Retrieve the external object by ID:
      #
      my $userClass = OP::Example->memberClass("userId");
    
      my $user = $userClass->load($exa->userId());
  • $class->doesIdExist($id)

    Returns true if the received ID exists in the receiving class's table.

  • $class->doesNameExist($name)

    Returns true if the received ID exists in the receiving class's table.

      my $name = "Bob";
    
      my $object = $class->doesNameExist($name)
        ? $class->loadByName($name)
        : $class->new( name => $name, ... );
  • $class->loadByName($name)

    Loader for named objects. Works just as load().

      my $object = $class->loadByName($name);
  • $class->spawn($name)

    Loader for named objects. Works just as load(). If the object does not exist on backing store, a new object with the received name is returned.

      my $object = $class->spawn($name);
  • $class->idForName($name)

    Return the corresponding row id for the received object name.

      my $id = $class->idForName($name);
  • $class->nameForId($id)

    Return the corresponding name for the received object id.

      my $name = $class->nameForId($id);
  • $class->allNames()

    Returns a list of all object ids in the receiving class. Requires DBI.

      my $names = $class->allNames();
    
      $names->each( sub {
        my $object = $class->loadByName($_);
    
        # Stuff ...
      } );
  • $class->quote($value)

    Returns a DBI-quoted (escaped) version of the received value. Requires DBI.

      my $quoted = $class->quote($hairy);
  • $class->databaseName()

    Returns the name of the receiving class's database. Corresponds to the lower-cased first-level Perl namespace, unless overridden in subclass.

      my $dbname = OP::Example->databaseName(); # returns "op"
    
      $dbname = Foo::Bar->databaseName(); # returns "foo"
  • $class->tableName()

    Returns the name of the receiving class's database table. Corresponds to the second-and-higher level Perl namespaces, using an underscore delimiter.

    Will probably want to override this, if subclass lives in a deeply nested namespace.

      my $table = OP::Example->tableName(); # returns "example"
    
      $table = Foo::Bar->tableName(); # returns "bar"
    
      $table = OP::Job::Example->tableName(); # returns "job_example"
  • $class->columnNames()

    Returns an OP::Array of all column names in the receiving class's table.

    This will be the same as the list returned by attributes(), minus any attributes which were asserted as Array or Hash and therefore live in a seperate linked table.

YAML Input

  • $class->loadYaml($string)

    Load the received string containing YAML into an instance of the receiving class.

    Warns and returns undef if the load fails.

      my $string = q|---
      foo: alpha
      bar: bravo
      |;
    
      my $object = $class->loadYaml($string);
  • $class->loadJson($string)

    Load the received string containing JSON into an instance of the receiving class.

    Warns and returns undef if the load fails.

PRIVATE CLASS METHODS

Class Callback Methods

Callback methods should be overridden when allocating a class with create() or package, but only if the defaults are not what the developer desires. Callback methods typically won't be used by external callers. This section documents the default behavior of OP::Persistence class callbacks, and provides override examples.

For ease of use, most of these methods can be overridden simply by setting a class variable with the same name, as the examples illustrate.

  • $class->__useYaml()

    Return a true value to maintain a YAML backend for all saved objects. If you want to skip YAML and use a database exclusively, this method should return a false value.

    Default inherited value is false. Set class variable to override.

      create "OP::Example" => {
        __useYaml => false
      };
  • $class->__useRcs()

    Returns a true value if the current class keeps revision history with its YAML backend, otherwise false.

    Default inherited value is false. Set class variable to override.

      create "OP::Example" => {
        __useRcs => false
      };
  • $class->__useDbi()

    Returns a true value if the current class uses a SQL backing store, otherwise false.

    If __useDbi() returns true, the database type specified by __dbiType() (MySQL, SQLite) will be used. See __dbiType() for how to override the class's database type.

    Default inherited value is true. Set class variable to override.

      create "OP::Example" => {
        __useDbi => false
      };
  • $class->__useMemcached()

    Returns a TTL in seconds, if the current class should attempt to use memcached to minimize database load.

    Returns a false value if this class should bypass memcached.

    If the memcached server can't be reached at package load time, callers will load from the physical backing store.

    Default inherited value is 300 seconds (5 minutes). Set class variable to override.

      create "OP::CachedExample" => {
        #
        # Ten minute TTL on cached objects:
        #
        __useMemcached => 600
      };

    Set to false or 0 to disable caching.

      create "OP::NeverCachedExample" => {
        #
        # No caching in play:
        #
        __useMemcached => false
      };
  • $class->__dbiType()

    This method is unused if __useDbi returns false.

    Returns the constant of the DBI adapter to be used. Applies only if $class->__useDbi() returns a true value. Override in subclass to specify the desired backing store.

    Returns a constant from the OP::Enum::DBIType enumeration. Currently, valid return values are OP::Enum::DBIType::MySQL and OP::Enum::DBIType::SQLite.

    Default inherited value is OP::Enum::DBIType::MySQL. Set class variable to override.

      create "OP::Example" => {
        __dbiType => OP::Enum::DBIType::SQLite
      };
  • $class->__baseAsserts()

    Assert base-level inherited assertions for objects. These include: id, name, mtime, ctime.

      __baseAsserts => sub($) {
        my $class = shift;
    
        my $base = $class->SUPER::__baseAsserts();
    
        $base->{parentId} ||= Str();
    
        return $base;
      }
  • $class->__basePath()

    Return the base filesystem path used to store objects of the current class.

    By default, this method returns a directory named after the current class, under the directory specified by yamlRoot in the local .oprc file.

      my $base = $class->__basePath();
    
      for my $path ( <$base/*> ) {
        print $path;
        print "\n";
      }

    To override the base path in subclass if needed:

      __basePath => sub($) {
        my $class = shift;
    
        return join( '/', customPath, $class );
      }
  • $class->__baseRcsPath()

    Returns the base filesystem path used to store revision history files.

    By default, this just tucks "RCS" onto the end of __basePath().

General

  • $class->__primaryKey()

    Returns the name of the attribute representing this class's primary ID. Unless overridden, this method returns the string "id".

  • $class->__localLoad($id)

    Load the object with the received ID from the backing store.

      my $object = $class->__localLoad($id);

Database I/O

  • $class->__loadFromDatabase($id)

    Instantiates an object by id, from the reporting database rather than the YAML backing store.

      my $object = $class->__loadFromDatabase($id);
  • $class->__marshal($hash)

    Loads any complex datatypes which were dumped to the database when saved, and blesses the received hash as an instance of the receiving class.

    Returns true on success, otherwise throws an exception.

      while ( my $object = $sth->fetchrow_hashref() ) {
        $class->__marshal($object);
      }
  • $class->__allIdsSth()

    Returns a statement handle to iterate over all ids. Requires DBI.

      my $sth = $class->__allIdsSth();
    
      while ( my ( $id ) = $sth->fetchrow_array() ) {
        my $object = $class->load($id);
    
        # Stuff ...
      }
    
      $sth->finish();
  • $class->__allNamesSth()

    Returns a statement handle to iterate over all names in class. Requires DBI.

      my $sth = $class->__allNamesSth();
    
      while ( my ( $name ) = $sth->fetchrow_array() ) {
        my $object = $class->loadByName($name);
    
        # Stuff ...
      }
    
      $sth->finish();
  • $class->__beginTransaction();

    Begins a new SQL transation.

  • $class->__rollbackTransaction();

    Rolls back the current SQL transaction.

  • $class->__commitTransaction();

    Commits the current SQL transaction.

  • $class->__beginTransactionStatement();

    Returns the SQL used to begin a SQL transaction

  • $class->__commitTransactionStatement();

    Returns the SQL used to commit a SQL transaction

  • $class->__rollbackTransactionStatement();

    Returns the SQL used to rollback a SQL transaction

  • $class->__schema()

    Returns the SQL used to construct the receiving class's table.

  • $class->__concatNameStatement()

    Return the SQL used to look up name concatenated with the other attributes which it is uniquely keyed with.

  • $class->__statementForColumn($attr, $type, $foreign, $unique)

    Returns the chunk of SQL used for this attribute in the CREATE TABLE syntax.

  • $class->__cacheKey($id)

    Returns the key for storing and retrieving this record in Memcached.

      #
      # Remove a record from the cache:
      #
      my $key = $class->__cacheKey($object->get($class->__primaryKey()));
    
      $memd->delete($key);
  • $class->__dropTable()

    Drops the receiving class's database table.

      use OP::Example;
    
      OP::Example->__dropTable();
  • $class->__createTable()

    Creates the receiving class's database table

      use OP::Example;
    
      OP::Example->__createTable();
  • $class->__selectRowStatement($id)

    Returns the SQL used to select a record by id.

  • $class->__allNamesStatement()

    Returns the SQL used to generate a list of all record names

  • $class->__allIdsStatement()

    Returns the SQL used to generate a list of all record ids

  • $class->__selectBool($query)

    Returns the results of the received query, as a binary true or false value.

  • $class->__selectSingle($query)

    Returns the first row of results from the received query, as a one-dimensional OP::Array.

      sub($$) {
        my $class = shift;
        my $name = shift;
    
        my $query = sprintf('select mtime, ctime from %s where name = %s',
          $class->tableName(), $class->quote($name)
        );
    
        return $class->__selectSingle($query);
      }
    
      #
      # Flat array of selected values, ie:
      #
      #   [ *userId, *ctime ]
      #
  • $class->__selectMulti($query)

    Returns each row of results from the received query, as a one-dimensional OP::Array.

      my $query = "select userId from session";
    
      #
      # Flat array of User IDs, ie:
      #
      #   [
      #     *userId,
      #     *userId,
      #     ...
      #   ]
      #
      my $userIds = $class->__selectMulti($query);

    Returns a two-dimensional OP::Array of OP::Arrays, if * or multiple columns are specified in the query.

      my $query = "select userId, mtime from session";
    
      #
      # Array of arrays, ie:
      #
      #   [
      #     [ *userId, *mtime ],
      #     [ *userId, *mtime ],
      #     ...
      #   ]
      #
      my $idsWithTime = $class->__selectMulti($query);
  • $class->__loadFromQuery($query)

    Returns the first row of results from the received query, as an instance of the current class. Good for simple queries where you don't want to have to deal with while().

      sub($$) {
        my $class = shift;
        my $id = shift;
    
        my $query = $class->__selectRowStatement($id);
    
        my $object = $class->__loadFromQuery($query) || die $@;
    
        # Stuff ...
      }
  • $class->__dbh()

    Creates a new DB connection, or returns the one which is currently active.

      sub($$) {
        my $class = shift;
        my $query = shift;
    
        my $dbh = $class->__dbh();
    
        my $sth = $dbh->prepare($query);
    
        while ( my $hash = $sth->fetchrow_hashref() ) {
          # Stuff ...
        }
    
        $sth->finish();
      }
  • $class->__dbi()

    Returns the currently active GlobalDBI object, or undef if there isn't one.

Flatfile I/O

  • $class->__loadYamlFromId($id)

    Return the instance with the received id (returns new instance if the object doesn't exist on disk yet)

      my $object = $class->__loadYamlFromId($id);
  • $class->__loadYamlFromPath($path)

    Return an instance from the YAML at the received filesystem path

      my $object = $class->__loadYamlFromPath('/tmp/foobar.123');
  • $class->__getSourceByPath($path)

    Quickly return the file contents for the received path. Basically cat a file into memory.

      my $example = $class->__getSourceByPath("/etc/passwd");
  • $class->__fsIds()

    Return the id of all instances of this class on disk.

      my $ids = $class->__fsIds();
    
      $ids->each( sub {
        my $object = $class->load($_);
    
        # Stuff ...
      } );
  • $class->__init()

    Override OP::Class->__init() to automatically create any missing tables in the database.

PUBLIC INSTANCE METHODS

General

  • $self->save($comment)

    Saves self to all appropriate backing stores.

      $object->save("This is a checkin comment");
  • $self->presave();

    Abstract callback method invoked just prior to saving an object.

    Implement this method in subclass, if additional object sanity tests are needed.

  • $self->remove()

    Remove self's DB record, and unlink the YAML backing store if present.

    Does not delete RCS history, if present.

      $object->remove();
  • $self->revert($version)

    Reverts self to the received version, which must be in the object's RCS file. This method is not usable unless __useRcs in the object's class is true.

  • $self->exists()

    Returns true if this object has ever been saved.

      my $object = OP::Example->new();
    
      my $false = $object->exists();
    
      $object->save();
    
      my $true = $object->exists();
  • $self->key()

    Returns the value for this object's primary database key, since the primary key may not always be "id".

    Equivalent to:

      $self->get( $class->__primaryKey() );

    Which, in most cases, yields the same value as:

      $self->id();
  • $self->memberInstances($key)

    Only applicable for attributes which were asserted as ExtID().

    Returns an OP::Array of objects referenced by the named attribute.

    Equivalent to using load() on the class returned by class method memberClass(), for each ID in the relationship.

      my $exa = OP::Example->spawn("Foo");
    
      my $users = $exa->memberInstances("userId");
  • $self->setIdsFromNames($attr, @names)

    Convenience setter for attributes which contain either an ExtID or an Array of ExtIDs.

    Sets the value for the received attribute name in self to the IDs of the received names. If a name is provided which does not correspond to a named object in the foreign table, a new object is created and saved, and the new ID is used.

    If the named objects do not yet exist, and have other required attributes other than "name", then this method will raise an exception. The referenced object will need to be explicitly saved before the referent.

      #
      # Set "parentId" in self to the ID of the object named "Mom":
      #
      $obj->setIdsFromNames("parentId", "Mom");
    
      #
      # Set "categoryIds" in self to the ID of the objects
      # named "Blue" and "Red":
      #
      $obj->setIdsFromNames("categoryIds", "Blue", "Red");

YAML Output

Methods for YAML output may be found in OP::Hash.

RCS Output

  • $self->revisions()

    Return an array of all of this object's revision numbers

  • $self->revisionInfo()

    Return a hash of info for this file's checkins

  • $self->head()

    Return the head revision number for self's backing store.

PRIVATE INSTANCE METHODS

General

  • $self->_localSave($comment)

    Saves self to all applicable backing stores.

  • $self->_localSaveInsideTransaction($comment);

    Private backend method called by _localSave() when inside of a database transaction.

Database I/O

  • $self->_updateRowStatement()

    Returns the SQL used to run an "UPDATE" statement for the receiving object.

  • $self->_insertRowStatement()

    Returns the SQL used to run an "INSERT" statement for the receiving object.

  • $self->_deleteRowStatement()

    Returns the SQL used to run a "DELETE" statement for the receiving object.

  • $self->_updateRecord()

    Executes an INSERT or UPDATE for this object's record. Callback method invoked from _localSave().

    Returns number of rows on UPDATE, or ID of object created on INSERT.

  • $self->_quotedValues()

    Callback method used to construct the values portion of an UPDATE or INSERT query.

Flatfile I/O

  • $self->_fsDelete()

    Unlink self's YAML datafile from the filesystem. Does not dereference self from memory.

  • $self->_fsSave()

    Save $self as YAML to a local filesystem. Path is $class->__basePath + $self->id();

  • $self->_path()

    Return the filesystem path to self's YAML data store.

  • $self->_rcsPath()

    Return the filesystem path to self's RCS history file.

  • $self->_saveToPath($path)

    Save $self as YAML to a the received filesystem path

RCS I/O

  • $self->_checkout($rcs, $path)

    Performs the RCS co command on the received path, using the received Rcs object.

  • $self->_checkin($rcs, $path, [$comment])

    Performs the RCS ci command on the received path, using the received Rcs object.

  • $self->_rcs()

    Return an instance of the Rcs class corresponding to self's backing store.

  • $self->_newId()

    Generates a new ID for the current object. Default is GUID-style.

      _newId => sub {
        my $self = shift;
    
        return OP::Utility::newId();
      }

SEE ALSO

Cache::Memcached::Fast, Rcs, YAML::Syck, GlobalDBI, DBI

This file is part of OP.