Class-Std-Storable

 view release on metacpan or  search on metacpan

lib/Class/Std/Storable.pm  view on Meta::CPAN

package Class::Std::Storable;

use version; $VERSION = qv('0.0.1');
use strict;
use warnings;
use Class::Std; #get subs from parent to export
use Carp;

#hold attributes by package
my %attributes_of;

my @exported_subs = qw(
    new
    ident
    DESTROY
    MODIFY_HASH_ATTRIBUTES
    MODIFY_CODE_ATTRIBUTES
    AUTOLOAD
    _DUMP
    STORABLE_freeze
    STORABLE_thaw
);

sub import {
    no strict 'refs';
    for my $sub ( @exported_subs ) {
        *{ caller() . '::' . $sub } = \&{$sub};
    }
}

#NOTE: this subroutine should override the one that's imported
#by the "use Class::Std" above.
{
    my $old_sub = \&Class::Std::MODIFY_HASH_ATTRIBUTES;
    my %positional_arg_of;
    my $new_sub = sub {
        my ($package, $referent, @attrs) = @_;
        my @return_attrs = $old_sub->(@_);

        for my $attr (@attrs) {
            next if $attr !~ m/\A ATTRS? \s* (?:[(] (.*) [)] )? \z/xms;
            my $name;
            #we have a backup if no name is given for the attribute.
            $positional_arg_of{$package} ||= "__Positional_0001";
            #but we would prefer to know the argument as the class does.
            if (my $config = $1) {
                $name = Class::Std::_extract_init_arg($config)
                    || Class::Std::_extract_get($config)
                    || Class::Std::_extract_set($config);
            }
            $name ||= $positional_arg_of{$package}++;
            push @{$attributes_of{$package}}, {
                ref      => $referent,
                name     => $name,
            };
        }
        return @return_attrs;
    };

    no warnings; #or this complains about redefining sub
    *MODIFY_HASH_ATTRIBUTES = $new_sub;
};

sub STORABLE_freeze {
    #croak "must be called from Storable" unless caller eq 'Storable';
    #unfortunately, Storable never appears on the call stack.
    my($self, $cloning) = @_;
    $self->STORABLE_freeze_pre($cloning)
        if UNIVERSAL::can($self, "STORABLE_freeze_pre");
    my $id = ident($self);
    require Storable;
    my $serialized = Storable::freeze( \ (my $anon_scalar) );

    my %frozen_attr; #to be constructed
    my @package_list = ref $self;
    my %package_seen = ( ref($self) => 1 ); #ignore diamond/looped base classes :-)
    PACKAGE:
    while( my $package = shift @package_list) {
        #make sure we add any base classes to the list of
        #packages to examine for attributes.
        { no strict 'refs';
            for my $base_class ( @{"${package}::ISA"} ) {
                push @package_list, $base_class
                    if !$package_seen{$base_class}++;
            }
        }
        #examine attributes from known packages only
        my $attr_list_ref = $attributes_of{$package} or next PACKAGE;

        #look for any attributes of this object for this package
        ATTR:
        for my $attr_ref ( @{$attr_list_ref} ) {
            #nothing to do if attr not set for this object
            next ATTR if !exists $attr_ref->{ref}{$id};
            #save the attr by name into the package hash
            $frozen_attr{$package}{ $attr_ref->{name} }
                = $attr_ref->{ref}{$id};
        }
    }

    $self->STORABLE_freeze_post($cloning, \%frozen_attr)
        if UNIVERSAL::can($self, "STORABLE_freeze_post");
    return ($serialized, \%frozen_attr );



( run in 1.652 second using v1.01-cache-2.11-cpan-bbb979687b5 )