1Dancer2::Plugin(3) User Contributed Perl Documentation Dancer2::Plugin(3)
2
3
4
6 Dancer2::Plugin - base class for Dancer2 plugins
7
9 version 0.301004
10
12 The plugin itself:
13
14 package Dancer2::Plugin::Polite;
15
16 use strict;
17 use warnings;
18
19 use Dancer2::Plugin;
20
21 has smiley => (
22 is => 'ro',
23 default => sub {
24 $_[0]->config->{smiley} || ':-)'
25 }
26 );
27
28 plugin_keywords 'add_smileys';
29
30 sub BUILD {
31 my $plugin = shift;
32
33 $plugin->app->add_hook( Dancer2::Core::Hook->new(
34 name => 'after',
35 code => sub { $_[0]->content( $_[0]->content . " ... please?" ) }
36 ));
37
38 $plugin->app->add_route(
39 method => 'get',
40 regexp => '/goodbye',
41 code => sub {
42 my $app = shift;
43 'farewell, ' . $app->request->params->{name};
44 },
45 );
46
47 }
48
49 sub add_smileys {
50 my( $plugin, $text ) = @_;
51
52 $text =~ s/ (?<= \. ) / $plugin->smiley /xeg;
53
54 return $text;
55 }
56
57 1;
58
59 then to load into the app:
60
61 package MyApp;
62
63 use strict;
64 use warnings;
65
66 use Dancer2;
67
68 BEGIN { # would usually be in config.yml
69 set plugins => {
70 Polite => {
71 smiley => '8-D',
72 },
73 };
74 }
75
76 use Dancer2::Plugin::Polite;
77
78 get '/' => sub {
79 add_smileys( 'make me a sandwich.' );
80 };
81
82 1;
83
85 Writing the plugin
86 "use Dancer2::Plugin"
87
88 The plugin must begin with
89
90 use Dancer2::Plugin;
91
92 which will turn the package into a Moo class that inherits from
93 Dancer2::Plugin. The base class provides the plugin with two
94 attributes: "app", which is populated with the Dancer2 app object for
95 which the plugin is being initialized for, and "config" which holds the
96 plugin section of the application configuration.
97
98 Modifying the app at building time
99
100 If the plugin needs to tinker with the application -- add routes or
101 hooks, for example -- it can do so within its "BUILD()" function.
102
103 sub BUILD {
104 my $plugin = shift;
105
106 $plugin->app->add_route( ... );
107 }
108
109 Adding keywords
110
111 Via "plugin_keywords"
112
113 Keywords that the plugin wishes to export to the Dancer2 app can be
114 defined via the "plugin_keywords" keyword:
115
116 plugin_keywords qw/
117 add_smileys
118 add_sad_kitten
119 /;
120
121 Each of the keyword will resolve to the class method of the same name.
122 When invoked as keyword, it'll be passed the plugin object as its first
123 argument.
124
125 sub add_smileys {
126 my( $plugin, $text ) = @_;
127
128 return join ' ', $text, $plugin->smiley;
129 }
130
131 # and then in the app
132
133 get '/' => sub {
134 add_smileys( "Hi there!" );
135 };
136
137 You can also pass the functions directly to "plugin_keywords".
138
139 plugin_keywords
140 add_smileys => sub {
141 my( $plugin, $text ) = @_;
142
143 $text =~ s/ (?<= \. ) / $plugin->smiley /xeg;
144
145 return $text;
146 },
147 add_sad_kitten => sub { ... };
148
149 Or a mix of both styles. We're easy that way:
150
151 plugin_keywords
152 add_smileys => sub {
153 my( $plugin, $text ) = @_;
154
155 $text =~ s/ (?<= \. ) / $plugin->smiley /xeg;
156
157 return $text;
158 },
159 'add_sad_kitten';
160
161 sub add_sad_kitten {
162 ...;
163 }
164
165 If you want several keywords to be synonyms calling the same function,
166 you can list them in an arrayref. The first function of the list is
167 taken to be the "real" method to link to the keywords.
168
169 plugin_keywords [qw/ add_smileys add_happy_face /];
170
171 sub add_smileys { ... }
172
173 Calls to "plugin_keywords" are cumulative.
174
175 Via the ":PluginKeyword" function attribute
176
177 For perl 5.12 and higher, keywords can also be defined by adding the
178 ":PluginKeyword" attribute to the function you wish to export.
179
180 For Perl 5.10, the export triggered by the sub attribute comes too late
181 in the game, and the keywords won't be exported in the application
182 namespace.
183
184 sub foo :PluginKeyword { ... }
185
186 sub bar :PluginKeyword( baz quux ) { ... }
187
188 # equivalent to
189
190 sub foo { ... }
191 sub bar { ... }
192
193 plugin_keywords 'foo', [ qw/ baz quux / ] => \&bar;
194
195 For an attribute
196
197 You can also turn an attribute of the plugin into a keyword.
198
199 has foo => (
200 is => 'ro',
201 plugin_keyword => 1, # keyword will be 'foo'
202 );
203
204 has bar => (
205 is => 'ro',
206 plugin_keyword => 'quux', # keyword will be 'quux'
207 );
208
209 has baz => (
210 is => 'ro',
211 plugin_keyword => [ 'baz', 'bazz' ], # keywords will be 'baz' and 'bazz'
212 );
213
214 Accessing the plugin configuration
215
216 The plugin configuration is available via the "config()" method.
217
218 sub BUILD {
219 my $plugin = shift;
220
221 if ( $plugin->config->{feeling_polite} ) {
222 $plugin->app->add_hook( Dancer2::Core::Hook->new(
223 name => 'after',
224 code => sub { $_[0]->content( $_[0]->content . " ... please?" ) }
225 ));
226 }
227 }
228
229 Getting default values from config file
230
231 Since initializing a plugin with either a default or a value passed via
232 the configuration file, like
233
234 has smiley => (
235 is => 'ro',
236 default => sub {
237 $_[0]->config->{smiley} || ':-)'
238 }
239 );
240
241 "Dancer2::Plugin" allows for a "from_config" key in the attribute
242 definition. Its value is the plugin configuration key that will be
243 used to initialize the attribute.
244
245 If it's given the value 1, the name of the attribute will be taken as
246 the configuration key.
247
248 Nested hash keys can also be referred to using a dot notation.
249
250 If the plugin configuration has no value for the given key, the
251 attribute default, if specified, will be honored.
252
253 If the key is given a coderef as value, it's considered to be a
254 "default" value combo:
255
256 has foo => (
257 is => 'ro',
258 from_config => sub { 'my default' },
259 );
260
261
262 # equivalent to
263 has foo => (
264 is => 'ro',
265 from_config => 'foo',
266 default => sub { 'my default' },
267 );
268
269 For example:
270
271 # in config.yml
272
273 plugins:
274 Polite:
275 smiley: ':-)'
276 greeting:
277 casual: Hi!
278 formal: How do you do?
279
280
281 # in the plugin
282
283 has smiley => ( # will be ':-)'
284 is => 'ro',
285 from_config => 1,
286 default => sub { ':-(' },
287 );
288
289 has casual_greeting => ( # will be 'Hi!'
290 is => 'ro',
291 from_config => 'greeting.casual',
292 );
293
294 has apology => ( # will be 'sorry'
295 is => 'ro',
296 from_config => 'apology',
297 default => sub { 'sorry' },
298 )
299
300 has closing => ( # will be 'See ya!'
301 is => 'ro',
302 from_config => sub { 'See ya!' },
303 );
304
305 Config becomes immutable
306
307 The plugin's "config" attribute is loaded lazily on the first call to
308 "config". After this first call "config" becomes immutable so you
309 cannot do the following in a test:
310
311 use Dancer2;
312 use Dancer2::Plugin::FooBar;
313
314 set plugins => {
315 FooBar => {
316 wibble => 1, # this is OK
317 },
318 };
319
320 flibble(45); # plugin keyword called which causes config read
321
322 set plugins => {
323 FooBar => {
324 wibble => 0, # this will NOT change plugin config
325 },
326 };
327
328 Accessing the parent Dancer app
329
330 If the plugin is instantiated within a Dancer app, it'll be accessible
331 via the method "app()".
332
333 sub BUILD {
334 my $plugin = shift;
335
336 $plugin->app->add_route( ... );
337 }
338
339 To use Dancer's DSL in your plugin:
340
341 $self->dsl->debug( “Hi! I’m logging from your plugin!” );
342
343 See "DSL KEYWORDS" in Dancer2::Manual for a full list of Dancer2 DSL.
344
345 Using the plugin within the app
346 A plugin is loaded via
347
348 use Dancer2::Plugin::Polite;
349
350 The plugin will assume that it's loading within a Dancer module and
351 will automatically register itself against its "app()" and export its
352 keywords to the local namespace. If you don't want this to happen,
353 specify that you don't want anything imported via empty parentheses
354 when "use"ing the module:
355
356 use Dancer2::Plugin::Polite ();
357
358 Plugins using plugins
359 It's easy to use plugins from within a plugin:
360
361 package Dancer2::Plugin::SourPuss;
362
363 use Dancer2::Plugin;
364 use Dancer2::Plugin::Polite;
365
366 sub my_keyword { my $smiley = smiley(); }
367
368 1;
369
370 This does not export "smiley()" into your application - it is only
371 available from within your plugin. However, from the example above, you
372 can wrap DSL from other plugins and make it available from your plugin.
373
374 Utilizing other plugins
375 You can use the "find_plugin" to locate other plugins loaded by the
376 user, in order to use them, or their information, directly:
377
378 # MyApp.pm
379 use Dancer2;
380 use Dancer2::Plugin::Foo;
381 use Dancer2::Plugin::Bar;
382
383 # Dancer2::Plugin::Bar;
384 ...
385
386 sub my_keyword {
387 my $self = shift;
388 my $foo = $self->find_plugin('Dancer2::Plugin::Foo')
389 or $self->dsl->send_error('Could not find Foo');
390
391 return $foo->foo_keyword(...);
392 }
393
394 Hooks
395 New plugin hooks are declared via "plugin_hooks".
396
397 plugin_hooks 'my_hook', 'my_other_hook';
398
399 Hooks are prefixed with "plugin.plugin_name". So the plugin "my_hook"
400 coming from the plugin "Dancer2::Plugin::MyPlugin" will have the hook
401 name "plugin.myplugin.my_hook".
402
403 Hooks are executed within the plugin by calling them via the associated
404 app.
405
406 $plugin->execute_plugin_hook( 'my_hook' );
407
408 You can also call any other hook if you provide the full name using the
409 "execute_hook" method:
410
411 $plugin->app->execute_hook( 'core.app.route_exception' );
412
413 Or using their alias:
414
415 $plugin->app->execute_hook( 'on_route_exception' );
416
417 Note: If your plugin consumes a plugin that declares any hooks, those
418 hooks are added to your application, even though DSL is not.
419
420 Writing Test Gotchas
421 Constructor for Dancer2::Plugin::Foo has been inlined and cannot be
422 updated
423
424 You'll usually get this one because you are defining both the plugin
425 and app in your test file, and the runtime creation of Moo's attributes
426 happens after the compile-time import voodoo dance.
427
428 To get around this nightmare, wrap your plugin definition in a "BEGIN"
429 block.
430
431 BEGIN {
432 package Dancer2::Plugin::Foo;
433
434 use Dancer2::Plugin;
435
436 has bar => (
437 is => 'ro',
438 from_config => 1,
439 );
440
441 plugin_keywords qw/ bar /;
442
443 }
444
445 {
446 package MyApp;
447
448 use Dancer2;
449 use Dancer2::Plugin::Foo;
450
451 bar();
452 }
453
454 You cannot overwrite a locally defined method (bar) with a reader
455
456 If you set an object attribute of your plugin to be a keyword as well,
457 you need to call "plugin_keywords" after the attribute definition.
458
459 package Dancer2::Plugin::Foo;
460
461 use Dancer2::Plugin;
462
463 has bar => (
464 is => 'ro',
465 );
466
467 plugin_keywords 'bar';
468
470 Dancer Core Developers
471
473 This software is copyright (c) 2021 by Alexis Sukrieh.
474
475 This is free software; you can redistribute it and/or modify it under
476 the same terms as the Perl 5 programming language system itself.
477
478
479
480perl v5.34.0 2022-01-21 Dancer2::Plugin(3)