view Lib/IMPL/Web/Application/CustomResource.pm @ 334:71221d79e6b4

removing web resources contracts
author cin
date Thu, 13 Jun 2013 02:24:57 +0400
parents 04a093f0a5a6
children e8be9062ecf2
line wrap: on
line source

package IMPL::Web::Application::CustomResource;
use strict;

use IMPL::Const qw(:prop);

use IMPL::declare {
    require => {
        NotAllowedException => 'IMPL::Web::NotAllowedException',
        HttpResponse => 'IMPL::Web::HttpResponse'
    },
    base => [
        'IMPL::Web::Application::Resource' => '@_'
    ],
    props => [
        accessCheck => PROP_RW
    ]
};

our %RESOURCE_BINDINGS = (
    GET => 'HttpGet',
    POST => 'HttpPost',
    PUT => 'HttpPut',
    DELETE => 'HttpDelete',
    HEAD => 'HttpHead'
);

__PACKAGE__->static_accessor(_rxResourcesMap => undef, 'own');
__PACKAGE__->static_accessor(_nameResourcesMap => undef, 'own');

sub namedResources {
    shift->_nameResourcesMap;
}

sub regexResources {
    shift->_rxResourcesMap;
}

sub CTOR {
	my ($this,%args) = @_;
	
	$this->accessCheck($args{accessCheck})
	   if $args{accessCheck};
	   
	$this->verbs->{options} ||= \&_HttpOptionsBinding;
    
    while(my ($verb,$methodName) = each %RESOURCE_BINDINGS) {
        $this->verbs->{lc($verb)} ||= sub {
            my ($resource,$action) = @_;
   
            if (eval { $resource->can($methodName) }) {
                return $resource->$methodName($action);
            } else {
                die NotAllowedException->new(allow => join(',', _GetAllowedHttpMethods($resource)));
            }
        }
    }
}

sub FindChildResourceInfo {
    my ( $this, $name ) = @_;
    
    $this->_PrepareResourcesCache()
        unless($this->_nameResourcesMap);
        
    return $this->next::method($name);
}

sub PrepareResourcesCache {
    # suppress default caching mechanisn
}

sub _PrepareResourcesCache {
    # a little bit wired
    my ($self) = @_;
    my %nameMap;
    my @rxMap;

    foreach my $res ($self->GetChildResources()) {
        #skip resources without contract
        next unless $res->{contract};
        
        if ( my $name = $res->{name} ) {
            $nameMap{$name} = $res;
        }
        if ( $res->{match} ) {
            push @rxMap,$res;
        }
    }

    $self->_rxResourcesMap(\@rxMap);
    $self->_nameResourcesMap(\%nameMap);
}

sub AccessCheck {
	my ($this,$verb) = @_;
	
	my $handler = $this->accessCheck; 
	
	if(ref($handler) eq 'CODE') {
		return &$handler($this,$verb);
	}
}

sub GetChildResources {
    
}

sub _HttpOptionsBinding {
    my ($this) = @_;
    
    my @allow = $this->_GetAllowedHttpMethods();
    return HttpResponse->new(
        status => '200 OK',
        headers => {
            allow => join ( ',', @allow )
        }
    );
}

sub _GetAllowedHttpMethods {
    my ($this) = @_;
    return grep $this->can($RESOURCE_BINDINGS{$_}), keys %RESOURCE_BINDINGS;
}


1;

__END__

=pod

=head1 NAME

C<IMPL::Web::Application::CustomResource> - базовый класс для ресурсов,
реальзуемых в коде.

=head1 SYNOPSIS

=begin code

package MyApp::Web::Resources::ProfileResource;
use IMPL::declare {
    base => [
        'IMPL::Web::Application::CustomResource' => '@_'
    ]
}

sub HttpGet {
    my ($this) = @_;
    return $this->model;
}

sub HttpPut {
    my ($this,$action) = @_;
    
    my $form = MyApp::Web::Schema::UpdateUser->new();
    
    $this->model->update( $form->Bind($action) );
}

sub GetChildResources {
    return {
        name => 'create',
        contract => {
            class => 'My::Web::FormResource',
            formName => 'create',
            schema => 'profile.schema'
        }
    },
    {
        match => qr/^(.*)$/,
        contract => {
            class => 'My::Web::ItemResource'
        }
    }
}

=end code

=head1 MEMBERS

=head2 C<[static]contractFactory>

Фабрика, используемая для получения контракта ресурса. По умолчанию
C<IMPL::Web::Application::CustomResourceContract>.

=head2 C<[static]contractInstance>

Экземпляр контракта для ресурса. Создается при первом обращении при помощи
метода C<InitContract()>.

=head2 C<[static]InitContract()>

Создает новый экземпляр контракта, используя фабрику из свойства C<contractFactory>.

=head2 C<[static]CreateContract(%args)>

Создает новый контракт, который при создании ресурсов будет передавать им в
конструкторе параметры C<%args>. Реализуется при помощи C<IMPL::Object::Factory>
которой задается параметр ссылка на C<%args>, т.о. при создании ресурса, ему в
конструкторе будет передан список из ключей и значений хеша C<%args>, а затем
остальные аргументы.

=head2 C<[static]CreateResource(%args)>

Создает контракт по-умолчанию и вызывает у него метод C<CreateResource(%args)>.

=head2 C<[static]GetChildResources()>

Статический метод, который должны переопределять новые классы ресурсов, у
которых есть дочерние ресурсы.

=begin code

package MyApp::Web::MyResource

sub GetChildResources {
    my $self = shift;
    return
        $self->SUPER::GetChildResources(),
        {
            name => 'info',
            contract => $contractInfo
        };
}

=end code

Метод возвращает список из хешей, которые будут переданы в качестве параметра
C<resources> контракту данного ресурса.

=cut