Revision 8 (by ahitrov@rambler.ru, 2010/03/29 11:46:38) Contenido UTF-8 core files
package Contenido::MVC::Dispatcher;

use strict;
use Utils;
use Data::Dumper;

# типы диспетчеров (сравнение пути, регексп пути)
my @DISPATCH_TYPES = qw(Path Regex);

sub init {
        my $proto = shift;
        my %opts = @_;
        my $class = ref($proto) || $proto;
        
        my $self = {};
        bless $self, $class;

        $self->load_dispatch_types( %opts );
        $self->load_actions( %opts );

        return $self;
}

# список доступных диспетчеров
sub get_dispatch_types {
        return @DISPATCH_TYPES;
}

# загрузка типов диспетчеров
sub load_dispatch_types {
        my $self = shift;

        foreach my $typename ( $self->get_dispatch_types() ) {

                my $class = 'Contenido::MVC::DispatchType::'.$typename;
                Utils::load_modules( [ $class ] );
                $self->{dispatch_types}->{ $typename } = new $class;

        }

}

# загрузка actions контроллера
sub load_actions {
        my $self = shift;
        my %opts = @_;

        my $controller_class = $opts{controller_class};
        $controller_class = [ $controller_class ] unless ref($opts{controller_class}) eq 'ARRAY';

        Utils::load_modules( $controller_class );

        foreach my $class (@$controller_class) {

                # добавление контроллера
                my $controller = $class->init();
                $self->register_controller( $controller );

                foreach my $typename ( $self->get_dispatch_types() ) {

                        my $dispatch_type = $self->{dispatch_types}->{ $typename };

                        my $method = 'get_'.lc($typename).'_actions';
                        my $actions = $controller->$method;
                
                        next unless $actions && ref($actions) eq 'ARRAY';
                
                        foreach my $action (@$actions) {
                                next unless $action && ref($action) eq 'HASH';

                                # запоминаем какому из контроллеров принадлежит данный action
                                $action->{'controller_class'} = $class;

                                # проверка на то, может ли быть выполнен метод
                                next unless $controller->can( $action->{'sub'} );

                                # регистрация метода
                                $dispatch_type->register( $action );
                        }
                
                }
        }

        # пост обработка (сейчас пригодилась для сортировки массива с регекспами)
        # к примеру: один из запросов выполняется чаще второго в 10 раз,
        # для этого в необязательном параметре sorder можно вручную поставить приоритет
        # исполнения, начиная с 0 - максимальный

        foreach my $typename ( $self->get_dispatch_types() ) {
                my $dispatch_type = $self->{dispatch_types}->{ $typename };
                $dispatch_type->after_register();
        }

        return 1;
}

# регистрация контроллера
sub register_controller {
        my $self = shift;
        my $controller = shift;

        my $class = ref($controller);
        $self->{controllers}->{$class} = $controller;
}

# получение контроллера в зависимости от action
sub get_controller {
        my $self = shift;
        my $action = shift;

        my $controller = $self->{controllers}->{ $action->{'controller_class'} };
        $controller->set_action( $action );
        return $controller;
}


# получение главного контролера
sub get_main_controller {
        my $self = shift;
        return $self->{controllers}->{'main'};
}

# установка главного контроллера
sub set_main_controller {
        my $self = shift;
        my %opts = @_;
        my $controller = $opts{controller_class}->init();
        $self->{controllers}->{'main'} = $controller;
}

# подготовка action
sub get_action {
        my $self = shift;
        my $r	= shift;

        my $path = $r->uri();
        $path =~ s/\/+/\//;
        $path =~ s/index\.\w+$//i;

        my $action = undef;

        # проходим по всем типам диспетчеров
        DP: foreach my $typename ( $self->get_dispatch_types() ) {

                my $dispatch_type = $self->{dispatch_types}->{ $typename };
                if ($action = $dispatch_type->match( $path )) {
                        last DP;
                }
        }
        return $action; 
}

1;