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


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.



    new( )

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.

A hash of %options may be added in the future, but isn't supported currently.


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.

  • strict => 1|0

    If true, request's param() and get_cookie() will emit HTTP error 422 whenever mandatory validation fails.

    If parameter or cookie is missing, just return default. This MAY change in the future.

    [EXPERIMENTAL] Name and meaning 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.


    $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.


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


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


  • 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.


    $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( { version => 0.99 }, path => '/api', %options );

%options may include:

  • path - restrict this set of defaults to given prefix(es);

  • method - restrict this set of defaults to given method(s);

  • exclude - exclude some prefixes;

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 default values take over any path-based defaults. Whatever the controller returns overrides all of these.


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

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


    $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( $method, $path )

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


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



    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.


    $neaf->set_forced_view( $view )

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

See "get_view".

Returns self.

magic( bool )

Get/set "magic" bit that triggers stuff like loading resources from __DATA__ on run() and such.

Neaf is magical by default.


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

Load pseudo-files from a file (typically __DATA__), say templates or static files.

As of 0.27, load_resources happens automatically upon run, but only once for each calling file. Use neaf->magic(0) if you know better (e.g. you want to use __DATA__ for something else).

The format is as follows:

    @@ /main.html view=TT

    [% some_tt_template %]

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

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

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

An entry starts with a literal @@, followed by 1 or more spaces, followed by a slash and a file name, optionally followed by a list of options, and finally by a newline.

Everything following the newline and until next such entry is considered file content.

Options may include:

  • type=ext | mime/type

  • format=base64

  • view=view_name,view_name... - specify a template for given view(s) Leading slash will be stripped in this case.

Entries with unknown options will be skipped with a warning.

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


    $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);


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

Set custom error handler.

Status MUST be a 3-digit number (as in HTTP).

%where may include path, method, and exclude keys. If omitted, just install error handler globally.

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 HTML error page will be returned.

Also available in static form, as set_error_handler( status => \%hash ).

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


    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.


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 the application. This SHOULD be the last statement in your application's main file.

When run() is called, the routes are compiled into one giant regex, and the post-setup is run, if needed.

Additionally if neaf is in magical mode, "load_resources" is called on the enclosing file's DATA descriptor. Magic mode is on by default. See "magic-bool".

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 --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.



    $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.


    $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.



    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.


    $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.


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 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( $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.


Apply view to a request.


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.


This module is part of MVC::Neaf suite.

Copyright 2016-2019 Konstantin S. Uvarin

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 for more information.