Dancer2
view release on metacpan or search on metacpan
* GH #916: Fix test example. (Peter Mottram - @SysPete)
* GH #912, #913: Fix documentation on when stacks are printed.
(Andrew Solomon)
0.160001 2015-05-14 20:40:10+02:00 Europe/Amsterdam
[ BUG FIXES ]
* GH #893, #895: Catch config parse errors when Config::Any doesn't throw
them. (Russell Jenkins)
* GH #899: Minimum YAML version supported is v0.86 (Shlomi Fish)
* GH #906: send_file - missing import and fix logic error for streaming
by default (Russell Jenkins)
[ DOCUMENTATION ]
* GH #897: Remove docs for unimplemented 'load' keyword (Fayland Lam)
[ ENHANCEMENT ]
* GH #894, #898: Add status and headers methods to ::Response::Delayed
(Yanick Champoux, Charlie Gonzalez)
0.160000 2015-04-27 00:12:55+02:00 Europe/Amsterdam
[ BUG FIXES ]
* GH #856: Memory leak when throwing exception from a hook. (Sawyer X)
0.159001 2015-02-25 15:31:35+01:00 Europe/Amsterdam
[ BUG FIXES ]
* GH #855: Ensure Dancer2::Test is compatible with Pod::Simple 3.30.
(Russell Jenkins)
[ DOCUMENTATION ]
* Add an example for delayed (async) streaming response. (Sawyer X)
* Small link fix. (Sawyer X)
0.159000 2015-02-24 04:51:20+01:00 Europe/Amsterdam
[ BUG FIXES ]
* GH #762: Delay app cleanup until errors are rendered. (Russell Jenkins)
* GH #835: Correct Logic error in Logger if no request exists.
(Lennart Hengstmengel)
* GH #839: Correct "no_server_tokens" definition in production.yml.
(Nikita K)
lib/Dancer2/Core/App.pm view on Meta::CPAN
( exists $options{'charset'} ) and $charset = $options{'charset'};
$content_type .= "; charset=$charset" if $content_type and $charset;
( defined $content_type )
and $self->response->header('Content-Type' => $content_type );
# content disposition
( exists $options{filename} )
and $self->response->header( 'Content-Disposition' =>
($options{content_disposition} || "attachment") . "; filename=\"$options{filename}\"" );
# use a delayed response unless server does not support streaming
my $use_streaming = exists $options{streaming} ? $options{streaming} : 1;
my $response;
my $env = $self->request->env;
if ( $env->{'psgi.streaming'} && $use_streaming ) {
my $cb = sub {
my $responder = $Dancer2::Core::Route::RESPONDER;
my $res = $Dancer2::Core::Route::RESPONSE;
return $responder->(
[ $res->status, $res->headers_to_array, $fh ]
);
};
Scalar::Util::weaken( my $weak_self = $self );
lib/Dancer2/Core/DSL.pm view on Meta::CPAN
local $Dancer2::Core::Route::RESPONDER = $responder;
local $Dancer2::Core::Route::WRITER = $writer;
local $Dancer2::Core::Route::ERROR_HANDLER = $handler;
$cb->(@_);
};
}
sub flush {
my $responder = $Dancer2::Core::Route::RESPONDER
or croak 'flush() called outside streaming response';
my $response = $Dancer2::Core::Route::RESPONSE;
$Dancer2::Core::Route::WRITER = $responder->([
$response->status, $response->headers_to_array,
]);
}
sub done {
my $writer = $Dancer2::Core::Route::WRITER
or croak 'done() called outside streaming response';
$writer->close;
}
sub pass { shift->app->pass }
#
# Route handler helpers
#
lib/Dancer2/Core/Response/Delayed.pm view on Meta::CPAN
error_cb => sub {
my ($error) = @_;
...
},
);
# or in an app
get '/' => sub {
# delayed response:
delayed {
# streaming content
content "data";
content "more data";
# close user connection
done;
} on_error => sub {
my ($error) = @_;
warning 'Failed to stream to user: ' . request->remote_address;
};
};
lib/Dancer2/Manual.pod view on Meta::CPAN
=head2 Delayed responses (Async/Streaming)
L<Dancer2> can provide delayed (otherwise known as I<asynchronous>) responses
using the C<delayed> keyword. These responses are streamed, although you can
set the content all at once, if you prefer.
get '/status' => sub {
delayed {
response_header 'X-Foo' => 'Bar';
# flush headers (in case of streaming)
flush;
# send content to the user
content 'Hello, world!';
# you can write more content
# all streaming
content 'Hello, again!';
# when done, close the connection
done;
# do whatever you want else, asynchronously
# the user socket closed by now
...
};
};
If you are streaming (calling C<content> several times), you must call
C<flush> first. If you're sending only once, you don't need to call C<flush>.
Here is an example of using delayed responses with L<AnyEvent>:
use Dancer2;
use AnyEvent;
my %timers;
my $count = 5;
get '/drums' => sub {
delayed {
print "Stretching...\n";
flush; # necessary, since we're streaming
$timers{'Snare'} = AE::timer 1, 1, delayed {
$timers{'HiHat'} ||= AE::timer 0, 0.5, delayed {
content "Tss...\n";
};
content "Bap!\n";
if ( $count-- == 0 ) {
%timers = ();
lib/Dancer2/Manual.pod view on Meta::CPAN
use Path::Tiny qw< path >;
use JSON::MaybeXS qw< encode_json >;
# Create CSV parser
my $csv = Text::CSV_XS->new({
binary => 1,
auto_diag => 1,
});
get '/' => sub {
# delayed response:
delayed {
# streaming content
flush;
# Read each row and stream it in JSON
my $fh = path('filename.csv')->openr_utf8;
while ( my $row = $csv->getline($fh) ) {
content encode_json $row;
}
# close user connection
done;
} on_error => sub {
my ($error) = @_;
lib/Dancer2/Manual/Keywords.pod view on Meta::CPAN
Advent Calendar.
=head2 dirname
Returns the dirname of the path given:
my $dir = dirname($some_path);
=head2 done
Close the streaming connection. Can only be called within a streaming
response callback.
=head2 dsl
Allows access to the DSL within your plugin/application. Is an instance of
L<Dancer2::Core::DSL>.
=head2 encode_json ($structure)
Serializes a structure to a UTF-8 binary JSON string.
lib/Dancer2/Manual/Keywords.pod view on Meta::CPAN
See L<Dancer2::Core::Role::Logger> for details on how to configure where log
messages go.
=head2 false
Constant that returns a false value (0).
=head2 flush
Flush headers when streaming a response. Necessary when L</content> is called
multiple times.
=head2 forward
Runs an "internal redirect" of the current route to another route. More
formally; when C<forward> is executed, the current dispatch of the route is
aborted, the request is modified (altering query params or request method),
and the modified request following a new route is dispatched again. Any
remaining code (route and hooks) from the current dispatch will never be run
and the modified route's dispatch will execute hooks for the new route normally.
lib/Dancer2/Manual/Keywords.pod view on Meta::CPAN
get '/some/route' => sub {
if (...) {
# OK, send her what she wants...
send_file(...);
# this code will be ignored
do_stuff();
}
};
C<send_file> will use PSGI streaming if the server supports it (most, if
not all, do). You can explicitly disable streaming by passing
C<streaming =E<gt> 0> as an option to C<send_file>.
get '/download/:file' => sub {
send_file( route_parameters->get('file'), streaming => 0 );
}
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, you are passing in a filehandle,
or you need to force a specific mime type, you can pass it to C<send_file>
as follows:
send_file(route_parameters->get('file'), content_type => 'image/png');
t/dsl/send_file.t view on Meta::CPAN
};
get '/check_content_type' => sub {
my $temp = File::Temp->new();
print $temp "hello";
close $temp;
send_file($temp->filename, content_type => 'image/png',
system_path => 1);
};
get '/no_streaming' => sub {
my $file = File::Spec->rel2abs(__FILE__);
send_file( $file, system_path => 1, streaming => 0 );
};
get '/options_streaming' => sub {
my $file = File::Spec->rel2abs(__FILE__);
send_file( $file, system_path => 1, streaming => 1 );
};
get '/content_disposition/attachment' => sub {
send_file('1x1.png', filename => '1x1.png');
};
get '/content_disposition/inline' => sub {
send_file('1x1.png', filename => '1x1.png', content_disposition => 'inline');
};
}
t/dsl/send_file.t view on Meta::CPAN
subtest "filehandles" => sub {
my $r = $cb->( GET '/filehandle' );
is( $r->code, 200, 'send_file set status to 200 (filehandle)');
is( $r->content_type, 'text/plain', 'expected content_type');
is( $r->content_type_charset, 'UTF-8', 'expected charset');
like( $r->content, qr{package StaticContent}, 'filehandle content' );
};
subtest "no streaming" => sub {
my $r = $cb->( GET '/no_streaming' );
is( $r->code, 200, 'send_file set status to 200 (no streaming)');
like( $r->content, qr{package StaticContent}, 'no streaming - content' );
};
subtest "options streaming" => sub {
my $r = $cb->( GET '/options_streaming' );
is( $r->code, 200, 'send_file set status to 200 (options streaming)');
like( $r->content, qr{package StaticContent}, 'options streaming - content' );
};
subtest 'send_file returns correct content type' => sub {
my $r = $cb->( GET '/check_content_type' );
ok($r->is_success, 'send_file returns success');
is($r->content_type, 'image/png', 'send_file returns correct content_type');
};
subtest 'Content-Disposition defaults to "attachment"' => sub {
( run in 0.323 second using v1.01-cache-2.11-cpan-4d50c553e7e )