.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Dancer2::Cookbook 3pm" .TH Dancer2::Cookbook 3pm "2018-12-23" "perl v5.28.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Dancer2::Cookbook \- Example\-driven quick\-start to the Dancer2 web framework .SH "VERSION" .IX Header "VERSION" version 0.207000 .SH "DESCRIPTION" .IX Header "DESCRIPTION" A quick-start guide with examples to get you up and running with the Dancer2 web framework. This document will be twice as useful if you finish reading the manual (Dancer2::Manual) first, but that is not required... :\-) .SH "BEGINNER'S DANCE" .IX Header "BEGINNER'S DANCE" .SS "A simple Dancer2 web app" .IX Subsection "A simple Dancer2 web app" Dancer2 has been designed to be easy to work with \- it's trivial to write a simple web app, but still has the power to work with larger projects. To start with, let's make an incredibly simple \*(L"Hello World\*(R" example: .PP .Vb 1 \& #!/usr/bin/env perl \& \& use Dancer2; \& \& get \*(Aq/hello/:name\*(Aq => sub { \& return "Why, hello there " . route_parameters\->get(\*(Aqname\*(Aq); \& }; \& \& dance; .Ve .PP Yes \- the above is a fully-functioning web app; running that script will launch a webserver listening on the default port (3000). Now you can make a request: .PP .Vb 2 \& $ curl http://localhost:3000/hello/Bob \& Why, hello there Bob .Ve .PP and it will say hello. The \f(CW\*(C`:name\*(C'\fR part is a named parameter within the route specification, whose value is made available through \f(CW\*(C`route_parameters\*(C'\fR. .PP Note that you don't need to use the \f(CW\*(C`strict\*(C'\fR and \f(CW\*(C`warnings\*(C'\fR pragmas; they are already loaded by Dancer2. .SS "Default Route" .IX Subsection "Default Route" In case you want to avoid a \fI404 error\fR, or handle multiple routes in the same way and you don't feel like configuring all of them, you can set up a default route handler. .PP The default route handler will handle any request that doesn't get served by any other route. .PP All you need to do is set up the following route as the \fBlast\fR route: .PP .Vb 4 \& any qr{.*} => sub { \& status \*(Aqnot_found\*(Aq; \& template \*(Aqspecial_404\*(Aq, { path => request\->path }; \& }; .Ve .PP Then you can set up the template like so: .PP .Vb 1 \& You tried to reach [% path %], but it is unavailable at the moment. \& \& Please try again or contact us at . .Ve .ie n .SS "Using the ""auto_page"" feature for automatic route creation" .el .SS "Using the \f(CWauto_page\fP feature for automatic route creation" .IX Subsection "Using the auto_page feature for automatic route creation" For simple \*(L"static\*(R" pages you can simply enable the \f(CW\*(C`auto_page\*(C'\fR config setting; this means you don't need to declare a route handler for those pages; if a request is for \f(CW\*(C`/foo/bar\*(C'\fR, Dancer2 will check for a matching view (e.g. \f(CW\*(C`/foo/bar.tt\*(C'\fR) and render it with the default layout, if found. For full details, see the documentation for the auto_page setting. .SS "Simplifying \s-1AJAX\s0 queries with the Ajax plugin" .IX Subsection "Simplifying AJAX queries with the Ajax plugin" As an \s-1AJAX\s0 query is just an \s-1HTTP\s0 query, it's similar to a \s-1GET\s0 or \s-1POST\s0 route. You may ask yourself why you may want to use the \f(CW\*(C`ajax\*(C'\fR keyword (from the Dancer2::Plugin::Ajax plugin) instead of a simple \f(CW\*(C`get\*(C'\fR. .PP Let's say you have a path like \f(CW\*(C`/user/:user\*(C'\fR in your application. You may want to be able to serve this page with a layout and \s-1HTML\s0 content. But you may also want to be able to call this same url from a javascript query using \&\s-1AJAX.\s0 .PP So, instead of having the following code: .PP .Vb 10 \& get \*(Aq/user/:user\*(Aq => sub { \& if ( request\->is_ajax ) { \& # create xml, set headers to text/xml, blablabla \& header( \*(AqContent\-Type\*(Aq => \*(Aqtext/xml\*(Aq ); \& header( \*(AqCache\-Control\*(Aq => \*(Aqno\-store, no\-cache, must\-revalidate\*(Aq ); \& to_xml({...}) \& } else { \& template users => {...} \& } \& }; .Ve .PP you can have .PP .Vb 3 \& ajax \*(Aq/user/:user\*(Aq => sub { \& to_xml( {...}, RootName => undef ); \& } .Ve .PP and .PP .Vb 3 \& get \*(Aq/user/:user\*(Aq => sub { \& template users => {...} \& } .Ve .PP Because it's an \s-1AJAX\s0 query, you know you need to return \s-1XML\s0 content, so the content type of the response is set for you. .PP \fIExample: Feeding graph data through \s-1AJAX\s0\fR .IX Subsection "Example: Feeding graph data through AJAX" .PP Let us assume we are building an application that uses a plotting library to generate a graph and expects to get its data, which is in the form of word count from an \s-1AJAX\s0 call. .PP For the graph, we need the url \fI/data\fR to return a \s-1JSON\s0 representation of the word count data. Dancer in fact has a \f(CW\*(C`encode_json()\*(C'\fR function that takes care of the \s-1JSON\s0 encapsulation. .PP .Vb 2 \& get \*(Aq/data\*(Aq => sub { \& open my $fh, \*(Aq<\*(Aq, $count_file; \& \& my %contestant; \& while (<$fh>) { \& chomp; \& my ( $date, $who, $count ) = split \*(Aq\es*,\es*\*(Aq; \& \& my $epoch = DateTime::Format::Flexible\->parse_datetime($date)\->epoch; \& my $time = 1000 * $epoch; \& $contestant{$who}{$time} = $count; \& } \& \& my @json; # data structure that is going to be JSONified \& \& while ( my ( $peep, $data ) = each %contestant ) { \& push @json, { \& label => $peep, \& hoverable => \e1, # so that it becomes JavaScript\*(Aqs \*(Aqtrue\*(Aq \& data => [ map { [ $_, $data\->{$_} ] } \& sort { $a <=> $b } \& keys %$data ], \& }; \& } \& \& my $beginning = DateTime::Format::Flexible\->parse_datetime( "2010\-11\-01")\->epoch; \& my $end = DateTime::Format::Flexible\->parse_datetime( "2010\-12\-01")\->epoch; \& \& push @json, { \& label => \*(Aqde par\*(Aq, \& data => [ \& [$beginning * 1000, 0], \& [ DateTime\->now\->epoch * 1_000, \& 50_000 \& * (DateTime\->now\->epoch \- $beginning) \& / ($end \- $beginning) \& ] \& ], \& \& }; \& \& encode_json( \e@json ); \& }; .Ve .PP For more serious \s-1AJAX\s0 interaction, there's also Dancer2::Plugin::Ajax that adds an \fIajax\fR route handler to the mix. .PP Because it's an \s-1AJAX\s0 query, you know you need to return \s-1XML\s0 content, so the content type of the response is set for you. .SS "Using the prefix feature to split your application" .IX Subsection "Using the prefix feature to split your application" For better maintainability, you may want to separate some of your application components into different packages. Let's say we have a simple web app with an admin section and want to maintain this in a different package: .PP .Vb 3 \& package myapp; \& use Dancer2; \& use myapp::admin; \& \& prefix undef; \& \& get \*(Aq/\*(Aq => sub {...}; \& \& 1; \& \& package myapp::admin; \& use Dancer2 appname => \*(Aqmyapp\*(Aq; \& \& prefix \*(Aq/admin\*(Aq; \& \& get \*(Aq/\*(Aq => sub {...}; \& \& 1; .Ve .PP The following routes will be generated for us: .PP .Vb 4 \& \- get / \& \- get /admin/ \& \- head / \& \- head /admin/ .Ve .PP By default, a separate application is created for every package that uses Dancer2. The \f(CW\*(C`appname\*(C'\fR tag is used to collect routes and hooks into a single Dancer2 application. In the above example, \f(CW\*(C`appname => \*(Aqmyapp\*(Aq\*(C'\fR adds the routes from \f(CW\*(C`myapp::admin\*(C'\fR to the routes of the app \f(CW\*(C`myapp\*(C'\fR. .PP When using multiple applications please ensure that your path definitions do not overlap. For example, if using a default route as described above, once a request is matched to the default route then no further routes (or applications) would be reached. .SS "Delivering custom error pages" .IX Subsection "Delivering custom error pages" \fIAt the Core\fR .IX Subsection "At the Core" .PP In Dancer2, creating new errors is done by creating a new Dancer2::Core::Error .PP .Vb 5 \& my $oopsie = Dancer2::Core::Error\->new( \& status => 418, \& message => "This is the Holidays. Tea not acceptable. We want eggnog.", \& app => $app, \& ) .Ve .PP If not given, the status code defaults to a 500, there is no need for a message if we feel taciturn, and while the \f(CW$app\fR (which is a \fIDancer2::Core::App\fR object holding all the pieces of information related to the current request) is needed if we want to take advantage of the templates, we can also do without. .PP However, to be seen by the end user, we have to populate the Dancer2::Core::Response object with the error's data. This is done via: .PP .Vb 1 \& $oopsie\->throw($response); .Ve .PP Or, if we want to use the response object already present in the \f(CW$app\fR (which is usually the case): .PP .Vb 1 \& $oopsie\->throw; .Ve .PP This populates the status code of the response, sets its content, and throws a \&\fI\f(BIhalt()\fI\fR in the dispatch process. .PP \fIWhat it will look like\fR .IX Subsection "What it will look like" .PP The error object has quite a few ways to generate its content. .PP First, it can be explicitly given .PP .Vb 3 \& my $oopsie = Dancer2::Core::Error\->new( \& content => \*(Aq

OMG

\*(Aq, \& ); .Ve .PP If the \f(CW$context\fR was given, the error will check if there is a template by the name of the status code (so, say you're using Template Toolkit, \fI418.tt\fR) and will use it to generate the content, passing it the error's \f(CW$message\fR, \f(CW$status\fR code and \f(CW$title\fR (which, if not specified, will be the standard http error definition for the status code). .PP If there is no template, the error will then look for a static page (to continue with our example, \fI418.html\fR) in the \fIpublic/\fR directory. .PP And finally, if all of that failed, the error object will fall back on an internal template. .PP \fIErrors in Routes\fR .IX Subsection "Errors in Routes" .PP The simplest way to use errors in routes is: .PP .Vb 4 \& get \*(Aq/xmas/gift/:gift\*(Aq => sub { \& die "sorry, we\*(Aqre all out of ponies\en" \& if route_parameters\->get(\*(Aqgift\*(Aq) eq \*(Aqpony\*(Aq; \& }; .Ve .PP The die will be intercepted by Dancer, converted into an error (status code 500, message set to the dying words) and passed to the response. .PP In the cases where more control is required, \f(CW\*(C`send_error()\*(C'\fR is the way to go: .PP .Vb 3 \& get \*(Aq/glass/eggnog\*(Aq => sub { \& send_error "Sorry, no eggnog here", 418; \& }; .Ve .PP And if total control is needed: .PP .Vb 7 \& get \*(Aq/xmas/wishlist\*(Aq => sub { \& Dancer2::Core::Error\->new( \& response => response(), \& status => 406, \& message => "nothing but coal for you, I\*(Aqm afraid", \& template => \*(Aqnaughty/index\*(Aq, \& )\->throw unless user_was_nice(); \& \& ...; \& }; .Ve .SS "Template Toolkit's \s-1WRAPPER\s0 directive in Dancer2" .IX Subsection "Template Toolkit's WRAPPER directive in Dancer2" Dancer2 already provides a WRAPPER-like ability, which we call a \*(L"layout\*(R". The reason we don't use Template Toolkit's \s-1WRAPPER\s0 (which also makes us incompatible with it) is because not all template systems support it. In fact, most don't. .PP However, you might want to use it, and be able to define \s-1META\s0 variables and regular Template::Toolkit variables. .PP These few steps will get you there: .IP "\(bu" 4 Disable the layout in Dancer2 .Sp You can do this by simply commenting (or removing) the \f(CW\*(C`layout\*(C'\fR configuration in the config file. .IP "\(bu" 4 Use the Template Toolkit template engine .Sp Change the configuration of the template to Template Toolkit: .Sp .Vb 2 \& # in config.yml \& template: "template_toolkit" .Ve .IP "\(bu" 4 Tell the Template Toolkit engine which wrapper to use .Sp .Vb 6 \& # in config.yml \& # ... \& engines: \& template: \& template_toolkit: \& WRAPPER: layouts/main.tt .Ve .PP Done! Everything will work fine out of the box, including variables and \s-1META\s0 variables. .PP However, disabling the internal layout it will also disable the hooks \f(CW\*(C`before_layout_render\*(C'\fR and \f(CW\*(C`after_layout_render\*(C'\fR. .SS "Customizing Template Toolkit in Dancer2" .IX Subsection "Customizing Template Toolkit in Dancer2" Please see Dancer2::Template::TemplateToolkit for more details. .SS "Accessing configuration information from a separate script" .IX Subsection "Accessing configuration information from a separate script" You may want to access your webapp's configuration from outside your webapp. You could, of course, use the \s-1YAML\s0 module of your choice and load your webapps's \f(CW\*(C`config.yml\*(C'\fR, but chances are that this is not convenient. .PP Use Dancer2 instead. You can simply use the values from \f(CW\*(C`config.yml\*(C'\fR and some additional default values: .PP .Vb 4 \& # bin/show_app_config.pl \& use Dancer2; \& printf "template: %s\en", config\->{\*(Aqtemplate\*(Aq}; # simple \& printf "log: %s\en", config\->{\*(Aqlog\*(Aq}; # undef .Ve .PP Note that \f(CW\*(C`config\->{log}\*(C'\fR should result in an uninitialized warning on a default scaffold since the environment isn't loaded and log is defined in the environment and not in \f(CW\*(C`config.yml\*(C'\fR. Hence \f(CW\*(C`undef\*(C'\fR. .PP Dancer2 will load your \f(CW\*(C`config.yml\*(C'\fR configuration file along with the correct environment file located in your \f(CW\*(C`environments\*(C'\fR directory. .PP The environment is determined by two environment variables in the following order: .IP "\(bu" 4 \&\s-1DANCER_ENVIRONMENT\s0 .IP "\(bu" 4 \&\s-1PLACK_ENV\s0 .PP If neither of those is set, it will default to loading the development environment (typically \f(CW\*(C`$webapp/environment/development.yml\*(C'\fR). .PP If you wish to load a different environment, you need to override these variables. .PP You can call your script with the environment changed: .PP .Vb 1 \& $ PLACK_ENV=production perl bin/show_app_config.pl .Ve .PP Or you can override them directly in the script (less recommended): .PP .Vb 2 \& BEGIN { $ENV{\*(AqDANCER_ENVIRONMENT\*(Aq} = \*(Aqproduction\*(Aq } \& use Dancer2; \& \& ... .Ve .SS "Using DBIx::Class" .IX Subsection "Using DBIx::Class" DBIx::Class, also known as \s-1DBIC,\s0 is one of the many Perl \s-1ORM\s0 (\fIObject Relational Mapper\fR). It is easy to use \s-1DBIC\s0 in Dancer2 using the Dancer2::Plugin::DBIC. .PP \fIAn example\fR .IX Subsection "An example" .PP This example demonstrates a simple Dancer2 application that allows one to search for authors or books. The application is connected to a database, that contains authors, and their books. The website will have one single page with a form, that allows one to query books or authors, and display the results. .PP Creating the application .IX Subsection "Creating the application" .PP .Vb 1 \& $ dancer2 \-a bookstore .Ve .PP To use the Template Toolkit as the template engine, we specify it in the configuration file: .PP .Vb 2 \& # add in bookstore/config.yml \& template: template_toolkit .Ve .PP Creating the view .IX Subsection "Creating the view" .PP We need a view to display the search form, and below, the results, if any. The results will be fed by the route to the view as an arrayref of results. Each result is a \fIhashref\fR, with a author key containing the name of the author, and a books key containing an \fIarrayref\fR of strings : the books names. .PP .Vb 8 \& # example of a list of results \& [ { author => \*(Aqauthor 1\*(Aq, \& books => [ \*(Aqbook 1\*(Aq, \*(Aqbook 2\*(Aq ], \& }, \& { author => \*(Aqauthor 2\*(Aq, \& books => [ \*(Aqbook 3\*(Aq, \*(Aqbook 4\*(Aq ], \& } \& ] .Ve .PP # bookstore/views/search.tt

Search query:


.PP An example of the view, displaying the search form, and the results, if any: .PP .Vb 10 \& <% IF query.length %> \&

Search query was : <% query %>.

\& <% IF results.size %> \& Results: \&