Laura Laura - 7 months ago 22
Perl Question

How to use Dancer2::Plugin::Database when code is not organized like a demo site

All code examples I have seen related to Dancer2 and database connections put all Dancer2 code directly in the anonymous subroutines attached to the various 'get' and 'put' requests.

I would like to organize my code in a way that myServices.pm file is essentially just a router to the other code files that contain the meat of what is executing. I can successfully use the params keyword in the MyServices::Submission module. However, I can't seem to use the database keyword from Dancer2::Plugin::Database in this context.

myServices.pm:

package myServices;

use Dancer2;
use Dancer2::Plugin::REST;
use Dancer2::Plugin::Database;
use Data::Dumper;
use MyServices::Submission;

get '/direct' => sub {
my $dbh = database;
return 'success';
};

get '/indirect' => sub {
MyServices::Submission::databaseTest();
};

true;


MyServices/Submission.pm:

package MyServices::Submission;

use Dancer2;
use Dancer2::Plugin::REST;
use Dancer2::Plugin::Database;
use Data::Dumper;


sub databaseTest{
my $dbh = database;
return 'success';
}

true;


The call to /direct returns 'success'.

The call to /indirect returns Error 500 - Internal Server Error with the message "Can't get a database connection without settings supplied!". It then prints out my settings, including the correct database configuration.

My configuration file must be fine, because the call to /direct is a success.

Q's:


  1. Can anyone else replicate this behavior? (Make sure I'm not missing
    something obvious.)

  2. Is there a way to successfully use Dancer2::Plugin::Database in the MyServices::Submission module, or
    do I need to search for another db connection solution in order to
    meet my code organization needs?


Answer

When you call use Dancer2; in MyServices::Submission, you're actually creating a separate Dancer2 app:

As soon as you import Dancer2 (by calling use Dancer2), you create a Dancer2 App. It will use your class name (defined by either the package function in Perl or the default in Perl: main) to define the Dancer2 App name. This is how Dancer2 will recognize your application.

This introduces an interesting situation. If you wish to separate your App to multiple files, you're actually creating multiple applications.

So what?

This means that any engine defined in an application, because the application is a complete separate scope, will not be available to a different application:

package MyApp::User {
      use Dancer2;
      set serializer => 'JSON';
      get '/view' => sub {...};
 }

package MyApp::User::Edit {
     use Dancer2;
     get '/edit' => sub {...};
 }

These are two different Dancer2 Apps. They have different scopes, contexts, and thus different engines. While MyApp::User has a serializer (the JSON one) defined, MyApp::User::Edit will not have that configuration.


You can use the appname option when you import Dancer2 to say that your module should extend an app instead of creating a new one:

package MyServices::Submission;

use Dancer2 appname => 'MyApp';
use Dancer2::Plugin::Database;

sub databaseTest {
    my $dbh = database;                                 
    return 'success';
}

1;

Now the configuration and engines from the main app will be available inside MyServices::Submission. You can even add additional routes here.


As an aside, splitting up your application like this is a great idea; if you're interested in other techniques, someone on the Dancer users mailing list wrote up some pretty thorough recommendations on how to organize medium- to large-scale Dancer applications.

Comments