view Lib/IMPL/Web/Application/CustomResource.pm @ 367:608e74bc309f

form metadata, mostly done
author cin
date Tue, 03 Dec 2013 17:55:36 +0400
parents e8be9062ecf2
children e12c14177848
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,
        resources   => PROP_RO,
        verbs       => PROP_RO,
        namedResources => PROP_RO,
        regexResources => PROP_RO
    ]
};

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

sub CTOR {
	my ($this,%args) = @_;
	
	$this->verbs($args{verbs} || {});
	$this->resources($args{resources} || []);
	
	$this->accessCheck($args{accessCheck})
	   if $args{accessCheck};
	   
    while(my ($verb,$methodName) = each %RESOURCE_BINDINGS) {
    	if(my $method = $this->can($methodName)) {
    		$this->verbs->{lc($verb)} ||= $method;
    	}
    }
}

sub FindChildResourceInfo {
    my ( $this, $name ) = @_;
    
    $this->PrepareResourcesCache()
        unless $this->namedResources;
    
    if ( my $info = $this->namedResources->{$name} ) {
        return $info, [$name];
    }
    else {
        foreach my $info ( @{$this->regexResources} ) {
            my $rx = $info->{match};
            if(my @childId = $name =~ m/$rx/) {
                return $info, \@childId; 
            }
        }
    }

    return;
}

sub GetAllowedMethods {
    map( uc, keys %{ shift->verbs } );
}

sub PrepareResourcesCache {
    my ($this) = @_;
    
    my @resources = ($this->GetChildResources(), @{$this->resources});
    
    my %nameMap;
    my @rxMap;

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

    $this->regexResources(\@rxMap);
    $this->namedResources(\%nameMap);
}

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

sub GetChildResources {
    
}

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

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