view Lib/IMPL/declare.pm @ 232:5c82eec23bb6

Fixed degradations due refactoring
author sergey
date Tue, 09 Oct 2012 20:12:47 +0400
parents 6d8092d8ce1b
children 9f394b27dccf
line wrap: on
line source

package IMPL::declare;
use strict;

use Scalar::Util qw(set_prototype);
use Carp qw(carp);
use IMPL::Class::PropertyInfo();
use IMPL::Const qw(:access);

sub import {
	my ( $self, $args ) = @_;

	return unless $args;

	die "A hash reference is required" unless ref $args eq 'HASH';

	no strict 'refs';

	my $caller = caller;

	my $aliases = $args->{require} || {};

	while ( my ( $alias, $class ) = each %$aliases ) {
		my $c = _require($class);

		*{"${caller}::$alias"} = set_prototype(
			sub {
				$c;
			},
			''
		);
	}

	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 ) {
			$class = $aliases->{$class} || _require($class);

			push @isa, $class;
			$ctor{$class} = $mapper;
		}
	}
	elsif ( ref $base eq 'HASH' ) {
		while ( my ( $class, $mapper ) = each %$base ) {
			$class = $aliases->{$class} || _require($class);

			push @isa, $class;
			$ctor{$class} = $mapper;
		}
	}
	
	*{"${caller}::CTOR"} = \%ctor;
    *{"${caller}::ISA"}  = \@isa;

	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) {
		for ( my $i = 0 ; $i < @$props - 1 ; $i = $i + 2 ) {
			my ( $prop, $spec ) = @{$props}[ $i, $i + 1 ];

			my $propInfo = IMPL::Class::PropertyInfo->new(
				{
					Name     => $prop,
					Mutators => $spec,
					Class    => $caller,
					Access   => $prop =~ /^_/
					? ACCESS_PRIVATE
					: ACCESS_PUBLIC
				}
			);
			$propInfo->Implement();
		}
	}
}

sub _require {
	my ($class) = @_;

	if ( not $class =~ s/^-// ) {
		( my $file = $class ) =~ s/::|'/\//g;
		require "$file.pm";
	}
	$class;
}

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