Version 0.32
[gitmo/MooseX-Storage.git] / lib / MooseX / Storage / Engine.pm
index d98fb6e..ccf2fde 100644 (file)
@@ -3,7 +3,7 @@ package MooseX::Storage::Engine;
 use Moose;
 use Scalar::Util qw(refaddr);
 
-our $VERSION   = '0.20';
+our $VERSION   = '0.32';
 our $AUTHORITY = 'cpan:STEVAN';
 
 # the class marker when
@@ -22,7 +22,7 @@ has 'seen' => (
     default => sub {{}}
 );
 
-has 'object' => (is => 'rw', isa => 'Object');
+has 'object' => (is => 'rw', isa => 'Object', predicate => '_has_object');
 has 'class'  => (is => 'rw', isa => 'Str');
 
 ## this is the API used by other modules ...
@@ -35,22 +35,22 @@ sub collapse_object {
        $self->seen->{refaddr $self->object} = undef;
        
     $self->map_attributes('collapse_attribute', \%options);
-    $self->storage->{$CLASS_MARKER} = $self->object->meta->identifier;   
+    $self->storage->{$CLASS_MARKER} = $self->object->meta->identifier;
        return $self->storage;
 }
 
 sub expand_object {
     my ($self, $data, %options) = @_;
-   
+
     $options{check_version}       = 1 unless exists $options{check_version};
-    $options{check_authority}     = 1 unless exists $options{check_authority};  
+    $options{check_authority}     = 1 unless exists $options{check_authority};
 
        # NOTE:
        # mark the root object as seen ...
-       $self->seen->{refaddr $data} = undef;   
-   
+       $self->seen->{refaddr $data} = undef;
+
     $self->map_attributes('expand_attribute', $data, \%options);
-       return $self->storage;   
+       return $self->storage;
 }
 
 ## this is the internal API ...
@@ -82,7 +82,7 @@ sub collapse_attribute_value {
         if ref $value;
 
     if (defined $value && $attr->has_type_constraint) {
-        my $type_converter = $self->find_type_handler($attr->type_constraint);
+        my $type_converter = $self->find_type_handler($attr->type_constraint, $value);
         (defined $type_converter)
             || confess "Cannot convert " . $attr->type_constraint->name;
         $value = $type_converter->{collapse}->($value, $options);
@@ -98,12 +98,12 @@ sub expand_attribute_value {
     if( ref $value and not(
         $options->{disable_cycle_check} or
         $self->class->does('MooseX::Storage::Traits::DisableCycleDetection')
-    )) {       
+    )) {
         $self->check_for_cycle_in_collapse($attr, $value)
     }
-   
+
     if (defined $value && $attr->has_type_constraint) {
-        my $type_converter = $self->find_type_handler($attr->type_constraint);
+        my $type_converter = $self->find_type_handler($attr->type_constraint, $value);
         $value = $type_converter->{expand}->($value, $options);
     }
        return $value;
@@ -143,7 +143,7 @@ sub map_attributes {
     } grep {
         # Skip our special skip attribute :)
         !$_->does('MooseX::Storage::Meta::Attribute::Trait::DoNotSerialize')
-    } ($self->object || $self->class)->meta->get_all_attributes;
+    } ($self->_has_object ? $self->object : $self->class)->meta->get_all_attributes;
 }
 
 ## ------------------------------------------------------------------
@@ -158,27 +158,27 @@ sub map_attributes {
 # below, so I need easy access
 my %OBJECT_HANDLERS = (
     expand => sub {
-        my ($data, $options) = @_;  
+        my ($data, $options) = @_;
         (exists $data->{$CLASS_MARKER})
             || confess "Serialized item has no class marker";
         # check the class more thoroughly here ...
         my ($class, $version, $authority) = (split '-' => $data->{$CLASS_MARKER});
         my $meta = eval { $class->meta };
-        confess "Class ($class) is not loaded, cannot unpack" if $@;    
-       
+        confess "Class ($class) is not loaded, cannot unpack" if $@;
+
         if ($options->{check_version}) {
             my $meta_version = $meta->version;
-            if (defined $meta_version && $version) {           
+            if (defined $meta_version && $version) {
                 if ($options->{check_version} eq 'allow_less_than') {
                     ($meta_version <= $version)
                         || confess "Class ($class) versions is not less than currently available."
-                                 . " got=($version) available=($meta_version)";               
+                                 . " got=($version) available=($meta_version)";
                 }
                 elsif ($options->{check_version} eq 'allow_greater_than') {
                     ($meta->version >= $version)
                         || confess "Class ($class) versions is not greater than currently available."
-                                 . " got=($version) available=($meta_version)";               
-                }           
+                                 . " got=($version) available=($meta_version)";
+                }
                 else {
                     ($meta->version == $version)
                         || confess "Class ($class) versions don't match."
@@ -186,15 +186,15 @@ my %OBJECT_HANDLERS = (
                 }
             }
         }
-       
+
         if ($options->{check_authority}) {
             my $meta_authority = $meta->authority;
             ($meta->authority eq $authority)
                 || confess "Class ($class) authorities don't match."
                          . " got=($authority) available=($meta_authority)"
-                if defined $meta_authority && defined $authority;           
+                if defined $meta_authority && defined $authority;
         }
-           
+
         # all is well ...
         $class->unpack($data, %$options);
     },
@@ -216,7 +216,7 @@ my %TYPES = (
     # the JSON engines are stupid/annoying/frustrating
     'Int'      => { expand => sub { $_[0] + 0 }, collapse => sub { $_[0] + 0 } },
     'Num'      => { expand => sub { $_[0] + 0 }, collapse => sub { $_[0] + 0 } },
-    # These are boring ones, so they use the identity function ...   
+    # These are boring ones, so they use the identity function ...
     'Str'      => { expand => sub { shift }, collapse => sub { shift } },
     'Bool'     => { expand => sub { shift }, collapse => sub { shift } },
     # These are the trickier ones, (see notes)
@@ -225,11 +225,11 @@ my %TYPES = (
     # your ArrayRef and/or HashRef one level
     # down and inflate any objects we find.
     # But this is where it ends, it is too
-    # expensive to try and do this any more 
+    # expensive to try and do this any more
     # recursively, when it is probably not
     # nessecary in most of the use cases.
     # However, if you need more then this, subtype
-    # and add a custom handler.   
+    # and add a custom handler.
     'ArrayRef' => {
         expand => sub {
             my ( $array, @args ) = @_;
@@ -242,7 +242,7 @@ my %TYPES = (
         },
         collapse => sub {
             my ( $array, @args ) = @_;
-            # NOTE:        
+            # NOTE:
             # we need to make a copy cause
             # otherwise it will affect the
             # other real version.
@@ -261,11 +261,11 @@ my %TYPES = (
                          && exists $hash->{$k}->{$CLASS_MARKER};
                 $hash->{$k} = $OBJECT_HANDLERS{expand}->($hash->{$k}, @args);
             }
-            $hash;           
+            $hash;
         },
         collapse => sub {
             my ( $hash, @args ) = @_;
-            # NOTE:        
+            # NOTE:
             # we need to make a copy cause
             # otherwise it will affect the
             # other real version.
@@ -273,7 +273,7 @@ my %TYPES = (
                 blessed($hash->{$_})
                     ? ($_ => $OBJECT_HANDLERS{collapse}->($hash->{$_}, @args))
                     : ($_ => $hash->{$_})
-            } keys %$hash }           
+            } keys %$hash }
         }
     },
     'Object'   => \%OBJECT_HANDLERS,
@@ -283,7 +283,7 @@ my %TYPES = (
     # - SL
     #'CodeRef' => {
     #    expand   => sub {}, # use eval ...
-    #    collapse => sub {}, # use B::Deparse ...       
+    #    collapse => sub {}, # use B::Deparse ...
     #}
 );
 
@@ -300,22 +300,30 @@ sub remove_custom_type_handler {
 }
 
 sub find_type_handler {
-    my ($self, $type_constraint) = @_;
-   
+    my ($self, $type_constraint, $value) = @_;
+
     # check if the type is a Maybe and
     # if its parent is not parameterized.
     # If both is true recurse this method
     # using ->type_parameter.
-    return $self->find_type_handler($type_constraint->type_parameter)
-        if $type_constraint->parent eq 'Maybe'
-          and not $type_constraint->parent->can('type_parameter');
+    return $self->find_type_handler($type_constraint->type_parameter, $value)
+        if ($type_constraint->parent && $type_constraint->parent eq 'Maybe'
+          and not $type_constraint->parent->can('type_parameter'));
+
+    # find_type_for is a method of a union type.  If we can call that method
+    # then we are dealign with a union and we need to ascertain which of
+    # the union's types we need to use for the value we are serializing.
+    if($type_constraint->can('find_type_for')) {
+        my $tc = $type_constraint->find_type_for($value);
+        return $self->find_type_handler($tc, $value) if defined($tc);
+    }
 
     # this should handle most type usages
     # since they they are usually just
     # the standard set of built-ins
     return $TYPES{$type_constraint->name}
         if exists $TYPES{$type_constraint->name};
-     
+
     # the next possibility is they are
     # a subtype of the built-in types,
     # in which case this will DWIM in
@@ -327,7 +335,7 @@ sub find_type_handler {
         return $TYPES{$type}
             if $type_constraint->is_subtype_of($type);
     }
-   
+
     # NOTE:
     # the reason the above will work has to
     # do with the fact that custom subtypes
@@ -335,16 +343,16 @@ sub find_type_handler {
     # the guts of a type, and not for some
     # weird structural thing which would
     # need to be accomidated by the serializer.
-    # Of course, mst or phaylon will probably 
+    # Of course, mst or phaylon will probably
     # do something to throw this assumption
     # totally out the door ;)
     # - SL
-   
+
     # NOTE:
     # if this method hasnt returned by now
     # then we have no been able to find a
     # type constraint handler to match
-    confess "Cannot handle type constraint (" . $type_constraint->name . ")";   
+    confess "Cannot handle type constraint (" . $type_constraint->name . ")";
 }
 
 sub find_type_handler_for {
@@ -366,7 +374,17 @@ MooseX::Storage::Engine - The meta-engine to handle collapsing and expanding obj
 
 =head1 DESCRIPTION
 
-No user serviceable parts inside. If you really want to know, read the source :)
+There really aren't any major user serviceable parts here. However the typical
+use case is adding new non-Moose classes to the type registry for
+serialization. Here is an example of this for DateTime objects. This
+assumes a C<DateTime> type has been registered.
+
+    MooseX::Storage::Engine->add_custom_type_handler(
+        'DateTime' => (
+            expand   => sub { DateTime->new(shift) },
+            collapse => sub { (shift)->iso8601 },
+        )
+    );
 
 =head1 METHODS