1Dancer2::Manual::MigratUisoenr(3C)ontributed Perl DocumeDnatnacteiro2n::Manual::Migration(3)
2
3
4
6 Dancer2::Manual::Migration - Migrating from Dancer to Dancer2
7
9 version 0.400001
10
12 This document covers some changes that users will need to be aware of
13 while upgrading from Dancer (version 1) to Dancer2.
14
15 Launcher script
16 The default launcher script bin/app.pl in Dancer looked like this:
17
18 #!/usr/bin/env perl
19 use Dancer;
20 use MyApp;
21 dance;
22
23 In Dancer2 it is available as bin/app.psgi and looks like this:
24
25 #!/usr/bin/env perl
26
27 use strict;
28 use warnings;
29 use FindBin;
30 use lib "$FindBin::Bin/../lib";
31
32 use MyApp;
33 MyApp->to_app;
34
35 So you need to remove the "use Dancer;" part, replace the "dance;"
36 command by "MyApp->to_app;" (where MyApp is the name of your
37 application), and add the following lines:
38
39 use strict;
40 use warnings;
41 use FindBin;
42 use lib "$FindBin::Bin/../lib";
43
44 There is a Dancer Advent Calendar <http://advent.perldancer.org>
45 article covering the "to_app" keyword
46 <http://advent.perldancer.org/2014/9> and its usage.
47
48 Configuration
49 You specify a different location to the directory used for serving
50 static (public) content by setting the "public_dir" option. In that
51 case, you have to set "static_handler" option also.
52
53 Apps
54 1. In Dancer2, each module is a separate application with its own
55 namespace and variables. You can set the application name in each of
56 your Dancer2 application modules. Different modules can be tied into
57 the same app by setting the application name to the same value.
58
59 For example, to set the appname directive explicitly:
60
61 "MyApp":
62
63 package MyApp;
64 use Dancer2;
65 use MyApp::Admin
66
67 hook before => sub {
68 var db => 'Users';
69 };
70
71 get '/' => sub {...};
72
73 1;
74
75 "MyApp::Admin":
76
77 package MyApp::Admin;
78 use Dancer2 appname => 'MyApp';
79
80 # use a lexical prefix so we don't override it globally
81 prefix '/admin' => sub {
82 get '/' => sub {...};
83 };
84
85 1;
86
87 Without the appname directive, "MyApp::Admin" would not have access to
88 variable "db". In fact, when accessing "/admin", the before hook would
89 not be executed.
90
91 See Dancer2::Cookbook
92 <https://metacpan.org/pod/Dancer2::Cookbook#Using-the-prefix-feature-
93 to-split-your-application> for details.
94
95 2. To speed up an app in Dancer2, install the recommended modules
96 listed in the "Performance Improvements" in Dancer2::Manual::Deployment
97 section.
98
99 Request
100 The request object (Dancer2::Core::Request) is now deferring much of
101 its code to Plack::Request to be consistent with the known interface to
102 PSGI requests.
103
104 Currently the following attributes pass directly to Plack::Request:
105
106 "address", "remote_host", "protocol", "port", "method", "user",
107 "request_uri", "script_name", "content_length", "content_type",
108 "content_encoding", "referer", and "user_agent".
109
110 If previous attributes returned undef for no value beforehand, they
111 will return whatever Plack::Request defines now, which just might be an
112 empty list.
113
114 For example:
115
116 my %data = (
117 referer => request->referer,
118 user_agent => request->user_agent,
119 );
120
121 should be replaced by:
122
123 my %data = (
124 referer => request->referer || '',
125 user_agent => request->user_agent || '',
126 );
127
128 Plugins: plugin_setting
129 "plugin_setting" returns the configuration of the plugin. It can only
130 be called in "register" or "on_plugin_import".
131
132 Routes
133 Dancer2 requires all routes defined via a string to begin with a
134 leading slash "/".
135
136 For example:
137
138 get '0' => sub {
139 return "not gonna fly";
140 };
141
142 would return an error. The correct way to write this would be to use
143 "get '/0'"
144
145 Route parameters
146 The "params" keyword which provides merged parameters used to allow
147 body parameters to override route parameters. Now route parameters take
148 precedence over query parameters and body parameters.
149
150 We have introduced "route_parameters" to retrieve parameter values from
151 the route matching. Please refer to Dancer2::Manual for more
152 information.
153
154 Tests
155 Dancer2 recommends the use of Plack::Test.
156
157 For example:
158
159 use strict;
160 use warnings;
161 use Test::More tests => 2;
162 use Plack::Test;
163 use HTTP::Request::Common;
164
165 {
166 package App::Test; # or whatever you want to call it
167 get '/' => sub { template 'index' };
168 }
169
170 my $test = Plack::Test->create( App::Test->to_app );
171 my $res = $test->request( GET '/' );
172
173 ok( $res->is_success, '[GET /] Successful' );
174 like( $res->content, qr{<title>Test2</title>}, 'Correct title' );
175
176 Other modules that could be used for testing are:
177
178 • Test::TCP
179
180 • Test::WWW::Mechanize::PSGI
181
182 Logs
183
184 The "logger_format" in the Logger role (Dancer2::Core::Role::Logger) is
185 now "log_format".
186
187 "read_logs" can no longer be used, as with Dancer2::Test. Instead,
188 Dancer2::Logger::Capture could be used for testing, to capture all logs
189 to an object.
190
191 For example:
192
193 use strict;
194 use warnings;
195 use Test::More import => ['!pass'];
196 use Plack::Test;
197 use HTTP::Request::Common;
198 use Ref::Util qw<is_coderef>;
199
200 {
201 package App;
202 use Dancer2;
203
204 set log => 'debug';
205 set logger => 'capture';
206
207 get '/' => sub {
208 debug 'this is my debug message';
209 return 1;
210 };
211 }
212
213 my $app = Dancer2->psgi_app;
214 ok( is_coderef($app), 'Got app' );
215
216 test_psgi $app, sub {
217 my $cb = shift;
218
219 my $res = $cb->( GET '/' );
220 is $res->code, 200;
221
222 my $trap = App->dancer_app->logger_engine->trapper;
223
224 is_deeply $trap->read, [
225 { level => 'debug', message => 'this is my debug message' }
226 ];
227 };
228
229 Exports: Tags
230 The following tags are not needed in Dancer2:
231
232 use Dancer2 qw(:syntax);
233 use Dancer2 qw(:tests);
234 use Dancer2 qw(:script);
235
236 The "plackup" command should be used instead. It provides a development
237 server and reads the configuration options in your command line
238 utilities.
239
240 Engines
241 • Engines receive a logging callback
242
243 Engines now receive a logging callback named "log_cb". Engines can
244 use it to log anything in run-time, without having to worry about
245 what logging engine is used.
246
247 This is provided as a callback because the logger might be changed
248 in run-time and we want engines to be able to always reach the
249 current one without having a reference back to the core application
250 object.
251
252 The logger engine doesn't have the attribute since it is the logger
253 itself.
254
255 • Engines handle encoding consistently
256
257 All engines are now expected to handle encoding on their own. User
258 code is expected to be in internal Perl representation.
259
260 Therefore, all serializers, for example, should deserialize to the
261 Perl representation. Templates, in turn, encode to UTF-8 if
262 requested by the user, or by default.
263
264 One side-effect of this is that "from_yaml" will call YAML's "Load"
265 function with decoded input.
266
267 Templating engine changes
268
269 Whereas in Dancer1, the following were equivalent for
270 Template::Toolkit:
271
272 template 'foo/bar'
273 template '/foo/bar'
274
275 In Dancer2, when using Dancer2::Template::TemplateToolkit, the version
276 with the leading slash will try to locate "/foo/bar" relative to your
277 filesystem root, not relative to your Dancer application directory.
278
279 The Dancer2::Template::Simple engine is unchanged in this respect.
280
281 Whereas in Dancer1, template engines have the methods:
282
283 $template_engine->view('foo.tt')
284 $template_engine->view_exists('foo.tt')
285
286 In Dancer2, you should instead write:
287
288 $template_engine->view_pathname('foo.tt')
289 $template_engine->pathname_exists($full_path)
290
291 You may not need these unless you are writing a templating engine.
292
293 Serializers
294
295 You no longer need to implement the "loaded" method. It is simply
296 unnecessary.
297
298 Sessions
299
300 Now the Simple session engine is turned on by default, unless you
301 specify a different one.
302
303 Configuration
304 "public_dir"
305
306 You cannot set the public directory with "setting" now. Instead you
307 will need to call "config":
308
309 # before
310 setting( 'public_dir', 'new_path/' );
311
312 # after
313 config->{'public_dir'} = 'new_path';
314
315 warnings
316
317 The "warnings" configuration option, along with the environment
318 variable "DANCER_WARNINGS", have been removed and have no effect
319 whatsoever.
320
321 They were added when someone requested to be able to load Dancer
322 without the warnings pragma, which it adds, just like Moose, Moo, and
323 other modules provide.
324
325 If you want this to happen now (which you probably shouldn't be doing),
326 you can always control it lexically:
327
328 use Dancer2;
329 no warnings;
330
331 You can also use Dancer2 within a narrower scope:
332
333 { use Dancer2 }
334 use strict;
335 # warnings are not turned on
336
337 However, having warnings turned it is very recommended.
338
339 server_tokens
340
341 The configuration "server_tokens" has been introduced in the reverse
342 (but more sensible, and Plack-compatible) form as "no_server_tokens".
343
344 "DANCER_SERVER_TOKENS" changed to "DANCER_NO_SERVER_TOKENS".
345
346 engines
347
348 If you want to use Template::Toolkit instead of the built-in simple
349 templating engine you used to enable the following line in the
350 config.yml file.
351
352 template: "template_toolkit"
353
354 That was enough to get started. The start_tag and end_tag it used were
355 the same as in the simple template <% and %> respectively.
356
357 If you wanted to further customize the Template::Toolkit you could also
358 enable or add the following:
359
360 engines:
361 template_toolkit:
362 encoding: 'utf8'
363 start_tag: '[%'
364 end_tag: '%]'
365
366 In Dancer 2 you can also enable Template::Toolkit with the same
367 configuration option:
368
369 template: "template_toolkit"
370
371 But the default start_tag and end_tag are now [% and %], so if you used
372 the default in Dancer 1 now you will have to explicitly change the
373 start_tag and end_tag values. The configuration also got an extra
374 level of depth. Under the "engine" key there is a "template" key and
375 the "template_toolkit" key comes below that. As in this example:
376
377 engines:
378 template:
379 template_toolkit:
380 start_tag: '<%'
381 end_tag: '%>'
382
383 In a nutshell, if you used to have
384
385 template: "template_toolkit"
386
387 You need to replace it with
388
389 template: "template_toolkit"
390 engines:
391 template:
392 template_toolkit:
393 start_tag: '<%'
394 end_tag: '%>'
395
396 Session engine
397
398 The session engine is configured in the "engine" section.
399
400 • "session_name" changed to "cookie_name".
401
402 • "session_domain" changed to "cookie_domain".
403
404 • "session_expires" changed to "cookie_duration".
405
406 • "session_secure" changed to "is_secure".
407
408 • "session_is_http_only" changed to "is_http_only".
409
410 Dancer2 also adds two attributes for session:
411
412 • "cookie_path" The path of the cookie to create for storing the
413 session key. Defaults to "/".
414
415 • "session_duration" Duration in seconds before sessions should
416 expire, regardless of cookie expiration. If set, then
417 SessionFactories should use this to enforce a limit on session
418 validity.
419
420 See Dancer2::Core::Role::SessionFactory for more detailed documentation
421 for these options, or the particular session engine for other supported
422 options.
423
424 session: Simple
425
426 engines:
427 session:
428 Simple:
429 cookie_name: dance.set
430 cookie_duration: '24 hours'
431 is_secure: 1
432 is_http_only: 1
433
434 Plack Middleware
435
436 In Dancer1 you could set up Plack Middleware using a
437 "plack_middlewares" key in your "config.yml" file. Under Dancer2 you
438 will instead need to invoke middleware using Plack::Builder, as
439 demonstrated in Dancer2::Manual::Deployment.
440
441 Keywords
442 Calling Keywords Explicitly
443
444 In Dancer1, keywords could be imported individually into a package:
445
446 package MyApp;
447 use Dancer qw< get post params session >;
448
449 get '/foo' { ... };
450
451 Any keywords you did't export could be called explicitly:
452
453 package MyApp;
454 use Dancer qw< get post params session >;
455 use List::Util qw< any >;
456
457 Dancer::any sub { ... };
458
459 Dancer2's DSL is implemented differently. Keywords only exist in the
460 namespace of the package which "use"s Dancer2, i.e. there is no
461 "Dancer2::any", only e.g. "MyApp::any".
462
463 If you only want individual keywords, you can write a shim as follows:
464
465 package MyApp::DSL;
466 use Dancer2 appname => 'MyApp';
467
468 use Exporter qw< import >;
469
470 our @EXPORT = qw< get post ... >
471
472 Then in other packages:
473
474 package MyApp;
475
476 use MyApp::DSL qw< get post >;
477
478 MyApp::DSL::any sub { ... };
479
480 appdir
481
482 This keyword does not exist in Dancer2. However, the same information
483 can be found in "config->{'appdir'}".
484
485 load
486
487 This keyword is no longer required. Dancer2 loads the environment
488 automatically and will not reload any other environment when called
489 with load. (It's a good thing.)
490
491 param_array
492
493 This keyword doesn't exist in Dancer2.
494
495 session
496
497 In Dancer a session was created and a cookie was sent just by rendering
498 a page using the "template" function. In Dancer2 one needs to actually
499 set a value in a session object using the "session" function in order
500 to create the session and send the cookie.
501
502 The session keyword has multiple states:
503
504 • No arguments
505
506 Without any arguments, the session keyword returns a
507 Dancer2::Core::Session object, which has methods for read, write,
508 and delete.
509
510 my $session = session;
511 $session->read($key);
512 $session->write( $key => $value );
513 $session->delete($key);
514
515 • Single argument (key)
516
517 If a single argument is provided, it is treated as the key, and it
518 will retrieve the value for it.
519
520 my $value = session $key;
521
522 • Two arguments (key, value)
523
524 If two arguments are provided, they are treated as a key and a
525 value, in which case the session will assign the value to the key.
526
527 session $key => $value;
528
529 • Two arguments (key, undef)
530
531 If two arguments are provided, but the second is undef, the key
532 will be deleted from the session.
533
534 session $key => undef;
535
536 In Dancer 1 it wasn't possible to delete a key, but in Dancer2 we can
537 finally delete:
538
539 # these two are equivalent
540 session $key => undef;
541
542 my $session = session;
543 $session->delete($key);
544
545 You can retrieve the whole session hash with the "data" method:
546
547 $session->data;
548
549 To destroy a session, instead of writing:
550
551 session->destroy
552
553 In Dancer2, we write:
554
555 app->destroy_session if app->has_session
556
557 If you make changes to the session in an "after" hook, your changes
558 will not be written to storage, because writing sessions to storage
559 also takes place in an (earlier) "after" hook.
560
562 Dancer Core Developers
563
565 This software is copyright (c) 2023 by Alexis Sukrieh.
566
567 This is free software; you can redistribute it and/or modify it under
568 the same terms as the Perl 5 programming language system itself.
569
570
571
572perl v5.38.0 2023-07-20 Dancer2::Manual::Migration(3)