Dancer2::Manual: confused as to use of 'appname' keyword to distribute code across packages
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
On Jul 10, 2016, at 11:20 AM, James E Keenan <jkeen@verizon.net> wrote:
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.
That’s because your “mount” for the api.pm routes prepends ‘/api’ to them. When you use the module standalone as you’re attempting to now, that bit is stripped off. You’ll find that you now have /userdata/$stuff now. Easy fix: wrap the route definitions in api.pm in a prefix block: prefix '/api' => sub { any ['get', 'post'] => '/userdata/:user' => sub { # etc }; }; You could even extract the other layer, too: prefix '/api' => sub { prefix '/userdata' => sub { any ['get', 'post'] => ':user' => sub { # etc }; }; };
On 07/12/2016 10:44 AM, Warren Young wrote:
On Jul 10, 2016, at 11:20 AM, James E Keenan <jkeen@verizon.net> wrote:
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.
That’s because your “mount” for the api.pm routes prepends ‘/api’ to them. When you use the module standalone as you’re attempting to now, that bit is stripped off. You’ll find that you now have /userdata/$stuff now.
Easy fix: wrap the route definitions in api.pm in a prefix block:
prefix '/api' => sub { any ['get', 'post'] => '/userdata/:user' => sub { # etc }; };
Thank you for your reply. However, when I wrap the endpoint in mywebapp::api with 'prefix' per your suggestion, I am still getting 404s on both 'http://localhost:5000/api/userdata/dancer_operator' and 'http://localhost:5000/userdata/dancer_operator'. Here is the diff of what I am trying: ##### diff --git a/bin/app.psgi b/bin/app.psgi index 46d8845..e9ac3da 100755 --- a/bin/app.psgi +++ b/bin/app.psgi @@ -6,11 +6,12 @@ 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; -}; +#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.pm b/lib/mywebapp.pm index 997bdc7..7c940da 100644 --- a/lib/mywebapp.pm +++ b/lib/mywebapp.pm @@ -1,6 +1,6 @@ package mywebapp; use v5.10.1; -use Dancer2; +use Dancer2 appname => mywebapp; use Dancer2::Plugin::Database; use Crypt::SaltedHash; use Data::Dump; diff --git a/lib/mywebapp/api.pm b/lib/mywebapp/api.pm index 3af1adf..88b47f7 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; @@ -13,6 +13,7 @@ set session => 'Simple'; set views => path( app->location, "templates" ); set serializer => 'JSON'; +prefix '/api' => sub { any ['get', 'post'] => '/userdata/:user' => sub { my $user_value = route_parameters->get('user'); @@ -34,6 +35,7 @@ any ['get', 'post'] => '/userdata/:user' => sub { } } }; +}; start; ###### So I'm still finding that 'appname' is not DWIMming. Thank you very much. Jim Keenan
Have you checked http://localhost:5000/api/api/userdata/dancer_operator? On 14 July 2016 at 14:37, James E Keenan <jkeen@verizon.net> wrote:
On 07/12/2016 10:44 AM, Warren Young wrote:
On Jul 10, 2016, at 11:20 AM, James E Keenan <jkeen@verizon.net> wrote:
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.
That’s because your “mount” for the api.pm routes prepends ‘/api’ to them. When you use the module standalone as you’re attempting to now, that bit is stripped off. You’ll find that you now have /userdata/$stuff now.
Easy fix: wrap the route definitions in api.pm in a prefix block:
prefix '/api' => sub { any ['get', 'post'] => '/userdata/:user' => sub { # etc }; };
Thank you for your reply. However, when I wrap the endpoint in mywebapp::api with 'prefix' per your suggestion, I am still getting 404s on both 'http://localhost:5000/api/userdata/dancer_operator' and ' http://localhost:5000/userdata/dancer_operator'.
Here is the diff of what I am trying:
##### diff --git a/bin/app.psgi b/bin/app.psgi index 46d8845..e9ac3da 100755 --- a/bin/app.psgi +++ b/bin/app.psgi @@ -6,11 +6,12 @@ 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; -}; +#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.pm b/lib/mywebapp.pm index 997bdc7..7c940da 100644 --- a/lib/mywebapp.pm +++ b/lib/mywebapp.pm @@ -1,6 +1,6 @@ package mywebapp; use v5.10.1; -use Dancer2; +use Dancer2 appname => mywebapp; use Dancer2::Plugin::Database; use Crypt::SaltedHash; use Data::Dump; diff --git a/lib/mywebapp/api.pm b/lib/mywebapp/api.pm index 3af1adf..88b47f7 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; @@ -13,6 +13,7 @@ set session => 'Simple'; set views => path( app->location, "templates" ); set serializer => 'JSON';
+prefix '/api' => sub { any ['get', 'post'] => '/userdata/:user' => sub {
my $user_value = route_parameters->get('user'); @@ -34,6 +35,7 @@ any ['get', 'post'] => '/userdata/:user' => sub { } } }; +};
start; ######
So I'm still finding that 'appname' is not DWIMming.
Thank you very much. Jim Keenan _______________________________________________ dancer-users mailing list dancer-users@dancer.pm http://lists.preshweb.co.uk/mailman/listinfo/dancer-users
On 07/14/2016 06:11 PM, Amelia Ireland wrote:
Have you checked http://localhost:5000/api/api/userdata/dancer_operator?
Another 404 Not Found.
I didn't read the first email properly -- I think what you want to do is this: in WebApp.pm: package WebApp; use Dancer2 appname => 'test'; # plus the other dependencies 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 '/' }; # etc. 1; in WebApp::API.pm: package WebApp::API; use Dancer2 appname => 'test'; # handles everything under /api prefix '/api' => sub { any ['get', 'post'] => '/userdata/:user' => sub { my $user_value = route_parameters->get('user'); my $user = database->quick_select('users', { username => $user_value }); # etc. }; }; 1; in app.psgi: use WebApp; use WebApp::API; builder { mount '/' => WebApp->to_app # note that WebApp::API->to_app also works here }; To make life even easier for yourself, you can make a wrapper to hold your various route modules: package WebAppWrap; use Dancer2 appname => 'test'; use WebApp; use WebApp::API; use WebApp::JSON; use WebApp::Complex::Package; # each of these modules has appname 'test' # etc. in app.psgi: use WebAppWrap; builder { mount '/' => WebAppWrap->to_app # pulls in all loaded modules with appname 'test' }; On 14 July 2016 at 18:53, James E Keenan <jkeen@verizon.net> wrote:
On 07/14/2016 06:11 PM, Amelia Ireland wrote:
Have you checked http://localhost:5000/api/api/userdata/dancer_operator?
Another 404 Not Found.
_______________________________________________ dancer-users mailing list dancer-users@dancer.pm http://lists.preshweb.co.uk/mailman/listinfo/dancer-users
On 07/14/2016 11:35 PM, Amelia Ireland wrote:
I didn't read the first email properly -- I think what you want to do is this:
in WebApp.pm:
package WebApp;
use Dancer2 appname => 'test'; # plus the other dependencies
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 '/' };
# etc.
1;
in WebApp::API.pm:
package WebApp::API; use Dancer2 appname => 'test';
# handles everything under /api prefix '/api' => sub {
any ['get', 'post'] => '/userdata/:user' => sub {
my $user_value = route_parameters->get('user'); my $user = database->quick_select('users', { username => $user_value }); # etc. }; };
1;
in app.psgi:
use WebApp; use WebApp::API;
builder { mount '/' => WebApp->to_app # note that WebApp::API->to_app also works here };
To make life even easier for yourself, you can make a wrapper to hold your various route modules:
package WebAppWrap; use Dancer2 appname => 'test'; use WebApp; use WebApp::API; use WebApp::JSON; use WebApp::Complex::Package; # each of these modules has appname 'test' # etc.
in app.psgi:
use WebAppWrap;
builder { mount '/' => WebAppWrap->to_app # pulls in all loaded modules with appname 'test' };
Amelia, Thank you for taking the time to respond. Starting from the same starting point as previously, I tried to follow your suggestions as explicitly as possible (i.e., using the existing name of the app and the modules). That meant that I was applying this diff: ##### $ git diff -w | cat diff --git a/bin/app.psgi b/bin/app.psgi index 46d8845..3ced854 100755 --- a/bin/app.psgi +++ b/bin/app.psgi @@ -7,10 +7,8 @@ use lib "$FindBin::Bin/../lib"; use mywebapp; use mywebapp::api; -use Plack::Builder; builder { mount '/' => mywebapp->to_app; - mount '/api' => mywebapp::api->to_app; }; diff --git a/lib/mywebapp.pm b/lib/mywebapp.pm index ce3cdc8..826d80e 100644 --- a/lib/mywebapp.pm +++ b/lib/mywebapp.pm @@ -1,6 +1,6 @@ package mywebapp; use v5.10.1; -use Dancer2; +use Dancer2 appname => 'mywebapp'; use Dancer2::Plugin::Database; use Crypt::SaltedHash; use Data::Dump; diff --git a/lib/mywebapp/api.pm b/lib/mywebapp/api.pm index 3af1adf..160b06b 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; @@ -13,6 +13,7 @@ set session => 'Simple'; set views => path( app->location, "templates" ); set serializer => 'JSON'; +prefix '/api' => sub { any ['get', 'post'] => '/userdata/:user' => sub { my $user_value = route_parameters->get('user'); @@ -34,6 +35,7 @@ any ['get', 'post'] => '/userdata/:user' => sub { } } }; +}; start; ##### But this led to an error as soon as I started the web server: ##### $ plackup -R . -p 5000 bin/app.psgi Watching . bin/lib bin/app.psgi for file updates. String found where operator expected at /home/jkeenan/learn/perl/dancer2/mywebapp/bin/app.psgi line 12, near "mount '/'" (Do you need to predeclare mount?) Error while loading /home/jkeenan/learn/perl/dancer2/mywebapp/bin/app.psgi: syntax error at /home/jkeenan/learn/perl/dancer2/mywebapp/bin/app.psgi line 12, near "mount '/'" ##### Adding 'use Plack::Builder;' to bin/app.psgi resolved this error. ##### -- /home/jkeenan/learn/perl/dancer2/mywebapp/bin/app.psgi updated. Killing the existing server (pid:3375) Successfully killed! Restarting the new server process. HTTP::Server::PSGI: Accepting connections at http://0:5000/ ##### However, when I then went to the browser and issued a previously working API call, I got this error output: ##### {"status":"500","title":"Error 500 - Internal Server Error","exception":"Failed to render template: file error - login.tt: not found at /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/Role/Template.pm line 132.\n","message":""} ##### In the server I got output like this: ##### [mywebapp:3420] core @2016-07-15 15:45:55> looking for get /login in /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/App.pm l. 840 [mywebapp:3420] core @2016-07-15 15:45:55> Entering hook core.app.before_request in /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/App.pm l. 840 [mywebapp:3420] error @2016-07-15 15:45:55> Route exception: Failed to render template: file error - login.tt: not found at /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/Role/Template.pm line 132. in /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/App.pm l. 840 ##### Needless to say, if I can't locate 'login.tt', I can't login and therefore can't make any of the other previously working API calls. I've spent more than 10 hours over several days trying to figure this out, and I'm beginning to lose hope that it will ever work. Or, more precisely, I'm beginning to think that the documentation in Dancer2::Manual is claiming that something will work which just will not do so. Thoughts? Thank you very much. Jim Keenan
Hello James, Apologies for not filling out all the modules in the app.psgi file and adding unnecessary confusion! The response you're getting, not being able to locate the template file, is usually due to a config error in setting the paths to the directory your app looks in for template files. Have you tried debugging by printing out the 'views' parameter (see https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Manual.pod#TEMPLAT... )? You can also check that the endpoints you have defined are working correctly by returning a text string; once that is verified, you know it's just the template engine parameters that need altering. On 15 July 2016 at 12:50, James E Keenan <jkeen@verizon.net> wrote:
However, when I then went to the browser and issued a previously working API call, I got this error output:
##### {"status":"500","title":"Error 500 - Internal Server Error","exception":"Failed to render template: file error - login.tt: not found at /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/Role/Template.pm line 132.\n","message":""} #####
In the server I got output like this: ##### [mywebapp:3420] core @2016-07-15 15:45:55> looking for get /login in /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/App.pm l. 840 [mywebapp:3420] core @2016-07-15 15:45:55> Entering hook core.app.before_request in /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/App.pm l. 840 [mywebapp:3420] error @2016-07-15 15:45:55> Route exception: Failed to render template: file error - login.tt: not found at /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/Role/Template.pm line 132. in /home/jkeenan/perl5/perlbrew/perls/perl-5.24.0/lib/site_perl/5.24.0/Dancer2/Core/App.pm l. 840 #####
Needless to say, if I can't locate 'login.tt', I can't login and therefore can't make any of the other previously working API calls.
I've spent more than 10 hours over several days trying to figure this out, and I'm beginning to lose hope that it will ever work. Or, more precisely, I'm beginning to think that the documentation in Dancer2::Manual is claiming that something will work which just will not do so.
Thoughts?
Thank you very much. Jim Keenan
_______________________________________________ dancer-users mailing list dancer-users@dancer.pm http://lists.preshweb.co.uk/mailman/listinfo/dancer-users
participants (3)
-
Amelia Ireland -
James E Keenan -
Warren Young