comparison lib/IMPL/declare.pm @ 407:c6e90e02dd17 ref20150831

renamed Lib->lib
author cin
date Fri, 04 Sep 2015 19:40:23 +0300
parents
children ee36115f6a34
comparison
equal deleted inserted replaced
406:f23fcb19d3c1 407:c6e90e02dd17
1 package IMPL::declare;
2 use strict;
3
4 use Carp qw(carp);
5 use IMPL::Class::PropertyInfo();
6 use IMPL::Const qw(:access);
7 use IMPL::require();
8
9 BEGIN {
10 *_require = *IMPL::require::_require;
11 *_trace = *IMPL::require::_trace;
12 }
13
14 sub import {
15 my ( $self, $args ) = @_;
16
17 return unless $args;
18
19 die "A hash reference is required" unless ref $args eq 'HASH';
20
21 no strict 'refs';
22 no warnings 'once';
23
24 my $caller = caller;
25
26 my $aliases = $args->{require} || {};
27
28 $IMPL::require::PENDING{$caller} = 1;
29 _trace("declare $caller");
30 $IMPL::require::level++;
31
32 *{"${caller}::SELF"} = sub () {
33 $caller;
34 };
35
36 while ( my ( $alias, $class ) = each %$aliases ) {
37 _trace("$alias => $class");
38 $IMPL::require::level++;
39 my $c = _require($class);
40
41 *{"${caller}::$alias"} = sub() {
42 $c;
43 };
44 $IMPL::require::level--;
45 }
46
47 my $base = $args->{base} || {};
48
49 my %ctor;
50 my @isa;
51
52 if ( ref $base eq 'ARRAY' ) {
53 carp "Odd elements number in require"
54 unless scalar(@$base) % 2 == 0;
55 while ( my ( $class, $mapper ) = splice @$base, 0, 2 ) {
56 _trace("parent $class");
57 $IMPL::require::level++;
58 $class = $aliases->{$class} || _require($class);
59 $IMPL::require::level--;
60
61 push @isa, $class;
62 $ctor{$class} = $mapper;
63 }
64 }
65 elsif ( ref $base eq 'HASH' ) {
66 while ( my ( $class, $mapper ) = each %$base ) {
67 _trace("parent $class");
68 $IMPL::require::level++;
69 $class = $aliases->{$class} || _require($class);
70 $IMPL::require::level--;
71
72 push @isa, $class;
73 $ctor{$class} = $mapper;
74 }
75 }
76
77 %{"${caller}::CTOR"} = %ctor;
78 push @{"${caller}::ISA"}, @isa;
79
80 if ( ref( $args->{meta} ) eq 'ARRAY' ) {
81 $caller->SetMeta($_) foreach @{ $args->{meta} };
82 }
83
84 my $props = $args->{props} || [];
85
86 if ( $props eq 'HASH' ) {
87 $props = [%$props];
88 }
89
90 die "A hash or an array reference is required in the properties list"
91 unless ref $props eq 'ARRAY';
92
93 carp "Odd elements number in properties declaration of $caller"
94 unless scalar(@$props) % 2 == 0;
95
96 if (@$props) {
97 $self->_implementProps( $props, $caller );
98 }
99
100 if ( $args->{_implement} ) {
101 $self->_implementProps( $caller->abstractProps, $caller );
102 $caller->abstractProps( [] );
103 }
104
105 $IMPL::require::level--;
106 delete $IMPL::require::PENDING{$caller};
107 }
108
109 sub _implementProps {
110 my ( $self, $props, $caller ) = @_;
111
112 for ( my $i = 0 ; $i < @$props - 1 ; $i = $i + 2 ) {
113 my ( $prop, $spec ) = @{$props}[ $i, $i + 1 ];
114
115 $caller->ClassPropertyImplementor->Implement(
116 $spec,
117 {
118 name => $prop,
119 class => $caller,
120 access => $prop =~ /^_/
121 ? ACCESS_PRIVATE
122 : ACCESS_PUBLIC
123 }
124 );
125 }
126 }
127
128 1;
129
130 __END__
131
132 =pod
133
134 =head1 NAME
135
136 C<IMPL::declare> - описывает класс
137
138 =head1 SYNOPSIS
139
140 =begin code
141
142 package My::Bar;
143
144 use IMPL::declare {
145 require => {
146 TFoo => 'My::Foo',
147 TBox => 'My::Box'
148 },
149 base => {
150 TFoo => '@_',
151 'IMPL::Object' => undef,
152 }
153 }
154
155 sub CreateBox {
156 my ($this) = @_;
157 return TBox->new($this);
158 }
159
160 =end code
161
162 Специальная ситрока C<@_> означает передачу параметров конструктора текущего класса конструктору
163 базового класса без изменений.
164
165 =head1 DESCRIPTION
166
167 Описывает текущий пакет(модуль) как класс. В качестве параметра получает ссылку на хеш,
168 в которой храняться метаданные для объявления класса.
169
170 =head1 METADATA
171
172 =head2 C<require>
173
174 Содержит ссылку на хеш с синонимами модулей, которые будут доступны в текушем модуле,
175 аналогично использованию C<IMPL::require>. Однако, если модуль не требует загрузки при
176 помощи C<require> нужно использовать префикс C<'-'> в его имени
177
178 =begin code
179
180 {
181 require => {
182 TObject => 'IMPL::Object', # will be loaded with require
183 TFoo => '-My:App::Data::Foo' # will not use 'require' to load module
184 }
185 }
186
187 =end code
188
189 =head2 C<base>
190
191 Обисывает базове классы для текущего класса. Если данный параметр - ссылка массив, то
192 этот массив будет превращен в массив C<@ISA>. Если данный параметр - ссылка на хеш, то
193 его ключи опичавют список базовых классов, а значения - преобразование параметров для
194 вызова базовых конструкторов.
195
196 В качестве имен базовых классов могут быть как полные имена модулей, так и назначенные
197 ранее псевдонимы. Использование префикса C<'-'> перед B<полным именем модуля> означает,
198 что модуль не требуется загружать, в случае с псевдонимами, префикс C<'-'> уже был указан
199 при их объявлении.
200
201 =begin code
202
203 {
204 require => {
205 TFoo => '-My:App::Data::Foo' # will not use 'require' to load module
206 },
207 base => {
208 TFoo => '@_', # pass parameters unchanged
209 'My::Base::Class' => sub { name => $_[0], data => $_[1] }, # remap parameters
210 '-My::Extentions' => undef, # do not pass any parameters
211 }
212 }
213
214 =end code
215
216 =cut