Dancer

 view release on metacpan or  search on metacpan

lib/Dancer.pm  view on Meta::CPAN

      or ($script_dirs[$#script_dirs - 1] eq 't');

    my $appdir = $ENV{DANCER_APPDIR} || (
          $LAYOUT_PRE_DANCER_1_2
        ? $script_path
        : File::Spec->rel2abs(Dancer::path($script_path, '..'))
    );
    Dancer::setting(appdir => $appdir);

    # once the dancer_appdir have been defined, we export to env
    $ENV{DANCER_APPDIR} = $appdir;

    Dancer::Logger::core("initializing appdir to: `$appdir'");

    Dancer::setting(confdir => $ENV{DANCER_CONFDIR}
      || $appdir) unless Dancer::setting('confdir');

    Dancer::setting(public => $ENV{DANCER_PUBLIC}
      || Dancer::FileUtils::path($appdir, 'public'));

    Dancer::setting(views => $ENV{DANCER_VIEWS}
      || Dancer::FileUtils::path($appdir, 'views'));

    my ($res, $error) = Dancer::ModuleLoader->use_lib(Dancer::FileUtils::path($appdir, 'lib'));
    $res or raise core => "unable to set libdir : $error";
}


# Scheme grammar as defined in RFC 2396
#  scheme = alpha *( alpha | digit | "+" | "-" | "." )
my $scheme_re = qr{ [a-z][a-z0-9\+\-\.]* }ix;
sub _redirect {
    my ($destination, $status) = @_;

    # RFC 2616 requires an absolute URI with a scheme,
    # turn the URI into that if it needs it
    if ($destination !~ m{^ $scheme_re : }x) {
        my $request = Dancer::SharedData->request;
        $destination = $request->uri_for($destination, {}, 1);
    }
    my $response = Dancer::SharedData->response;
    $response->status($status || 302);
    $response->headers('Location' => $destination);
}

sub _session {
    engine 'session'
      or raise core => "Must specify session engine in settings prior to using 'session' keyword";
      @_ == 0 ? Dancer::Session->get
    : @_ == 1 ? Dancer::Session->read(@_)
    :           Dancer::Session->write(@_);
}

sub _send_file {
    my ($path, %options) = @_;
    my $env = Dancer::SharedData->request->env;

    my $request = Dancer::Request->new_for_request('GET' => $path);
    Dancer::SharedData->request($request);

    # if you asked for streaming but it's not supported in PSGI
    if ( $options{'streaming'} && ! $env->{'psgi.streaming'} ) {
        # TODO: throw a fit (AKA "exception") or a Dancer::Error?
        raise core => 'Sorry, streaming is not supported on this server.';
    }

    if (exists($options{content_type})) {
        $request->content_type($options{content_type});
    }

    # If we're given an IO::Scalar object, DTRT (take the scalar ref from it)
    if (Scalar::Util::blessed($path) && $path->isa('IO::Scalar')) {
        $path = $path->sref;
    }

    my $resp;
    if (ref($path) eq "SCALAR") {
        # send_data
        $resp = Dancer::SharedData->response() || Dancer::Response->new();
        $resp->header('Content-Type' => exists($options{content_type}) ?
                                        $options{content_type} : Dancer::MIME->default());
        $resp->content($$path);
    } else {
        # real send_file
        if ($options{system_path} && -f $path) {
            $resp = Dancer::Renderer->get_file_response_for_path($path);
        } else {
            $resp = Dancer::Renderer->get_file_response();
        }
    }

    if ($resp) {

        if (exists($options{filename})) {
            $resp->push_header('Content-Disposition' => 
                "attachment; filename=\"$options{filename}\""
            );
        }

        if ( $options{'streaming'} ) {
            # handle streaming
            $resp->streamed( sub {
                my ( $status, $headers ) = @_;
                my %callbacks = defined $options{'callbacks'} ?
                                %{ $options{'callbacks'} }    :
                                ();

                return sub {
                    my $respond = shift;
                    exists $callbacks{'override'}
                        and return $callbacks{'override'}->( $respond, $resp );

                    # get respond callback and set headers, get writer in return
                    my $writer = $respond->( [
                        $status,
                        $headers,
                    ] );

                    # get content from original response
                    my $content = $resp->content;

                    exists $callbacks{'around'}
                        and return $callbacks{'around'}->( $writer, $content );

                    if ( ref $content ) {
                        my $bytes = $options{'bytes'} || '43008'; # 42K (dams)
                        my $buf;
                        while ( ( my $read = sysread $content, $buf, $bytes ) != 0 ) {
                            if ( exists $callbacks{'around_content'} ) {
                                $callbacks{'around_content'}->( $writer, $buf );
                            } else {
                                $writer->write($buf);
                            }
                        }
                    } else {
                        $writer->write($content);
                    }
                };
            } );
        }

        return $resp;

    }

    Dancer::Error->new(
        code    => 404,
        message => "No such file: `$path'"
    )->render();
}

# Start/Run the application with the chosen apphandler
sub _start {
    my ($class, $request) = @_;
    Dancer::Config->load;

    # Backward compatibility for app.psgi that has sub { Dancer->dance($req) }
    if ($request) {
        Dancer::Handler->init_request_headers( $request->env );
        # TODO _build_headers should either not be private, or we should call
        # init

lib/Dancer.pm  view on Meta::CPAN


=head2 request

Returns a L<Dancer::Request> object representing the current request.

See the L<Dancer::Request> documentation for the methods you can call, for
example:

    request->referer;         # value of the HTTP referer header
    request->remote_address;  # user's IP address
    request->user_agent;      # User-Agent header value

=head2 send_error

Returns an HTTP error.  By default the HTTP code returned is 500:

    get '/photo/:id' => sub {
        if (...) {
            send_error("Not allowed", 403);
        } else {
           # return content
        }
    }

B<WARNING> : Issuing a send_error immediately exits the current route, and perform
the send_error. Thus, any code after a send_error is ignored, until the end of the route.
So it's not necessary anymore to use C<return> with send_error.

    get '/some/route' => sub {
        if (...) {
            # we want to let the next matching route handler process this one
            send_error(..);
            # This code will be ignored
            do_stuff();
        }
    };

=head2 send_file

Lets the current route handler send a file to the client. Note that
the path of the file must be relative to the B<public> directory unless you use
the C<system_path> option (see below).

    get '/download/:file' => sub {
        send_file(params->{file});
    }

B<WARNING> : Issuing a send_file immediately exits the current route, and performs
the send_file. Thus, any code after a send_file is ignored until the end of the route.
So it's not necessary any more to use C<return> with send_file.

    get '/some/route' => sub {
        if (...) {
            # we want to let the next matching route handler process this one
            send_file(...);
            # This code will be ignored
            do_stuff();
        }
    };

Send file supports streaming possibility using PSGI streaming. The server should
support it but normal streaming is supported on most, if not all.

    get '/download/:file' => sub {
        send_file( params->{file}, streaming => 1 );
    }

You can control what happens using callbacks.

First, C<around_content> allows you to get the writer object and the chunk of
content read, and then decide what to do with each chunk:

    get '/download/:file' => sub {
        send_file(
            params->{file},
            streaming => 1,
            callbacks => {
                around_content => sub {
                    my ( $writer, $chunk ) = @_;
                    $writer->write("* $chunk");
                },
            },
        );
    }

You can use C<around> to all get all the content (whether a filehandle if it's
a regular file or a full string if it's a scalar ref) and decide what to do with
it:

    get '/download/:file' => sub {
        send_file(
            params->{file},
            streaming => 1,
            callbacks => {
                around => sub {
                    my ( $writer, $content ) = @_;

                    # we know it's a text file, so we'll just stream
                    # line by line
                    while ( my $line = <$content> ) {
                        $writer->write($line);
                    }
                },
            },
        );
    }

Or you could use C<override> to control the entire streaming callback request:

    get '/download/:file' => sub {
        send_file(
            params->{file},
            streaming => 1,
            callbacks => {
                override => sub {
                    my ( $respond, $response ) = @_;

                    my $writer = $respond->( [ $newstatus, $newheaders ] );
                    $writer->write("some line");
                },
            },
        );
    }

You can also set the number of bytes that will be read at a time (default being
42K bytes) using C<bytes>:

    get '/download/:file' => sub {
        send_file(
            params->{file},
            streaming => 1,
            bytes     => 524288, # 512K
        );
    };

The content-type will be set depending on the current MIME types definition
(see C<mime> if you want to define your own).

If your filename does not have an extension, or you need to force a
specific mime type, you can pass it to C<send_file> as follows:

    send_file(params->{file}, content_type => 'image/png');

Also, you can use your aliases or file extension names on
C<content_type>, like this:

    send_file(params->{file}, content_type => 'png');

For files outside your B<public> folder, you can use the C<system_path>
switch. Just bear in mind that its use needs caution as it can be
dangerous.

   send_file('/etc/passwd', system_path => 1);

If you have your data in a scalar variable, C<send_file> can be useful
as well. Pass a reference to that scalar, and C<send_file> will behave
as if there were a file with that contents:

   send_file( \$data, content_type => 'image/png' );

Note that Dancer is unable to guess the content type from the data
contents. Therefore you might need to set the C<content_type>
properly. For this kind of usage an attribute named C<filename> can be
useful.  It is used as the Content-Disposition header, to hint the
browser about the filename it should use.

   send_file( \$data, content_type => 'image/png'
                             filename     => 'onion.png' );

=head2 set

Defines a setting:

    set something => 'value';

You can set more than one value at once:

    set something => 'value', otherthing => 'othervalue';

=head2 setting

Returns the value of a given setting:

    setting('something'); # 'value'

=head2 set_cookie

Creates or updates cookie values:

    get '/some_action' => sub {
        set_cookie name => 'value',



( run in 0.493 second using v1.01-cache-2.11-cpan-2b0bae70ee8 )