Mercurial > pub > Impl
diff Lib/IMPL/Class/Meta.pm @ 90:dc1da0389db7
Small improvements in the abstract object class
Added support for a class data, documentation
Additional tests for the new functionality
author | wizard |
---|---|
date | Mon, 26 Apr 2010 03:10:03 +0400 |
parents | 16ada169ca75 |
children | 6c25ea91c985 |
line wrap: on
line diff
--- a/Lib/IMPL/Class/Meta.pm Wed Apr 21 17:39:45 2010 +0400 +++ b/Lib/IMPL/Class/Meta.pm Mon Apr 26 03:10:03 2010 +0400 @@ -1,7 +1,11 @@ package IMPL::Class::Meta; use strict; +use Class::Data::Inheritable; +use Storable qw(dclone); + my %class_meta; +my %class_data; sub set_meta { my ($class,$meta_data) = @_; @@ -32,12 +36,186 @@ wantarray ? @result : \@result; } -=pod -__PACKAGE_->set_meta($metaObject); -__PACKAGE_->get_meta('MyMetaClass',sub { - my ($item) = @_; - $item->Name eq 'Something' ? 1 : 0 -} ); -=cut +sub class_data { + my $class = shift; + $class = ref $class || $class; + + if (@_ > 1) { + my ($name,$value) = @_; + return $class_data{$class}{$name} = $value; + } else { + my ($name) = @_; + + if( exists $class_data{$class}{$name} ) { + $class_data{$class}{$name}; + } else { + if ( my $value = $class->_find_class_data($name) ) { + $class_data{$class}{$name} = dclone($value); + } else { + undef; + } + } + } +} + +sub _find_class_data { + my ($class,$name) = @_; + + no strict 'refs'; + + exists $class_data{$_}{$name} and return $class_data{$_}{$name} foreach @{"${class}::ISA"}; + + my $val; + $val = $_->_find_class_data($name) and return $val foreach @{"${class}::ISA"}; +} 1; + +__END__ + +=pod + +=head1 NAME + +C<IMPL::Class::Meta> - информация хранимая на уровне класса. + +=head1 SYNOPSIS + +=begin code + +package InfoMeta; + +use base qw(IMPL::Object IMPL::Object::Autofill); +use IMPL::Class::Property; + +__PACKAGE__->PassThroughArgs; + +BEGIN { + public property name => prop_get | owner_set; +} + +package InfoExMeta; +use base qw(InfoMeta); + +__PACKAGE__->PassThroughArgs; + +BEGIN { + public property description => prop_all; +} + +package Foo; + +__PACKAGE__->set_meta(new InfoMeta(name => 'info')); +__PACKAGE__->set_meta(new InfoExMeta(name => 'infoEx', description => 'extended info' )); + +package main; + +# get all InfoMeta, InfoExMeta will be included, becouse it's derived from InfoMeta +my @info = Foo->get_meta('InfoMeta'); # will get two objects, 'info' and 'infoEx'; + +# get all InfoExMeta meta +@info = Foo->get_meta('InfoExMeta'); # will get only 'infoEx' + +# get filtered meta +@info = Foo->get_meta('InfoMeta', sub { $_->name eq 'info'} ); # will get only 'info' + +=end code + +=head1 DESCRIPTION + +Позвоялет расширять информацию о типах (класса) при помощи метаданных, метаданными являются любые объекты, +притом выборка метаданных приоизводится по их типу (классу), что позволяет выбрать все однотипные метаданные. + +Существует возможность выборки метаданных с учетом унаследованных от базовых классов + +=head1 MEMBERS + +=over + +=item C<set_meta($meta_data)> + +Добавляет метаданные C<$meta_data> к классу. + +=item C<get_meta($meta_class,$predicate,$deep)> + +Выбирает метаданные типа C<$meta_class> и его наследников, с возможностью фильтрации и получения +метаданных базовых классов. + +=over + +=item C<$meta_class> + +Тип метаданных + +=item C<$predicate> + +Подпрограмма, которая будет вызываться для каждых найденных метаданных и на основе результата +ее выполнения метаданные будут включены в результат или нет. Получеат в качестве параметра +объект с метаданными, возвращает C<true> - включить метаданные в результа, C<false> - пропустить +метаданные как не подходящие. Также переменная C<$_> ссылается на текущий объект с метаданными. + +=begin code + +my @info = Foo->get_meta( + 'InfoMeta', + sub { ref $_ eq 'InfoMeta'}, # exclude subclasses ('InfoExMeta') + 1 # deep search +); + +my @info = Foo->get_meta( + 'InfoMeta', + sub { + my $item = shift; + ref $item eq 'InfoMeta' # exclude subclasses ('InfoExMeta') + }, + 1 # deep search +); + +=end code + +=item C<$deep> + +Осуществлять поиск по базовым классам. + +=back + +=item C<class_data($name,$new_value)> + +В отличии от метаданных, C<class_data> не накапливает информацию, +а хранит только один экземпляр для одного ключа C<$name>. + +Если новое значение не задано, то осуществляется выборка сохраненного, +если текущий класс не имеет сохраненного значения, то оно ищется в базовых +классах, затем копия найденного значения сохраняется в текущем классе и +возвращается наружу. Это позволяет базовым классам задавать значение по-умолчанию, +которые могут быть изменены или заменены субклассами. + +=begin code + +package Foo; +use base qw(IMPL::Class::Meta); + +__PACKAGE__->class_data( info => { version => 1 } ); # will be default for all subclasses + +sub say_version { + my ($self) = @_; + + print $self->class_data(info)->{version}; +} + +package Bar; +use base qw(Foo); + +__PACKAGE__->class_data('info')->{ language } = 'English'; + +package main; + +Foo->class_data('info')->{version} = 2; +Bar->say_version; # will print '1'; +Foo->say_version; # will print '2'; + +=end code + +=back + +=cut