File Coverage

blib/lib/Prancer/Plugin/Database.pm
Criterion Covered Total %
statement 77 83 92.7
branch 15 26 57.6
condition 7 23 30.4
subroutine 15 16 93.7
pod 0 1 0.0
total 114 149 76.5


line stmt bran cond sub pod time code
1             package Prancer::Plugin::Database;
2              
3 2     2   53866 use strict;
  2         5  
  2         77  
4 2     2   8 use warnings FATAL => 'all';
  2         2  
  2         81  
5              
6 2     2   592 use version;
  2         1699  
  2         11  
7             our $VERSION = '1.04';
8              
9 2     2   1082 use Prancer::Plugin;
  2         21995  
  2         70  
10 2     2   15 use parent qw(Prancer::Plugin Exporter);
  2         2  
  2         10  
11              
12 2     2   111597 use Module::Load ();
  2         1822  
  2         42  
13 2     2   11 use Try::Tiny;
  2         3  
  2         118  
14 2     2   9 use Carp;
  2         3  
  2         221  
15              
16             our @EXPORT_OK = qw(database);
17             our %EXPORT_TAGS = ('all' => [ @EXPORT_OK ]);
18              
19             # even though this *should* work automatically, it was not
20             our @CARP_NOT = qw(Prancer Try::Tiny);
21              
22             sub load {
23 6     6 0 77558 my $class = shift;
24              
25             # already got an object
26 6 50       25 return $class if ref($class);
27              
28             # this is a singleton
29 6         11 my $instance = undef;
30             {
31 2     2   8 no strict 'refs';
  2         2  
  2         504  
  6         8  
32 6         11 $instance = \${"${class}::_instance"};
  6         31  
33 6 100       21 return $$instance if defined($$instance);
34             }
35              
36 5         17 my $self = bless({}, $class);
37              
38 5   50     30 my $config = ($self->config() && $self->config->get("database")) || {};
39 5 50 33     731 unless (defined($config) && ref($config) && ref($config) eq "HASH") {
      33        
40 0         0 croak "could not initialize database connection: no configuration found";
41             }
42              
43 5         11 my $handles = {};
44 5         7 for my $key (keys %{$config}) {
  5         19  
45 7         40 my $subconfig = $config->{$key};
46              
47 7 50 33     65 unless (defined($subconfig) && ref($subconfig) && ref($subconfig) eq "HASH" && $subconfig->{'driver'}) {
      33        
      33        
48 0         0 croak "could not initialize database connection '${key}': no database driver configuration";
49             }
50              
51 7         8 my $module = $subconfig->{'driver'};
52              
53             # try to load the module and make sure it has required subroutines
54             try {
55             # load the module
56 7     7   222 Module::Load::load($module);
57              
58             # make sure it has necessary implementation details
59 7 50       481 die "${module} does not implement 'handle'\n" unless ($module->can("handle"));
60              
61             # make the connection to the database
62 7         34 $handles->{$key} = $module->new($subconfig->{'options'}, $key);
63             } catch {
64 0 0   0   0 my $error = (defined($_) ? $_ : "unknown");
65 0         0 croak "could not initialize database connection '${key}': not able to load ${module}: ${error}";
66 7         56 };
67             }
68 5         78 $self->{'_handles'} = $handles;
69              
70             # now export the keyword with a reference to $self
71             {
72             ## no critic (ProhibitNoStrict ProhibitNoWarnings)
73 2     2   17 no strict 'refs';
  2         5  
  2         56  
  5         8  
74 2     2   13 no warnings 'redefine';
  2         3  
  2         765  
75 5         6 *{"${\__PACKAGE__}::database"} = sub {
  5         143  
76 13 0 33 13   5381 my $this = ref($_[0]) && $_[0]->isa(__PACKAGE__) ?
    50 0        
77             shift : (defined($_[0]) && $_[0] eq __PACKAGE__) ?
78             bless({}, shift) : bless({}, __PACKAGE__);
79 13         31 return $self->_database(@_);
80 5         19 };
81             }
82              
83 5         241 $$instance = $self;
84 5         26 return $self;
85             }
86              
87             sub _database {
88 13     13   16 my ($self, $connection) = @_;
89              
90 13 100       31 if ($connection) {
91 5 50       17 if (!exists($self->{'_handles'}->{$connection})) {
92 0         0 croak "could not get connection to database: no connection named '${connection}'";
93             }
94              
95 5         18 return $self->{'_handles'}->{$connection}->handle();
96             }
97              
98             # down here when there is no connection name given
99              
100             # if only one connection exists then use that one
101 8 100       7 if (scalar(keys %{$self->{'_handles'}}) == 1) {
  8         75  
102 6         7 $connection = (keys %{$self->{'_handles'}})[0];
  6         16  
103 6         25 return $self->{'_handles'}->{$connection}->handle();
104             }
105              
106             # if a connection named "default" exists then use that
107 2 100       9 if (exists($self->{'_handles'}->{'default'})) {
108 1         2 $connection = 'default';
109 1         5 return $self->{'_handles'}->{$connection}->handle();
110             }
111              
112             # if more than one connection exists then croak for that
113 1 50       1 if (scalar(keys %{$self->{'_handles'}}) > 1) {
  1         4  
114 1         178 croak "could not get connection to database: no connection name given and multiple connections exist\n";
115             }
116              
117             # no connections actually exist
118 0           croak "could not get connection to database: no connections defined\n";
119             }
120              
121             1;
122              
123             =head1 NAME
124              
125             Prancer::Plugin::Database
126              
127             =head1 SYNOPSIS
128              
129             This plugin enables connections to a database and exports a keyword to access
130             those configured connections.
131              
132             It's important to remember that when running your application in a single-
133             threaded, single-process application server like, say, L, all users of
134             your application will use the same database connection. If you are using
135             callbacks then this becomes very important and you will want to take care to
136             avoid crossing transactions or expecting a database connection or transaction
137             to be in the same state it was before a callback.
138              
139             To use a database connector, add something like this to your configuration
140             file:
141              
142             database:
143             connection-name:
144             driver: Prancer::Plugin::Database::Driver::DriverName
145             options:
146             username: test
147             password: test
148             database: test
149             hostname: localhost
150             port: 5432
151             autocommit: true
152             charset: utf8
153             connection_check_threshold: 10
154             dsn_extra:
155             RaiseError: 0
156             PrintError: 1
157             on_connect:
158             - SET search_path=public
159              
160             The "connection-name" can be anything you want it to be. This will be used when
161             requesting a connection from the plugin to determine which connection to
162             return. If only one connection is configured and no specific connection name is
163             requested then the only configured connection will be returned. If more than
164             one connection is configured and no specific connection name is requested then
165             any connection that is named "default" will be used. Otherwise an error will be
166             thrown. For example:
167              
168             use Prancer::Plugin::Database qw(database);
169              
170             Prancer::Plugin::Database->load();
171              
172             # if only one connection is configured then this will return that
173             # connection
174             my $dbh = database;
175              
176             # if multiple connections are configured and one of them is called
177             # "default" then this will return the connection named "default" or throw
178             # an error if no connection is named "default"
179             my $dbh = database;
180              
181             # this will return a configured connection named "foo" or throw an error if
182             # no connections are named "foo"
183             my $dbh = database("foo");
184              
185             =head1 OPTIONS
186              
187             =over
188              
189             =item database
190              
191             B The name of the database to connect to.
192              
193             =item username
194              
195             The username to use when connecting. If this option is not set then the default
196             is the user running the application server or the current user.
197              
198             =item password
199              
200             The password to use when connecting. If this option is not set then the default
201             is to connect with no password.
202              
203             =item hostname
204              
205             The host name of the database server. If this option is not set then the
206             default is to connect to localhost.
207              
208             =item port
209              
210             The port number on which the database server is listening. If this option is
211             not set then the default is to connect on the database's default port.
212              
213             =item autocommit
214              
215             If set to a true value -- like 1, yes, or true -- then this will enable
216             autocommit. If set to a false value -- like 0, no, or false -- then this will
217             disable autocommit. By default, autocommit is enabled.
218              
219             =item charset
220              
221             The character set to connect to the database with. If this is set to "utf8"
222             then the database connection will attempt to make UTF8 data Just Work if
223             available.
224              
225             =item connection_check_threshold
226              
227             This sets the number of seconds that must elapse between calls to get a
228             database handle before performing a check to ensure that a database connection
229             still exists and will reconnect if one does not. This handles cases where the
230             database handle hasn't been used in a while and the underlying connection has
231             gone away. If this is not set then it will default to 30 seconds.
232              
233             =item dsn_extra
234              
235             If you have any further connection parameters that need to be appended to the
236             dsn then you can put them in the configuration as a hash. This hash will be
237             merged into the default parameters and overwrite any that are duplicated. The
238             dsn parameters set by default are C to 1, C to 1, and
239             C to 0. This option will take precedence over the C
240             flag above.
241              
242             =item on_connect
243              
244             This can be an array of commands execute on a successful connection. These will
245             be executed on every connection so if the connection goes away but is re-
246             established then these commands will be run again.
247              
248             =back
249              
250             =head1 CREDIT
251              
252             This module is derived from L. Thank you to David
253             Precious.
254              
255             =head1 COPYRIGHT
256              
257             Copyright 2014, 2015 Paul Lockaby. All rights reserved.
258              
259             This library is free software; you can redistribute it and/or modify it under
260             the same terms as Perl itself.
261              
262             =head1 SEE ALSO
263              
264             =over
265              
266             =item
267              
268             L
269              
270             =back
271              
272             =cut