view Lib/IMPL/declare.pm @ 250:129e48bb5afb

DOM refactoring ObjectToDOM methods are virtual QueryToDOM uses inflators Fixed transform for the complex values in the ObjectToDOM QueryToDOM doesn't allow to use complex values (HASHes) as values for nodes (overpost problem)
author sergey
date Wed, 07 Nov 2012 04:17:53 +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