bigpresh / Dancer-Plugin-Database

Dancer::Plugin::Database - easy database support for Dancer applications
http://search.cpan.org/dist/Dancer-Plugin-Database
37 stars 36 forks source link

Documentation about Multiple Connections may be misleading #85

Closed jkeenan closed 8 years ago

jkeenan commented 8 years ago

Here is a situation where, when I think I am following the documentation correctly, I don't get the intended results -- and I don't know whether it's my ignorance/inexperience or the docs or some combination of the two.

Here are three files which, to the best of my knowledge, produce a correctly working Dancer application.

-- lib/mywebapp.pm

package mywebapp;
use v5.10.1;
use Dancer2;
use Dancer2::Plugin::Database;
use Crypt::SaltedHash;
use Data::Dump;

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 { return "Home Page"; };

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 = 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') || '/';
        }
        else {
            debug "Login failed; password incorrect for: " . $user_value;
            redirect '/login?failed=1';
        }
    }
};

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

get '/hello' => sub {
    return "Hello World\n";
};

start;

-- views/login.tt

<html>
  <head>
    <title>Session and logging in</title>
  </head>
  <body>
    <form action='/login' method='POST'>
        User Name : <input type='text' name='user'/>
        Password: <input type='password' name='pass' />

        <!-- Put the original path requested into a hidden
                   field so it's sent back in the POST and can be
                   used to redirect to the right page after login -->
        <input type='hidden' name='path' value='<% path %>'/>

        <input type='submit' value='Login' />
    </form>
  </body>
</html>

-- config.yml

appname: "mywebapp"
layout: "main"
charset: "UTF-8"
template: "simple"
plugins:
    Database:
        driver: 'Pg'
        database: 'dancer_study'
        host: 'localhost'
        port: 5432
        username: 'dancer_operator'
        connection_check_threshold: 10
        dbi_params:
            RaiseError: 1
            AutoCommit: 1
            pg_enable_utf8: 1
        on_connect_do: []
        log_queries: 1
        handle_class:

Of these three files we are here concerned only with config.yml. Note that the entries for driver and database are located underneath Database which is in turn located under plugins.

Now, as I read the documentation for Dancer2::Plugin::Database (https://metacpan.org/pod/Dancer2::Plugin::Database#DEFINING-MULTIPLE-CONNECTIONS), I believe that, in order to allow for multiple configurations in the future, I shoule be able to get the same results using a configuration like this:

appname: "mywebapp"
layout: "main"
charset: "UTF-8"
template: "simple"
plugins:
    Database:
        connections:
            dancer:
                driver: 'Pg'
                database: 'dancer_study'
        port: 5432
        username: 'dancer_operator'
        connection_check_threshold: 10
        dbi_params:
            RaiseError: 1
            AutoCommit: 1
            pg_enable_utf8: 1
        on_connect_do: []
        log_queries: 1
        handle_class:

However, when I try to run this app, I get these failures:

Error 500 - Internal Server Error
Runtime Error
Can't get a database connection without settings supplied!
Please check you've supplied settings in config as per the Dancer::Plugin::Database 
documentation at .../Dancer/Plugin/Database/Core.pm line 206.

The relevant code (https://metacpan.org/source/AMBS/Dancer-Plugin-Database-Core-0.14/lib/Dancer/Plugin/Database/Core.pm#L205) is:

    if (!$settings->{dsn} && !$settings->{driver}) {
        die "Can't get a database connection without settings supplied!\n"
            . "Please check you've supplied settings in config as per the "
            . "Dancer::Plugin::Database documentation";
    }

Now, the section on "DEFINING MULTIPLE CONNECTIONS" doesn't have anything to say about the dsn setting. And I thought that I had set a driver setting under plugins -> Database -> connections -> dancer.

Can anyone explain what's going wrong?

Thank you very much. Jim Keenan

bigpresh commented 8 years ago

With your driver and database params now under the named connection dancer in the config, calling just database() to get the "default" handle will no longer work - it needs to be called as database('dancer').

In other words, database() with no connection name supplied will look for connection details under config->{plugins}{Database}{connections}, but database($connection_name) will look for connection details under config->{plugins}{Database}{connections}{$connection_name} - does that make sense?

jkeenan commented 8 years ago

@bigpresh, thanks; that appears to have solved the problem. In lib/mywebapp.pm (and, noting for reference, in lib/mywebapp/api.pm), I have made this change:

diff --git a/lib/mywebapp.pm b/lib/mywebapp.pm
index 5d96325..124b075 100644
--- a/lib/mywebapp.pm
+++ b/lib/mywebapp.pm
@@ -30,7 +30,7 @@ get '/login' => sub {
 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 });
+    my $user = database('dancer')->quick_select('users', { username => $user_value });

     if (! $user) {
         warning "Failed login for unrecognised user $user_value";