--- Types-Serialiser/Serialiser.pm 2013/10/27 19:49:33 1.1 +++ Types-Serialiser/Serialiser.pm 2013/11/04 15:12:16 1.8 @@ -17,9 +17,9 @@ package Types::Serialiser; -use common::sense; +use common::sense; # required to suppress annoying warnings -our $VERSION = 0.01; +our $VERSION = 0.03; =head1 SIMPLE SCALAR CONSTANTS @@ -99,6 +99,28 @@ =cut +BEGIN { + # for historical reasons, and to avoid extra dependencies in JSON::PP, + # we alias *Types::Serialiser::Boolean with JSON::PP::Boolean. + package JSON::PP::Boolean; + + *Types::Serialiser::Boolean:: = *JSON::PP::Boolean::; +} + +{ + # this must done before blessing to work around bugs + # in perl < 5.18 (it seems to be fixed in 5.18). + package Types::Serialiser::BooleanBase; + + use overload + "0+" => sub { ${$_[0]} }, + "++" => sub { $_[0] = ${$_[0]} + 1 }, + "--" => sub { $_[0] = ${$_[0]} - 1 }, + fallback => 1; + + @Types::Serialiser::Boolean::ISA = Types::Serialiser::BooleanBase::; +} + our $true = do { bless \(my $dummy = 1), Types::Serialiser::Boolean:: }; our $false = do { bless \(my $dummy = 0), Types::Serialiser::Boolean:: }; our $error = do { bless \(my $dummy ), Types::Serialiser::Error:: }; @@ -112,19 +134,11 @@ sub is_false ($) { !$_[0] && UNIVERSAL::isa $_[0], Types::Serialiser::Boolean:: } sub is_error ($) { UNIVERSAL::isa $_[0], Types::Serialiser::Error:: } -package Types::Serialiser::Boolean; - -use overload - "0+" => sub { ${$_[0]} }, - "++" => sub { $_[0] = ${$_[0]} + 1 }, - "--" => sub { $_[0] = ${$_[0]} - 1 }, - fallback => 1; - package Types::Serialiser::Error; sub error { require Carp; - Carp::croak ("caught attempt to use Types::Serialiser::error value"); + Carp::croak ("caught attempt to use the Types::Serialiser::error value"); }; use overload @@ -133,6 +147,81 @@ "--" => \&error, fallback => 1; +=head1 NOTES FOR XS USERS + +The recommended way to detect whether a scalar is one of these objects +is to check whether the stash is the C or +C stash, and then follow the scalar reference to +see if it's C<1> (true), C<0> (false) or C (error). + +While it is possible to use an isa test, directly comparing stash pointers +is faster and guaranteed to work. + +For historical reasons, the C stash is +just an alias for C. When printed, the classname +with usually be C, but isa tests and stash pointer +comparison will normally work correctly (i.e. Types::Serialiser::true ISA +JSON::PP::Boolean, but also ISA Types::Serialiser::Boolean). + +=head1 A GENERIC OBJECT SERIALIATION PROTOCOL + +This section explains the object serialisation protocol used by +L. It is meant to be generic enough to support any kind of +generic object serialiser. + +This protocol is called "the Types::Serialiser object serialisation +protocol". + +=head2 ENCODING + +When the encoder encounters an object that it cannot otherwise encode (for +example, L can encode a few special types itself, and will first +attempt to use the special C serialisation protocol), it will +look up the C method on the object. + +If it exists, it will call it with two arguments: the object to serialise, +and a constant string that indicates the name of the data model or data +format. For example L uses C, and L and L +(or any other JSON serialiser), would use C as second argument. + +The C method can then return zero or more values to identify the +object instance. The serialiser is then supposed to encode the class name +and all of these return values (which must be encodable in the format) +using the relevant form for perl objects. In CBOR for example, there is a +registered tag number for encoded perl objects. + +The values that C returns must be serialisable with the serialiser +that calls it. Therefore, it is recommended to use simple types such as +strings and numbers, and maybe array references and hashes (basically, the +JSON data model). You can always use a more complex format for a specific +data model by checking the second argument. + +=head2 DECODING + +When the decoder then encounters such an encoded perl object, it should +look up the C method on the stored classname, and invoke it with the +classname, the constant string to identify the data model/data format, and +all the return values returned by C. + +=head2 EXAMPLES + +See the C section in the L manpage for +more details, an example implementation, and code examples. + +Here is an example C/C method pair: + + sub My::Object::FREEZE { + my ($self, $model) = @_; + + ($self->{type}, $self->{id}, $self->{variant}) + } + + sub My::Object::THAW { + my ($class, $model, $type, $id, $variant) = @_; + + $class->new (type => $type, id => $id, variant => $variant) + } + =head1 BUGS The use of L makes this module much heavier than it should be