view Lib/IMPL/Web/Application/ControllerUnit.pm @ 120:41e9d9ea3db5

Merge with 79cdd6c86409806bd1de092d9f0fb2b048775720
author wizard
date Mon, 07 Jun 2010 17:45:14 +0400 (2010-06-07)
parents 7b14e0122b79
children c8dfbbdd8005
line wrap: on
line source
package IMPL::Web::Application::ControllerUnit;
use strict;
use base qw(IMPL::Object);

use IMPL::Class::Property;
use IMPL::DOM::Transform::PostToDOM;
use IMPL::DOM::Schema;
use Class::Inspector;
use File::Spec;

use constant {
	CONTROLLER_METHODS => 'controller_methods',
	STATE_CORRECT => 'correct',
	STATE_NEW => 'new',
	STATE_INVALID => 'invalid'
};

BEGIN {
	public property action => prop_get | owner_set;
	public property application => prop_get | owner_set;
	public property query => prop_get | owner_set;
	public property formData => prop_get | owner_set;
	public property formSchema => prop_get | owner_set;
	public property formErrors => prop_get | owner_set;
}

__PACKAGE__->class_data(CONTROLLER_METHODS,{});

sub CTOR {
	my ($this,$action,$args) = @_;
	
	$this->action($action);
	$this->application($action->application);
	$this->query($action->query);
	
	$this->$_($args->{$_}) foreach qw(formData formSchema formErrors);
}

sub forms {
	my ($self,%forms) = @_;
	
	while ( my ($method,$info) = each %forms ) {
		die new IMPL::Exception("A method doesn't exists in the controller",$self,$method) unless $self->can($method);
		if ( not ref $info ) {
			$self->class_data(CONTROLLER_METHODS)->{$method} = {
				wrapper => 'FormWrapper',
				schema => $info
			};
		} elsif (ref $info eq 'HASH') {
			die new IMPL::Exception("A schema must be specified",$self,$method) unless $info->{schema};
			
			$self->class_data(CONTROLLER_METHODS)->{$method} = {
				wrapper => 'FormWrapper',
				schema => $info->{schema} 
			};
		} else {
			die new IMPL::Exception("Unsupported method information",$self,$method);
		}
	}
}

sub transactions {
	my ($self,@names) = @_;
	
	$self->class_data(CONTROLLER_METHODS)->{$_} = {} foreach @names;
}

sub InvokeAction {
	my ($self,$method,$action) = @_;
	
	if (my $methodInfo = $self->class_data(CONTROLLER_METHODS)->{$method}) {
		if (my $wrapper = $methodInfo->{wrapper}) {
			return $self->$wrapper($method,$action,$methodInfo);
		} else {
			return $self->TransactionWrapper($method,$action,$methodInfo);			
		}
	} else {
		die new IMPL::InvalidOperationException("Invalid method call",$self,$method);
	}
}

sub TransactionWrapper {
	my ($self,$method,$action,$methodInfo) = @_;
	
	my $unit = $self->new($action);
	return $unit->$method();
}

sub FormWrapper {
	my ($self,$method,$action,$methodInfo) = @_;
	
	my $schema = $self->loadSchema($methodInfo->{schema});
	
	my $process = $action->query->param('process') || 0;
	my $form = $methodInfo->{form}
		|| $action->query->param('form')
		|| $schema->selectSingleNode('ComplexNode')->name
			or die new IMPL::Exception('No situable form name could be determined',$self,$method);
	
	my %result;
	
	my $transform = IMPL::DOM::Transform::PostToDOM->new(
		undef,
		$schema,
		$form
	);
	
	$result{formSchema} = $schema;
	$result{formData} = $transform->Transform($self->query);
	
	if ($process) {
		$result{formErrors} = $transform->Errors->as_list;
		if ($transform->Errors->Count) {
			$result{state} = STATE_INVALID;
		} else {
			$result{state} = STATE_CORRECT;
			my $unit = $self->new($action,\%result);
			$result{result} = $unit->method();
		}
	} else {
		$result{state} = STATE_NEW;
	}
	
	return \%result;
}

sub loadSchema {
	my ($self,$name) = @_;

	my ($vol,$dir,$file) = File::Spec->splitpath( Class::Inspector->resolved_filename(ref $self || $self) );
	
	return IMPL::DOM::Schema->LoadSchema(File::Spec->catfile($vol,$dir,$name));
}

1;

__END__

=pod

=head1 NAME

C<IMPL::Web::Application::ControllerUnit> - ������� ����� ��� ����������� ���������� � ������ �����������.

=head1 DESCRIPTION

������, ����������� �� ������� ������ ���������� ������� ����������. ����� ������� � ����� ������
����������� ��� ���������� ��� ������ ������� C<transaction>, C<form>.

����� ����������� ���������� ��������� ��������� �������, � ������ �������� ����� ��������� ����������.
��� ����� ���������� ����� C<InvokeAction($method,$action)>, ������� �������/��������������� ��������
����������.

���������� �� ������ ������ ������� �� ������� � �����.

=head2 ������� ����������

������� ���������� ������� ������ ������, ��� ��������������� ���������, � ������������ ��������� ��������
���������� ������������.

=head2 �����

��� ������������� ���� ������ �������������� ��������������, ��� ��������� DOM ��������� � ������� �����.
��� ������������ DOM ��������� ������������ �����. ��� ���� ���������� �������� �������������� ��������
C<formData>, C<formSchema>, C<formErrors>.

��������� ���������� ���������� �� ������������ �������� ������������, � ���������� � ����� �����, �������
�������� ��������� �������

=begin code

{
	state => '{ new | correct | invalid }',
	result => $transactionResult,
	formData => $formDOM,
	formSchema => $formSchema,
	formErrors => @errors
}

=end code

=over

=item C<state>

��������� ����������� �����.

=over

=item C<new>

�������������� ���������� �����, ��� ����� ���� ������������, �� ��� ���������.
� ������ ��������� ���������� ������ �� �����������.

=item C<correct>

������ ����� ���������, ���������� ���������, � �� ��������� �������� ����� ���� C<result>

=item C<invalid>

���������� ����� �� ������ �����������, ������ �������� ����� ���� C<formErrors>. ����������
�� �����������.

=back

=item C<result>

��������� ���������� ����������, ���� ������� ������� �����������.

=item C<formData>

��� �������� � ������� �������. �������� ���������� ������, �� �������� �� ��� ������������,
����� ���� ����������� ��� ���������� �����, ��� ����������� �����������.

=item C<formSchema>

����� ������ �����, ����� �������������� ��� ���������� ������������ ����.

=item C<formErrors>

������ ����������� ������, ���� ������� ����

=back

=head1 MEMBERS

=over

=item C<[get] application>

=item C<[get] query>

=item C<[get] response>

=item C<[get] formData>

=item C<[get] formSchema>

=item C<[get] formErrors>

=item C<InvokeAction($method,$action)>

������������ �������� ���������� ����������, ����� ���� ������������� ��� ��������������� ��������� ��
����� ��������.

=item C<TransactionWrapper($method,$action,$methodInfo)>

������� ��� ��������������� ������� ����������, ����� ���� ������������� ��� ��������������� ��������� ��
����� ��������.

=item C<FormWrapper($method,$action,$methodInfo)>

������� ��� ��������������� ����, ����� ���� ������������� ��� ��������������� ��������� ��
����� ��������.

=back

=head1 EXAMPLE

=begin code

package MyBooksUnit;
use strict;
use base qw(IMPL::Web::Application::ControllerUnit);

__PACKAGE__->PassThroughArgs;

__PACKAGE__->transactions(qw(
	find 
	info
));
__PACKAGE__->forms(
	create => 'books.create.xml'
);

sub find {
	my ($this) = @_;
	
	return $this->application->dataSource->resultset('Books')->find({author => $this->query->param('author')});
}

sub info {
	my ($this) = @_;
	
	return $this->application->dataSource->resultset('Books')->find({id => $this->query->param('id')});
}

sub create {
	my ($this) = @_;
	
	my %book = map {
		$_ => $this->formData->selectSingleNode($_)->nodeValue
	} qw(author_id title year ISBN);
	
	return $this->application->datasource->resultset('Books')->create(\%book);
}

=end code

=cut