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 )