File: Manual.pod

package info (click to toggle)
libdancer2-perl 2.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,768 kB
  • sloc: perl: 8,671; sql: 14; makefile: 8
file content (2588 lines) | stat: -rw-r--r-- 76,503 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
# 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.1

=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<documentation map|Dancer2/Documentation Index>
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<Dancer>, 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<quick start guide|Dancer2::Manual::QuickStart> 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<HEAD> requests is automatically created when
you create a C<GET> route.

=item post

This keyword defines a route that responds to HTTP POST requests. It
works similarly to C<get>, 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<GET>,
C<POST>, C<PUT>, C<DEL>, C<OPTIONS>, and C<PATCH> 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<sub {...}>) is the route handler.

=item The route definition I<and> 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<any> to match all, or a specified list of HTTP methods.

The following will match any HTTP request to the path C</visitor-center>:

    any '/visitor-center' => sub {
        # Write code to do something at the visitor center!
    }

The following will match GET or POST requests to C</visitor-center>:

    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<uri_for> and C<uri_for_route>
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<uri_for> 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<uri_for> generates a full URI for the ride name in
Danceyland.

=head3 uri_for_route

The C<uri_for_route> 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<Dancer2::Manual::Keywords/uri_for_route>.

=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<animals>, and C<uri_for_route>
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<uri_for_route> to generate a URL that includes the
named route parameter C<name>.

=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<uri_for_route> generates a URL for the named route
C<search> with the query parameter C<q> 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<param> and C<params> 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<route_parameters>, C<query_parameters>, and C<body_parameters> 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<param> and C<params> 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<body_parameters> 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</search?q=rides&cat=thrill>.

=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<get_all>
will return an array reference containing all selected values, even if
there is only a single value returned.

Example of C<get_all>:

    # 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</route_parameters>
keyword.

=head3 Wildcard Route Parameters

Wildcard route parameters allow you to capture arbitrary parts of the URL.
There are two types: C<splat> and C<megasplat>. C<splat> 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<Int>: This constraint ensures that the C<id> parameter must be an integer.

=item *

B<Str>: This ensures that the C<name> parameter must be a string.

=back

Note: For more complex parameter types, the C<Dancer2::Plugin::ParamTypes>
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/(?<product_id>\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<prefix> 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</cinemaland> and all rides under C</actionland>.

=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<forward> 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</guest-services>, they are
forwarded to the C</info> route internally, however, the vistor's browser
still shows they are in C</guest-services>.

=head3 redirect

C<redirect> 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</old-roller-coaster>, they are redirected to
C</new-roller-coaster>. The browser's URL will now show C</new-roller-coaster>.

=head3 pass

C<pass> 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<views/ride.tt>:

    <h1><% ride_name %> Ride</h1>
    <p>Enjoy the thrilling <% ride_name %> ride at Danceyland!</p>

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</map> route uses a template to display the park's map.

=item *

The C</info> route uses a template to display various pieces of information about the park.

=back

=head2 New Keywords

=head3 template

The C<template> keyword renders a template file with the provided data.
It takes two parameters: the template name and a hash reference of the
data to pass to the template.

    template 'template_name', { key1 => 'value1', key2 => 'value2' };

=head2 Layouts

Layouts in Dancer2 allow you to wrap views with a common structure, such
as a header, footer, or navigation bar, that remains consistent across
Danceyland. A layout is just another template that wraps around your page
content; it allows you to maintain a consistent look and feel across
multiple pages while rendering the dynamic content of each page within
the layout.

=head3 Example: Implementing Layouts in Danceyland

Consider Danceyland having a consistent navigation bar across all sections
of the site. You can use a layout for this.

    # views/layouts/main.tt
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Danceyland</title>
    </head>
    <body>
        <header>
            <h1>Welcome to Danceyland</h1>
            <nav>
                <a href="/">Home</a>
                <a href="/cinemaland/film-gallery">Cinemaland</a>
                <a href="/actionland/thrill-rides">Actionland</a>
            </nav>
        </header>

        <section>
            <% content %>  <!-- This is where the view content gets inserted -->
        </section>

        <footer>
            <p>© 2024 Danceyland</p>
        </footer>
    </body>
    </html>

C<content> is a special variable for layouts; it is replaced by the
output from the C<template> keyword.

=head4 Route Implementation Using the Layout

If you have a separate layouts for park employees and park guests, you
can explicitly choose which layout should be used to wrap page content:

    get '/food-court/burger-stand' => sub {
        template items => {
            foods => \@foods,
            drinks => \@drinks,
        }, { layout => 'main' };
    };

    get '/cinemaland/change-film-reels' => sub {
        template films => { films => \@films }, { layout => 'employee' };
    };

    get '/actionland/thrill-rides' => sub {
        template rides => { rides => \@rides }, { layout => 'visitor' };
    };

In this example:

=over 4

=item *

Employees and guests both choose from the same menu, so in the food court,
they should be presented with the same standard page elements and options.

=item *

Guests of Cinemaland aren't required to change the film reels between
showings, but park employees are. By specifying a layout of C<employee.tt>,
they will get a special look and set of options appropriate to park
workers. The route renders a template (C<films.tt>) within that layout.

=item *

Park visitors interact with rides differently than employees. By
displaying the C<rides.tt> template within the C<visitor.tt> layout, we
can help guests board and get off rides with consistent messaging and
instructions.

=back

This helps maintain consistency across different pages in Danceyland by
reusing the layout and changing only the dynamic content for each route.

=head3 Default Layout

When no layout is explicitly specified in a route, Dancer2 will use the
C<main> layout as the default:

    get '/home' => sub {
        template 'homepage';
    };

In this case, Dancer2 renders C<homepage.tt> within the default layout
C<main.tt>.

=head3 Disabling Layouts

If you want to render a view without a layout, you can pass
C<< { layout => undef } >> to the C<template> keyword:

    get '/payment-options' => sub {
        template 'payment-options', { }, { layout => undef };
    };

This route renders the C<payment-options.tt> template without using the
default or any specified layout.

=head4 When should I use a layout, and when shouldn't I?

There are three different ways to return data from a Dancer2 app:

=over

=item *

Returning HTML content that represents the entire content of the page
(and allowing the browser to render it)

=item *

HTML content for only a portion of the page (using JS to insert that HTML
dynamically in the page, and the browser then takes over rendering it)

=item *

JSON/XML/etc. content that the JS decides what to do with, possibly
rendering some front-end template and updating the DOM nodes

=back

For option 1, you would use a layout to ensure a full HTML page is
returned. For option 2, you would disable the layout. In option 3,
you would serialize a data structure returned from a route.

The benefit of returning a part of a HTML page (option 2) is when:

=over

=item *

You don't want to render the entire page in the backend

=item *

You don't want to fetch all the data for the entire page (imagine menus,
headers, etc.)

=item *

You prefer to inject the HTML directly to an element, reducing front-end
complexity

=back

If you are building interfaces with toolkits like L<htmx|https://htmx.org/> or
L<Turbo|https://turbo.hotwired.dev/>, it's important to only return
a partial page.

=head2 Default template variables

=over

=item perl_version

The version of Perl that's running Danceyland. This aligns with Perl's
special C<$^V> variable.

=item dancer_version

The current version of Dancer2. Similar to C<< Dancer2->VERSION >>.

=item settings

This is a hash containing the configuration of Danceyland.

=item request

This represents the user's current request. Calling C<request> returns
a L<Dancer2::Core::Request> object.

=item params

C<params> provides a hash reference of all parameters passed to Danceyland.

=item vars

This is the list of variables for this request. It is the same thing you
would get if you used the C<vars> keyword in the application itself. We'll
learn more about C<vars> in the L</Configuration> section.

=item session

This variable contains all information in the user's session, should one
exist. We'll talk more about sessions in the next section.

=back

=head2 Configuring the Template Engine

You can configure the template engine in Dancer2 to customize aspects
such as the open and closing tags used for placeholders.

=head3 Changing Open and Closing Tags

In your configuration file (e.g., C<config.yml>), you can set the
C<start_tag> and C<stop_tag> options:

    template: "template_toolkit"
    engines:
      template:
        template_toolkit:
          start_tag: '[%'
          stop_tag: '%]'

Dancer2 defaults placeholder tags to C<< <% %> >>. The above configuration
restores the Template toolkit defaults of C<< [% %] >>. Your template
variables will now look like this:

    <h1>[% title %]</h1>
    <p>Welcome to Danceyland!</p>

Most template engines support this functionality, but some (such as
L<Dancer2::Template::Tiny>) do not.

=head2 Encoding in Templates

Dancer2 supports encoding for templates to ensure that the content is
correctly displayed, especially when dealing with non-ASCII characters:

    template: "template_toolkit"
    engines:
      template:
        template_toolkit:
          encoding: 'utf-8'

This configuration ensures that the templates are processed using UTF-8
encoding. This is particularly important if your templates contain
special characters, such as international text or symbols.

=head3 New Keywords

=over

=item * engine

At the heart of every attraction in Danceyland lies a powerful engine that
brings it to life. Similarly, in Dancer2, the C<engine> keyword provides
access to the current engine object, whether it's the template engine
rendering the views or the logger keeping track of events:

    my $template_engine = engine 'template';

=back

=head2 Comparison of Supported Template Engines

Dancer2 supports various template engines to give you flexibility in how
you render views. Here's a brief comparison of the available engines:

=over 4

=item Dancer2::Template::TemplateToolkit (L<Template> Toolkit, TT)

A powerful and flexible templating system with extensive features like
loops, conditions, macros, and plugins. Suitable for complex templating
needs.

=item Dancer2::Template::Mason (L<Mason>)

A component-based templating system that allows embedding Perl code
within templates. Ideal for complex web applications that require
reusable components.

=item Dancer2::Template::Xslate (L<Xslate>)

A high-performance templating engine that offers a mix of syntax styles.
Great for applications requiring speed and flexibility.

=item Dancer2::Template::TemplateFlute (L<Flute>)

A template engine focused on separating logic from presentation.
Templates contain no logic and are designer-friendly.

=item Dancer2::Template::Tiny (L<Template::Tiny>)

A minimalistic template engine with limited features. Suitable for small
projects or when you need a simple, lightweight option.

=item Dancer2::Template::Simple

A basic and straightforward templating engine. Not recommended unless you
are migrating applications from Dancer 1 built with L<Dancer::Template::Simple>.

=item Dancer2::Template::Mustache (L<Mustache:https://mustache.github.io/>, L<Dancer2::Template::Mustache>)

A logic-less template engine with a strong emphasis on separation of
logic and presentation. Great for consistent rendering across different
languages and platforms.

=item Dancer2::Template::Handlebars (L<Handlebars|>, L<Text::Handlebars>)

An extension of Mustache with additional features like helpers and more
expressive templates. Ideal for complex templates that still maintain a
clean and readable structure.

=item B<Dancer2::Template::Haml> (L<HAML|https://haml.info/>, L<Text::Haml>)

A clean and concise templating system that uses indentation to define
HTML elements. Perfect for those who prefer a more human-readable
approach to HTML templating.

=back

Choose a template engine based on the complexity and requirements of your
Danceyland application.

=head1 Sessions: Multi-Day Passes

Sessions are like multi-day or season passes for visitors. They allow
visitors to return to the park multiple times without having to buy a
new ticket each time.

=head2 The importance of state

HTTP and HTTPS are stateless protocols, meaning each request from a
client to a server is independent. The server processes each request
and responds, but it doesn’t remember past interactions. Therefore, each
new request must include all necessary information, as the server doesn’t
retain any “state” between requests — this is something developers must
implement if needed.

This lack of memory between requests is why web frameworks use B<session
management>. Sessions allow servers to remember users' actions across
multiple requests, creating a sense of continuity, or "state." To do
this, web frameworks often rely on B<cookies> — small pieces of data stored
in the browser. When a client connects, the server assigns a unique
session ID stored in a cookie, which the browser sends with each request.
This session ID allows the server to "remember" the user's previous
interactions, making it possible to build personalized and seamless web
experiences.

The following section explains how to manage sessions with Dancer2.

=head2 Managing visitor sessions

To create or update a session, use the C<session> keyword. Sessions store
data across multiple requests using a unique session ID.

    set session => 'Simple';

    get '/login' => sub {
        session user => 'John Doe';
        return "Welcome back, John Doe!";
    };

    get '/profile' => sub {
        my $user = session('user');
        return "User profile for: $user";
    };

=over 4

=item *

A session stores the user information once they log in.

=item *

The C</login> route initiates a session for the user.

=item *

The C</profile> route retrieves the user information from the session.

=back

=head3 New Keywords

=over 4

=item set

This keyword is used to configure settings for your application, like setting the session engine:

    set session => 'Simple';

=item session

This keyword is used to store and retrieve session data:

    session user => 'John Doe';  # Store data
    my $user_1 = session('user');  # Retrieve data
    my $user_2 = session->read('user');  # Also retrieves data

=back

=head3 Changing the Session ID

For security reasons, you might want to change the session ID, especially
after a user logs in. This can help prevent L<session fixation attacks|https://owasp.org/www-community/attacks/Session_fixation>.

    get '/secure-login' => sub {
        my $username = body_parameters->get( 'username' );
        session user => $username;
        app->change_session_id;  # Change the session ID
        return "Welcome back, $username!";
    };

=head3 Destroying Sessions

To log the user out and destroy the session, use the C<< app->destroy_session >>.

    get '/logout' => sub {
        app->destroy_session;
        return "You have been logged out.";
    };

=head2 Configuring the Session Engine

Dancer2 supports various session engines that determine how and where
session data is stored. You can configure the session engine in the
C<config.yml> file:

    session: "Simple"
    session_options:
      cookie_name: "danceyland_session"
      expires: 3600

This configuration uses the C<Simple> session engine, storing session
data in memory, and sets the session to expire in one hour.

=head2 Session Storage Backends

Dancer2 supports multiple session storage backends, allowing you to
choose how and where to store session data.

=over 4

=item Dancer2::Session::Simple

Stores session data in memory. Suitable for development and testing but
not recommended for production due to its non-persistent nature.

=item Dancer2::Session::YAML

Stores session data in a YAML file. It's useful for development and
debugging, but not well suited for production.

=item Dancer2::Session::JSON

Stores session data in a JSON file. Similar restrictions to the YAML
session engine.

=item Dancer2::Session::Cookie

Stores session data within the user's browser as a cookie. Data is not
stored server-side. Good for lightweight sessions.

=item Dancer2::Session::PSGI

Leverages PSGI's built-in session management. Useful if you want to
delegate session handling to the PSGI server, or if you are sharing
session data with non-Dancer2 applications.

=item Dancer2::Session::DBI

Stores session data in a database using L<DBI>. Ideal for larger
applications that require a robust and scalable storage solution.

=item Dancer2::Session::Redis

Stores session data in a Redis database. Suitable for high-performance,
distributed, and scalable session storage.

=item Dancer2::Session::Sereal

Uses Sereal for fast, compact session serialization. Suitable for
high-performance applications requiring efficient serialization.

=item Dancer2::Session::CHI

Uses the L<CHI> caching framework for session storage. Flexible and
supports various caching backends like memory, file, and databases.

=item Dancer2::Session::Memcached

Stores session data in a Memcached server. Suitable for distributed
environments requiring quick access to session data.

=item Dancer2::Session::MongoDB

Stores session data in a MongoDB database. Ideal for applications
requiring a NoSQL storage solution.

=back

Choose a session backend based on the complexity and storage needs of
your Danceyland application.

=head1 Cookies

Cookies are small pieces of data stored on the client's browser, not just
goodies available for purchase in Danceyland. They are often used to keep
track of session information and user preferences.

Sessions in Dancer2 use cookies to store the session ID on the client's
browser.

=head2 Setting and Updating Cookies

You can set a cookie using the C<cookie> keyword. To update a cookie,
simply set it again with a new value:

    get '/set-cookie' => sub {
        cookie 'visitor' => 'regular';
        return "Cookie has been set!";
    };

This route sets a cookie named C<visitor> with the value C<regular>.

=head2 Retrieving Cookies

To retrieve a cookie, use the C<cookie> keyword with the cookie's name:

    get '/get-cookie' => sub {
        my $visitor_type = cookie('visitor');
        return "Visitor type: $visitor_type";
    };

This route retrieves the value of the C<visitor> cookie.

To retrieve all cookies, use the C<cookies> keyword:

    get '/check-cookies' => sub {
        my $cookies = cookies;
        for my $name ( keys $cookies->%* ) {
            debug "You have a cookie named $name";
            # Do other stuff...
        }
    }

=head2 Deleting Cookies

To delete a cookie, set it with an expiration time in the past:

    get '/delete-cookie' => sub {
        cookie 'visitor' => 'expired', expires => '-1d';
        return "Cookie has been deleted.";
    };

This route deletes the C<visitor> cookie by setting its expiration date
to a past date.

=head1 Error Handling: Managing Ride Breakdowns and Other Park Issues

Just like managing ride breakdowns or out-of-stock concessions in a theme
park, error handling in a web application involves dealing with unexpected
issues gracefully.

=head2 Why should we fail gracefully?

Regardless if a problem is one we can control or not, not handling a problem

Whether or not a problem is one we can predict, handing errors in a poor
or disruptive way can really ruin a visitor's day at the park. It may not
be as rude as the pretzel vendor that yells at you for not leaving a tip,
but it can certainly sour a vistor for future visits.

Errors happen. Handling errors in a predictable, friendly manner can make
all the difference. Give visitors a friendly message that explains what
went wrong, and if you can still provide them with some limited park
functionality, then do so.

=head3 500 - the error of last resort

When a 500 error occurs, either it means the park operators didn't think
to check for a particular error, or something absolutely unpredictable
happened (a gust of wind knocked a tree branch across a park walkway).

When issuing a 500 error, try not to give away too much information (such
as a stack trace) so as to not give a potential attacker too much
information.

Try to catch as many things as you can. Sending a 500 error isn't the
end of park operations, but they should show up sparingly, at worst.

=head3 Handling Errors When a Ride is Closed

    get '/roller-coaster' => sub {
        status 'service_unavailable';
        return "Sorry, the roller coaster is currently closed for maintenance.";
    };

This route catches the request to the roller coaster when it's closed
and returns a friendly error message.

=head2 Error Pages

You can define custom error pages to provide a friendly message to your
users when something goes wrong.

To create a custom error page, place a template in the C<views> directory
named after the error code, like C<views/404.tt>:

    <h1>Oops! Page Not Found</h1>
    <p>The page you are looking for does not exist in Danceyland.</p>

This custom page will be displayed whenever a 404 error occurs.

=head2 New Keywords

=over 4

=item status

Status sets the HTTP status code that is returned by response from
Dancer2. It can be specified as either a number (C<status 404>), or
its lower-case name (C<status 'not_found'>).

=item halt

The C<halt> keyword immediately stops the processing of the current route
and sends a response back to the client:

    get '/restricted' => sub {
        halt "Access denied!" unless session('is_admin');
        return "Welcome to the restricted area.";
    };

In this example, if the visitor is not an admin, C<halt> stops execution
and returns C<Access denied!>.

=item send_error

The C<send_error> keyword sends an error response with a specific status
code:

    get '/data' => sub {
        my $data = fetch_data();
        send_error "Data not found", 404 unless $data;
        return $data;
    };

This example sends a 404 error response if the data is not found.

=back

=head1 Static Files and File Uploads

In Danceyland, we don't just offer thrilling rides; we also serve files!
Whether you’re providing a downloadable park map or processing guest
photo uploads, Dancer2 has you covered.

=head2 Serving Static Files from Dancer2

Sometimes you want to make certain information (in the form of files)
available to visitors without asking them to stand in line. Dancer2 makes
serving static files easy.

=head3 From a Directory

By default, Dancer2 serves static files from the C<public> directory in
your app. Just place your files (like images or PDFs) in the C<public>
folder, and they’ll be accessible at C</static/path>:

    # Access the file public/images/logo.png via
    http://danceyland.local/static/images/logo.png

=head3 Sending Static Files from a Route Handler

You can also serve static files programmatically from a route handler
using C<send_file>:

    get '/download-map' => sub {
        send_file 'park-map.pdf', content_type => 'application/pdf';
    };

Using C<send_file> immediately exits the current route.

=head3 When Might You Not Want to Serve Static Files?

While serving static files is convenient, there are times when you may
not want to do it directly:

=over 4

=item *

You want to offload static content delivery to a CDN (Content Delivery
Network) for performance.

=item *

You need fine-grained access control on your files.

=item *

Your static content is large or frequently changing, and you'd prefer a
more scalable solution.

=back

=head3 How to Disable Static File Serving

If you'd prefer not to serve static files from your Dancer2 app, you can
disable it in your C<config.yml> file:

    static: 0

Now, all file requests will be routed through your handlers or passed
to another service.

=head2 Using C<send_file> to Deliver Files from Memory

You’re not limited to files on disk! You can use C<send_file> to deliver
files created or stored in memory:

    get '/dynamic-file' => sub {
        my $data = "This is a dynamically generated file.";
        return send_file(\$data, content_type => 'text/plain', filename => 'dynamic.txt');
    };

In this example, we generate a file on-the-fly and send it back to the user.

=head2 Handling File Uploads

If guests are submitting photos or other files at Danceyland, you'll
need to handle file uploads. Dancer2 makes it easy with the C<upload> keyword:

    post '/upload-photo' => sub {
        my $upload = upload('photo');
        return "No file uploaded" unless $upload;

        my $filename = $upload->filename;
        my $filehandle = $upload->file_handle;
        open my $out, '>', "/uploads/$filename" or die "Failed to open: $!";
        while (<$filehandle>) {
            print $out $_;
        }
        close $out;

        return "Uploaded $filename successfully!";
    };

=head2 Working with Paths and Directories

Dancer2 provides keywords for navigating paths and directories, similar
to navigating Danceyland.

=head3 path

Navigating through Danceyland requires clear paths and an understanding
of the surroundings. In Dancer2, the C<path> keyword allows you to
create a path from several different directories:
    use Dancer2;

    get '/attraction/:name' => sub {
        my $current_path = path( '/the', 'road', 'less', 'traveled' );
        return "You're visiting: $current_path";
    };

=head3 dirname

Given a path through Danceyland, like above, C<dirname> will tell you the
directory you are located in:

    use Dancer2;

    get '/resource' => sub {
        my $file_path = '/path/to/danceyland/config.yml';
        my $directory = dirname($file_path);
        return "Config file is located in: $directory";
    };

=head2 Keywords Covered

=over

=item send_file

Used to send a file, either from disk or memory, to the client.

=item upload

Used to retrieve an uploaded file.

=back

=head1 Configuration, Startup, Initialization, Versioning

Danceyland needs its rides and attractions to work just right, and so
does your Dancer2 app. Configuration is key to ensuring your app runs
smoothly in every environment.

=head2 How Do Environments Help Us Configure Our Applications?

Dancer2 uses environments (such as C<development> or C<production>) to
customize configuration depending on where the app is running. This
allows you to tweak things like logging, error reporting, or even
database connections, based on your environment.

    environments:
      development:
        logger: "console"
        show_errors: 1
      production:
        logger: "file"
        show_errors: 0

All of your applications will use F<config.yml>, which is the base
configuration across B<all> of your application's environments. It is
here where you will configure such things as your template engine and
other extensions/settomgs that do not change across environments.

=head2 Using Environment-Specific Config Files

You can create environment-specific config files, like C<production.yml>
and C<development.yml>, in the F<environments> directory. Dancer2 will
load the environment-specific file after the main config, applying those
settings last.

You are not limited to the environment names used; different projects and
companies will have different requirements and different environments.

Applications are not limited to YAML-based configuration files. Dancer2
supports config files in any format supported by L<Config::Any>, including
JSON and Apache-style configs.

=head2 Config File Resolution Order

Dancer2 resolves configuration in the following order:

=over 4

=item *

C<config.yml>

=item *

Environment-specific config (e.g., C<environments/development.yml>)

=item *

Settings in the environment or explicitly set during runtime (using the
C<set> keyword)

=back

=head2 Accessing Configuration Information

You can access configuration values in your app using the C<config> keyword.

Example:

    get '/info' => sub {
        my $app_name = config->{'appname'};
        return "Welcome to $app_name!";
    };

=head2 Using C<set> Explicitly

You can explicitly set configuration values using the C<set> keyword.

Example:

    set session => 'Simple';
    set logger => 'console';

=head2 Plugin Configuration

You can configure plugins in your C<config.yml> file under the C<plugins> section.

Example:

    plugins:
      Database:
        driver: 'SQLite'
        database: 'danceyland.db'

=head2 Startup and Initialization

Some keywords can help prepare Danceyland to start operations in the morning,
while others offer you insider access to critical areas of the park. Let's
take a look at what some of these are,

=head3 Accessing the Application Object

Sometimes, you really have to get your hands dirty and go behind the scenes
of things at Danceyland. The C<app> keyword gives you access to the
application object (of type L<Dancer2::Core::App>) to change configuration
or perform certain functions. Another keyword, C<dancer_app>, also lets you
access the application object.

=head3 Running Code at Startup

The C<prepare_app> keyword takes a coderef that will be run once at the
start of your application. If there is any one-time setup that needs to
be performed (like starting up another service, connecting to a service,
initializing a cache, etc.), C<prepare_app> will give you a place to do
this.

For example:

    prepare_app {
        debug "Starting our morning prep work";
        $rides->power_on();
        $vendors->prep_food();
        debug "Open for business!";
    };

=head3 Starting the Application

The traditional way of kicking things off at Danceyland was to, well, dance!

    use Dancer2;

    get '/' => sub {
        return 'Welcome to Danceyland!';
    };

    dance;

As time passed and things changed, dancing wasn't always the right way
to start the day. C<to_app> became tbe better and preferred way to start
our Dancer2 applications. C<psgi_app> may also be used.

=head3 Versioning and Compatibility

As Danceyland introduces new attractions, it's essential to ensure they
align with the park's current standards. In the realm of Dancer2, you can
use C<dancer_version> to retrieve the full version number of your framework,
aiding in maintaining compatibility and leveraging the latest features:

    use Dancer2;
    return "Running on Dancer2 version " . dancer_version;

C<dancer_major_version> provides the major version of Dancer2, helping in
ensuring compatibility with plugins or external components:

    use Dancer2;
    return "Running on Dancer2 major version " . dancer_major_version;

=head2 New Keywords

=over 4

=item app

Returns the application object. App object is a L<Dancer2::Core::App>.

=item config

Retrieves configuration values.

=item dance

Starts the application. Not recommended.

=item dancer_app

Returns the application object. Synonym for C<app>.

=item dancer_major_version

Returns the major version of Dancer2.

=item dancer_version

Returns the full version number of Dancer2.

=item prepare_app

Runs a coderef at the start of your application only.

=item psgi_app

Synonym for C<to_app>.

=item set

Explicitly sets configuration options.

=item setting

Returns the value of the specified config setting.

=item to_app

Returns a coderef to your application, to be started by a Plack server.

=item var

Set and retrieve a temporary named value within your application.

=item vars

Returns a hashref or all temporary variables/values in your application.

=back

=head1 Logging

Just like monitoring the lines for the roller coasters, logging helps
you keep track of what’s happening in your Dancer2 app.

=head2 Why Should We Log?

Logging is important for tracking errors, performance, and overall
activity in your app. It helps you understand what’s going right (or
wrong) in Danceyland.

=head3 What Types of Things Might We Want to Log?

=over 4

=item *

Errors (e.g., a ride breaking down)

=item *

Important events (e.g., a new guest registering)

=item *

Debug information (e.g., the details of a request)

=back

=head2 Configuring Logging in Your Dancer2 App

You can configure logging in your C<config.yml> file:

    logger: "console"
    log: "debug"

This configuration sends logs to the console and logs all messages at
the C<debug> level or higher.

=head2 Logging Your Own Messages

You can log your own messages using the logging keywords:

    debug "This is a debug message.";
    info "A new user has registered.";
    warning "The ride is slowing down!";
    error "The roller coaster broke down!";

=head2 Link to Extending Section

For more information on extending the logger or creating custom loggers,
refer to the Extending section.

=head2 Keywords Covered

=over 4

=item debug

Logs a debug-level message.

=item info

Logs an info-level message.

=item warning

Logs a warning-level message.

=item error

Logs an error-level message.

=item log

Logs a message at the specified log level:

    log( debug => 'This is a debug message' );

=back

=head1 Testing

Testing is essential to ensure that Danceyland's attractions are safe
and fun — and the same goes for your Dancer2 app.

=head2 Why Test?

Testing helps you find bugs before your guests (users) do. It ensures
that your app behaves as expected, even when things get complicated. Tests
help ensure that you don't accidentally break existing functionality when
making changes to your application.

=head2 Example: Using C<Plack::Test>

C<Plack::Test> allows you to test your app’s behavior without spinning up
a web server:

    use Plack::Test;
    use HTTP::Request::Common;
    use MyApp;

    test_psgi app => MyApp->to_app, client => sub {
        my $cb = shift;
        my $res = $cb->(GET "/");
        like $res->content, qr/Welcome to Danceyland/, "Home page works!";
    };

=head2 Example: Using C<Test::WWW::Mechanize::PSGI>

C<Test::WWW::Mechanize::PSGI> provides a more user-friendly interface
for testing your app, making it easier to simulate interactions:

    use Test::WWW::Mechanize::PSGI;
    use MyApp;

    my $mech = Test::WWW::Mechanize::PSGI->new( app => MyApp->to_app );
    $mech->get_ok('/');
    $mech->content_contains('Welcome to Danceyland');

=head2 More Information

For more in-depth coverage of testing in Dancer2, check out L<Dancer2::Manual::Testing>.

=head1 Plugins

Danceyland is full of exciting rides and attractions, but sometimes you
need to add something extra—a little popcorn stand or a souvenir shop.
That’s where plugins come in!

=head2 What Are Plugins?

Plugins in Dancer2 are reusable modules that extend your app's
functionality. Whether it's connecting to a database, handling
authentication, or integrating with third-party services, plugins make
it easy to add features to your Dancer2 app without reinventing the
wheel.

=head2 Why Do We Need Plugins?

Plugins allow you to add functionality without having to reinvent the
wheel. Need authentication? Validation? Plugins are there to help you
focus on building the fun stuff, while they handle the heavy lifting.

Examples:

=over 4

=item Dancer2::Plugin::Auth::Tiny

L<Dancer2::Plugin::Auth::Tiny> provides a simple and easy way to protect
your routes with minimal authentication logic. For example, you can
restrict access to certain routes with this plugin.

=item Dancer2::Plugin::DataTransposeValidator

L<Dancer2::Plugin::DataTransposeValidator> allows you to validate
incoming data using L<Data::Transpose>, giving you flexibility when
handling forms and other user inputs.

=back

=head2 How to Write a Plugin?

Writing a plugin for Dancer2 is as simple as creating a module that hooks
into the Dancer2 app lifecycle. It’s a fun ride! For detailed guidance on
writing plugins, refer to L<Dancer2::Manual::Extending>.

=head1 Complete Guide to Keywords

Danceyland has its own lingo that makes it easy to construct and
interact with your park, and with other developers working on the park.
Dancer2 provides you with a DSL (Domain-Specific Language) which makes
it easy to implement, maintain, and extend your applications. You've
already learned many of these keywords, but there are additional ones
that provide even greater functionality.

See L<the keyword guide|Dancer2::Manual::Keywords> for a complete list of
keywords provided by Dancer2.

=head1 Advanced Topics

Danceyland isn’t just for beginners—there’s always more to explore. Here
are some advanced features of Dancer2 that will take your app to the
next level.

=head2 Composing an App with C<appname>

In a theme park as vast as Danceyland, sometimes you need different
sections, like the Magical Kingdom or Actionland. Similarly, Dancer2
allows you to build separate applications, but unite them together under
a single name using the C<appname> keyword, so each section can have its
own unique routing and logic.

=head3 Example: Composing multiple apps with C<appname> into one app

Using C<appname>, you can compose different apps within one big web app.
Each package can have its own Perl namespace but uses the same Dancer2
namespace, so the routes are all available in the one application.

    # In file MagicApp.pm
    package MagicApp;
    use Dancer2 appname => 'MainApp';

    get '/wand-shop' => sub {
        return "Welcome to the Wand Shop in the Magic Kingdom!";
    };

    # In file ActionApp.pm
    package ActionApp;
    use Dancer2 appname => 'MainApp';

    get '/thrill-rides' => sub {
        return "Ready for the thrill rides in Actionland?";
    };

    # in MainApp.pm
    package MainApp;
    use Dancer2;
    use MagicApp;
    use ActionApp;

    # In the handler
    use MainApp;
    MainApp->to_app();

In this example, the C<MagicApp> package defines routes for the Magical
Kingdom, and the C<ActionApp> package defines routes for Actionland -
both using C<< appname => 'MainApp' >>.

=head2 Manually Adjusting MIME settings

In Danceyland, each attraction offers a unique experience, much like how
different content types are handled in your application. The C<mime>
keyword provides access to your applicaion's MIME processing object,
L<Dancer2::Core::MIME>:

    use Dancer2;

    get '/file/:name' => sub {
        my $filename = route_parameters->get('name');
        my $file_path = "/path/to/files/$filename";
        my $content_type = mime->for_file($filename);
        send_file($file_path, content_type => $content_type);
    };

=head2 Adding Additional Response Headers

In Danceyland, enhancing visitor experience often involves adding new
signposts without replacing existing ones. Similarly, in Dancer2, the
C<push_response_header> keyword allows you to add new values to an
existing response header without overwriting its current content. This
is particularly useful for headers like C<Set-Cookie>, where multiple
values might be necessary:

    use Dancer2;

    get '/set_cookies' => sub {
        push_response_header 'Set-Cookie' => 'user=alice';
        push_response_header 'Set-Cookie' => 'theme=light';
        return "Cookies have been set.";
    };

In this example, two cookies are added to the response without overwriting
each other.

=head2 Automatic Serializing and Deserializing Data

In Danceyland, sometimes we need to provide data to our visitors in
different formats, like JSON, YAML, or XML. Dancer2 makes serializing
data a breeze. You can even configure Dancer2 to automatically serialize
your responses based on the content type requested by the client.

=head3 Example: Serializing JSON for output

    set serializer => 'JSON';

    get '/ride-info' => sub {
        return { name => 'Roller Coaster', status => 'Open' };
    };

With this setup, the response will automatically be serialized to JSON
when requested.

=head3 Example: Deserializing JSON from input

Dancer2 can also deserialize incoming requests automatically when a
serializer is enabled. If the client sends JSON data, it will be
automatically parsed.

    post '/update-ride' => sub {
        my $data = request->data;  # Automatically deserialized JSON input
        return "Ride updated: " . $data->{name};
    };

Here, the incoming request body is JSON, and Dancer2 deserializes it
into a Perl data structure that you can access through C<< request->data >>.

=head2 Manual Serializing and Deserializing

Dancer2 also provides manual serialization methods for specific use cases
where automatic serialization isn't enough, or can't be used.

=head3 C<send_as>

C<send_as> allows you to manually serialize your response in a specific
format like JSON or YAML. This is useful when you need explicit control
over the response format:

    get '/info' => sub {
        return send_as JSON => { name => 'Danceyland', status => 'open' };
    };

Here, C<send_as> explicitly returns the response as JSON.

=head3 C<encode_json>

In managing Danceyland, park data comes and goes in various formats. Dancer2
provides tools to handle these formats efficiently. The C<encode_json>
keyword allows you to serialize a Perl data structure into a JSON string:

    use Dancer2;

    get '/data' => sub {
        my $data = { attraction => 'Roller Coaster', status => 'open' };
        return encode_json($data);
    };

C<encode_json> automatically performs UTF-8 encoding of the data for you.

Since you are manually serializing the response data, the serialization
hooks provided by Dancer2 are not called. See L</Hooks> for more information.

=head3 C<to_json>

C<to_json> converts a Perl data structure to a JSON string. It's useful
when you want to manually prepare JSON data before sending it:

    my $json_string = to_json({ name => 'Danceyland', status => 'open' });

You likely want to use C<encode_json> instead, since C<to_json> does not
do any data encoding.

=head3 C<decode_json>

You can decode incoming JSON payloads from the client with the C<decode_json>
keyword; it deserializes a JSON string into a Perl data structure:

    use Dancer2;

    post '/update' => sub {
        my $json_data = request->body;
        my $data = decode_json($json_data);
        # Now, do something with it...
    };

Since you are manually deserializing the incoming JSON, the serialization
hooks will not run.

=head3 C<from_json>

C<from_json> also decodes JSON to a Perl data structure, but it does not
deal with UTF-8 encoding. You likely want C<decode_json> instead

=head3 C<to_yaml>

C<to_yaml> takes a Perl datastructure and converts it to a YAML string.

=head3 C<from_yaml>

Similarly, C<from_yaml> lets you deserialize a YAML string into a Perl
data structure:

    use Dancer2;

    get '/config' => sub {
        my $yaml_data = request->body;
        my $data = from_yaml($yaml_data);
        # Now, do something with $data...
    };

=head3 C<to_dumper>

Deserializes a string to a L<Data::Dumper>-compatible data structure.

=head3 C<from_dumper>

Takes a data structure produced from L<Data::Dumper> and serializes it
to a string.

=head2 Hooks

Hooks are like special backstage passes at Danceyland. They give you
special access by allowing you to execute code at specific points in the
request/response cycle. You can use them to modify requests, responses,
or even add your own logic before or after routes are processed.

=head3 Using Hooks with C<vars>

Sometimes you want to share information across routes. You can use hooks
to store data using C<vars>, making it available for later routes:

    hook before => sub {
        my $request = request;
        vars->{visitor_id} = "VIP-" . $request->address;
    };

    get '/vip-area' => sub {
        return "Welcome, " . vars->{visitor_id} . "!";
    };

In this example, we store the visitor’s ID (based on their IP address)
in the C<vars> keyword during the C<before> hook. That value is later
used in the C</vip-area> route to personalize the response.

=head3 Request Hooks

At the ticket booth just inside the entrance of Danceyland, the friendly
gatekeeper scans every visitor's pass and gives them a sticker as they pass
into the park:

    hook before => sub {
        var sticker => 1;
    };

The C<before> hook runs before any other code just before each request is
handled. In this example, we set a var, C<sticker>, that can be used later
in the request.

As each visitor leaves, a park employee stamps each person's hand so they
can be readmitted later in the day:

    hook after => sub {
        my $user = var user;
        $user->stamp_hand;
    };

For this example, An object representing a user was stored in a var earlier
in the request. On the user's way out (via the C<after> hook), their hand
gets stamped, and could be checked the next time the user enters the park
to skip buying another ticket.

=head3 Template Hooks

At Danceyland’s Caricature Corner, the brilliant artist B<Picasso McGlitterpants>
creates a sketch of every guest who stops by. The caricature goes through
several phases before it’s ready to hang on your wall:

=over

=item * Before the sketch is drawn: Picasso sharpens their pencils

=item * While drawing: the artist sneaks in little embellishments

=item * When the sketch is done: Picasso proudly signs their masterpiece

=item * Before handing it to you: the park staff pops it into a fancy frame

=back

Before the sketching starts, Picasso ensures all the materials are in order:

    hook before_template_render => sub {
        my $tokens = shift;

        # Picasso McGlitterpants sharpens their pencils and sets up the
        # paper before drawing begins.
        $tokens->{materials_ready} = 1;
    };

Template hooks allow you to manipulate data before or after a view
(i.e., template) is rendered. C<materials_ready> is added as a template
token before the view gets rendered. That token is made available in the
template as C<[% materials_ready %]>.

When the sketch is finished, Picasso adds their signature (with extra glitter,
of course) to make sure the caricature is authentic:

    hook after_template_render => sub {
        my $content = shift;
        $$content .= "\n\n-- Signed by Picasso McGlitterpants 🎨✨";
    };

This happens after the view/template is rendered, but before it is shown
to the user.

Before the sketch is mounted, the artist sprinkles it with sparkles, making
sure the presentation twinkles with Danceyland charm:

    hook before_layout_render => sub {
        my ($tokens, $content) = @_;

        $tokens->{special_effects} = 'sparkles';
    };

Layout hooks apply after views are rendered, when the layout is going
to be applied. The C<before_layout_render> hook is run prior to the layout
being applied. It's a useful place to check for things such as whether or
not is authorized to view select content.

At the very end, just before the guest receives their caricature, the staff pops
it into a silvery frame so it’s ready to hang at home:

    hook after_layout_render => sub {
        my $content = shift;

        $$content = '<div class="silver-frame">' . $$content . "</div>";
    };

The C<after_layout_render> hook is the last chance to alter content before
being sent to the browser.

=head3 Error Handling Hooks

Even in Danceyland, sometimes the roller coaster gets stuck at the top, or
the cotton candy machine makes a little too much fluff. That’s when our
friendly park attendants step in!

Error hooks let you step in whenever something goes wrong in an application.
Just like Danceyland staff rushing over with a smile and a toolkit, your
error hook can log what happened, show a helpful message, or gently guide
the guest to the right exit.

    hook on_route_exception => sub {
        my ($error) = @_;
        warning "Picasso McGlitterpants spilled glitter on the server: $error";
    };

Or you can catch a general error and provide your own response:

    hook before_error => sub {
        my ($error) = @_;
        $error->message("Oops! One of the rides jammed. " .
            "Please enjoy a free churro while we fix it.");
    };

Other error hooks include:

=over

=item * init_error

This runs right after a new L<Dancer2::Core::Error> object is built. The new
error object is passed to the hook as an argument.

=item * after_error

This hook is called right after error is thrown, and receives a
L<Dancer2::Core::Response> object as an argument.

=item * on_hook_exception

This hook is special, and is only called when an exception is caught in
another hook, just before logging it and rethrowing it higher.

This hook receives as arguments:

=over

=item * A L<Dancer2::Core::App> object

=item * The error string

=item * The name of the hook where the exception occurred

The hook name is the full hook name (e.g. C<core.app.route_exception>).

=back

If the function provided to C<on_hook_exception> causes an exception itself, then
this will be ignored (thus preventing a potentially recursive situation).
However, it is still possible for the function to set a custom response and
halt it (and optionally die), thus providing a method to render custom content
in the event of an exception in a hook:

    hook on_hook_exception => sub {
        my ($app, $error, $hook_name) = @_;
        $app->response->content("Oh noes! The popcorn machine " .
            "overheated and overflowed!");
        $app->response->halt;
    };

=back

Think of error hooks as the ever-present Danceyland helpers: they
make sure that when a mishap occurs, guests are cared for and can
keep smiling on their way to the next attraction.

=head3 File Rendering Hooks

File rendering hooks are triggered when static files are served:

    hook before_file_render => sub {
        my $file_path = shift;
        debug "Starting to serve file: $file_path";
    };

    hook after_file_render => sub {
        my $response = shift;
        debug "Finished serving file.";
    };

=head3 Logging Hooks

Logging hooks can be used to give additional information or context to
Danceyland maintenance workers when something breaks down, or to ride
operators to help ensure smooth operation of rides:

    hook 'engine.logger.before' => sub {
        my ( $logger, $level, @messages ) = @_;
        push @messages, 'Request ID: ' . vars->{request_id};
    };

    hook 'engine.logger.after' => sub {
        my ( $logger, $level, @messages ) = @_;
        if( $level eq 'error' ) {
            # Add code to send an email here
        }
    };

These hooks are useful when you want to prepend or append information to
a log message, write messages out in a specific format, or take additional
action based on message severity.

=head3 Serializer Hooks

Every great visit to Danceyland ends with a souvenir! Serializer hooks
are the way Danceyland decides how to wrap up your experience before
sending you home. Maybe you get your caricature rolled up in a tube,
maybe it’s slipped into a glossy folder, or maybe Picasso McGlitterpants
mails it to you as a postcard.

That’s what serializers do: they take the "thing" (your response)
and package it in the right format (JSON, YAML, plain text, etc.)
so you can carry it with you outside the park.

For example:

    set serializer => 'JSON';

Now, every souvenir is neatly wrapped in shiny JSON paper.

And if you want a custom wrapper, you can hook into the
serializer process yourself:

    hook before_serializer => sub {
        my ($content) = @_;
        debug "Preparing to wrap up: $content";
    };

    hook after_serializer => sub {
        my ($serialized) = @_;
        debug "Souvenir has been wrapped: $serialized";
    };

So whenever you see serializer hooks, think of the Danceyland
souvenir shop — the place that makes sure you don’t just have
a memory, but also a neatly wrapped keepsake to take home.

=head2 Handlers

Handlers are the rides that keep Danceyland running. Dancer2 provides
several built-in handlers to manage files, pages, and routes. You can
also create your own handlers to extend the park’s offerings.

=head2 File Handler

The L<Dancer2::Handler::File> component is used to serve static files,
like images and stylesheets, from the C<public> directory. It comes with
two hooks; see the L</Hooks> section for more information.

=head2 Auto Page Handler

When a template matches a request, the L<Dancer2::Handler::AutoPage>
component is used. It automatically renders a template if one exists for
the requested path.

If someone visits C</about> and there is a template called C<about.tt>,
the auto page handler will automatically render that template.

=head2 Writing Your Own Handlers

You can create your own handler by consuming the L<Dancer2::Core::Role::Handler>
role. This allows you to define your own logic for processing routes:

    package MyHandler;
    use Moo;
    with 'Dancer2::Core::Role::Handler';

    sub methods { qw(GET) }

    sub regexp { '/custom/:page' }

    sub code {
        return sub {
            my $app = shift;
            my $page = $app->request->params->{page};
            return "You requested the custom page: $page";
        };
    }

    sub register {
        my ($self, $app) = @_;
        $app->add_route(
            method => $_,
            regexp => $self->regexp,
            code   => $self->code,
        ) for $self->methods;
    }

=head2 Middleware

Middleware in Dancer2 is like the behind-the-scenes magic at Danceyland,
like security or cleaning staff at Danceyland. It handles tasks between
the request and response cycles, allowing you to modify or inspect the
request/response without changing your app code.

You can easily add middleware using Plack, Dancer2’s underlying engine.

=head2 Why Use Middleware Instead of App Logic?

You could, in theory, implement directly in your code everything that a
middleware does. So why use middlewares? Middlewares allow you to modify
the request/response cycle without changing your app’s core code. It’s
useful for:

=over 4

=item *

Managing sessions and cookies.

=item *

Handling authentication.

=item *

Compressing responses or managing headers.

=back

Middleware operates in-between the client’s request and the app’s
response, providing flexibility and reducing duplication in your
application logic.

Additionally, you can use middlewares across different code-bases. Given
best practices, they would not be tied to a particular web application.

=head3 Adding Middlewares

    builder {
        enable 'Session';
        enable 'Static', path => qr{^/(images|css)/}, root => './public';
        app;
    };

This example uses Plack middleware to manage sessions and serve static files.

=head2 Asynchronous Programming

In Danceyland, we never want our visitors to wait in line! Sometimes, a
task can take a long time, like fetching data from an external service.
Asynchronous programming allows you to handle these tasks in the
background, keeping your app responsive.

Dancer2 supports asynchronous programming to help you manage I/O-bound
operations more efficiently.

=head2 Why Choose Async Over Blocking?

With async programming, you can handle long-running tasks without
blocking the rest of your app. This is useful for:

=over 4

=item *

Fetching data from an external API.

=item *

Handling large uploads or downloads.

=item *

Sending notifications or emails.

=back

=head2 Choosing the Right PSGI Server

To use asynchronous programming in Dancer2, your app must be running on
a PSGI server that supports event loops, like L<Twiggy>. Such servers can
handle async tasks, unlike traditional PSGI servers that utilize forking
to handle multiple parallel tasks.

=head2 Sending Content Asynchronously

Dancer2 provides keywords for implementing asynchronous code.

=head3 content

The C<content> keyword sets the response content from within a delayed
response.

=head3 delayed

Some events at Danceyland, like the grand parade, are worth watching but
take some time to complete. Similarly, Dancer2 offers the C<delayed> keyword
to initiate an asynchronous response, allowing you to deliver long-running
results, or handling long-running operations.

=head2 done

Once everything is set, you can use C<done> to finalize and send the
response to the visitor.

=head2 flush

To send parts of the response incrementally, C<flush> allows streaming
content to the client in a delayed response without closing the connection.

=head2 Example: Asynchronous HTTP Request

Here’s how you can fetch data asynchronously in Dancer2. Instead of
waiting for a response, the request runs in the background and delivers
the result when it’s ready:

    use Dancer2;
    use Future::HTTP;

    get '/fetch-ride-status' => sub {
        delayed {
            content 'The grand parade is starting!';
            flush;

            http_get('http://parade-status.com/api/v1/floats')->then( sub {
                my ($body, $headers) = @_;
                content $body;
                flush;
            });

            content 'The grand parade is finished!';
            done;
        };
    };

In this example, we fetch the status of a ride asynchronously using
C<http_get>. The C<delayed> keyword defers the response until the
background request completes. The C<$resume> callback is used to send
the content back to the visitor once the request finishes.

For a deeper dive, check out these fantastic articles on async programming
in Dancer2: L<https://advent.perldancer.org/2020/22> and
L<https://advent.perldancer.org/2020/23>.

=head1 AUTHOR

Dancer Core Developers

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2025 by Alexis Sukrieh.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut