The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

MVC::Neaf::Route::Main - main application class for Not Even A Framework.

DESCRIPTION

This class contains a MVC::Neaf application structure and implements the core of Neaf logic.

It is a MVC::Neaf::Route object itself, containing a hash of other routes designated by their path prefixes.

APPLICATION SETUP METHODS

new()

    new( %options )

This is also called by MVC::Neaf->new, in case one wants to instantiate a Neaf application object instead of using the default "neaf" in MVC::Neaf.

Options may include:

force_view - use that view instead of anything specified by controller. See "load_view" for details about how view is declared. Useful for debugging.

add_route()

Define a handler for given by URI path and HTTP method(s). This is the backend behind NEAF's get + post route specifications.

    route( '/path' => CODEREF, %options )

Any incoming request to uri matching /path (/path/something/else too, but NOT /pathology) will now be directed to CODEREF.

Longer paths are GUARANTEED to be checked first.

Dies if the same method and path combination is given twice (but see tentative and override below). Multiple methods may be given for the same path.

Exactly one leading slash will be prepended no matter what you do. (path, /path and /////path are all the same).

The CODEREF MUST accept exactly one argument, referred to as $request or $req hereafter, and return an unblessed hashref with response data.

%options may include:

  • method - list of allowed HTTP methods. Default is [GET, POST]. Multiple handles can be defined for the same path, provided that methods do not intersect. HEAD method is automatically handled if GET is present, however, one MAY define a separate HEAD handler explicitly.

  • path_info_regex => qr/.../ - allow URI subpaths to be handled by this handler.

    A 404 error will be generated unless path_info_regex is present and PATH_INFO matches the regex (without the leading slashes).

    If path_info_regex matches, it will be available in the controller as $req->path_info.

    If capture groups are present in said regular expression, their content will also be available as $req->path_info_split.

    [EXPERIMENTAL] Name and semantics MAY change in the future.

  • param_regex => { name => qr/.../, name2 => '\d+' }

    Add predefined regular expression validation to certain request parameters, so that they can be queried by name only. See param() in MVC::Neaf::Request.

    [EXPERIMENTAL] Name and semantics MAY change in the future.

  • view - default View object for this Controller. Must be a name of preloaded view, an object with a render method, or a CODEREF receiving hashref and returning a list of two scalars (content and content-type).

    [DEPRECATED] Use -view instead, meaning is exactly the same.

  • cache_ttl - if set, set Expires: HTTP header accordingly.

    [EXPERIMENTAL] Name and semantics MAY change in the future.

  • default - a \%hash of values that will be added to results EVERY time the handler returns. Consider using neaf default ... below if you need to append the same values to multiple paths.

  • override => 1 - replace old route even if it exists. If not set, route collisions causes exception. Use this if you know better.

    This still issues a warning.

    [EXPERIMENTAL] Name and meaning may change in the future.

  • tentative => 1 - if route is already defined, do nothing. If not, allow to redefine it later.

    [EXPERIMENTAL] Name and meaning may change in the future.

  • description - just for information, has no action on execution. This will be displayed if application called with --list (see MVC::Neaf::CLI).

  • public => 0|1 - a flag just for information. In theory, public endpoints should be searchable from the outside while non-public ones should only be reachable from other parts of application. This is not enforced whatsoever.

Also, any number of dash-prefixed keys MAY be present. This is the same as putting them into default hash.

static()

    $neaf->static( '/path' => $local_path, %options );

    $neaf->static( '/other/path' => [ "content", "content-type" ] );

Serve static content located under $file_path. Both directories and single files may be added.

If an arrayref of [ $content, $content_type ] is given as second argument, serve content from memory instead.

%options may include:

  • buffer => nnn - buffer size for reading/writing files. Default is 4096. Smaller values may be set, but are NOT recommended.

  • cache_ttl => nnn - if given, files below the buffer size will be stored in memory for cache_ttl seconds.

    [EXPERIMENTAL] Cache API is not yet established.

  • allow_dots => 1|0 - if true, serve files/directories starting with a dot (.git etc), otherwise give a 404.

    [EXPERIMENTAL]

  • dir_index => 1|0 - if true, generate index for a directory; otherwise a 404 is returned, and deliberately so, for security reasons.

    [EXPERIMENTAL]

  • dir_template - specify template for directory listing (with images etc). A sane default is provided.

    [EXPERIMENTAL]

  • view - specify view object for rendering directory template. By default a localized TT instance is used.

    [EXPERIMENTAL] Name MAY be changed (dir_view etc).

  • override - override the route that was here before. See route above.

  • tentative - don't complain if replaced later.

  • description - comment. The default is "Static content at $directory"

  • public => 0|1 - a flag just for information. In theory, public endpoints should be searchable from the outside while non-public ones should only be reachable from other parts of application. This is not enforced whatsoever.

See MVC::Meaf::X::Files for implementation.

File type detection is based on extentions so far, and the list is quite short. This MAY change in the future. Known file types are listed in %MVC::Neaf::X::Files::ExtType hash. Patches welcome.

It is probably a bad idea to serve files in production using a web application framework. Use a real web server instead. Not need to set up one for merely testing icons/js/css, though.

alias()

    $neaf->alias( $newpath => $oldpath )

Create a new name for already registered route. The handler will be executed as is, but the hooks and defaults will be re-calculated. So be careful.

[CAUTION] As of 0.21, alias does NOT adhere tentative/override switches. This needs to be fixed in the future.

set_path_defaults

    set_path_defaults( { value => 42 }, path => [ '/foo', '/bar' ], method => 'PUT' )

    set_path_defaults( "/api" => { version => 0.99 });

Append the given values to the hash returned by any route under the given path(s) and method(s).

Longer paths take over the shorter ones, route's own values take over in turn, and values returned explicitly by the controller have the highest priority.

get_path_defaults

    get_path_defaults ( $methods, $path, [ \%override ... ] )

Fetch default values for given (path, method) combo as a single hash.

add_hook()

    $neaf->add_hook ( phase => CODEREF, %options );

Set hook that will be executed on a given request processing phase.

Valid phases include:

  • pre_route [die]

  • pre_logic [die]

  • pre_content

  • pre_render [die]

  • pre_reply [reverse]

  • pre_cleanup [reverse]

See "REQUEST PROCESSING PHASES AND HOOKS" in MVC::Neaf below for detailed discussion of each phase.

The CODEREF receives one and only argument - the $request object. Return value is ignored, see explanation below.

Use $request's session, reply, and stash methods for communication between hooks.

Dying in a hook MAY cause interruption of request processing or merely a warning, depending on the phase.

%options may include:

  • path => '/path' - where the hook applies. Default is '/'. Multiple locations may be supplied via [ /foo, /bar ...]

  • exclude => '/path/skip' - don't apply to these locations, even if under '/path'. Multiple locations may be supplied via [ /foo, /bar ...]

  • method => 'METHOD' || [ list ] List of request HTTP methods to which given hook applies.

  • prepend => 0|1 - all other parameters being equal, hooks will be executed in order of adding. This option allows to override this and run given hook first. Note that this does NOT override path bubbling order.

get_hooks

    get_hooks( $method, $path )

Fetch all hooks previously set for given path as a { phase => [ list ] } hash.

set_helper

    set_helper( name => \&code, %options )

get_helpers

load_view()

    load_view( "name", $view_object );  # stores object
                                        # assuming it's an L<MVC::Neaf::View>
    load_view( "name", $module_name, %params ); # calls new()
    load_view( "name", $module_alias ); # ditto, see list of aliases below
    load_view( "name", \&CODE );        # use that sub to generate
                                        # content from hash

Setup view under name $name. Subsequent requests with -view = $name would be processed by that view object.

Use get_view to fetch the object itself.

  • if object is given, just save it.

  • if module name + parameters is given, try to load module and create new() instance.

    Short aliases JS, TT, and Dumper may be used for corresponding MVC::Neaf::View::* modules.

  • if coderef is given, use it as a render method.

Returns the view object, NOT the object this method was called on.

set_forced_view()

    $neaf->set_forced_view( $view )

If set, this view object will be user instead of ANY other view.

See "get_view".

Returns self.

load_resources()

    $neaf->load_resources( $file_name || \*FH )

Load pseudo-files from a file, like templates or static files.

The format is as follows:

    @@ [TT] main.html

    [% some_tt_template %]

    @@ /favicon.ico format=base64 type=png

    iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAAL
    GPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hS<....more encoded lines>

This is obviously stolen from Mojolicious, in a slightly incompatible way.

If view is specified in brackets, preload template. A missing view is skipped, no error.

Otherwise file is considered a static resource.

Extra options may follow file name:

  • type=ext | mime/type

  • format=base64

Unknown options are skipped. Unknown format value will cause exception though.

[EXPERIMENTAL] This method and exact format of data is being worked on.

set_session_handler()

    $neaf->set_session_handler( %options )

Set a handler for managing sessions.

If such handler is set, the request object will provide session(), save_session(), and delete_session() methods to manage cross-request user data.

% options may include:

  • engine (required in method form, first argument in DSL form) - an object providing the storage primitives;

  • ttl - time to live for session (default is 0, which means until browser is closed);

  • cookie - name of cookie storing session id. The default is "session".

  • view_as - if set, add the whole session into data hash under this name before view processing.

The engine MUST provide the following methods (see MVC::Neaf::X::Session for details):

  • session_ttl (implemented in MVC::Neaf::X::Session);

  • session_id_regex (implemented in MVC::Neaf::X::Session);

  • get_session_id (implemented in MVC::Neaf::X::Session);

  • create_session (implemented in MVC::Neaf::X::Session);

  • save_session (required);

  • load_session (required);

  • delete_session (implemented in MVC::Neaf::X::Session);

set_error_handler()

    $neaf->set_error_handler ( $status => CODEREF( $request, %options ) )

Set custom error handler.

Status must be a 3-digit number (as in HTTP). Other allowed keys MAY appear in the future.

The following options will be passed to coderef:

  • status - status being returned;

  • caller - file:line where the route was set up; This is DEPRECATED and will silently disappear around version 0.25

  • error - exception, an MVC::Neaf::Exception object.

The coderef MUST return an unblessed hash just like a normal controller does.

In case of exception or unexpected return format default JSON-based error will be returned.

Also available as set_error_handler( status => \%hash ).

This is a synonym to sub { +{ status => $status, ... } }.

on_error()

    on_error( sub { my ($request, $error) = @_ } )

Install custom error handler for a dying controller. Neaf's own exceptions, redirects, and die \d\d\d status returns will NOT trigger it.

E.g. write to log, or something.

Return value from this callback is ignored. If it dies, only a warning is emitted.

post_setup

This function is run after configuration has been completed, but before first request is served.

It goes as follows:

  • compile all the routes into a giant regexp;

  • Add HEAD handling to where only GET exists;

  • finish set_session_handler works

  • set the lock on route;

Despite the locking, further modifications are not prohibited. This MAY change in the future.

run()

    $neaf->run();

Run the application. This SHOULD be the last statement in your application's main file.

If called in void context, assumes execution as CGI and prints results to STDOUT. If command line options are present at the moment, enters debug mode via MVC::Neaf::CLI. Call perl yourapp.pl --help for more.

Otherwise returns a PSGI-compliant coderef. This will also happen if you application is require'd, meaning that it returns a true value and actually serves nothing until run() is called again.

Running under mod_perl requires setting a handler with MVC::Neaf::Request::Apache2.

INTROSPECTION AND TESTING METHODS

run_test()

    $neaf->run_test( \%PSGI_ENV, %options )

    $neaf->run_test( "/path?parameter=value", %options )

Run a PSGI request and return a list of ($status, HTTP::Headers::Fast, $whole_content ).

Returns just the content in scalar context.

Just as the name suggests, useful for testing only (it reduces boilerplate).

Continuation responses are supported, but will be returned in one chunk.

%options may include:

  • method - set method (default is GET)

  • cookie = \%hash - force HTTP_COOKIE header

  • header = \%hash - override some headers This gets overridden by type, cookie etc. in case of conflict

  • body = 'DATA' - force body in request

  • type - content-type of body

  • uploads - a hash of MVC::Neaf::Upload objects.

  • secure = 0|1 - http vs https

  • override = \%hash - force certain data in ENV Gets overridden by all of the above.

get_routes()

    $neaf->get_routes( $callback->(\%route_spec, $path, $method) )

Returns a 2-level hashref with ALL routes for inspection.

So $hash{'/path'}{'GET'} = { handler, expected params, description etc }

If callback is present, run it against route definition and append to hash its return value, but ONLY if it's true.

As of 0.20, route definitions are only protected by shallow copy, so be careful with them.

This SHOULD NOT be used by application itself.

RUN TIME METHODS

handle_request

    handle_request( $req )

This is the CORE of Not Even A Framework. Should not be called directly - use run() instead.

handle_request really boils down to

    my ($self, $req) = @_;

    my $req->path =~ /($self->{GIANT_ROUTING_RE})/
        or die 404;

    my $endpoint = $self->{ROUTES}{$1}{ $req->method }
        or die 405;

    my $reply_hash = $endpoint->{CODE}->($req);

    my $content = $reply_hash->{-view}->render( $reply_hash );

    return [ $reply_hash->{-status}, [...], [ $content ] ];

The rest 200+ lines of it, spread across this module and MVC::Neaf::Route, are for running callbacks, handling corner cases, and substituting sane defaults.

get_view()

    $route->get_view( "name", $lazy )

Fetch view object by name.

This is used to fetch/instantiate whatever is in -view of the controller return hash.

Uses load_view ( name => name ) if needed, unless $lazy flag is on.

If "set_forced_view" was called, return its argument instead.

INTERNAL LOGIC METHODS

The following methods are part of NEAF's core and should not be called unless you want something very special.

The following terminology is used hereafter:

  • prefix - part of URI path preceding what's currently being processed;

  • stem - part of URI that matched given NEAF route;

  • suffix - anything after the matching part but before query parameters (the infamous path_info).

When recursive routing is applied, prefix is left untouched, stem becomes prefix, and suffix is split into new stem + suffix.

When a leaf route is found, it matches $suffix to its own regex and either dies 404 or proceeds with application logic.

find_route( $method, $suffix )

Find subtree that matches given ($method, $suffix) pair.

May die 404 or 405 if no suitable route is found.

Otherwise returns (route, new_stem, new_suffix).

dispatch_logic

    dispatch_logic( $req, $prefix, $suffix )

Find a matching route and apply it to the request.

This is recursive, may die, and may spoil $req.

Upon successful termination, a reply hash is returned. See also "dispatch_logic" in MVC::Neaf::Route.

dispatch_view

Apply view to a request.

error_to_reply

mangle_headers

Fixup content & headers

DEPRECATED METHODS

Some methods become obsolete during Neaf development. Anything that is considered deprecated will continue to be supported for at least three minor versions after official deprecation and a corresponding warning being added.

Please keep an eye on Changes though.

Here is the list of such methods, for the sake of completeness.

  • $neaf->error_template( { param => value } )

    Use "set_error_handler" aka neaf \d\d\d => sub { ... } instead.

  • $neaf->set_default ( key => value, ... )

    Use MVC::Neaf->set_path_defaults( '/', { key => value, ... } ); as a drop-in replacement.

  • $neaf->server_stat ( MVC::Neaf::X::ServerStat->new( ... ) )

    Record server performance statistics during run.

    The interface of MVC::Neaf::X::ServerStat is as follows:

        my $stat = MVC::Neaf::X::ServerStat->new (
            write_threshold_count => 100,
            write_threshold_time  => 1,
            on_write => sub {
                my $array_of_arrays = shift;
    
                foreach (@$array_of_arrays) {
                    # @$_ = (script_name, http_status,
                    #       controller_duration, total_duration, start_time)
                    # do something with this data
                    warn "$_->[0] returned $_->[1] in $_->[3] sec\n";
                };
            },
        );

    on_write will be executed as soon as either count data points are accumulated, or time is exceeded by difference between first and last request in batch.

    Returns self.

    [DEPRECATED] Just use pre_route/pre_reply/pre_cleanup hooks if you need to gather performance statistics.

  • route

    Same as "add_route", but also supports weird multicomponent path notation.

    [NOTE] For some reason ability to add multicomponent paths like (foo => bar => \&code) was added in the past, resulting in "/foo/bar" => \&code.

    It was never documented, will issue a warning, and will be removed for good it v.0.25.

LICENSE AND COPYRIGHT

This module is part of MVC::Neaf suite.

Copyright 2016-2018 Konstantin S. Uvarin khedin@cpan.org.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.