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 )