diff lib/IMPL/Class/Meta.pm @ 407:c6e90e02dd17 ref20150831

renamed Lib->lib
author cin
date Fri, 04 Sep 2015 19:40:23 +0300
parents
children ee36115f6a34
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/IMPL/Class/Meta.pm	Fri Sep 04 19:40:23 2015 +0300
@@ -0,0 +1,344 @@
+package IMPL::Class::Meta;
+use strict;
+
+use Carp qw(carp confess);
+use IMPL::clone qw(clone);
+
+my %class_meta;
+my %class_data;
+
+sub SetMeta {
+    my ($class,$meta_data) = @_;
+    $class = ref $class || $class;
+    
+    # тут нельзя использовать стандартное исключение, поскольку для него используется
+    # класс IMPL::Object::Accessor, который наследуется от текущего класса
+    confess "The meta_data parameter should be an object" if not ref $meta_data;
+    
+    push @{$class_meta{$class}{ref $meta_data}},$meta_data;
+}
+
+sub set_meta {
+	goto &SetMeta;
+}
+
+sub GetMeta {
+    my ($class,$meta_class,$predicate,$deep) = @_;
+    $class = ref $class if ref $class;
+    no strict 'refs';
+    my @result;
+    
+    if ($predicate) {
+        push @result,grep( &$predicate($_), map( @{$class_meta{$class}{$_}}, grep( $_->isa($meta_class), keys %{$class_meta{$class} || {}} ) ) );
+    } else {
+        push @result, map( @{$class_meta{$class}{$_} || []}, grep( $_->isa($meta_class), keys %{$class_meta{$class} || {}} ) );
+    }
+    
+    if ($deep) {
+        push @result, map { $_->can('GetMeta') ? $_->GetMeta($meta_class,$predicate,$deep) : () } @{$class.'::ISA'};
+    }
+    
+    wantarray ? @result : \@result;
+}
+
+sub get_meta {
+	goto &GetMeta;
+}
+
+sub class_data {
+    my $class = shift;
+    $class = ref $class || $class;
+    
+    carp 'The method is obsolete, use static_accessor($name,$value,\'clone\') instead';
+    
+    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} = clone($value);
+            } else {
+                undef;
+            }
+        }
+    }
+}
+
+sub static_accessor {
+    my ($class,$name,$value,$inherit) = @_;
+    
+    $inherit ||= 'inherit';
+    
+    my $method = "static_accessor_$inherit";
+    
+    return $class->$method($name,$value);
+}
+
+sub static_accessor_clone {
+    my ($class,$name,$value) = @_;
+    $class = ref $class || $class;
+    
+    no strict 'refs';
+    
+    *{"${class}::${name}"} = sub {
+    	my $self = shift;
+    	
+    	$self = ref $self || $self;
+    	
+        if (@_ > 0) {            
+            if ($class ne $self) {
+                $self->static_accessor_clone( $name => $_[0] ); # define own class data
+            } else {
+                $value = $_[0];
+            }
+        } else {
+        	return $self ne $class
+        	   ? $self->static_accessor_clone($name => clone($value))
+        	   : $value;
+        }
+    };
+    return $value;
+};
+
+sub static_accessor_inherit {
+    my ($class,$name,$value) = @_;
+    
+    no strict 'refs';
+    
+    *{"${class}::$name"} = sub {
+        my $self = shift;
+        
+        if (@_ > 0) {            
+            $self = ref $self || $self;            
+            
+            if ($class ne $self) {
+                $self->static_accessor_inherit( $name => $_[0] ); # define own class data
+            } else {
+                $value = $_[0];
+            }
+        } else {
+            $value ;
+        }        
+    };
+    return $value;
+}
+
+sub static_accessor_own {
+    my ($class,$name,$value) = @_;
+    
+    no strict 'refs';
+    
+    *{"${class}::$name"} = sub {
+        my $self = shift;
+        $self = ref $self || $self;
+        
+        if ($class ne $self) {
+            if (@_ > 0) {
+                $self->static_accessor_own( $name => $_[0] ); # define own class data
+            } else {
+                return;
+            }
+        } else {
+            if ( @_ > 0 ) {
+                $value = $_[0];
+            } else {
+                return $value;
+            }
+        }    
+    };
+    
+    return $value;
+}
+
+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 = $_->can('_find_class_data') ? $_->_find_class_data($name) : undef and return $val foreach @{"${class}::ISA"}; 
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+C<IMPL::Class::Meta> - информация хранимая на уровне класса.
+
+=head1 SYNOPSIS
+
+=begin code
+
+package InfoMeta;
+
+use parent qw(IMPL::Object IMPL::Object::Autofill);
+use IMPL::Class::Property;
+
+__PACKAGE__->PassThroughArgs;
+
+BEGIN {
+    public property name => prop_get | owner_set;
+}
+
+package InfoExMeta;
+use parent 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
+
+=head2 C<set_meta($meta_data)>
+
+Добавляет метаданные C<$meta_data> к классу.
+
+=head2 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  
+
+=head2 C<static_accessor($name[,$value[,$inherit]])>
+
+Создает статическое свойство с именем C<$name> и начальным значением C<$value>.
+
+Параметр C<$inherit> контролирует то, как наследуются значения.
+
+=over
+
+=item * C<inherit>
+
+По умолчанию. Означает, что если для класса не определено значение, оно будет
+получено от родителя.
+
+=item * C<clone>
+
+Если для класса не определено значение, то оно будет клонировано из
+родительского значения при первом обращении. Полезно, когда родитель задает
+значение по-умолчанию, которое разделяется между несколькими потомками,
+которые модифицирю само значение (например значением является ссылка на хеш,
+а потомки добавляют или меняют значения в этом хеше).
+
+=item * C<own>
+
+Каждый класс имеет свое собственное значение не зависящее от того, что было
+у предка. Начальное значение для этого статического свойства C<undef>.
+
+=back
+
+Данный метод является заглушкой, он передает управление 
+C<static_accessor_inherit>, C<static_accessor_clone>, C<static_accessor_own>
+соответственно. Эти методы можно вызывать явно 
+C<static_accessor_*($name[,$value])>. 
+
+
+=begin code
+
+package Foo;
+use parent qw(IMPL::Class::Meta);
+
+__PACKAGE__->static_accessor( info => { version => 1 } );
+__PACKAGE__->static_accessor( mappings => { toString => \&ToString }, 'clone' );
+__PACKAGE__->static_accessor( _instance => undef, 'own' );
+
+sub ToString {
+    "[object Foo]";
+}
+
+sub default {
+    my ($self) = @_;
+    
+    $self = ref $self || $self;
+    return $self->_instance ? $self->_instance : $self->_instance($self->new());
+}
+
+package Bar;
+use parent qw(Foo);
+
+__PACKAGE__->info({language => 'English', version => 2}); # will define own 'info' but will loose original data.
+__PACKAGE__->mappings->{sayHello} = \&SayHello; # will not affect Foo->mappings;
+
+package main;
+
+my $foo = Foo->default; # will be a Foo object
+my $bar = Bar->default; # will be a Bar object 
+
+=end code
+
+=cut