CatalystX-Action-Negotiate

 view release on metacpan or  search on metacpan

lib/CatalystX/ActionRole/Negotiate.pm  view on Meta::CPAN

variant-generating operation and the variant-selecting operation. It
is placed as an C<ARRAY> reference for your convenience in
C<< $c->stash->{variants} >>. This structure is exactly the same as
that which is passed into L<HTTP::Negotiate>, save for these
exceptions:

=over 4

=item 1.

Variants do not need to be a string identifier, but in fact can be
anything that can be consumed by a view or middleware component, e.g.,
a file handle or any other kind of supported object.

=item 2.

L<Path::Class::File> objects get special treatment, as they are what
the initial static variant list is made out of.

=item 3.

Append an additional integer to the end of a variant's record to
supply an artificial C<Last-Modified> value as a UNIX time stamp.

=back

Otherwise, consult L<HTTP::Negotiate> for how to construct the
records. This modification enables you to mix static variants in with
dynamic ones, or overwrite the list with purely dynamic variants.

=head CAVEATS

Note that this module may conflict with L<Catalyst::Plugin::Static::Simple>.
In future releases I will attempt to bring this module up to par so
that it can be a viable replacement, or at the very least be a better
cohabitant.

=cut

before execute => sub {

    my $self = shift;
    my ($ctl, $c, @args) = @_;

    my $req  = $c->req;
    my $resp = $c->res;

    $resp->status(404);

    my $root = Path::Class::Dir->new($c->config->{root});

    my @ps;

    # XXX flip this later maybe
    if ($c->stash->{negotiate_use_args}) {
        @ps = @args;
    }
    else {
        # get a clean URI path. (unfortunately Path::Class doesn't get it
        # this clean)
        @ps = map { (/^([^;]*)(?:;.*)?$/) } split m!/+!, $req->path;
        my $i = 0;
        while ($i < @ps) {
            if ($ps[$i] eq '' or $ps[$i] eq '.') {
                splice @ps, $i, 1;
            }
            elsif ($ps[$i] eq '..') {
                $i > 0 ? splice @ps, $i-1, 2 : splice @ps, $i, 1;
            }
            else {
                $i++;
            }
        }
    }

    $c->log->debug('Negotiate: trying ' . $root->file(@ps));

    # if the path terminates with a slash, what does it mean?

    # * the path is a legitimate directory /foo/ which should be
    # forwarded internally to /foo/index

    # * the client (probably robot) is appending a trailing slash to
    # /foo which ordinarily wouldn't have one.

    # ok, so how do we want it to behave?

    # if @ps is length 0, then we are looking at the root URI, so
    # append 'index' and start looking for variants.

    # if @ps is any longer than 0, then we are looking at something
    # beneath the root.

    # if there is a trailing slash in the request, we want to do an
    # exact match on the dir(/index) first, then exact match for
    # files, then fuzzy match on the files. if no trailing slash, then
    # exact match on files first, then fuzzy match on files, then
    # dir(/index). this is different from mod_negotiate.

    # if there is a trailing slash on the request and what was found
    # was a file, 301 to a url with no trailing slash. similarly, if
    # the request had no trailing slash and what was found was a dir,
    # 301 to a url with a trailing slash.

    my $slash = $c->stash->{negotiate_use_args} ? undef :
        $req->path =~ m!/(?:;[^/]*)?$!;
    my $dpath = $root->file(@ps, 'index');
    my $fpath = $root->file(@ps);

    my @indices = grep { $_ and my $x = $_->stat; $x and -f $x }
        map { Path::Class::File->new($_) } glob(quotemeta($dpath) . "{,.*}");

    my @files = grep { $_ and my $x = $_->stat; $x and -f $x }
        ($fpath, map { Path::Class::File->new($_) }
             glob(quotemeta($fpath) . "{,.*}")) if @ps > 0;

    # XXX we needed to switch the order of operations around a bit,
    # otherwise this stashed stuff will never get assigned

    # gin up some maps so we can figure out where the chosen variant
    # came from



( run in 1.576 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )