# ABSTRACT: A guide to building web applications with Dancer2 package Dancer2::Manual; __END__ =pod =encoding UTF-8 =head1 NAME Dancer2::Manual - A guide to building web applications with Dancer2 =head1 VERSION version 2.0.0 =head1 Introduction to Dancer2: Managing Danceyland Welcome to Danceyland! As the new manager of this amazing park, you'll be maintaining and enhancing the experience for all your visitors. Imagine each attraction, food stall, and ticket booth as different locations in your web application. Let's explore how to manage these various components using Dancer2. If you're not sure if you're at the correct spot in the park, the L can help you find your way. =head2 What is Dancer2? Dancer2 is a free and open-source web application framework written in Perl. It’s a complete rewrite of the original L, designed to be powerful and flexible, yet incredibly easy to use. With Dancer2, getting your web app up and running is a breeze. It boasts a rich ecosystem of adapters for popular template engines, session storage solutions, logging methods, serializers, and plugins. This means you can build your app your way, effortlessly. In this guide, we'll leverage those strengths to build and manage Danceyland. Before we learn the ins and outs of managing your park, make sure you head over to the L and get your development machine set up with Dancer2. Once it's installed, make sure to build your park: dancer2 gen -a Danceyland =head1 Routes: Different Attractions and Facilities Core to Dancer2 is the concept of routes. Each attraction in your theme park is like a route in your web application. Visitors (users) can navigate to different attractions, just as they would visit different routes in your app. Let's show some of the rides and facilities in our park: # Defining routes for our theme park get '/' => sub { return "Welcome to Danceyland!"; }; get '/roller-coaster' => sub { return "Enjoy the thrilling roller coaster ride!"; }; post '/buy-ticket' => sub { my $ticket = body_parameters->get('ticket'); # Do something with ticket data return "You bought a $ticket ticket!"; }; =over =item The `/` route is like the main entrance to our theme park. Visitors are greeted with a welcome message. =item The `/roller-coaster` route is a thrilling ride. When visitors take this path, they get a special message. =item The `/buy-ticket` route is the ticket booth. Visitors buy their tickets here. =back =head2 New Keywords =over =item get This keyword defines a route that responds to HTTP GET requests. It takes two parameters: the route path and a subroutine that defines the response. get '/path' => sub { return "Response text"; }; Note that a route to match C requests is automatically created when you create a C route. =item post This keyword defines a route that responds to HTTP POST requests. It works similarly to C, but is used for actions like submitting forms or buying tickets. post '/path' => sub { my $param = body_parameters->get('param'); return "You submitted: $param"; }; =back So exactly what are these HTTP requests, and what are they all about? =head2 HTTP Methods: Visitor Actions Think of HTTP methods as the different actions visitors can take in your theme park. Entering the park, buying tickets, updating ticket details, and leaving the park are all actions represented by C, C, C, C, C, and C methods respectively. Handling visitor actions in the park: # Defining HTTP methods for visitor actions get '/' => sub { return "Welcome to Danceyland!"; }; post '/buy-ticket' => sub { my $ticket = body_parameters->get('ticket'); return "You bought a $ticket ticket!"; }; put '/update-ticket' => sub { my $new_ticket = body_parameters->get('new_ticket'); return "Your ticket has been updated to $new_ticket!"; }; del '/leave-park' => sub { return "Thank you for visiting! Please come again!"; }; options '/park/info' => sub { return "Allowed methods: GET, POST, PUT"; }; patch '/profile/:id' => sub { my $user_id = route_parameters->get('id'); my $new_email = body_parameters->get('email'); return "Updated profile for user $user_id with email $new_email"; }; =over =item GET: Visitors enter the park and see a welcome message. =item POST: Visitors buy a ticket. =item PUT: Visitors update their ticket details. =item DELETE: Visitors leave the park. =item PATCH: Update part of a visitor's profile (in this case, email). =item OPTIONS: Describing the available operations on a specific route. Keep in mind, you would rarely implement this in your web application. =back These are good conventions to follow, but you can make your route handlers do whatever makes the most sense for your application. =head2 Routes, Route Definitions, and Route Handlers You may hear other Dancer developers talk about "routes", "route definitions", and "route handlers". "Route definitions" refers to the HTTP method and URL to respond to, while "route handler" is only the code implementing functionality for that definition. The two of these together make what's known as a route. get '/' => sub {...}; =over =item The verb, path, and subroutine is a route definition (AKA "route"). =item Only the subroutine reference (C) is the route handler. =item The route definition I the route handler collectively are the route. =back Each route requires a defintion and handler. The route needs to either return a string or Perl data structure to be rendered for the client. We'll learn more about rendering data structures later in our guide to Danceyland; for now, we're going to focus on returning strings, which will be rendered as HTML. =head3 What if we want a single park location to respond to multiple request types? Route definitions can use C to match all, or a specified list of HTTP methods. The following will match any HTTP request to the path C: any '/visitor-center' => sub { # Write code to do something at the visitor center! } The following will match GET or POST requests to C: any ['get', 'post'] => '/visitor-center' => sub { # Write code to do something at the visitor center! }; =head2 URI Generation Dancer2 can generate URIs using the C and C keywords. Letting Dancer2 generate URIs helps ensure consistency, and reduces the amount of maintenance needed by making sure URIs are always up to date as the application evolves over time. =head3 uri_for The C keyword is used to generate a URI for a given path within your application, including query parameters. It's especially useful when you want to construct URLs dynamically inside your routes. =head4 Example: Generating a URI in Danceyland get '/ride/:name' => sub { my $ride_name = route_parameters->get('name'); return 'Enjoy the ride at ' . uri_for("/ride/$ride_name"); }; In this example, C generates a full URI for the ride name in Danceyland. =head3 uri_for_route The C keyword creates a URL for a named route. In Danceyland, it can be used to generate a URL to any part of the park that has a named route. For more information, see L. =head3 Example 1: Basic Usage get 'films' => '/cinemaland/film-gallery' => sub { return "See the films at " . uri_for_route('films'); }; In this example, the route is named C, and C generates a URL pointing to it. =head3 Example 2: Using Route Parameters get 'ride' => '/ride/:name' => sub { my $ride_name = route_parameters->get('name'); return "Ride details: " . uri_for_route('ride', { name => $ride_name }); }; This example uses C to generate a URL that includes the named route parameter C. =head3 Example 3: Including Query Parameters get 'search' => '/search' => sub { return uri_for_route('search', { q => 'roller coaster' }); }; get '/search' => sub { my $query = query_parameters->get('q'); return "Search results for: $query"; }; In this example, C generates a URL for the named route C with the query parameter C set to "roller coaster". =head2 Parameter Handling In Danceyland, visitors will often need to communicate with park staff. Similarly, your apps will need to take in information from application users via parameters. Dancer2 provides several methods for handling different types of parameters. =head3 Keywords for working with parameters =head4 param and params (Not Recommended) The C and C keywords are legacy methods to access request parameters. While still functional, they are not recommended for new applications. Instead, you should use the more specific keywords like C, C, and C for clarity and precision. =head4 Example: Using param (Not Recommended) get '/submit' => sub { # Please use query_parameters() shown below my $name = param('name'); return "Submitted name: $name"; }; =head4 Example: Using params (Not Recommended) get '/all-params' => sub { # Pleaase use query_parameters() shown below my %all_params = params; return "All parameters: " . join(', ', %all_params); }; C and C are included here for completeness but are not the preferred methods for handling parameters in Danceyland. =head4 body_parameters This keyword retrieves parameters from the body of the request, typically used in POST requests. Example of C with multiple values: post '/submit' => sub { my $name = body_parameters->get('name'); my $email = body_parameters->get('email'); return "Submitted name: $name, email: $email"; }; =head4 query_parameters This keyword retrieves parameters from the query string of the request URL: # Matches URL: /search?q=rides&cat=thrill get '/search' => sub { my $query = query_parameters->get('q'); my $category = query_parameters->get('cat'); return "Search query: $query in category: $category"; }; The above route would match the URL C. =head4 route_parameters This keyword retrieves named parameters from the route declaration: # Matches URL: /user/123 get '/user/:id' => sub { my $user_id = route_parameters->get('id'); return "User ID: $user_id"; }; =head4 get_all This method works for all of the above parameter-fetching keywords. If you have a parameter that may contain more than one value, C will return an array reference containing all selected values, even if there is only a single value returned. Example of C: # Matches URL: /all-params?name=John&age=30 get '/all-params' => sub { my $params = query_parameters->get_all(); return "All query parameters: " . join(', ', %$params); }; =head3 Named Route Parameters Named route parameters allow you to capture specific segments of the URL and use them in your route handler: # Matches URL: /ride/roller-coaster get '/ride/:name' => sub { my $ride_name = route_parameters->get('name'); return "Welcome to the $ride_name ride!"; }; Named route parameters are retrieved with the L keyword. =head3 Wildcard Route Parameters Wildcard route parameters allow you to capture arbitrary parts of the URL. There are two types: C and C. C is represented by a single asterisk (C<*>), and megaspat is represented by a double asterisk (C<**>). Examples of wildcard route parameters include: =over =item splat: Captures one segment of the URL # Matches URL: /files/document.txt get '/files/*' => sub { my ($file) = splat; return "You requested the file: $file"; }; =item megasplat: Captures multiple segments of the URL # Matches URL: /files/documents/reports/2023/summary.txt get '/files/**' => sub { my @files = splat; return "You requested the files: " . join(', ', @files); }; =back =head3 Combining named and wildcard parameters You can combine named and wildcard parameters in your routes to capture both specific and arbitrary segments of the URL. Example combining named and wildcard parameters: # Matches URL: /user/123/files/documents/reports/2023/summary.txt get '/user/:id/files/**' => sub { my $user_id = route_parameters->get('id'); my @files = splat; return "User ID: $user_id requested the files: " . join(', ', @files); }; =head3 Named parameters with type constraints Type constraints allow you to enforce specific types for named parameters, ensuring that the parameters meet certain criteria. Dancer2 natively supports named parameters with type constraints without needing to rely on external plugins. Here’s how to declare and use type constraints for named route parameters in Dancer2: use Dancer2; get '/ride/:id[Int]' => sub { my $id = route_parameters->get('id'); return "Ride ID: $id"; }; get '/guest/:name[Str]' => sub { my $name = route_parameters->get('name'); return "Guest Name: $name"; }; MyApp->to_app(); =over 4 =item * B: This constraint ensures that the C parameter must be an integer. =item * B: This ensures that the C parameter must be a string. =back Note: For more complex parameter types, the C module provides additional constraints and validation for all parameter types. =head3 Wildcard Parameters with Type Constraints You can also enforce type constraints on wildcard parameters: # Matches URL: /images/photo.jpg get '/images/*.*[ArrayRef[Str]]' => sub { my ($filename, $extension) = splat; return "Filename: $filename, Extension: $extension"; }; # Matches URL: /documents/folder/subfolder/file.txt get '/documents/**[ArrayRef[Str]]' => sub { my @path = splat; return "Document path: " . join('/', @path); }; =head3 Regex Route Matching Regex route matching allows you to define routes using regular expressions, providing more flexibility in matching URLs: # Matches URL: /product/12345 get qr{/product/(\d+)} => sub { my ($product_id) = splat; return "Product ID: $product_id"; }; # Matches URL: /category/electronics get qr{/category/(\w+)} => sub { my ($category_name) = splat; return "Category: $category_name"; }; It is also possible to use named captures in regular expressions: # Matches URL: /product/12345 get qr{/product/(?\d+)} => sub { my $product_id = captures->{'product_id'}; return "Product ID: $product_id"; }; =head3 Combining Examples # Matches URL: /item/42/specifications get '/item/:id[Int]/*[ArrayRef[Str]]' => sub { my $item_id = route_parameters->get('id'); my ($detail) = splat; return "Item ID: $item_id, Detail: $detail"; }; # Matches URL: /archive/2023/07/10 get qr{/archive/(\d{4})/(\d{2})/(\d{2})} => sub { my ($year, $month, $day) = splat; return "Archive for: $year-$month-$day"; }; =head2 Organizing routes and growing your app using prefix The prefix DSL keyword helps you group related routes. For example, you can organize all the routes of a given section in Danceyland with the C keyword as such: package MyApp; use Dancer2; # Prefix for Cinemaland prefix '/cinemaland' => sub { get '/film-gallery' => sub { return "Welcome to Cinemaland! Here you can learn about famous movies."; }; get '/movie-schedule' => sub { return "Movie Schedule: 10 AM and 4 PM."; }; }; # Prefix for Actionland prefix '/actionland' => sub { get '/thrill-rides' => sub { return "Welcome to Actionland! Enjoy the thrill rides."; }; get '/roller-coaster' => sub { return "The best roller coaster in the park!"; }; }; MyApp->to_app(); This example organizes routes by grouping all cinema-related activities under C and all rides under C. =head2 Controlling the flow with forward, redirect, and pass These DSL keywords in Dancer2 allow you to manage the flow of actions within your routes. Here’s how they work in Danceyland. =head3 forward C allows you to forward the current request to another route handler, as if it were redirected, but without sending a new HTTP request. Put another way, a different route than the one requested is processed, but the address in the user's browser's bar stays the same. =head4 Example get '/guest-services' => sub { forward '/info'; # Forwards to /info }; get '/info' => sub { return "Welcome to Guest Services. How can we help you today?"; }; In this example, when a visitor goes to C, they are forwarded to the C route internally, however, the vistor's browser still shows they are in C. =head3 redirect C sends an HTTP redirect to the client, instructing their browser to make a new request to a different URL. At the end of the request, the user's browser will show a different URL than the one they originally navigated to. =head4 Example get '/old-roller-coaster' => sub { redirect '/new-roller-coaster'; }; get '/new-roller-coaster' => sub { return "Welcome to the new and improved roller coaster!"; }; When a visitor requests C, they are redirected to C. The browser's URL will now show C. =head3 pass C tells Dancer2 to skip the current route and continue looking for the next matching route. =head4 Example get '/vip-area' => sub { if (!session('is_vip')) { pass; # Skip to the next matching route if the user is not VIP } return "Welcome to the VIP area!"; }; get '/vip-area' => sub { return "Access Denied. This area is for VIPs only."; }; In this example, if the session doesn’t indicate the user is a VIP, the request will skip to the next matching route, which denies access. =head1 Templates: Displaying Information, and More! Templates in a web application help present information in a structured and visually appealing way, much like maps, schedules, and banners in a theme park. =head2 Why do we use templates? Templates help separate your display logic from your programming logic. The same code that sends JSON to satisfy an API quest could also be used to display an HTML page containing park information to a user. If the code needed to display HTML is intertwined with the code to gather the data needed, the code necessary to just produce JSON becomes unnecessarily complicated. Templates take the HTML out of your Perl code. =head2 Views In Dancer2, "views" refer to the template files that define the structure of the content presented to the users. Views are typically HTML files with embedded placeholders that are dynamically filled with data when rendered. =head3 How Views Work Suppose you have a template file called C:

<% ride_name %> Ride

Enjoy the thrilling <% ride_name %> ride at Danceyland!

You can use this view in your route handler: get '/ride/:name' => sub { my $ride_name = route_parameters->get('name'); template 'ride' => { ride_name => $ride_name }; }; =head3 Example: Displaying a welcome message on a pretty banner get '/' => sub { template 'welcome', { message => "Welcome to Danceyland!" }; }; In this example, the C route uses a template to display a welcome message. =head3 Example: Displaying the park map get '/map' => sub { template 'map', { attractions => \@attractions }; }; =head3 Example: Using templates to display various information get '/info' => sub { template 'info', { title => "Park Information", content => "Here you can find all the information about Danceyland.", hours => "Open from 10 AM to 8 PM", contact => "Call us at 123-456-7890", }; }; =over 4 =item * The C route uses a template to display a welcome message. =item * The C route uses a template to display the park's map. =item * The C route uses a template to display various pieces of information about the park. =back =head2 New Keywords =head3 template The C