As I work my way through Dancer2::Manual, I am having difficulty understanding how to use the 'appname' keyword to distribute code among different packages in order to improve maintainability.

I. Starting point: two packages, each in separate file, using 'builder'.

cat lib/mywebapp.pm
#####
package mywebapp;
use v5.10.1;
use Dancer2;
use Dancer2::Plugin::Database;
use Crypt::SaltedHash;
use Data::Dump;
use Dancer2::Core::Request::Upload;
use URL::Encode qw (url_decode);

our $VERSION = '0.1';
set session => 'Simple';

hook before => sub {
    if (!session('user') && request->dispatch_path !~ m{^/login}) {
        forward '/login', { requested_path => request->dispatch_path };
    }
};

get '/' => sub { template 'index'; };
get '/index' => sub { redirect '/' };

get '/login' => sub {
    # Display a login page; the original URL they requested is available as
# param('requested_path'), so could be put in a hidden field in the form
    template 'login', { path => param('requested_path') };
};

post '/login' => sub {
    my $user_value = body_parameters->get('user');
    my $pass_value = url_decode(body_parameters->get('pass'));
my $user = database->quick_select('users', { username => $user_value });

    if (! $user) {
        warning "Failed login for unrecognised user $user_value";
        redirect '/login?failed=1';
    }
    else {
        my $csh = Crypt::SaltedHash->new(algorithm => 'SHA-1');
        $csh->add($user->{password});
        my $salted = $csh->generate;
        if (Crypt::SaltedHash->validate($salted, $pass_value)) {
            debug "Password correct";
            session user => $user;
            redirect body_parameters->get('path') || '/';
# Note: When using curl and supplying a value for 'path' among the # KVPs for POST /login, any such endpoint must be defined herein # as: any ['get', 'post'] => '/my_endpoint' to work around curl's
            # apparent refusal to switch from POST to GET
        }
        else {
            debug "Login failed; password incorrect for: " . $user_value;
            redirect '/login?failed=1';
        }
    }
};

get '/logout' => sub {
    app->destroy_session;
};

any ['get', 'post'] => '/hello' => sub {
    return "Hello World\n";
};

{
    hook before => sub { var time => scalar(localtime) };

    any ['get', 'post'] => '/welcome/:name' => sub {
        my $name = route_parameters->get('name');
        template 'welcome.tt', { name => $name };
    };
}

any ['get', 'post'] => '/appname' => sub {
    return "This is " . config->{appname} . "\n";
};

start;
#####

Serialized responses must go into a separate package. Hence, I have written, and successfully used, this package:

cat lib/mywebapp/api.pm
#####
package mywebapp::api;
use v5.10.1;
use Dancer2;
use Dancer2::Plugin::Database;
use Crypt::SaltedHash;
use Data::Dump;
use Dancer2::Core::Request::Upload;
use URL::Encode qw (url_decode);
use HTTP::Status qw(:constants status_message);

our $VERSION = '0.1';
set session => 'Simple';
set views => path( app->location, "templates" );
set serializer => 'JSON';

any ['get', 'post'] => '/userdata/:user' => sub {

    my $user_value = route_parameters->get('user');
my $user = database->quick_select('users', { username => $user_value });

    if (! $user) {
        warning "Failed to recognize user '$user_value'";
        my $code = HTTP_BAD_REQUEST;
        {
            status_code => $code,
            message     => status_message($code),
        }
    }
    else {
        {
            username    => $user->{username},
            first_name  => $user->{first_name},
            last_name   => $user->{last_name},
        }
    }
};

start;
#####

And, following earlier documentation in Dancer2::Manual, I have gotten these two packages to work together via:

#####
$ cat bin/app.psgi
#!/usr/bin/env perl

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";

use mywebapp;
use mywebapp::api;
use Plack::Builder;

builder {
    mount '/'       => mywebapp->to_app;
    mount '/api'    => mywebapp::api->to_app;
};
#####

With a valid user 'dancer_operator', I am able to say:

#####
http://localhost:5000/api/userdata/dancer_operator
#####

... and get back in browser:

#####
{"username":"dancer_operator","last_name":"Operator","first_name":"Dancer"}
#####

II.  Attempt to refactor this with 'appname'

If I am reading documentation in Dancer2::Manual and Dancer2::Cookbook correctly, I believe I should be able to make the following revisions and have all my endpoints just work.

$ git diff | cat
#####
diff --git a/bin/app.psgi b/bin/app.psgi
index 46d8845..b236a2d 100755
--- a/bin/app.psgi
+++ b/bin/app.psgi
@@ -6,11 +6,5 @@ use FindBin;
 use lib "$FindBin::Bin/../lib";

 use mywebapp;
-use mywebapp::api;
-use Plack::Builder;
-
-builder {
-    mount '/'       => mywebapp->to_app;
-    mount '/api'    => mywebapp::api->to_app;
-};

+mywebapp->to_app;
diff --git a/lib/mywebapp/api.pm b/lib/mywebapp/api.pm
index 3af1adf..97a9d0e 100644
--- a/lib/mywebapp/api.pm
+++ b/lib/mywebapp/api.pm
@@ -1,6 +1,6 @@
 package mywebapp::api;
 use v5.10.1;
-use Dancer2;
+use Dancer2 appname => mywebapp;
 use Dancer2::Plugin::Database;
 use Crypt::SaltedHash;
 use Data::Dump;
#####

All the endpoints defined in lib/mywebapp.pm continue to function as expected. But the endpoint, /api/userdata/:user, defined in lib/mywebapp/api.pm, no longer works. When I call,

#####
http://localhost:5000/api/userdata/dancer_operator
#####

... I get a 404 NOT_FOUND response. If I omit the '/api' from the URL above, I also get a 404 -- though that's more expected. And when I try using "prefix = '/api'" as suggested by Dancer2::Cookbook, I still get 404s.

What am I missing and/or what are the docs failing to explain clearly?

Thank you very much.
Jim Keenan
_______________________________________________
dancer-users mailing list
[email protected]
http://lists.preshweb.co.uk/mailman/listinfo/dancer-users

Reply via email to