App-unbelievable
view release on metacpan or search on metacpan
eg/content/porting-a-dancer-plugin-to-dancer2.md view on Meta::CPAN
}
plugin_keywords qw(is_mobile_device); # replaces register_plugin()
```
In the body of the function, the Dancer plugin directly accessed the DSL keyword `request`. The Dancer2 plugin instead accesses the request via `$self->dsl->request`.
Porting hooks
-------------
Dancer plugins add hooks using the DSL `hook` keyword. For example, this `before_template` hook makes `is_mobile_device` available in templates:
```perl
hook before_template => sub {
my $tokens = shift;
$tokens->{'is_mobile_device'} = is_mobile_device();
};
```
Dancer2 handles hooks very differently. The pluginâs Moo constructor, `BUILD`, is called when a plugin instance is created. In `BUILD`, the plugin registers the hook. I added `BUILD` and called
`$self->dsl->hook` to add the hook:
```perl
sub BUILD {
my $self = shift;
$self->dsl->hook( before_template_render => sub {
my $tokens = shift;
$tokens->{is_mobile_device} = $plugin->is_mobile_device;
});
}
```
If your hook functions are too long to move into `BUILD`, you can leave them where they are and say `$self->dsl->hook( hook_name => \&sub_name );`.
Porting the tests
-----------------
Dancer::Plugin::MobileDevice has a full test suite. These tests are extremely useful to developers, as they allow you to to see if a Dancer2 port behaves the same as the Dancer original. That said, you have to port the tests themselves before you ca...
The Dancer tests define a simple Web application using the plugin. They exercise that application using helpers in [Dancer::Test]({{< mcpan "Dancer::Test" >}}). For example (simplified from `t/01-is-mobile-device.t`):
```perl
{ # The simple application
use Dancer;
use Dancer::Plugin::MobileDevice;
get '/' => sub { return is_mobile_device; };
}
use Dancer::Test;
$ENV{HTTP_USER_AGENT} = 'iPhone';
my $response = dancer_response GET => '/'; # dancer_response() is from Dancer::Test
is( $response->{content}, 1 );
```
Dancer2, on the other hand, uses the [Plack]({{< mcpan "Plack" >}}) ecosystem for testing instead of its own helpers. To work in that ecosystem, I changed the
above test as described in the
[Dancer2 manualâs âtestingâ section]({{< mcpan "Dancer2::Manual#TESTING" >}}):
```perl
use Plack::Test; # Additional testing modules
use HTTP::Request::Common;
{
package TestApp; # Still a simple application, but now with a name
use Dancer2;
use Dancer2::Plugin::MobileDevice;
get '/' => sub { return is_mobile_device; };
}
my $dut = Plack::Test->create(TestApp->to_app); # a fake Web server
my $response = $dut->request(GET '/', 'User-Agent' => 'iPhone');
is( $response->content, 1 );
```
Dancer2 tests use more boilerplate than Dancer tests, but Dancer2 tests are more modular and flexible than Dancer tests. With Plack, you donât have to use the global state (`%ENV`) any more, and you can test more than one application or use case p...
Conclusion
----------
I am a newbie at Dancer2, and have never used Dancer. But I was able to port Dancer::Plugin::MobileDevice to Dancer2 in less than a day â including time to read the documentation and figure out how! When you need a Dancer function in Dancer2, gra...
Acknowledgements
----------------
My thanks to Kelly Deltoro-White for her insights, and to the authors of Dancer::Plugin::MobileDevice and Dancer2 for a strong foundation to build on.
More information on Dancer2 plugins
-----------------------------------
- â[The new Dancer2 plugin system](http://advent.perldancer.org/2016/22)â by Sawyer X, for an overview
- [Dancer2::Plugin]({{< mcpan "Dancer2::Plugin" >}}), for details
Quick reference: porting plugins from Dancer to Dancer2
-------------------------------------------------------
**Port keywords:**
- Make keywords freestanding `sub`s, not arguments of `register`
- Access data through `$self` rather than DSL keywords
- Change `register_plugin` to `plugin_keywords`
**Port hooks:**
- Add a `BUILD` function
- Move the hook functions into `BUILD`, or refer to them from `BUILD`
- Wrap each hook function in a `$self->dsl->hook` call
**Port tests:**
- Import[Plack::Test]({{< mcpan "Plack::Test" >}}}) and
[HTTP::Request::Common]({{< mcpan "HTTP::Request::Common" >}})
instead of Dancer::Test
- Give the application under test a `package` statement
- Create a Plack::Test instance representing the application
- Create requests using HTTP::Request::Common methods
- Change `$response->{content}` to `$response->content`
( run in 0.762 second using v1.01-cache-2.11-cpan-acf6aa7dc9e )