#
# $Id: Serializer.pm,v 0.1 2001/04/22 17:57:03 ram Exp $
#
#  Copyright (c) 1998-2001, Raphael Manfredi
#  Copyright (c) 2000-2001, Christophe Dehaudt
#  
#  You may redistribute only under the terms of the Artistic License,
#  as specified in the README file that comes with the distribution.
#
# HISTORY
# $Log: Serializer.pm,v $
# Revision 0.1  2001/04/22 17:57:03  ram
# Baseline for first Alpha release.
#
# $EndLog$
#

use strict;

package CGI::MxScreen::Serializer;

use Carp::Datum;
use Log::Agent;

#
# ->make
#
# Creation routine.
#
sub make { logconfess "deferred" }

#
# ->_init
#
# Records freezer, thawer and compression parameters.
#
sub _init {
	DFEATURE my $f_;
	my $self = shift;
	my ($freeze, $thaw, $compress) = @_;

	$self->{freezer} = $freeze;
	$self->{thawer} = $thaw;
	$self->{compress} = $compress;
}

#
# Attributes
#

sub freezer		{ $_[0]->{freezer} }
sub thawer		{ $_[0]->{thawer} }
sub compress	{ $_[0]->{compress} }

#
# ->serialize
#
# Serialize reference.
# Returns frozen string, undef on error.
#
sub serialize {
	DFEATURE my $f_;
	my $self = shift;
	my ($ref) = @_;

	DREQUIRE ref $ref, "serializing a reference";

	my $frozen;
	eval {
		$frozen = &{$self->freezer}($ref);
	};
	logerr "unable to serialize $ref: $@" if chop $@;
	return DVAL undef unless defined $frozen;

	if ($self->compress) {
		require Compress::Zlib;
		$frozen = Compress::Zlib::compress($frozen);
	}

	return DVAL $frozen;
}

#
# ->deserialize
#
# Deserialize frozen string, passed as reference.
# Returns ref to thawed data.
#
sub deserialize {
	my $self = shift;
	my ($frozen) = @_;

	DREQUIRE !ref $frozen, "deserializing a scalar";

	if ($self->compress) {
		require Compress::Zlib;
		$frozen = Compress::Zlib::uncompress($frozen);
	}

	my $data;
	eval {
		$data = &{$self->thawer}($frozen);
	};
	logerr "unable to deserialize $frozen: $@" if chop $@;
	return DVAL undef unless defined $data;

	DENSURE ref $data, "data returned as reference";

	return DVAL $data;
}

1;

=head1 NAME

CGI::MxScreen::Serializer - Abstract serializer interface

=head1 SYNOPSIS

 # Deferred class, only heirs may be instantiated

 # In heirs:
 use base qw(CGI::MxScreen::Serializer);

 sub make {             # define the creation routine
     ...
     $self->_init(\&freeze, \&thaw, $compress);
 }

 # Access interface - $ser is an instantiated heir
 my $serialized   = $ser->serialize($ref);
 my $deserialized = $ser->deserialize($serialized);

=head1 DESCRIPTION

This module implements a serializer abstraction and should probably be
a CPAN module of its own.  I'm seriously thinking about it.

The C<CGI::MxScreen::Serializer> class is B<deferred>.  The only thing
it lacks is a creation routine, which will initialize the
C<freeze> and C<thaw> attributes with code references to the proper
freezing and thawing routines.

It bluntly assumes those routine will conform to the following signatures:

    freeze(x: REF): STRING
    thaw(x: STRING): REF

and satisfy the following condition, for every I<x>:

    thaw(freeze(x)).deep_equal(x)

where I<deep_equal> is an operation testing that its arguments are
structurally equivalent, whatever that means.

This class also supports on-the-fly compression and decompression of the
serialized data via C<Compress::Zlib>.

=head1 SUPPORTED SERIALIZERS

Currently, the only serializer available is
C<CGI::MxScreen::Serializer::Storable> which is simply installing
C<Storable>.

=head1 INTERFACE

=head2 Attributes

The following read-only attributes are defined.  The are meant to be
initialized at creation time via C<_init()>:

=over 4

=item C<compress>

A boolean flag, stating whether C<Compress::Zlib> compression and decompression
should be performed.

=item C<freezer>

A CODE reference on the freezing routine.

=item C<thawer>

A CODE reference on the thawing routine.

=back

=head2 Routines

=over 4

=item C<_init> I<freezer>, I<thawer>, I<compress_flag>

This routine must be called by the C<make()> routine in heirs to initialize
the attributes.

=item C<deserialize> I<frozen>

Deserialize the I<frozen> serialized string generated by C<serialize()>
and return a reference to its root object.

=item C<serialize> I<reference>

Serialize the I<reference> and return a string that can be deserialized
by C<deserialize>.

=back

=head1 AUTHOR

Raphael Manfredi F<E<lt>Raphael_Manfredi@pobox.comE<gt>>

=head1 SEE ALSO

CGI::MxScreen::Serializer::Storable(3).

=cut