view release on metacpan or search on metacpan
examples/example1.pl view on Meta::CPAN
use Digest::SHA1 qw(sha1_hex);
use Abstract::Meta::Class ':all';
has '$.id';
has '$.name';
has '$.password' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref) = @_;
$$value_ref = sha1_hex($$value_ref);
$self;
}
);
has '$.email' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref) = @_;
die "invalid email format:" . $$value_ref
unless $$value_ref =~ m/^<?[^@<>]+@[^@.<>]+(?:\.[^@.<>]+)+>?$/;
$self;
}
);
has '$.address';
has '%.roles' ;
sub is_valid_password {
my ($self, $password) = @_;
!! ($self->password eq sha1_hex($password));
}
##################
my $user = User->new(id => 1, name => 'Scott', email => 'scott@email.com', password => '1234567');
lib/Abstract/Meta/Attribute.pm view on Meta::CPAN
=head2 METHODS
=over
=item new
=cut
sub new {
my $class = shift;
unshift @_, $class;
bless {&initialise}, $class;
}
=item initialise
Initialises attribute
=cut
{
my %supported_type = (
'$' => 'Scalar',
'@' => 'Array',
'%' => 'Hash',
'&' => 'Code',
);
sub initialise {
my ($class, %args) = @_;
foreach my $k (keys %args) {
confess "unknown attribute $k"
unless Abstract::Meta::Attribute->can($k);
}
my $name = $args{name} or confess "name is requried";
my $storage_type = $args{storage_type} = $args{transistent} ? 'Hash' : $args{storage_type} || '';
my $attribute_index = 0;
if($storage_type eq 'Array') {
lib/Abstract/Meta/Attribute.pm view on Meta::CPAN
$options{'$.accessor'} = $accessor_name;
$options{'$.' . $_ } = $args{$_}
for grep {exists $args{$_}}
(qw(class required default item_accessor associated_class data_type_validation index_by the_other_end transistent storage_type));
$options{'$.perl_type'} = $supported_type{$type};
unless ($args{default}) {
if($type eq '%') {
$options{'$.default'} = sub{ {} };
} elsif ($type eq '@') {
$options{'$.default'} = sub { [] };
}
}
%options;
}
}
=item name
Returns attribute name
=cut
sub name { shift()->{'$.name'} }
=item class
Attribute's class name.
=cut
sub class { shift()->{'$.class'} }
=item storage_key
Returns storage attribute key in object
=cut
sub storage_key { shift()->{'$.storage_key'} }
=item perl_type
Returns attribute type, Scalar, Hash, Array, Code
=cut
sub perl_type { shift()->{'$.perl_type'} }
=item accessor
Returns accessor name
=cut
sub accessor { shift()->{'$.accessor'} }
=item mutator
Returns mutator name
=cut
sub mutator { shift()->{'$.mutator'} }
=item required
Returns required flag
=cut
sub required { shift()->{'$.required'} }
=item default
Returns default value
=cut
sub default { shift()->{'$.default'} }
=item storage_type
Hash|Array
=cut
sub storage_type { shift()->{'$.storage_type'} ||= 'Hash' }
=item transistent
If this flag is set, than storage of that attribte, will be force outside the object,
so you cant serialize that attribute,
It is especially useful when using callback, that cant be serialised (Storable dclone)
This option will generate cleanup and DESTORY methods.
=cut
sub transistent { shift()->{'$.transistent'} }
=item item_accessor
Returns name that will be used to construct the hash or array item accessor.
It will be used to retrieve or set array or hash item item
has '%.items' => (item_accessor => 'item');
...
my $item_ref = $obj->items;
$obj->item(x => 3);
my $value = $obj->item('y')'
=cut
sub item_accessor { shift()->{'$.item_accessor'} }
=item associated_class
Return name of the associated class.
=cut
sub associated_class { shift()->{'$.associated_class'} }
=item index_by
Name of the asscessor theat will return unique attribute for associated objects.
Only for toMany associaion, by deault uses objecy reference as index.
package Class;
use Abstract::Meta::Class ':all';
has '$.name' => (required => 1);
lib/Abstract/Meta/Attribute.pm view on Meta::CPAN
index_by => 'id',
item_accessor => 'detail',
);
my $obj = Class->
=cut
sub index_by { shift()->{'$.index_by'} }
=item the_other_end
Name of the asscessor/mutator on associated class to keep bideriectional association
This option will generate cleanup method.
=cut
sub the_other_end { shift()->{'$.the_other_end'} }
=item data_type_validation
Flag that turn on/off data type validation.
Data type validation happens when using association_class or Array or Hash data type
unless you explicitly disable it by seting data_type_validation => 0.
=cut
sub data_type_validation { shift()->{'$.data_type_validation'} }
=item on_read
Returns code reference that will be replace data read routine
has '%.attrs.' => (
item_accessor => 'attr'
on_read => sub {
my ($self, $attribute, $scope, $key) = @_;
my $values = $attribute->get_values($self);
if ($scope eq 'accessor') {
return $values;
} else {
return $values->{$key};
}
},
);
has '@.array_attrs.' => (
item_accessor => 'array_item'
on_read => sub {
my ($self, $attribute, $scope, $index) = @_;
my $values = $attribute->get_values($self);
if ($scope eq 'accessor') {
return $values;
} else {
return $values->[$index];
}
},
);
=cut
sub on_read { shift()->{'&.on_read'} }
=item set_on_read
Sets code reference that will be replace data read routine
my $attr = MyClass->meta->attribute('attrs');
$attr->set_on_read(sub {
my ($self, $attribute, $scope, $key) = @_;
#do some stuff
});
=cut
sub set_on_read {
my ($attr, $value) = @_;
$attr->{'&.on_read'} = $value;
my $meta= $attr->class->meta;
$meta->install_attribute_methods($attr, 1);
}
=item on_change
Code reference that will be executed when data is set,
Takes reference to the variable to be set.
=cut
sub on_change { shift()->{'&.on_change'} }
=item set_on_change
Sets code reference that will be executed when data is set,
my $attr = MyClass->meta->attribute('attrs');
$attr->set_on_change(sub {
my ($self, $attribute, $scope, $value, $key) = @_;
if($scope eq 'mutator') {
my $hash = $$value;
foreach my $k (keys %$hash) {
# do some stuff
#$self->validate_trigger($k, $hash->{$k});
}
} else {
# do some stuff
$self->validate_trigger($key. $$value);
}
$self;
});
=cut
sub set_on_change {
my ($attr, $value) = @_;
$attr->{'&.on_change'} = $value;
my $meta= $attr->class->meta;
$meta->install_attribute_methods($attr, 1);
}
=item on_validate
Returns on validate code reference.
It is executed before the data type validation happens.
=cut
sub on_validate { shift()->{'&.on_validate'} }
=item set_on_validate
Sets code reference that will be replace data read routine
my $attr = MyClass->meta->attribute('attrs');
$attr->set_on_read(sub {
my ($self, $attribute, $scope, $key) = @_;
#do some stuff
});
=cut
sub set_on_validate {
my ($attr, $value) = @_;
$attr->{'&.on_validate'} = $value;
my $meta= $attr->class->meta;
$meta->install_attribute_methods($attr, 1);
}
1;
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
has '$.attr1' => (default => 0);
=head2 methods
=over
=item generate_scalar_accessor_method
=cut
sub generate_scalar_accessor_method {
my $attr = shift;
my $mutator = $attr->mutator;
my $storage_key = $attr->storage_key;
my $transistent = $attr->transistent;
my $on_read = $attr->on_read;
my $array_storage_type = $attr->storage_type eq 'Array';
$array_storage_type ?
($transistent ? sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read
? $on_read ->($self, $attr, 'accessor')
: get_attribute($self, $storage_key);
$result;
}
: (
$on_read ?
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read
? $on_read ->($self, $attr, 'accessor')
: $self->[$storage_key];
$result;
} :
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if @args >= 1;
$self->[$storage_key];
}
)
)
:
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read
? $on_read ->($self, $attr, 'accessor')
: $transistent ? get_attribute($self, $storage_key) : $self->{$storage_key};
$result;
};
}
=item generate_code_accessor_method
=cut
sub generate_code_accessor_method {
my $attr = shift;
$attr->generate_scalar_accessor_method;
}
=item generate_mutator_method
=cut
sub generate_mutator_method {
my $attr = shift;
my $storage_key = $attr->storage_key;
my $transistent = $attr->transistent;
my $accessor = $attr->accessor;
my $required = $attr->required;
my $default = $attr->default;
my $associated_class = $attr->associated_class;
my $perl_type = $attr->perl_type;
my $index_by = $attr->index_by;
my $on_change = $attr->on_change;
my $data_type_validation = $attr->data_type_validation;
my $on_validate = $attr->on_validate;
my $array_storage_type = $attr->storage_type eq 'Array';
$array_storage_type ?
sub {
my ($self, $value) = @_;
if (! defined $value && defined $default) {
if (ref($default) eq 'CODE') {
$value = $default->($self, $attr);
} else {
$value = $default;
}
}
$on_validate->($self, $attr, 'mutator', \$value) if $on_validate;
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
if ($transistent) {
set_attribute($self, $storage_key, $value);
} else {
$self->[$storage_key] = $value;
}
$self;
}
:
sub {
my ($self, $value) = @_;
if (! defined $value && defined $default) {
if (ref($default) eq 'CODE') {
$value = $default->($self, $attr);
} else {
$value = $default;
}
}
$on_validate->($self, $attr, 'mutator', \$value) if $on_validate;
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
}
$self;
};
}
=item index_association_data
=cut
sub index_association_data {
my ($data, $attr_name, $index) = @_;
return $data if ref($data) eq 'HASH';
my %result;
if($index && $$data[0]->can($index)) {
%result = (map {($_->$index, $_)} @$data);
} else {
%result = (map {($_ . "", $_)} @$data);
}
\%result;
}
=item validate_data_type
=cut
sub validate_data_type {
my ($attr, $self, $value, $accessor, $associated_class, $perl_type) = @_;
my $array_storage_type = $attr->storage_type eq 'Array';
if ($perl_type eq 'Array') {
confess "$accessor must be $perl_type type"
unless (ref($value) eq 'ARRAY');
if ($associated_class) {
validate_associated_class($attr, $self, $_)
for @$value;
}
} elsif ($perl_type eq 'Hash') {
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
validate_associated_class($attr, $self, $value);
}
}
}
=item validate_associated_class
=cut
sub validate_associated_class {
my ($attr, $self, $value) = @_;
my $associated_class = $attr->associated_class;
my $name = $attr->name;
my $value_type = ref($value)
or confess "$name must be of the $associated_class type";
return &associate_the_other_end if $value_type eq $associated_class;
return &associate_the_other_end if $value->isa($associated_class);
confess "$name must be of the $associated_class type, is $value_type";
}
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
{ my %pending_association;
=item start_association_process
Start association process (to avoid infinitive look of associating the others ends)
Takes obj reference.
=cut
sub start_association_process {
my ($self) = @_;
$pending_association{$self} = 1;
}
=item has_pending_association
Returns true is object is during association process.
=cut
sub has_pending_association {
my ($self) = @_;
$pending_association{$self};
}
=item end_association_process
Compleetes association process.
=cut
sub end_association_process {
my ($self) = @_;
delete $pending_association{$self};
}
}
=item associate_the_other_end
Associate current object reference to the the other end associated class.
TODO
=cut
sub associate_the_other_end {
my ($attr, $self, $value) = @_;
my $the_other_end = $attr->the_other_end;
my $name = $attr->name;
return if ! $the_other_end || has_pending_association($self);
my $associated_class = $attr->associated_class;
my $the_other_end_attribute = $associated_class->meta->attribute($the_other_end);
confess "missing other end attribute on ". ref($value) . "::" . $the_other_end
unless $the_other_end_attribute;
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
end_association_process($value);
die $@ if $@;
}
=item associate_scalar_as_the_other_end
=cut
sub associate_scalar_as_the_other_end {
my ($attr, $self, $value) = @_;
my $the_other_end = $attr->the_other_end;
$value->$the_other_end($self);
}
=item associate_hash_as_the_other_end
=cut
sub associate_hash_as_the_other_end {
my ($attr, $self, $value) = @_;
my $the_other_end = $attr->the_other_end;
my $associated_class = $attr->associated_class;
my $the_other_end_attribute = $associated_class->meta->attribute($the_other_end);
my $item_accessor = $the_other_end_attribute->item_accessor;
my $index_by = $the_other_end_attribute->index_by;
if ($index_by) {
$value->$item_accessor($self->$index_by, $self);
} else {
$value->$item_accessor($self . "", $self);
}
}
=item associate_array_as_the_other_end
=cut
sub associate_array_as_the_other_end {
my ($attr, $self, $value) = @_;
my $the_other_end = $attr->the_other_end;
my $associated_class = $attr->associated_class;
my $the_other_end_attribute = $associated_class->meta->attribute($the_other_end);
my $other_end_accessor = $the_other_end_attribute->accessor;
my $setter = "push_${other_end_accessor}";
$value->$setter($self);
}
=item deassociate
Deassociates assoication values
=cut
sub deassociate {
my ($attr, $self) = @_;
my $transistent = $attr->transistent;
my $storage_key = $attr->storage_key;
my $array_storage_type = $attr->storage_type eq 'Array';
my $value = ($transistent ? get_attribute($self, $storage_key) : ($array_storage_type ? $self->[$storage_key] : $self->{$storage_key})) or return;
my $the_other_end = $attr->the_other_end;
return if ! $the_other_end || has_pending_association($value);
start_association_process($self);
my $associated_class = $attr->associated_class;
my $the_other_end_attribute = $associated_class->meta->attribute($the_other_end);
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
$the_other_end_attribute->$deassociation_call($self, $value);
}
end_association_process($self);
}
=item deassociate_scalar_as_the_other_end
=cut
sub deassociate_scalar_as_the_other_end {
my ($attr, $self, $the_other_end_obj) = @_;
$the_other_end_obj or return;
my $accessor = $attr->accessor;
$the_other_end_obj->$accessor(undef);
undef;
}
=item deassociate_hash_as_the_other_end
=cut
sub deassociate_hash_as_the_other_end {
my ($attr, $self, $the_other_end_obj) = @_;
my $accessor = $attr->accessor;
my $value = $the_other_end_obj->$accessor;
my $index_by = $attr->index_by;
if ($index_by) {
delete $value->{$self->$index_by} if exists($value->{$self->$index_by});
} else {
my @keys = keys %$value;
foreach my $k (@keys) {
if ($value->{$k} eq $self) {
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
}
}
undef;
}
=item deassociate_array_as_the_other_end
=cut
sub deassociate_array_as_the_other_end {
my ($attr, $self, $the_other_end_obj) = @_;
my $accessor = $attr->accessor;
my $value = $the_other_end_obj->$accessor;
for my $i (0 .. $#{$value}) {
if ($value->[$i] eq $self) {
splice @$value, $i--, 1;
}
}
undef;
}
=item generate_scalar_mutator_method
=cut
sub generate_scalar_mutator_method {
shift()->generate_mutator_method;
}
=item generate_code_mutator_method
=cut
sub generate_code_mutator_method {
shift()->generate_mutator_method;
}
=item generate_array_accessor_method
=cut
sub generate_array_accessor_method {
my $attr = shift;
my $mutator = $attr->mutator;
my $storage_key = $attr->storage_key;
my $transistent = $attr->transistent;
my $on_read = $attr->on_read;
my $array_storage_type = $attr->storage_type eq 'Array';
$array_storage_type ?
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read ? $on_read->($self, $attr, 'accessor')
: ($transistent ? get_attribute($self, $storage_key) : ($self->[$storage_key] ||= []));
wantarray ? @$result : $result;
}
:
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read ? $on_read->($self, $attr, 'accessor')
: ($transistent ? get_attribute($self, $storage_key) : ($self->{$storage_key} ||= []));
wantarray ? @$result : $result;
};
}
=item generate_array_mutator_method
=cut
sub generate_array_mutator_method {
shift()->generate_mutator_method;
}
=item generate_hash_accessor_method
=cut
sub generate_hash_accessor_method {
my $attr = shift;
my $mutator = $attr->mutator;
my $storage_key = $attr->storage_key;
my $transistent = $attr->transistent;
my $on_read = $attr->on_read;
my $array_storage_type = $attr->storage_type eq 'Array';
$attr->associated_class
? $attr->generate_to_many_accessor_method
: ($array_storage_type ?
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read
? $on_read->($self, $attr, 'accessor')
: ($transistent ? get_attribute($self, $storage_key) : ($self->[$storage_key] ||= {}));
wantarray ? %$result : $result;
}
: sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read
? $on_read->($self, $attr, 'accessor')
: ($transistent ? get_attribute($self, $storage_key) : ($self->{$storage_key} ||= {}));
wantarray ? %$result : $result;
});
}
=item generate_to_many_accessor_method
=cut
sub generate_to_many_accessor_method {
my $attr = shift;
my $mutator = $attr->mutator;
my $storage_key = $attr->storage_key;
my $transistent = $attr->transistent;
my $on_read = $attr->on_read;
my $array_storage_type = $attr->storage_type eq 'Array';
$array_storage_type ?
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read
? $on_read->($self, $attr, 'accessor')
: ($transistent ? get_attribute($self, $storage_key) : ($self->[$storage_key] ||= {}));
wantarray ? %$result : $result;
}
:
sub {
my ($self, @args) = @_;
$self->$mutator(@args) if scalar(@args) >= 1;
my $result = $on_read
? $on_read->($self, $attr, 'accessor')
: ($transistent ? get_attribute($self, $storage_key) : ($self->{$storage_key} ||= {}));
wantarray ? %$result : $result;
};
}
=item generate_hash_mutator_method
=cut
sub generate_hash_mutator_method {
shift()->generate_mutator_method;
}
=item generate_hash_item_accessor_method
=cut
sub generate_hash_item_accessor_method {
my $attr = shift;
my $accesor = $attr->accessor;
my $on_change = $attr->on_change;
my $on_read = $attr->on_read;
sub {
my $self = shift;
my ($key, $value) = (@_);
my $hash_ref = $self->$accesor();
if(defined $value) {
$on_change->($self, $attr, 'item_accessor', \$value, $key) or return $hash_ref->{$key}
if ($on_change);
$hash_ref->{$key} = $value;
}
$on_read ? $on_read->($self, $attr, 'item_accessor', $key) : $hash_ref->{$key};
};
}
=item generate_hash_add_method
=cut
sub generate_hash_add_method {
my $attr = shift;
my $accessor = $attr->accessor;
my $item_accessor = $attr->item_accessor;
my $on_change = $attr->on_change;
my $on_read = $attr->on_read;
my $index_by = $attr->index_by;
sub {
my ($self, @values) = @_;
my $hash_ref = $self->$accessor();
foreach my $value (@values) {
next unless ref($value);
my $key = ($index_by ? $value->$index_by : $value . "") or confess "unknown key hash at add_$accessor";
$attr->validate_associated_class($self, $value);
$on_change->($self, $attr, 'item_accessor', \$value, $key) or return $hash_ref->{$key}
if ($on_change);
$hash_ref->{$key} = $value;
}
$self;
};
}
=item generate_scalar_reset_method
=cut
sub generate_scalar_reset_method {
my $attr = shift;
my $mutator = $attr->mutator;
my $index_by = $attr->index_by;
sub {
my ($self, ) = @_;
$self->$mutator(undef);
};
}
=item generate_scalar_has_method
=cut
sub generate_scalar_has_method {
my $attr = shift;
sub {
my ($self, ) = @_;
!! $attr->get_value($self);
};
}
=item generate_hash_reset_method
=cut
sub generate_hash_reset_method {
my $attr = shift;
my $mutator = $attr->mutator;
my $index_by = $attr->index_by;
sub {
my ($self, ) = @_;
$self->$mutator({});
};
}
=item generate_hash_has_method
=cut
sub generate_hash_has_method {
my $attr = shift;
sub {
my ($self, ) = @_;
my $value = $attr->get_value($self);
!! ($value && keys %$value);
};
}
=item generate_array_reset_method
=cut
sub generate_array_reset_method {
my $attr = shift;
my $mutator = $attr->mutator;
my $index_by = $attr->index_by;
sub {
my ($self, ) = @_;
$self->$mutator([]);
};
}
=item generate_array_has_method
=cut
sub generate_array_has_method {
my $attr = shift;
sub {
my ($self, ) = @_;
my $value = $attr->get_value($self);
!! ($value && @$value);
};
}
=item generate_hash_remove_method
=cut
#TODO add on_remove trigger
sub generate_hash_remove_method {
my $attr = shift;
my $accessor = $attr->accessor;
my $item_accessor = $attr->item_accessor;
my $the_other_end = $attr->the_other_end;
my $meta = Abstract::Meta::Class::meta_class($attr->associated_class);
my $reflective_attribute = $the_other_end && $meta ? $meta->attribute($the_other_end) : undef;
my $index_by = $attr->index_by;
sub {
my ($self, @values) = @_;
my $hash_ref = $self->$accessor();
foreach my $value (@values) {
next unless ref($value);
my $key = ($index_by && ref($value) ? $value->$index_by : $value . "");
$attr->deassociate($self);
$reflective_attribute->set_value($hash_ref->{$key}, undef)
if $reflective_attribute;
delete $hash_ref->{$key};
}
$self;
};
}
=item generate_array_item_accessor_method
=cut
sub generate_array_item_accessor_method {
my $attr = shift;
my $accesor = $attr->accessor;
my $on_change = $attr->on_change;
my $on_read = $attr->on_read;
sub {
my $self = shift;
my ($index, $value) = (@_);
my $hash_ref = $self->$accesor();
if (defined $value) {
$on_change->($self, $attr, 'item_accessor', \$value, $index) or return $hash_ref->[$index]
if ($on_change);
$hash_ref->[$index] = $value;
}
$on_read ? $on_read->($self, $attr, 'item_accessor', $index) : $hash_ref->[$index];
};
}
=item generate_array_push_method
=cut
sub generate_array_push_method {
my $attr = shift;
my $accesor = $attr->accessor;
sub {
my $self = shift;
my $array_ref = $self->$accesor();
push @$array_ref, @_;
};
}
=item generate_array_pop_method
=cut
sub generate_array_pop_method {
my $attr = shift;
my $accesor = $attr->accessor;
sub {
my $self = shift;
my $array_ref = $self->$accesor();
pop @$array_ref;
};
}
=item generate_array_shift_method
=cut
sub generate_array_shift_method {
my $attr = shift;
my $accesor = $attr->accessor;
sub {
my $self = shift;
my $array_ref= $self->$accesor();
shift @$array_ref;
};
}
=item generate_array_unshift_method
=cut
sub generate_array_unshift_method {
my $attr = shift;
my $accesor = $attr->accessor;
sub {
my $self = shift;
my $array_ref = $self->$accesor();
unshift @$array_ref, @_;
};
}
=item generate_array_count_method
=cut
sub generate_array_count_method {
my $attr = shift;
my $accesor = $attr->accessor;
sub {
my $self = shift;
my $array_ref = $self->$accesor();
scalar @$array_ref;
};
}
=item generate_array_add_method
=cut
sub generate_array_add_method {
my $attr = shift;
my $accesor = $attr->accessor;
my $accessor = $attr->accessor;
my $the_other_end = $attr->the_other_end;
my $associated_class = $attr->associated_class;
sub {
my ($self, @values) = @_;
my $array_ref = $self->$accesor();
foreach my $value (@values) {
$attr->validate_associated_class($self, $value, $accessor, $associated_class, $the_other_end);
push @$array_ref, $value;
}
$self;
};
}
=item generate_array_remove_method
=cut
#TODO add on_remove trigger
sub generate_array_remove_method {
my $attr = shift;
my $accesor = $attr->accessor;
my $accessor = $attr->accessor;
my $the_other_end = $attr->the_other_end;
my $meta = Abstract::Meta::Class::meta_class($attr->associated_class);
my $reflective_attribute = $the_other_end && $meta ? $meta->attribute($the_other_end) : undef;
sub {
my ($self, @values) = @_;
my $array_ref = $self->$accesor();
foreach my $value(@values) {
for my $i (0 .. $#{$array_ref}) {
if ($array_ref->[$i] && $array_ref->[$i] eq $value) {
$reflective_attribute->set_value($value, undef)
if $reflective_attribute;
splice @$array_ref, $i--, 1;
}
}
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
};
}
=item generate
Returns code reference.
=cut
sub generate {
my ($self, $method_name) = @_;
my $call = "generate_" . lc($self->perl_type) . "_${method_name}_method";
$self->$call;
}
=item set_value
Sets value for attribute
=cut
sub set_value {
my ($attr, $self, $value) = @_;
my $array_storage_type = $attr->storage_type eq 'Array';
my $storage_key = $attr->storage_key;
my $transistent = $attr->transistent;
if($transistent) {
set_attribute($self, $storage_key, $value);
} elsif($array_storage_type) {
$self->[$storage_key] = $value;
} else {
$self->{$storage_key} = $value;
}
}
=item get_value
Returns value for attribute
=cut
sub get_value {
my ($attr, $self) = @_;
my $storage_key = $attr->storage_key;
my $transistent = $attr->transistent;
my $array_storage_type = $attr->storage_type eq 'Array';
if ($transistent) {
return get_attribute($self, $storage_key);
} elsif($array_storage_type) {
$self->[$storage_key];
} else {
return $self->{$storage_key};
lib/Abstract/Meta/Attribute/Method.pm view on Meta::CPAN
{
my %storage;
=item get_attribute
Return object's attribute value
=cut
sub get_attribute {
my ($self, $key) = @_;
my $object = $storage{$self} ||= {};
return $object->{$key};
}
=item set_attribute
Sets for passed in object attribue's value
=cut
sub set_attribute {
my ($self, $key, $value) = @_;
my $object = $storage{$self} ||= {};
$object->{$key} = $value;
}
=item delete_object
Deletes passed in object's attribute
=cut
sub delete_object {
my ($self) = @_;
delete $storage{$self};
}
}
1;
__END__
lib/Abstract/Meta/Class.pm view on Meta::CPAN
package Dummy;
use Abstract::Meta::Class ':all';
has '$.attr1' => (default => 0);
has '%.attrs2' => (default => {a => 1, b => 3}, item_accessor => 'attr2');
has '@.atts3' => (default => [1, 2, 3], required => 1, item_accessor => 'attr3');
has '&.att3' => (required => 1);
has '$.att4' => (default => sub { 'stuff' } , required => 1);
my $dummt = Dummy->new(
att3 => 3,
);
use Dummy;
my $obj = Dummy->new(attr3 => sub {});
my $attr1 = $obj->attr1; #0
$obj->set_attr1(1);
$obj->attr2('c', 4);
$obj->attrs2 #{a => 1, b => 3. c => 4};
my $val_a = $obj->attr2('a');
my $item_1 = $obj->attr3(1);
$obj->count_attrs3();
$obj->push_attrs3(4);
lib/Abstract/Meta/Class.pm view on Meta::CPAN
package Dummy;
use Abstract::Meta::Class ':all';
storage_type 'Array';
has '$.attr1' => (default => 0);
has '%.attrs2' => (default => {a => 1, b => 3}, item_accessor => 'attr2');
has '@.attrs3' => (default => [1, 2, 3], required => 1, item_accessor => 'attr3');
has '&.attr4' => (required => 1);
has '$.attr5';
has '$.attr6' => (default => sub { 'stuff' } , required => 1);
my $dummy = Dummy->new(
attr4 => sub {},
);
use Data::Dumper;
warn Dumper $dummy;
# bless [0, {a =>1,b => 3}, [1,2,3],sub{},undef,sub {}], 'Dummy'
=head2 simple validation and default values
package Dummy;
use Abstract::Meta::Class ':all';
has '$.attr1' => (default => 0);
has '&.att3' => (required => 1);
lib/Abstract/Meta/Class.pm view on Meta::CPAN
- on_read
- initialise_method
package Triggers;
use Abstract::Meta::Class ':all';
has '@.y' => (
on_change => sub {
my ($self, $attribute_name, $scope, $value_ref, $index) = @_;
# scope -> mutator, item_accessor
... do some stuff
# process further in standard way by returning true
$self;
},
# replaces standard read
on_read => sub {
my ($self, $attr_name, $scope, $index)
#scope can be: item_accessor, accessor
...
#return requested value
},
item_accessor => 'y_item'
);
use Triggers;
lib/Abstract/Meta/Class.pm view on Meta::CPAN
package Class;
use Abstract::Meta::Class ':all';
has '%.attrs' => (item_accessor => 'attr');
my $attr = DynamicInterceptor->meta->attribute('attrs');
my $obj = DynamicInterceptor->new(attrs => {a => 1, b => 2});
my $a = $obj->attr('a');
my %hook_access_log;
my $ncode_ref = sub {
my ($self, $attribute, $scope, $key) = @_;
#do some stuff
# or
if ($scope eq 'accessor') {
return $values;
} else {
return $values->{$key};
}
};
lib/Abstract/Meta/Class.pm view on Meta::CPAN
use Abstract::Meta::Class ':all';
has '$.attr1';
abstract => 'method1';
package Class;
use base 'BaseClass';
sub method1 {};
use Class;
my $obj = BaseClass->new;
# abstract classes
package InterfaceA;
lib/Abstract/Meta/Class.pm view on Meta::CPAN
abstract_class;
abstract => 'method1';
abstract => 'method2';
package ClassA;
use base 'InterfaceA';
sub method1 {};
sub method2 {};
use Class;
my $classA = Class->new;
package Class;
use Abstract::Meta::Class ':all';
lib/Abstract/Meta/Class.pm view on Meta::CPAN
=head2 METHODS
=over
=item new
=cut
sub new {
my $class = shift;
my $self = bless {}, $class;
unshift @_, $self;
&apply_contructor_parameters;
}
=item install_cleanup
Install cleanup method
=cut
sub install_cleanup {
my ($self) = @_;
my $attributes;
return if $self->has_cleanup_method;
add_method($self->associated_class, 'cleanup' , sub {
my $this = shift;
my $has_transistent;
my $attributes ||= $self ? $self->all_attributes : [];
for my $attribute (@$attributes) {
$attribute or next;
$has_transistent = 1 if($attribute->transistent);
if($attribute->the_other_end) {
$attribute->deassociate($this);
my $accessor = "set_" . $attribute->accessor;
$this->$accessor(undef);
lib/Abstract/Meta/Class.pm view on Meta::CPAN
$self->set_cleanup_method(1);
}
=item install_destructor
Install destructor method
=cut
sub install_destructor {
my ($self) = @_;
return if $self->has_destory_method;
add_method($self->associated_class, 'DESTROY' , sub {
my $this = shift;
$this->cleanup;
$this;
});
$self->set_destroy_method(1);
}
=item install_constructor
Install constructor
=cut
sub install_constructor {
my ($self) = @_;
add_method($self->associated_class, 'new' ,
$self->storage_type eq 'Array' ?
sub {
my $class = shift;
my $this = bless [], $class;
unshift @_, $this;
&apply_contructor_parameters;
}: sub {
my $class = shift;
my $this = bless {}, $class;
unshift @_, $this;
&apply_contructor_parameters;
});
}
=item apply_contructor_parameters
Applies constructor parameters.
=cut
{
sub apply_contructor_parameters {
my ($self, @args) = @_;
my $mutator;
my $class = ref($self);
eval {
for (my $i = 0; $i < $#args; $i += 2) {
$mutator = "set_" . $args[$i];
$self->$mutator($args[$i + 1]);
}
};
lib/Abstract/Meta/Class.pm view on Meta::CPAN
my $initialise = $self->can($meta->initialise_method);
$initialise->($self) if $initialise;
$self;
}
}
=item meta
=cut
sub meta { shift(); }
=item attributes
Returns attributes for meta class
=cut
sub attributes { shift()->{'@.attributes'} || {};}
=item set_attributes
Mutator sets attributes for the meta class
=cut
sub set_attributes { $_[0]->{'@.attributes'} = $_[1]; }
=item has_cleanup_method
Returns true if cleanup method was generated
=cut
sub has_cleanup_method { shift()->{'$.cleanup'};}
=item set_cleanup_method
Sets clean up
=cut
sub set_cleanup_method { $_[0]->{'$.cleanup'} = $_[1]; }
=item has_destory_method
Returns true is destroy method was generated
=cut
sub has_destory_method { shift()->{'$.destructor'};}
=item set_destroy_method
Sets set_destructor flag.
=cut
sub set_destroy_method { $_[0]->{'$.destructor'} = $_[1]; }
=item initialise_method
Returns initialise method's name default is 'initialise'
=cut
sub initialise_method { shift()->{'$.initialise_method'};}
=item is_abstract
Returns is class is an abstract class.
=cut
sub is_abstract{ shift()->{'$.abstract'};}
=item set_abstract
Set an abstract class flag.
=cut
sub set_abstract{ shift()->{'$.abstract'} = 1;}
=item set_initialise_method
Mutator sets initialise_method for the meta class
=cut
sub set_initialise_method { $_[0]->{'$.initialise_method'} = $_[1]; }
=item associated_class
Returns associated class name
=cut
sub associated_class { shift()->{'$.associated_class'} }
=item set_associated_class
Mutator sets associated class name
=cut
sub set_associated_class { $_[0]->{'$.associated_class'} = $_[1]; }
=item all_attributes
Returns all_attributes for all inherited meta classes
=cut
sub all_attributes {
my $self = shift;
if(my @super_classes = $self->super_classes) {
my %attributes;
foreach my $super (@super_classes) {
my $meta_class = meta_class($super) or next;
$attributes{$_->name} = $_ for @{$meta_class->all_attributes};
}
$attributes{$_->name} = $_ for @{$self->attributes};
return [values %attributes];
}
$self->attributes;
}
=item attribute
Returns attribute object
=cut
sub attribute {
my ($self, $name) = @_;
my $attributes = $self->all_attributes;
my @result = (grep {$_->accessor eq $name} @$attributes);
@result ? $result[0] : undef;
}
=item super_classes
=cut
sub super_classes {
my $self = shift;
no strict 'refs';
my $class = $self->associated_class;
@{"${class}::ISA"};
}
{
my %meta;
=item install_meta_class
Adds class to meta repository.
=cut
sub install_meta_class {
my ($class) = @_;
$meta{$class} = __PACKAGE__->new(
associated_class => $class,
attributes => [],
initialise_method => 'initialise'
);
add_method($class, 'meta', sub{$meta{$class}});
}
=item meta_class
Returns meta class object for passed in class name.
=cut
sub meta_class {
my ($class) = @_;
install_meta_class($class)unless $meta{$class};
$meta{$class};
}
}
=item add_attribute
=cut
sub add_attribute {
my ($self, $attribute) = @_;
$self->install_attribute_methods($attribute);
push @{$self->attributes}, $attribute;
}
=item attribute_class
Returns meta attribute class
=cut
sub attribute_class { 'Abstract::Meta::Attribute' }
=item has
Creates a meta attribute.
Takes attribute name, and the following attribute options:
see also L<Abstract::Meta::Attribute>
=cut
sub has {
my $name = shift;
my $package = caller();
my $meta_class = meta_class($package);
my $attribute = $meta_class->attribute_class->new(name => $name, @_, class => $package, storage_type => $meta_class->storage_type);
$meta_class->add_attribute($attribute);
$meta_class->install_cleanup
if($attribute->transistent || $attribute->index_by);
$meta_class->install_destructor
if $attribute->transistent;
$attribute;
}
=item storage_type
Sets storage type for the attributes.
allowed values are Array/Hash
=cut
sub storage_type {
my ($param) = @_;
return $param->{'$.storage_type'} ||= 'Hash'
if (ref($param));
my $type = $param;
confess "unknown storage type $type - should be Array or Hash"
unless($type =~ /Array|Hash/);
my $package = caller();
my $meta_class = meta_class($package);
$meta_class->{'$.storage_type'} = $type;
remove_method($meta_class->associated_class, 'new');
lib/Abstract/Meta/Class.pm view on Meta::CPAN
}
=item abstract
Creates an abstract method
=cut
sub abstract {
my $name = shift;
my $package = caller();
my $meta_class = meta_class($package);
$meta_class->install_abstract_methods($name);
}
=item abstract_class
Creates an abstract method
=cut
sub abstract_class {
my $name = shift;
my $package = caller();
my $meta_class = meta_class($package);
$meta_class->set_abstract(1);
no warnings 'redefine';
no strict 'refs';
*{"${package}::new"} = sub {
confess "Can't instantiate abstract class " . $package;
};
}
=item install_abstract_methods
=cut
sub install_abstract_methods {
my ($self, $method_name) = @_;
add_method($self->associated_class, $method_name, sub {
confess $method_name . " is an abstract method";
});
}
=item install_attribute_methods
Installs attribute methods.
=cut
sub install_attribute_methods {
my ($self, $attribute, $remove_existing_method) = @_;
my $accessor = $attribute->accessor;
foreach (qw(accessor mutator)) {
add_method($self->associated_class, $attribute->$_, $attribute->generate($_), $remove_existing_method);
}
my $perl_type = $attribute->perl_type ;
if ($perl_type eq 'Array') {
add_method($self->associated_class, "${_}_$accessor", $attribute->generate("$_"), $remove_existing_method)
for qw(count push pop shift unshift);
lib/Abstract/Meta/Class.pm view on Meta::CPAN
}
=item add_method
Adds code reference to the class symbol table.
Takes a class name, method name and CODE reference.
=cut
sub add_method {
my ($class, $name, $code, $remove_existing_method) = @_;
remove_method($class, $name) if $remove_existing_method;
no strict 'refs';
*{"${class}::$name"} = $code;
}
=item remove_method
Adds code reference to the class symbol table.
Takes a class name, method name and CODE reference.
=cut
sub remove_method {
my ($class, $name) = @_;
no strict 'refs';
delete ${"${class}::"}{"$name"};
}
=item constructor_attributes
Returns a list of attributes that need be validated and all that have default value
=cut
sub constructor_attributes {
my ($self) = @_;
my $all_attributes = $self->all_attributes || [];
grep {$_->required || defined $_->default} @$all_attributes;
}
1
__END__
=back
t/meta/array_storage/attribute.t view on Meta::CPAN
{
package Dummy::Default;
use Abstract::Meta::Class ':all'; storage_type 'Array';
has '$.z' => (default => 0);
has '$.x' => (default => 'x');
has '%.h' => (default => {a => 1});
has '@.a' => (default => [1, 2, 3], required => 1);
has '&.c' => (required => 1);
has '$.d' => (default => sub { 'stuff' } , required => 1);
}
my $default = Dummy::Default->new(c => sub {123});
isa_ok($default, 'Dummy::Default');
is($default->x, 'x', 'should have default for the x attribute');
is_deeply({$default->h}, {a => 1}, 'should have default for the h attribute');
is_deeply([$default->a], [1, 2, 3], 'should have default for the a attribute');
is($default->d, 'stuff', 'should have default for the x attribute');
is($default->z, 0, 'should have 0 as default value');
is($default->c->(), '123', 'should have code value');
{
package Dummy::OnChange;
use Abstract::Meta::Class ':all'; storage_type 'Array';
has '$.a' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref) = @_;
# validate
# does not change anything if return false
return !! 0;
},
);
my $x_value;
my $x_attribute;
my $x_scope;
my $x_attr = has '$.x' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref) = @_;
$x_value = $$value_ref;
$x_attribute = $attribute;
$x_scope = $scope;
$self;
},
);
my $y_value;
my $y_attribute;
my $y_scope;
my $y_index;
my $y_attr = has '@.y' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref, $index) = @_;
$y_value = $$value_ref;
$y_attribute = $attribute;
$y_scope = $scope;
$y_index = $index;
$self;
},
item_accessor => 'y_item'
);
my $z_value;
my $z_attribute;
my $z_scope;
my $z_key;
my $z_attr = has '%.z' => (
on_change => sub {
my ($self, $attribute, $scope, $value, $key) = @_;
$z_value = $$value;
$z_attribute = $attribute;
$z_scope = $scope;
$z_key = $key;
$self;
},
item_accessor => 'z_value'
);
t/meta/array_storage/attribute.t view on Meta::CPAN
}
{
package DynamicInterceptor;
use Abstract::Meta::Class ':all'; storage_type 'Array';
my %access_log;
has '%.attrs' => (
on_read => sub {
my ($self, $attribute, $scope, $key) = @_;
my $values = $attribute->get_value($self);
$access_log{$scope}++;
if ($scope eq 'accessor') {
return $values;
} else {
return $values->{$key};
}
},
item_accessor => 'attr'
);
my $attr = DynamicInterceptor->meta->attribute('attrs');
my $code_ref = $attr->on_read;
my $obj = DynamicInterceptor->new(attrs => {a => 1, b => 2});
my $a = $obj->attr('a');
my %hook_access_log;
my $ncode_ref = sub {
my ($self, $attribute, $scope, $key) = @_;
$hook_access_log{$scope}++;
#do some stuff
$code_ref->($self, $attribute, $scope, $key);
};
$attr->set_on_read($ncode_ref);
my $b = $obj->attr('b');
t/meta/array_storage/attribute.t view on Meta::CPAN
has '$.x' => (required => 1, storage_key => 'x');
has '@.y' => (required => 1, storage_key => 'y');
my $obj = StorageKey->new(x => 1, y => [1,2]);
::is_deeply($obj, [1, [1,2]], 'should have storage key');
}
{
package Validate;
use Abstract::Meta::Class ':all'; storage_type 'Array';
my $attr = has '$.x' => (on_validate => sub {
});
$attr->set_on_validate(
sub {
my ($self, $attribute, $scope, $value) = @_;
die 'invalid value' if($$value ne 1);
}
);
eval {
Validate->new(x => 2);
};
::like($@, qr{invalid value}, 'should validate');
::isa_ok(Validate->new(x => 1), 'Validate');
}
t/meta/array_storage/class.t view on Meta::CPAN
my $custom = Custom->new;
isa_ok($custom, 'Custom');
{
package Initialise;
use Abstract::Meta::Class ':all';
storage_type 'Array';
has '$.attr';
Initialise->meta->set_initialise_method('init');;
sub init {
my ($self) = @_;
$self->set_attr('initialise ...');
}
}
my $init = Initialise->new;
is($init->attr,'initialise ...', 'should have initialise ...');
{
t/meta/attribute.t view on Meta::CPAN
{
package Dummy::Default;
use Abstract::Meta::Class ':all';
has '$.z' => (default => 0);
has '$.x' => (default => 'x');
has '%.h' => (default => {a => 1});
has '@.a' => (default => [1, 2, 3], required => 1);
has '&.c' => (required => 1);
has '$.d' => (default => sub { 'stuff' } , required => 1);
}
my $default = Dummy::Default->new(c => sub {123});
isa_ok($default, 'Dummy::Default');
is($default->x, 'x', 'should have default for the x attribute');
is_deeply({$default->h}, {a => 1}, 'should have default for the h attribute');
is_deeply([$default->a], [1, 2, 3], 'should have default for the a attribute');
is($default->d, 'stuff', 'should have default for the x attribute');
is($default->z, 0, 'should have 0 as default value');
is($default->c->(), '123', 'should have code value');
{
package Dummy::OnChange;
use Abstract::Meta::Class ':all';
has '$.a' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref) = @_;
# validate
# does not change anything if return false
return !! 0;
},
);
my $x_value;
my $x_attribute;
my $x_scope;
my $x_attr = has '$.x' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref) = @_;
$x_value = $$value_ref;
$x_attribute = $attribute;
$x_scope = $scope;
$self;
},
);
my $y_value;
my $y_attribute;
my $y_scope;
my $y_index;
my $y_attr = has '@.y' => (
on_change => sub {
my ($self, $attribute, $scope, $value_ref, $index) = @_;
$y_value = $$value_ref;
$y_attribute = $attribute;
$y_scope = $scope;
$y_index = $index;
$self;
},
item_accessor => 'y_item'
);
my $z_value;
my $z_attribute;
my $z_scope;
my $z_key;
my $z_attr = has '%.z' => (
on_change => sub {
my ($self, $attribute, $scope, $value, $key) = @_;
$z_value = $$value;
$z_attribute = $attribute;
$z_scope = $scope;
$z_key = $key;
$self;
},
item_accessor => 'z_value'
);
t/meta/attribute.t view on Meta::CPAN
}
{
package DynamicInterceptor;
use Abstract::Meta::Class ':all';
my %access_log;
has '%.attrs' => (
on_read => sub {
my ($self, $attribute, $scope, $key) = @_;
my $values = $attribute->get_value($self);
$access_log{$scope}++;
if ($scope eq 'accessor') {
return $values;
} else {
return $values->{$key};
}
},
item_accessor => 'attr'
);
my $attr = DynamicInterceptor->meta->attribute('attrs');
my $code_ref = $attr->on_read;
my $obj = DynamicInterceptor->new(attrs => {a => 1, b => 2});
my $a = $obj->attr('a');
my %hook_access_log;
my $ncode_ref = sub {
my ($self, $attribute, $scope, $key) = @_;
$hook_access_log{$scope}++;
#do some stuff
$code_ref->($self, $attribute, $scope, $key);
};
$attr->set_on_read($ncode_ref);
my $b = $obj->attr('b');
t/meta/attribute.t view on Meta::CPAN
has '$.x' => (required => 1, storage_key => 'x');
has '@.y' => (required => 1, storage_key => 'y');
my $obj = StorageKey->new(x => 1, y => [1,2]);
::is_deeply($obj, {x => 1, y =>[1,2]}, 'should have storage key');
}
{
package Validate;
use Abstract::Meta::Class ':all';
my $attr = has '$.x' => (on_validate => sub {
});
$attr->set_on_validate(
sub {
my ($self, $attribute, $scope, $value) = @_;
die 'invalid value' if($$value ne 1);
}
);
eval {
Validate->new(x => 2);
};
::like($@, qr{invalid value}, 'should validate');
::isa_ok(Validate->new(x => 1), 'Validate');
}
t/meta/class.t view on Meta::CPAN
my $custom = Custom->new;
isa_ok($custom, 'Custom');
{
package Initialise;
use Abstract::Meta::Class ':all';
has '$.attr';
Initialise->meta->set_initialise_method('init');;
sub init {
my ($self) = @_;
$self->set_attr('initialise ...');
}
}
my $init = Initialise->new;
is($init->attr,'initialise ...', 'should have initialise ...');
{