=head1 NAME

SPOPS::Manual::Serialization - How SPOPS serializes objects

=head1 SYNOPSIS

This part of the SPOPS manual aims to answer the following questions:

=over 4

=item *

How does serialization work?

=item *

How can I customize serialization?

=item *

How does caching work?

=item *

How can I lazy-load my fields?

=back

=head1 DESCRIPTION

=head1 CUSTOMIZING SERIALIZATION

=head2 Pre/Post Hooks

SPOPS allows you to create multiple pre- and post-actions to
C<save()>, C<fetch()> and C<remove()>. These actions can be as simple
or complex as you like and allow objects to be extremely flexible. 

These the actions implemented in the hooks are called rules, and each
collection of rules is called a ruleset. Rulesets are documented in
L<SPOPS::Manual::ObjectRules|SPOPS::Manual::ObjectRules>.

=head2 Failed Actions

If an action fails, the 'fail' method associated with that action is
triggered. This can be a notification to an administrator, or saving
the data in the filesystem after a failed save.

B<fail_fetch()>

Called after a fetch has been unsuccessful.

B<fail_save()>

Called after a save has been unsuccessful.

B<fail_remove()>

Called after a remove has been unsuccessful.

=head1 CACHING

SPOPS has hooks for object caching. You will need to return a caching
object via the a C<global_cache()> method implemented either in your
SPOPS object class one of its parents.

The caching object will have a simple interface so it's easy to wrap
it around your favorite caching backend. (Who wants to write this
stuff from scratch themselves?)

B<pre_cache_fetch()>

Called before an item is fetched from the cache; if this is called, we
know that the object is in the cache, we just have not retrieved it
yet.

B<post_cache_fetch()>

Called after an item is successfully retrieved from the cache.

B<pre_cache_save()>

Called before an object has been cached.

B<post_cache_save()>

Called after an object has been cached.

B<pre_cache_remove()>

Called before an object is removed from the cache.

B<post_cache_remove()>

Called after an object is successfully removed from the cache.

=head1 LAZY LOADING

This section describes how to implement lazy loading for your objects.

Every implementation should be able to handle the following column
groups that should always be available:

=over 4

=item *

B<_id_field>: Column group containing only the ID field. This can be
useful if you are cycling through large groups of objects only for
their ID value. (For instance, to set security values for many objects
at once.)

=back

=head2 What You Need To Do

Here are the methods you need to create to implement lazy loading:

B<get_lazy_load_sub()>

Called by SPOPS when initializing a new object if one or more
'column_group' entries are found in the configuration. It should
return a coderef that implements lazy loading for a single field. (See
below.)

B<perform_lazy_load( $class, \%data, $field )>

Interface for a subclass to implement lazy loading. The method
C<get_lazy_load_sub()> should return a coderef conforming to this
interface.

The implementation should return the value for C<$field> given the
object information C<\%data>, which is a map of fieldname to value and
includes the ID field and value of the object.

For lazy loading usage, see L<SPOPS::Manual::Object|SPOPS::Manual::Object>.

=head2 What SPOPS does

These are methods implemented by SPOPS for lazy loading.

B<is_loaded( $field )>

Returns true if C<$field> has been loaded, false if not.

B<set_loaded( $field )>

Sets the 'loaded' property of C<$field> to true.

B<clear_loaded( $field )>

Sets the 'loaded' property of C<$field> to false.

B<set_all_loaded()>

Sets the 'loaded' property of all fields in the object to true.

B<clear_all_loaded()>

Sets the 'loaded' property of all fields in the object to false.

For an example of how a SPOPS subclass implements lazy-loading, see
L<SPOPS::DBI|SPOPS::DBI>.

=head1 STORABLE SERIALIZATION

The main L<SPOPS|SPOPS> class from which all SPOPS objects derive has
the methods C<store()>, C<nstore()>, C<retrieve()> and
C<fd_retrieve()> acting as delegates for methods of the same name in
L<Storable|Storable>. (We can add more as needed.)

Example:

  1:  # Fetch the user object and if retrieved, store it in the session
  2: 
  3:  my $session = $session_class->create;
  4:  my $login_name = read_login_name();
  5:  my $user = eval { $user_class->fetch_by_login_name( $login_name ) };
  6:  if ( $@ ) { ... }
  7:  $session->{user_object} = $user->store;
  8: 
  9:  # ... time passes ...
 10: 
 11:  my $session = $session_class->fetch( $session_id );
 12:  my ( $user );
 13:  if ( $session->{user_object} ) {
 14:     $user = $user_class->retrieve( $session->{user_object} );
 15:  }
 16:  else {
 17:      my $login_name = read_login_name();
 18:      my $user = eval { $user_class->fetch_by_login_name( $login_name ) };
 19:      ...
 20:  }

This has not been extensively tested, particularly with the
C<nstore()> option for transporting objects among different
architectures. In theory, this shouldn't be an issue at all, as long
as when thawing the object the specific SPOPS class has been
previously initialized.

This could be an interesting area of future development, since you
could in theory send an object over a network along with a minimal
configuration and object definition that, when it reached the
C<read_code> section of the code generation, reached out over the
network to read in the actual meat of a class. Basically, the thawing
routine could check to see if it had processed the SPOPS class and if
not, grab it off the network and whip it up.

=head1 COPYRIGHT

Copyright (c) 2001-2002 Chris Winters. All rights reserved.

See L<SPOPS::Manual|SPOPS::Manual> for license.

=head1 AUTHORS

Chris Winters E<lt>chris@cwinters.comE<gt>