Skip to content

Commit f95fdf3

Browse files
committed
New example: Dynamic content with 'send_file()'
1 parent 6d906c2 commit f95fdf3

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

lib/dancer_bootstrap_fontawesome_template.pm

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use examples::photo_gallery;
1212
use examples::markdown;
1313
use examples::template_plugins;
1414
use examples::error_handling;
15+
use examples::dynamic_content;
1516

1617
our $VERSION = '0.1';
1718

lib/examples/dynamic_content.pm

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package examples::dynamic_content;
2+
use Dancer ':syntax';
3+
use strict;
4+
use warnings;
5+
use POSIX qw(strftime);
6+
use Data::Dumper;
7+
8+
=pod
9+
This module demonstrates returning dynamic content to the user from a Dancer route handler.
10+
11+
The data is generated on-line, without having to temporarily store it in a file or in memory.
12+
=cut
13+
14+
# Show the main page to the user
15+
get '/dynamic_content' => sub {
16+
template 'examples/dynamic_content.tt';
17+
};
18+
19+
20+
=pod
21+
Return dynamic content to the user.
22+
The content is generated on-line, not from a stored file or a memory buffer.
23+
Common usage examples:
24+
Returning the results of a "select * from TABLE" query, without storing the results in a temporary file.
25+
26+
27+
Couple of technical notes:
28+
1. send_file() documentation:
29+
https://metacpan.org/module/Dancer#send_file
30+
31+
2. send_file was originally designed to send either physical files, or in-memory scalars.
32+
The streaming option was added later - so some hacks are required.
33+
34+
3. The "override" callback will take care of all data sent to the user/front-end-HTTP-server,
35+
and so the first parameter to 'send_file' doesn't matter
36+
(just needs to be a scalar-ref, otherwise it will be mistaken for a file-name).
37+
38+
4. The "$respond" parameter is a PSGI-related opaque object. Use it to generate the 'writer' object.
39+
40+
5. The "$response" parameter is the original Dancer::Response object that Dancer generated.
41+
Can be safely ignored, as we generate our own response.
42+
43+
=cut
44+
get '/dynamic_content_download' => sub {
45+
send_file(
46+
\"foo", # anything, as long as it's a scalar-ref
47+
streaming => 1, # enable streaming
48+
callbacks => {
49+
override => sub {
50+
my ( $respond, $response ) = @_;
51+
52+
53+
my $http_status_code = 200 ;
54+
# Tech.note: This is a hash of HTTP header/values, but the
55+
# function below requires an even-numbered array-ref.
56+
my @http_headers = ( 'Content-Type' => 'text/plain',
57+
'Content-Disposition' => 'attachment; filename="foo.txt"',
58+
59+
# Since this is dynamic content,
60+
# try hard (with extra HTTP headers) to prevent caching.
61+
'Last-Modified' => strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime),
62+
'Expires' => 'Tue, 03 Jul 2001 06:00:00 GMT',
63+
'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0',
64+
'Pragma' => 'no-cache' );
65+
66+
# Send the HTTP headers
67+
# (back to either the user or the upstream HTTP web-server front-end)
68+
my $writer = $respond->( [ $http_status_code, \@http_headers ] );
69+
70+
# Generate a lot of text...
71+
foreach my $line_number ( 1 .. 1000000 ) {
72+
$writer->write("Hello World (line $line_number)\n");
73+
}
74+
},
75+
},
76+
);
77+
};
78+
79+
80+
true;

views/examples/dynamic_content.tt

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<!-- BootStrap NavBar -->
2+
<div class="navbar">
3+
<div class="navbar-inner">
4+
<div class="container">
5+
<h3><a class="brand" href="[% request.uri_base %]"><img src="images/dancer_man.png"> Perl Dancer</a></h3>
6+
</div>
7+
</div>
8+
</div>
9+
10+
<!-- HEADER line -->
11+
<div class="container">
12+
13+
<div class="page-header">
14+
<div class="row">
15+
<div class="span12">
16+
<h2>Dancer + Bootstrap Examples</h2>
17+
<h1>Dynamic Content Generation</h1>
18+
</div>
19+
</div>
20+
</div>
21+
22+
<div class="row">
23+
<div class="span12">
24+
25+
This example shows how to return dynamic content (generated by the Perl/Dancer script) to the user.
26+
<br/>
27+
Useful if you need to send data to the client, that is not based on a physical file or a variable in memory (or if the data is too big to fit in memory).
28+
<br/>
29+
<br/>
30+
<br/>
31+
32+
<h3>Code Highlights</h3>
33+
<ul>
34+
<li>The Dancer code is in
35+
<a href="[% request.uri_for("/show_file",file => "dynamic_content.pm", example => "Dynamic Content", url => request.uri_for("/dynamic_content") ) %]">
36+
<code>./lib/examples/dynamic_content.pm</code> <i class="icon-eye-open"></i> </a>.
37+
</li>
38+
39+
<li>This HTML template is in
40+
<a href="[% request.uri_for("/show_file",file => "dynamic_content.tt", example =>"Dynamic Content", url => request.uri_for("/dynamic_content") ) %]">
41+
<code>./views/examples/dynamic_content.tt</code> <i class="icon-eye-open"></i> </a>.
42+
</li>
43+
<li>This is functionally equivalent to the following 'classic' CGI code:
44+
<pre>
45+
#!/usr/bin/perl
46+
use CGI qw/:standard/;
47+
48+
print header(-type=>"text/plain", -attachment=>"foo.txt");
49+
50+
foreach $line ( 1 .. 10000 ) {
51+
print "Hello World (line $line)\n";
52+
}
53+
</pre>
54+
</li>
55+
<li>Due the Dancer architecture, we can't simply <code>print</code> the data we want to send to the use (as in classic CGI programs). Instead, we supply a callback <code>sub</code> which will send the data to the user.</li>
56+
</li>
57+
<li>Using <code>send_file</code> with the <b>streaming</b> parameter, we create a subroutine that will send the data back to the user.</li>
58+
<li>See the <b>dynamic_content.pm</b> perl module for a detailed example.</li>
59+
</ul>
60+
61+
</div>
62+
</div>
63+
64+
<br/>
65+
<br/>
66+
<br/>
67+
68+
<div class="row">
69+
<div class="span12">
70+
<h1>Download</h1>
71+
Clicking this button will send you a 25MB text file.<br/>
72+
The content is dynamically generated by the Dancer code.<br/>
73+
<a class="btn btn-primary" href="[% request.uri_for("/dynamic_content_download") %]">Download Dynamic Content</a>
74+
</div>
75+
</div>
76+
77+
<br/>
78+
<br/>
79+
<br/>
80+
<br/>
81+
<br/>
82+
<br/>

views/index.tt

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
<li><a href="[% request.uri_for("/markdown")%]">Markdown Text Rendering</a></li>
9494
<li><a href="[% request.uri_for("/template_plugins")%]">Template::Toolkit plugins</a></li>
9595
<li><a href="[% request.uri_for("/error_handling")%]">Dancer's Error Handling</a></li>
96+
<li><a href="[% request.uri_for("/dynamic_content")%]">Generate Dynamic Content</a></li>
9697
</ul>
9798
</div>
9899
</div>

0 commit comments

Comments
 (0)