diff lib/IMPL/declare.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/declare.pm	Fri Sep 04 19:40:23 2015 +0300
@@ -0,0 +1,216 @@
+package IMPL::declare;
+use strict;
+
+use Carp qw(carp);
+use IMPL::Class::PropertyInfo();
+use IMPL::Const qw(:access);
+use IMPL::require();
+
+BEGIN {
+	*_require = *IMPL::require::_require;
+	*_trace   = *IMPL::require::_trace;
+}
+
+sub import {
+	my ( $self, $args ) = @_;
+
+	return unless $args;
+
+	die "A hash reference is required" unless ref $args eq 'HASH';
+
+	no strict 'refs';
+	no warnings 'once';
+
+	my $caller = caller;
+
+	my $aliases = $args->{require} || {};
+
+	$IMPL::require::PENDING{$caller} = 1;
+	_trace("declare $caller");
+	$IMPL::require::level++;
+
+	*{"${caller}::SELF"} = sub () {
+		$caller;
+	};
+
+	while ( my ( $alias, $class ) = each %$aliases ) {
+		_trace("$alias => $class");
+		$IMPL::require::level++;
+		my $c = _require($class);
+
+		*{"${caller}::$alias"} = sub() {
+			$c;
+		};
+		$IMPL::require::level--;
+	}
+
+	my $base = $args->{base} || {};
+
+	my %ctor;
+	my @isa;
+
+	if ( ref $base eq 'ARRAY' ) {
+		carp "Odd elements number in require"
+		  unless scalar(@$base) % 2 == 0;
+		while ( my ( $class, $mapper ) = splice @$base, 0, 2 ) {
+			_trace("parent $class");
+			$IMPL::require::level++;
+			$class = $aliases->{$class} || _require($class);
+			$IMPL::require::level--;
+
+			push @isa, $class;
+			$ctor{$class} = $mapper;
+		}
+	}
+	elsif ( ref $base eq 'HASH' ) {
+		while ( my ( $class, $mapper ) = each %$base ) {
+			_trace("parent $class");
+			$IMPL::require::level++;
+			$class = $aliases->{$class} || _require($class);
+			$IMPL::require::level--;
+
+			push @isa, $class;
+			$ctor{$class} = $mapper;
+		}
+	}
+
+	%{"${caller}::CTOR"} = %ctor;
+	push @{"${caller}::ISA"}, @isa;
+
+	if ( ref( $args->{meta} ) eq 'ARRAY' ) {
+		$caller->SetMeta($_) foreach @{ $args->{meta} };
+	}
+
+	my $props = $args->{props} || [];
+
+	if ( $props eq 'HASH' ) {
+		$props = [%$props];
+	}
+
+	die "A hash or an array reference is required in the properties list"
+	  unless ref $props eq 'ARRAY';
+
+	carp "Odd elements number in properties declaration of $caller"
+	  unless scalar(@$props) % 2 == 0;
+
+	if (@$props) {
+		$self->_implementProps( $props, $caller );
+	}
+
+	if ( $args->{_implement} ) {
+		$self->_implementProps( $caller->abstractProps, $caller );
+		$caller->abstractProps( [] );
+	}
+
+	$IMPL::require::level--;
+	delete $IMPL::require::PENDING{$caller};
+}
+
+sub _implementProps {
+	my ( $self, $props, $caller ) = @_;
+
+	for ( my $i = 0 ; $i < @$props - 1 ; $i = $i + 2 ) {
+		my ( $prop, $spec ) = @{$props}[ $i, $i + 1 ];
+
+		$caller->ClassPropertyImplementor->Implement(
+			$spec,
+			{
+				name   => $prop,
+				class  => $caller,
+				access => $prop =~ /^_/
+				? ACCESS_PRIVATE
+				: ACCESS_PUBLIC
+			}
+		);
+	}
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+C<IMPL::declare> - описывает класс
+
+=head1 SYNOPSIS
+
+=begin code
+
+package My::Bar;
+
+use IMPL::declare {
+	require => {
+		TFoo => 'My::Foo',
+		TBox => 'My::Box'
+	},
+	base => {
+		TFoo => '@_',
+		'IMPL::Object' => undef,
+	}
+}
+
+sub CreateBox {
+	my ($this) = @_;
+	return TBox->new($this);
+}
+
+=end code
+
+Специальная ситрока C<@_> означает передачу параметров конструктора текущего класса конструктору
+базового класса без изменений.
+
+=head1 DESCRIPTION
+
+Описывает текущий пакет(модуль) как класс. В качестве параметра получает ссылку на хеш,
+в которой храняться метаданные для объявления класса.
+
+=head1 METADATA
+
+=head2 C<require>
+
+Содержит ссылку на хеш с синонимами модулей, которые будут доступны в текушем модуле,
+аналогично использованию C<IMPL::require>. Однако, если модуль не требует загрузки при
+помощи C<require> нужно использовать префикс C<'-'> в его имени
+
+=begin code
+
+{
+	require => {
+		TObject => 'IMPL::Object', # will be loaded with require
+		TFoo => '-My:App::Data::Foo' # will not use 'require' to load module
+	}
+}
+
+=end code
+
+=head2 C<base>
+
+Обисывает базове классы для текущего класса. Если данный параметр - ссылка массив, то
+этот массив будет превращен в массив C<@ISA>. Если данный параметр - ссылка на хеш, то
+его ключи опичавют список базовых классов, а значения - преобразование параметров для
+вызова базовых конструкторов.
+
+В качестве имен базовых классов могут быть как полные имена модулей, так и назначенные
+ранее псевдонимы. Использование префикса C<'-'> перед B<полным именем модуля> означает,
+что модуль не требуется загружать, в случае с псевдонимами, префикс C<'-'> уже был указан
+при их объявлении.
+
+=begin code
+
+{
+    require => {
+        TFoo => '-My:App::Data::Foo' # will not use 'require' to load module
+    },
+    base => {
+    	TFoo => '@_', # pass parameters unchanged
+    	'My::Base::Class' => sub { name => $_[0], data => $_[1] },  # remap parameters
+    	'-My::Extentions' => undef, # do not pass any parameters
+    }
+}
+
+=end code
+
+=cut