# NAME

Catalyst::ActionRole::Public - Mount a public url to files in your project directory.

# SYNOPSIS

    package MyApp::Controller::Root;

    use Moose;
    use MooseX::MethodAttributes;

    sub static :Local Does(Public) At(/:actionname/*) { ... }

    __PACKAGE__->config(namespace=>'');

Will create an action that from URL 'localhost/static/a/b/c/d.js' will serve
file $c->{root} . '/static' . '/a/b/c/d.js'.  Will also set content type, length
and Last-Modified HTTP headers as needed.  If the file does not exist, will not
match (allowing possibly other actions to match).

# DESCRIPTION

Use this actionrole to map a public facing URL attached to an action to a file
(or files) on the filesystem, off the $c->{root} directory.  If the file does
not exist, the action will not match.  No default 'notfound' page is created,
unlike [Plack::App::File](https://metacpan.org/pod/Plack::App::File) or [Catalyst::Plugin::Static::Simple](https://metacpan.org/pod/Catalyst::Plugin::Static::Simple).  The action
method body may be used to modify the response before finalization.

A template may be constructed to determine how we map an incoming request to
a path on the filesystem.  You have extensive control how an incoming HTTP
request maps to a file on the filesystem.  You can even use this action role
in the middle of a chained style action (although its hard to imagine the
use case for that...)

## ACTION METHOD BODY

The body of your action will be executed after we've created a filehandle to
the found file and setup the response.  You may leave it empty, or if you want
to do additional logging or work, you can. Also, you will find a stash key 
`public_file_path` has been populated with a [Path::Class](https://metacpan.org/pod/Path::Class) object which is
pointing to the found file.  The action method body will not be executed if
the file associated with the action does not exist.

# ACTION ATTRIBUTES

Actions the consume this role provide the following subroutine attributes.

## ShowDebugging

Enabled developer debugging output.  Example:

    sub myaction :Local Does(Public) ShowDebugging { ... }

If present do not surpress the extra developer mode debugging information.  Useful
if you have trouble serving files and you can't figure out why.

## At 

Used to set the action class template used to match files on the filesystem to
incoming requests.  Examples:

    package MyApp::Controller::Basic;

    use Moose;
    use MooseX::MethodAttributes;

    extends  'Catalyst::Controller';

    #localhost/basic/css => $c->{root} .'/basic/*'
    sub css :Local Does(Public) At(/:namespace/*) { }

    #localhost/basic/static => $c->{root} .'/basic/static/*'
    sub static :Local Does(Public) { }

    #localhost/basic/111/aaa/link2/333/444.txt => $c->{root} .'/basic/link2/333/444.txt'
    sub chainbase :Chained(/) PathPrefix CaptureArgs(1) { }

      sub link1 :Chained(chainbase) PathPart(aaa) CaptureArgs(0) { }

        sub link2 :Chained(link1) Args(2) Does(Public) { }

    #localhost/chainbase2/111/aaa/222.txt/link4/333 => $c->{root} . '/basic/link3/222.txt'
    sub chainbase2 :Chained(/)  CaptureArgs(1) { }

      sub link3 :Chained(chainbase2) PathPart(aaa) CaptureArgs(1) Does(Public) { }

        sub link4 :Chained(link3) Args(1)  { }

    1;

**NOTE:** You're template may be 'relative or absolute' to the $c->{root} value
based on if the first character in the template is '/' or not.   If it is '/'
that is an 'absolute' template which will be added to $c->{root}.  Generally
if you are making a template this is what you want.  However if you don't have
a '/' prepended to the start of your template (such as in At(file.txt)) we then
make your filesystem lookup relative to the action private path.  So in the
example:

    package MyApp::Controller::Basic;

    sub absolute_path :Path('/example1') Does(Public) At(/example.txt) { }
    sub relative_path :Path('/example2') Does(Public) At(example.txt) { }

Then http://localhost/example1 => $c->{root} . '/example.txt' but
http://localhost/example2 => $c->{root} . '/basic/relative\_path/example.txt'.
You may find this a useful "DWIW" when an action is linked to a particular file.

**NOTE:** The following expansions are recognized in your `At` declaration:

- :namespace

    The action namespace, determined from the containing controller.  Usually this
    is based on the Controller package name but you can override it via controller
    configuration.  For example:

        package MyApp::Controller::Foo::Bar::Baz;

    Has a namespace of 'foo/bar/baz' by default.

- :privatepath
- :private\_path

    The action private\_path value.  By default this is the namespace + the action
    name.  For example:

        package MyApp::Controller::Foo::Bar::Baz;

        sub myaction :Path('abcd') { ... }

    The action `myaction` has a private\_path of '/foo/bar/baz/myaction'.

    **NOTE:** the expansion `:private_path` is mapped to this value as well.

- actionname
- action\_name

    The name of the action (typically the subroutine name)

        sub static :Local Does(Public) At(/:actionname/*) { ... }

    In this case actionname = 'static'

- :args
- '\*'

    The arguments to the request.  For example:

        Package MyApp::Controller::Static;

        sub myfiles :Path('') Does(Public) At(/:namespace/*) { ... }

    Would map 'http://localhost/static/a/b/c/d.txt' to $c->{root} . '/static/a/b/c/d.txt'.

    In this case $args = \['a', 'b', 'c', 'd.txt'\]

## ContentType

Used to set the respone Content-Type header. Example:

    sub myaction :Local Does(Public) ShowDebugging { ... }

By default we inspect the request URL extension and set a content type based on
the extension text (defaulting to 'application/octet' if we cannot determine.  If
you set this to a MIME type, we will alway set the response content type based on
this, no matter what the extension, if any, says.

# RESPONSE INFO

If we find a file we serve the filehandle directly to you plack handler, and set
a 'with\_path' value so that you can use this with something like [Plack::Middleware::XSendfile](https://metacpan.org/pod/Plack::Middleware::XSendfile).
We also set the Content-Type, Content-Length and Last-Modified headers.  If you
need to add more information before finalizing the response you may do so with
the matching action metod body.

# AUTHOR

John Napiorkowski [email:jjnapiork@cpan.org](email:jjnapiork@cpan.org)

# SEE ALSO

[Catalyst](https://metacpan.org/pod/Catalyst), [Catalyst::Controller](https://metacpan.org/pod/Catalyst::Controller), [Plack::App::Directory](https://metacpan.org/pod/Plack::App::Directory),
[Catalyst::Controller::Assets](https://metacpan.org/pod/Catalyst::Controller::Assets).

# COPYRIGHT & LICENSE

Copyright 2015, John Napiorkowski [email:jjnapiork@cpan.org](email:jjnapiork@cpan.org)

This library is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
