Ambrosia
view release on metacpan or search on metacpan
lib/Ambrosia/core/Object.pm view on Meta::CPAN
package Ambrosia::core::Object;
use strict;
use warnings;
use Carp;
use integer;
use XML::LibXML();
use Data::Serializer;
use overload '%{}' => \&__get_hash, fallback => 1;
use Ambrosia::error::Exceptions;
use Ambrosia::core::Nil;
use Ambrosia::Assert;
our $VERSION = 0.010;
unless ( $::__AMBROSIA_ACCESS_ALLOW )
{
*__get_hash = sub {
$_[0]->[1] ||= {};
return $_[0]->[1] if $::__AMBROSIA_ACCESS_ALLOW;
my $pkg = caller(0);
my $self = shift;
if ( $pkg eq ref $self || $self->isa($pkg) )
{
return $self->[1];
}
else
{
throw Ambrosia::error::Exception::AccessDenied("Access denied for $pkg in $self (@_); caller0: " . join ';', grep {$_} caller(0) );
}
};
}
else
{
*__get_hash = sub { return $_[0]->[1] ||= {}; };
}
### constructor ###
sub new
{
my $proto = shift;
my $class = ref($proto) || $proto;
if ($class->__AMBROSIA_IS_ABSTRACT__)
{
throw Ambrosia::error::Exception 'You cannot instance abstract class ' . $class;
}
my $self = bless [[]], $class;
$self->_init(@_);
return $self;
}
sub fields
{
return ();
}
### run from new ###
sub _init
{
my $self = shift;
return $self unless scalar @_;
my %params = @_ == 1 ? %{$_[0]} : @_;
foreach ( keys %params )
{
if ( eval {$self->can($_)} )
{
$self->$_ = $params{$_};
}
else
{
croak 'Not found property ' . $_ . ' in ' . ref($self);
}
}
return $self;
}
sub value
{
my $self = shift;
my %FLDS; @FLDS{$self->fields} = ();
my @res = map { $self->$_ } @_ ? (grep {exists $FLDS{$_} || throw Ambrosia::error::Exception::AccessDenied 'value: access denied - ' . $_} @_) : $self->fields;
return wantarray ? @res : \@res;
}
sub string_dump
{
return Data::Serializer->new(serializer => 'Storable', compress => 1)->serialize($_[0]);
}
sub string_restore
{
my $dump = shift;
return new Ambrosia::core::Nil unless $dump;
my $obj = Data::Serializer->new(serializer => 'Storable')->deserialize($dump);
reflection( sub {
if ( my $refObj = ref $_[0] )
{
Ambrosia::core::ClassFactory::load_class($refObj)
}
}, $obj );
my $caller = ref $obj;
no strict 'refs';
foreach my $f ( keys %{"$caller\::__AMBROSIA_INTERNAL_FLDS__"} )
{
reflection( sub {
if ( my $refObj = ref $_[0] )
{
Ambrosia::core::ClassFactory::load_class($refObj)
}
}, $obj->$f );
}
return $obj;
}
sub reflection #(&@)
{
my $filter = shift;
my $value = shift;
my $refValue = ref $value;
if ( $refValue eq 'ARRAY' )
{
return [ map { reflection($filter, $_) } @$value ];
}
elsif ( $refValue eq 'HASH' )
{
return { map { $_ => reflection($filter, $_) } keys %$value };
}
elsif ( $refValue eq 'CODE' )
{
my $r = $filter->($value->());
return \$r;
}
else
{
return $filter->($value);
lib/Ambrosia/core/Object.pm view on Meta::CPAN
my $hash = {};
foreach my $f ( $self->fields )
{
$hash->{$f} = reflection( sub {
my $v = shift;
if ( ref $v eq 'SCALAR' )
{
return $$v;
}
elsif ( ref $v && eval{ $v->can('as_hash') } )
{
return $v->as_hash($ignore, $f);
}
else
{
return $v;
}
}, $self->$f );
}
foreach ( @methods )
{
eval
{
my($m, $a, $p, @P);
@P = ();
if ( ($m, $a, $p) = ( $_ =~ /^(.+?)(?::(.+?))?\{(.*)\}$/s ) )
{
while ( $p =~ /^\s*(.+?(?:\{(?:.*?)\})?)\s*(?:,|$)(.*)/s )
{
$p = $2;
push @P, $1;
}
}
elsif ( ($m, $a) = ( $_ =~ /^(.+?)(?::(.+?))$/s ) )
{
}
else
{
$m = $_;
}
$hash->{$a||$m} = reflection( sub {
my $v = shift;
if ( ref $v eq 'SCALAR' )
{
return $$v;
}
elsif ( ref $v && eval{ $v->can('as_hash') } )
{
return $v->as_hash($ignore, $p);
}
else
{
return $v;
}
}, $self->$m(@P) );
};
if ( $@ )
{
throw Ambrosia::error::Exception $@ unless $ignore;
};
}
return $hash;
}
sub copy_to
{
my $self = shift;
my $dest = shift;
throw Ambrosia::error::Exception("Cannot copy $self to $dest.") unless ref $self eq ref $dest;
foreach my $f ( $self->fields )
{
$dest->$f = $self->$f;
}
}
sub clone
{
no strict 'refs';
my $self = shift;
my $deep = shift;
my $obj = $self->new();
my $pkg = ref $self;# || die 'clone: for object context only!';
assert {$pkg} 'clone: for object context only!';
my @__FIELDS__ = (keys(%{"$pkg\::__AMBROSIA_INTERNAL_FLDS__"}), $self->parent_fields());
if ( $deep )
{
foreach my $fn ( @__FIELDS__ )
{
eval
{
$obj->$fn = reflection( sub {
my $v = shift;
my $refV = ref $v;
if( $refV eq 'SCALAR' )
{
my $t = $$v;
return \$t;
}
elsif ( $refV eq 'GLOB' || $refV eq 'CODE' )
{
return $v;
}
elsif ( $refV && eval{$v->isa( __PACKAGE__ )} )
{
return $v->clone(1);
}
elsif ( $refV && eval{$v->can( 'clone' )} )
{
return $v->clone;
}
else
{
#die "cannot clone $v";
return $v;
}
}, $self->$fn );
};
if ( $@ )
{
croak "$@\n";
throw Ambrosia::error::Exception "FOR $fn CANNOT CLONE " . $self->$fn . ' :(', $@;
}
}
}
else
{
foreach my $f ( @__FIELDS__ )
{
$obj->$f = $self->$f;
}
}
return $obj;
}
sub proces_node
{
my $document = shift;
my $node = shift;
my $name = shift;
my $value = shift;
my $error_ignore = shift;
unless (defined $value)
{
$value = '';
}
if ( ref $value )
{
as_xml_nodes($document, $node, $name, $value, $error_ignore);
}
else
{
$node->setAttribute( $name, $value );
}
return $node;
}
sub as_xml_nodes #( $document, $node, $p, $v, $params{error_ignore} )
{
my $document = shift;
my $ex_node = shift;
my $p = shift;
my $v = shift;
my $error_ignore = shift;
my $force_node = shift;
my $refV = ref $v;
local $@;
if ( $refV eq 'ARRAY' )
{
as_xml_nodes($document, $ex_node, $p, $_, $error_ignore, $p) foreach @$v;
}
elsif ( $refV eq 'HASH' )
{
my $node = $document->createElement($p);
proces_node( $document, $node, $_, $v->{$_}, $error_ignore ) foreach keys %$v;
$ex_node->addChild($node);
}
elsif ( $refV eq 'SCALAR' )
{
lib/Ambrosia/core/Object.pm view on Meta::CPAN
$name_node = ref $self;
$name_node =~ s/::/_/gs;
}
unless ( $document = $params{document} )
{
$document = XML::LibXML->createDocument( '1.0', $params{charset} || 'UTF-8' );
}
my $node = $document->createElement($name_node);
my $addChild = sub {
my $p = shift;
my $v = shift;
if ( ref $v )
{
$node->addChild($_) foreach as_xml_nodes( $document, $node, $p, $v, $params{error_ignore} );
}
else
{
$node->setAttribute($p, $v);
}
};
foreach ( $self->fields )
{
$addChild->($_, $self->$_);
}
foreach ( @{$params{methods}} )
{
eval
{ # --== BNF ==--
# mlist := method+
# method := 'class_method_real_name'[[:alias]{method+}]
# alias := 'alternative_name'
#
my($m, $a, $p, @P);
@P = ();
if ( ($m, $a, $p) = ( $_ =~ /^(.+?)(?::(.+?))?\{(.*)\}$/s ) )
{
while ( $p =~ /^\s*(.+?(?:\{(?:.*?)\})?)\s*(?:,|$)(.*)/s )
{
$p = $2;
push @P, $1;
}
}
elsif ( ($m, $a) = ( $_ =~ /^(.+?)(?::(.+?))$/s ) )
{
}
else
{
$m = $_;
}
$node->addChild($_) foreach as_xml_nodes($document, $node, $a||$m, $self->$m(@P));
};
if ( $@ )
{
throw Ambrosia::error::Exception $@ unless $params{error_ignore};
};
}
if ( $params{need_node} )
{
return $node;
}
else
{
$document->setDocumentElement($node);
return $document;
}
}
sub __fields_equal
{
my $f1 = shift;
my $f2 = shift;
my $ref1 = ref $f1;
my $ref2 = ref $f2;
return 0 if $ref1 ne $ref2;
if ( $ref1 eq 'ARRAY' )
{
return 0 if scalar @$f1 != scalar @$f2;
for( my ($i,$j)=(0, scalar @$f1); $i < $j; ++$i )
{
unless ( my $res = __fields_equal($f1->[$i],$f2->[$i]) )
{
return $res;
}
}
return 1;
}
elsif ( $ref1 eq 'HASH' )
{
my @keys = keys %$f1;
return 0 if scalar @keys != scalar keys %$f2;
return 0 if scalar @keys != scalar grep {exists $f2->{$_}} @keys;
foreach ( @keys )
{
unless ( my $res = __fields_equal($f1->{$_}, $f2->{$_}) )
{
return $res;
}
}
return 1;
}
elsif ( $ref1 eq 'SCALAR' )
{
return $f1 == $f2 || $$f1 eq $$f2;
}
elsif ( $ref1 eq 'CODE' )
{
return undef;#__fields_equal($f1->(), $f2->());
}
elsif ( $ref1 && $f1->isa('Ambrosia::core::Object') )
{
return $f1->equal($f2,1);
}
( run in 0.651 second using v1.01-cache-2.11-cpan-ceb78f64989 )