Mercurial > pub > Impl
comparison 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 |
comparison
equal
deleted
inserted
replaced
89:3d1f584aea60 | 90:dc1da0389db7 |
---|---|
1 package IMPL::Class::Meta; | 1 package IMPL::Class::Meta; |
2 use strict; | 2 use strict; |
3 | 3 |
4 use Class::Data::Inheritable; | |
5 use Storable qw(dclone); | |
6 | |
4 my %class_meta; | 7 my %class_meta; |
8 my %class_data; | |
5 | 9 |
6 sub set_meta { | 10 sub set_meta { |
7 my ($class,$meta_data) = @_; | 11 my ($class,$meta_data) = @_; |
8 $class = ref $class if ref $class; | 12 $class = ref $class if ref $class; |
9 | 13 |
30 push @result, map( @{$class_meta{$class}{$_} || []}, grep( $_->isa($meta_class), keys %{$class_meta{$class} || {}} ) ); | 34 push @result, map( @{$class_meta{$class}{$_} || []}, grep( $_->isa($meta_class), keys %{$class_meta{$class} || {}} ) ); |
31 } | 35 } |
32 wantarray ? @result : \@result; | 36 wantarray ? @result : \@result; |
33 } | 37 } |
34 | 38 |
39 sub class_data { | |
40 my $class = shift; | |
41 $class = ref $class || $class; | |
42 | |
43 if (@_ > 1) { | |
44 my ($name,$value) = @_; | |
45 return $class_data{$class}{$name} = $value; | |
46 } else { | |
47 my ($name) = @_; | |
48 | |
49 if( exists $class_data{$class}{$name} ) { | |
50 $class_data{$class}{$name}; | |
51 } else { | |
52 if ( my $value = $class->_find_class_data($name) ) { | |
53 $class_data{$class}{$name} = dclone($value); | |
54 } else { | |
55 undef; | |
56 } | |
57 } | |
58 } | |
59 } | |
60 | |
61 sub _find_class_data { | |
62 my ($class,$name) = @_; | |
63 | |
64 no strict 'refs'; | |
65 | |
66 exists $class_data{$_}{$name} and return $class_data{$_}{$name} foreach @{"${class}::ISA"}; | |
67 | |
68 my $val; | |
69 $val = $_->_find_class_data($name) and return $val foreach @{"${class}::ISA"}; | |
70 } | |
71 | |
72 1; | |
73 | |
74 __END__ | |
75 | |
35 =pod | 76 =pod |
36 __PACKAGE_->set_meta($metaObject); | 77 |
37 __PACKAGE_->get_meta('MyMetaClass',sub { | 78 =head1 NAME |
38 my ($item) = @_; | 79 |
39 $item->Name eq 'Something' ? 1 : 0 | 80 C<IMPL::Class::Meta> - информация хранимая на уровне класса. |
40 } ); | 81 |
82 =head1 SYNOPSIS | |
83 | |
84 =begin code | |
85 | |
86 package InfoMeta; | |
87 | |
88 use base qw(IMPL::Object IMPL::Object::Autofill); | |
89 use IMPL::Class::Property; | |
90 | |
91 __PACKAGE__->PassThroughArgs; | |
92 | |
93 BEGIN { | |
94 public property name => prop_get | owner_set; | |
95 } | |
96 | |
97 package InfoExMeta; | |
98 use base qw(InfoMeta); | |
99 | |
100 __PACKAGE__->PassThroughArgs; | |
101 | |
102 BEGIN { | |
103 public property description => prop_all; | |
104 } | |
105 | |
106 package Foo; | |
107 | |
108 __PACKAGE__->set_meta(new InfoMeta(name => 'info')); | |
109 __PACKAGE__->set_meta(new InfoExMeta(name => 'infoEx', description => 'extended info' )); | |
110 | |
111 package main; | |
112 | |
113 # get all InfoMeta, InfoExMeta will be included, becouse it's derived from InfoMeta | |
114 my @info = Foo->get_meta('InfoMeta'); # will get two objects, 'info' and 'infoEx'; | |
115 | |
116 # get all InfoExMeta meta | |
117 @info = Foo->get_meta('InfoExMeta'); # will get only 'infoEx' | |
118 | |
119 # get filtered meta | |
120 @info = Foo->get_meta('InfoMeta', sub { $_->name eq 'info'} ); # will get only 'info' | |
121 | |
122 =end code | |
123 | |
124 =head1 DESCRIPTION | |
125 | |
126 Позвоялет расширять информацию о типах (класса) при помощи метаданных, метаданными являются любые объекты, | |
127 притом выборка метаданных приоизводится по их типу (классу), что позволяет выбрать все однотипные метаданные. | |
128 | |
129 Существует возможность выборки метаданных с учетом унаследованных от базовых классов | |
130 | |
131 =head1 MEMBERS | |
132 | |
133 =over | |
134 | |
135 =item C<set_meta($meta_data)> | |
136 | |
137 Добавляет метаданные C<$meta_data> к классу. | |
138 | |
139 =item C<get_meta($meta_class,$predicate,$deep)> | |
140 | |
141 Выбирает метаданные типа C<$meta_class> и его наследников, с возможностью фильтрации и получения | |
142 метаданных базовых классов. | |
143 | |
144 =over | |
145 | |
146 =item C<$meta_class> | |
147 | |
148 Тип метаданных | |
149 | |
150 =item C<$predicate> | |
151 | |
152 Подпрограмма, которая будет вызываться для каждых найденных метаданных и на основе результата | |
153 ее выполнения метаданные будут включены в результат или нет. Получеат в качестве параметра | |
154 объект с метаданными, возвращает C<true> - включить метаданные в результа, C<false> - пропустить | |
155 метаданные как не подходящие. Также переменная C<$_> ссылается на текущий объект с метаданными. | |
156 | |
157 =begin code | |
158 | |
159 my @info = Foo->get_meta( | |
160 'InfoMeta', | |
161 sub { ref $_ eq 'InfoMeta'}, # exclude subclasses ('InfoExMeta') | |
162 1 # deep search | |
163 ); | |
164 | |
165 my @info = Foo->get_meta( | |
166 'InfoMeta', | |
167 sub { | |
168 my $item = shift; | |
169 ref $item eq 'InfoMeta' # exclude subclasses ('InfoExMeta') | |
170 }, | |
171 1 # deep search | |
172 ); | |
173 | |
174 =end code | |
175 | |
176 =item C<$deep> | |
177 | |
178 Осуществлять поиск по базовым классам. | |
179 | |
180 =back | |
181 | |
182 =item C<class_data($name,$new_value)> | |
183 | |
184 В отличии от метаданных, C<class_data> не накапливает информацию, | |
185 а хранит только один экземпляр для одного ключа C<$name>. | |
186 | |
187 Если новое значение не задано, то осуществляется выборка сохраненного, | |
188 если текущий класс не имеет сохраненного значения, то оно ищется в базовых | |
189 классах, затем копия найденного значения сохраняется в текущем классе и | |
190 возвращается наружу. Это позволяет базовым классам задавать значение по-умолчанию, | |
191 которые могут быть изменены или заменены субклассами. | |
192 | |
193 =begin code | |
194 | |
195 package Foo; | |
196 use base qw(IMPL::Class::Meta); | |
197 | |
198 __PACKAGE__->class_data( info => { version => 1 } ); # will be default for all subclasses | |
199 | |
200 sub say_version { | |
201 my ($self) = @_; | |
202 | |
203 print $self->class_data(info)->{version}; | |
204 } | |
205 | |
206 package Bar; | |
207 use base qw(Foo); | |
208 | |
209 __PACKAGE__->class_data('info')->{ language } = 'English'; | |
210 | |
211 package main; | |
212 | |
213 Foo->class_data('info')->{version} = 2; | |
214 Bar->say_version; # will print '1'; | |
215 Foo->say_version; # will print '2'; | |
216 | |
217 =end code | |
218 | |
219 =back | |
220 | |
41 =cut | 221 =cut |
42 | |
43 1; |