JSORB::Dispatcher::Path - Simple path based dispatcher


JSORB documentation Contained in the JSORB distribution.

Index


Code Index:

NAME

Top

JSORB::Dispatcher::Path - Simple path based dispatcher

DESCRIPTION

Top

This module will dispatch RPC methods/procedures that are in a path-like format, such as:

  { method : 'math/simple/add', params : [ 2, 2 ] }

This will look for the add procedure in the Math::Simple namespace.

BUGS

Top

All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT.

AUTHOR

Top

Stevan Little <stevan.little@iinteractive.com>

COPYRIGHT AND LICENSE

Top


JSORB documentation Contained in the JSORB distribution.

package JSORB::Dispatcher::Path;
use Moose;

use Try::Tiny;
use Path::Router;

our $VERSION   = '0.04';
our $AUTHORITY = 'cpan:STEVAN';

with 'MooseX::Traits';

has 'namespace' => (
    is       => 'ro',
    isa      => 'JSORB::Namespace',
    trigger  => sub {
        my $self = shift;
        $self->_clear_router if $self->_has_router;
        # the router will get
        # initialized the next
        # time it is needed
    }
);

has 'router' => (
    is        => 'ro',
    isa       => 'Path::Router',
    lazy      => 1,
    builder   => '_build_router',
    clearer   => '_clear_router',
    predicate => '_has_router',
);

sub handler {
    my ($self, $call, @args) = @_;
    (blessed $call && $call->isa('JSON::RPC::Common::Procedure::Call'))
        || confess "You must pass a JSON::RPC::Common::Procedure::Call to the handler, not $call";

    my $procedure = $self->get_procedure_from_call($call);

    return $self->throw_error(
        $call, "Could not find method " . $call->method . " in " . $self->namespace->name
    ) unless defined $procedure;

    try {
        $call->return_result(
            $self->call_procedure(
                $procedure,
                $call,
                @args
            )
        );
    } catch {
        $self->throw_error($call, $_);
    };
}

sub get_procedure_from_call {
    my ($self, $call) = @_;
    my $match = $self->router->match($call->method);
    return unless $match;
    return $match->target;
}

sub call_procedure {
    my ($self, $procedure, $call, @args) = @_;
    $procedure->call( $self->assemble_params_list( $call, @args ) );
}

sub assemble_params_list {
    my ($self, $call, @args) = @_;
    return $call->params_list;
}

sub throw_error {
    my ($self, $call, $message) = @_;
    return $call->return_error(
        message => $message,
        code    => 1,
    );
}

# ........

sub _build_router {
    my $self = shift;
    my $router = Path::Router->new;
    $self->_process_elements(
        $router,
        '/',
        $self->namespace
    );
    $router;
}

sub _process_elements {
    my ($self, $router, $base_url, $namespace) = @_;

    $base_url .= lc($namespace->name) . '/';

    foreach my $element (@{ $namespace->elements }) {
        $self->_process_interface($router, $base_url, $element)
            if $element->isa('JSORB::Interface');
        $self->_process_elements($router, $base_url, $element);
    }
}

sub _process_interface {
    my ($self, $router, $base_url, $interface) = @_;

    $base_url .= lc($interface->name) . '/';

    # NOTE:
    # perhaps I want to actually do:
    #  $router->add_route(
    #      ($base_url . ':method'),
    #      target => $interface,
    #  );
    # instead so that the method becomes
    # a param and then the interface
    # itself is the target ... which
    # means I can then hand off the
    # rest of the dispatching to the
    # interface .. hmmm

    foreach my $procedure (@{ $interface->procedures }) {
        $router->add_route(
            ($base_url . lc($procedure->name)),
            target => $procedure
        );
    }
}

__PACKAGE__->meta->make_immutable;

no Moose; 1;

__END__