| Abstract-Meta-Class documentation | Contained in the Abstract-Meta-Class distribution. |
Abstract::Meta::Attribute::Method - Method generator.
Generates methods for attribute's definition.
use Abstract::Meta::Class ':all';
has '$.attr1' => (default => 0);
Start association process (to avoid infinitive look of associating the others ends) Takes obj reference.
Returns true is object is during association process.
Compleetes association process.
Associate current object reference to the the other end associated class.
TODO
Deassociates assoication values
Returns code reference.
Sets value for attribute
Returns value for attribute
Return object's attribute value
Sets for passed in object attribue's value
Deletes passed in object's attribute
The Abstract::Meta::Attribute::Method module is free software. You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.
Adrian Witas, adrian@webapp.strefa.pl
| Abstract-Meta-Class documentation | Contained in the Abstract-Meta-Class distribution. |
package Abstract::Meta::Attribute::Method; use strict; use warnings; use Carp 'confess'; use vars qw($VERSION); $VERSION = 0.06;
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; }; }
sub generate_code_accessor_method { my $attr = shift; $attr->generate_scalar_accessor_method; }
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; if ($data_type_validation) { $value = index_association_data($value, $accessor, $index_by) if ($associated_class && $perl_type eq 'Hash'); $attr->validate_data_type($self, $value, $accessor, $associated_class, $perl_type); if($required) { if ($perl_type eq 'Hash') { confess "attribute $accessor is required" unless scalar %$value; } elsif ($perl_type eq 'Array') { confess "attribute $accessor is required" unless scalar @$value; } } } else { confess "attribute $accessor is required" if $required && ! defined $value; } $on_change->($self, $attr, 'mutator', \$value) or return $self if ($on_change && defined $value); 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; if ($data_type_validation) { $value = index_association_data($value, $accessor, $index_by) if ($associated_class && $perl_type eq 'Hash'); $attr->validate_data_type($self, $value, $accessor, $associated_class, $perl_type); if($required) { if ($perl_type eq 'Hash') { confess "attribute $accessor is required" unless scalar %$value; } elsif ($perl_type eq 'Array') { confess "attribute $accessor is required" unless scalar @$value; } } } else { confess "attribute $accessor is required" if $required && ! defined $value; } $on_change->($self, $attr, 'mutator', \$value) or return $self if ($on_change && defined $value); if ($transistent) { set_attribute($self, $storage_key, $value); } else { $self->{$storage_key} = $value; } $self; }; }
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; }
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') { confess "$accessor must be $perl_type type" unless (ref($value) eq 'HASH'); if ($associated_class) { validate_associated_class($attr, $self, $_) for values %$value; } } elsif ($associated_class) { my $transistent = $attr->transistent; my $storage_key = $attr->storage_key; my $current_value = $transistent ? get_attribute($self, $storage_key) : ($array_storage_type ? $self->[$storage_key] : $self->{$storage_key}); return if ($value && $current_value && $value eq $current_value); $attr->deassociate($self); if (defined $value) { validate_associated_class($attr, $self, $value); } } }
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"; }
{ my %pending_association;
sub start_association_process {
my ($self) = @_;
$pending_association{$self} = 1;
}
sub has_pending_association {
my ($self) = @_;
$pending_association{$self};
}
sub end_association_process {
my ($self) = @_;
delete $pending_association{$self};
}
}
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; confess "invalid definition for " . ref($self) ."::". $name . " - associatied class not defined on " . ref($value) ."::" . $the_other_end unless $the_other_end_attribute->associated_class; start_association_process($value); eval { my $association_call = 'associate_' . lc($the_other_end_attribute->perl_type) . '_as_the_other_end'; $attr->$association_call($self, $value); }; end_association_process($value); die $@ if $@; }
sub associate_scalar_as_the_other_end { my ($attr, $self, $value) = @_; my $the_other_end = $attr->the_other_end; $value->$the_other_end($self); }
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); } }
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); }
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); my $deassociation_call = 'deassociate_' . lc($the_other_end_attribute->perl_type) . '_as_the_other_end'; if(ref($value) eq 'ARRAY') { $the_other_end_attribute->$deassociation_call($self, $_) for @$value; } elsif(ref($value) eq 'HASH') { $the_other_end_attribute->$deassociation_call($self, $value->{$_}) for(keys %$value); } else { $the_other_end_attribute->$deassociation_call($self, $value); } end_association_process($self); }
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; }
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) { delete $value->{$k}; return; } } } undef; }
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; }
sub generate_scalar_mutator_method { shift()->generate_mutator_method; }
sub generate_code_mutator_method { shift()->generate_mutator_method; }
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; }; }
sub generate_array_mutator_method { shift()->generate_mutator_method; }
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; }); }
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; }; }
sub generate_hash_mutator_method { shift()->generate_mutator_method; }
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}; }; }
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; }; }
sub generate_scalar_reset_method { my $attr = shift; my $mutator = $attr->mutator; my $index_by = $attr->index_by; sub { my ($self, ) = @_; $self->$mutator(undef); }; }
sub generate_scalar_has_method { my $attr = shift; sub { my ($self, ) = @_; !! $attr->get_value($self); }; }
sub generate_hash_reset_method { my $attr = shift; my $mutator = $attr->mutator; my $index_by = $attr->index_by; sub { my ($self, ) = @_; $self->$mutator({}); }; }
sub generate_hash_has_method { my $attr = shift; sub { my ($self, ) = @_; my $value = $attr->get_value($self); !! ($value && keys %$value); }; }
sub generate_array_reset_method { my $attr = shift; my $mutator = $attr->mutator; my $index_by = $attr->index_by; sub { my ($self, ) = @_; $self->$mutator([]); }; }
sub generate_array_has_method { my $attr = shift; sub { my ($self, ) = @_; my $value = $attr->get_value($self); !! ($value && @$value); }; }
#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; }; }
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]; }; }
sub generate_array_push_method { my $attr = shift; my $accesor = $attr->accessor; sub { my $self = shift; my $array_ref = $self->$accesor(); push @$array_ref, @_; }; }
sub generate_array_pop_method { my $attr = shift; my $accesor = $attr->accessor; sub { my $self = shift; my $array_ref = $self->$accesor(); pop @$array_ref; }; }
sub generate_array_shift_method { my $attr = shift; my $accesor = $attr->accessor; sub { my $self = shift; my $array_ref= $self->$accesor(); shift @$array_ref; }; }
sub generate_array_unshift_method { my $attr = shift; my $accesor = $attr->accessor; sub { my $self = shift; my $array_ref = $self->$accesor(); unshift @$array_ref, @_; }; }
sub generate_array_count_method { my $attr = shift; my $accesor = $attr->accessor; sub { my $self = shift; my $array_ref = $self->$accesor(); scalar @$array_ref; }; }
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; }; }
#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; } } } $self; }; }
sub generate { my ($self, $method_name) = @_; my $call = "generate_" . lc($self->perl_type) . "_${method_name}_method"; $self->$call; }
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; } }
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}; } } { my %storage;
sub get_attribute {
my ($self, $key) = @_;
my $object = $storage{$self} ||= {};
return $object->{$key};
}
sub set_attribute {
my ($self, $key, $value) = @_;
my $object = $storage{$self} ||= {};
$object->{$key} = $value;
}
sub delete_object {
my ($self) = @_;
delete $storage{$self};
}
}
1;
__END__