File Coverage

blib/lib/DBIx/Config.pm
Criterion Covered Total %
statement 73 75 97.3
branch 36 40 90.0
condition 3 6 50.0
subroutine 18 18 100.0
pod 4 10 40.0
total 134 149 89.9


line stmt bran cond sub pod time code
1             package DBIx::Config;
2 11     11   275459 use 5.005;
  11         105  
3 11     11   57 use warnings;
  11         20  
  11         349  
4 11     11   60 use strict;
  11         39  
  11         246  
5 11     11   16919 use DBI;
  11         203276  
  11         735  
6 11     11   6331 use File::HomeDir;
  11         60764  
  11         11397  
7              
8             our $VERSION = '0.000005'; # 0.0.5
9             $VERSION = eval $VERSION;
10              
11             sub new {
12 16     16 0 3869 my ( $class, $args ) = @_;
13            
14 16         60 my $self = bless {
15             config_paths => [
16             get_env_vars(),
17             './dbic',
18             './dbi',
19             File::HomeDir->my_home . '/.dbic',
20             File::HomeDir->my_home . '/.dbi',
21             '/etc/dbic',
22             '/etc/dbi',
23             ],
24             config_files => [],
25             }, $class;
26              
27 16         1211 for my $arg ( keys %{$args} ) {
  16         73  
28 8 50       151 $self->$arg( delete $args->{$arg} ) if $self->can( $arg );
29             }
30              
31 16 50       68 die "Unknown arguments to the constructor: " . join( " ", keys %$args )
32             if keys( %$args );
33              
34 16         80 return $self;
35             }
36              
37             sub get_env_vars {
38 16 50   16 0 72 if ( exists $ENV{DBIX_CONFIG_DIR} ) {
39 0         0 return ($ENV{DBIX_CONFIG_DIR}.'/dbic', $ENV{DBIX_CONFIG_DIR}.'/dbi');
40             }
41 16         113 return ();
42             }
43              
44             sub connect {
45 10     10 1 35111 my ( $self, @info ) = @_;
46              
47 10 100 66     90 if ( ( ! ref($self) ) || ( ref($self) ne __PACKAGE__) ) {
48 3         23 return $self->new->connect(@info);
49             }
50            
51 7         32 return DBI->connect( $self->connect_info(@info) );
52             }
53              
54             sub connect_info {
55 9     9 0 51 my ( $self, @info ) = @_;
56              
57 9 50 33     69 if ( ( ! ref($self) ) || ( ref($self) ne __PACKAGE__) ) {
58 0         0 return $self->new->connect_info(@info);
59             }
60              
61 9         36 my $config = $self->_make_config(@info);
62              
63             # Take responsibility for passing through normal-looking
64             # credentials.
65             $config = $self->default_load_credentials($config)
66 9 100       94 unless $config->{dsn} =~ /dbi:/i;
67              
68 9         338 return $self->_dbi_credentials($config);
69             }
70              
71             # Normalize arguments into a single hash. If we get a single hashref,
72             # return it.
73             # Check if $user and $pass are hashes to support things like
74             # ->connect( 'CONFIG_FILE', { hostname => 'db.foo.com' } );
75              
76             sub _make_config {
77 29     29   8288 my ( $class, $dsn, $user, $pass, $dbi_attr, $extra_attr ) = @_;
78 29 100       103 return $dsn if ref $dsn eq 'HASH';
79              
80              
81             return {
82             dsn => $dsn,
83 24 100       140 %{ref $user eq 'HASH' ? $user : { user => $user }},
84 24 100       96 %{ref $pass eq 'HASH' ? $pass : { password => $pass }},
85 24 100       138 %{$dbi_attr || {} },
86 24 100       44 %{ $extra_attr || {} }
  24         202  
87             };
88             }
89              
90             # DBI's ->connect expects
91             # ( "dsn", "user", "password", { option_key => option_value } )
92             # this function changes our friendly hashref into this format.
93              
94             sub _dbi_credentials {
95 16     16   56 my ( $class, $config ) = @_;
96              
97             return (
98             delete $config->{dsn},
99             delete $config->{user},
100             delete $config->{password},
101 16         242 $config,
102             );
103             }
104              
105             sub default_load_credentials {
106 11     11 0 29 my ( $self, $connect_args ) = @_;
107            
108             # To allow overriding without subclassing, if you pass a coderef
109             # to ->load_credentials, we will replace our default load_credentials
110             # without that function.
111 11 100       33 if ( $self->load_credentials ) {
112 1         4 return $self->load_credentials->( $self, $connect_args );
113             }
114            
115 10         2121 require Config::Any; # Only loaded if we need to load credentials.
116              
117             # While ->connect is responsible for returning normal-looking
118             # credential information, we do it here as well so that it can be
119             # independently unit tested.
120 10 100       45561 return $connect_args if $connect_args->{dsn} =~ /^dbi:/i;
121              
122             # If we have ->config_files, we'll use those and load_files
123             # instead of the default load_stems.
124 8         44 my %cf_opts = ( use_ext => 1 );
125 8 100       27 my $ConfigAny = @{$self->config_files}
  8         29  
126             ? Config::Any->load_files({ files => $self->config_files, %cf_opts })
127             : Config::Any->load_stems({ stems => $self->config_paths, %cf_opts });
128              
129 8         86069 return $self->default_filter_loaded_credentials(
130             $self->_find_credentials( $connect_args, $ConfigAny ),
131             $connect_args
132             );
133              
134             }
135              
136             # This will look through the data structure returned by Config::Any
137             # and return the first instance of the database credentials it can
138             # find.
139             sub _find_credentials {
140 8     8   57 my ( $class, $connect_args, $ConfigAny ) = @_;
141            
142 8         53 for my $cfile ( @$ConfigAny ) {
143 8         43 for my $filename ( keys %$cfile ) {
144 8         21 for my $database ( keys %{$cfile->{$filename}} ) {
  8         41  
145 11 100       79 if ( $database eq $connect_args->{dsn} ) {
146 8         69 return $cfile->{$filename}->{$database};
147             }
148             }
149             }
150             }
151             }
152              
153             sub default_filter_loaded_credentials {
154 8     8 0 31 my ( $self, $loaded_credentials,$connect_args ) = @_;
155 8 100       46 if ( $self->filter_loaded_credentials ) {
156 1         7 return $self->filter_loaded_credentials->(
157             $self, $loaded_credentials,$connect_args
158             );
159             }
160 7         94 return $loaded_credentials;
161             }
162              
163             # Assessors
164             sub config_paths {
165 13     13 0 30 my $self = shift;
166 13 100       49 $self->{config_paths} = shift if @_;
167 13         78 return $self->{config_paths};
168             }
169              
170             sub config_files {
171 10     10 1 19 my $self = shift;
172 10 100       35 $self->{config_files} = shift if @_;
173 10         63 return $self->{config_files};
174             }
175              
176             sub filter_loaded_credentials {
177 10     10 1 28 my $self = shift;
178 10 100       47 $self->{filter_loaded_credentials} = shift if @_;
179 10         53 return $self->{filter_loaded_credentials};
180             }
181              
182             sub load_credentials {
183 13     13 1 26 my $self = shift;
184 13 100       39 $self->{load_credentials} = shift if @_;
185 13         43 return $self->{load_credentials};
186             }
187              
188             1;
189              
190             =head1 NAME
191              
192             DBIx::Config - Manage credentials for DBI
193              
194             =head1 DESCRIPTION
195              
196             DBIx::Config wraps around L to provide a simple way of loading database
197             credentials from a file. The aim is make it simpler for operations teams to
198             manage database credentials.
199              
200             =head1 SYNOPSIS
201              
202             Given a file like C, containing:
203              
204             MY_DATABASE:
205             dsn: "dbi:Pg:host=localhost;database=blog"
206             user: "TheDoctor"
207             password: "dnoPydoleM"
208             TraceLevel: 1
209              
210             The following code would allow you to connect the database:
211              
212             #!/usr/bin/perl
213             use warnings;
214             use strict;
215             use DBIx::Config;
216              
217             my $dbh = DBIx::Config->connect( "MY_DATABASE" );
218              
219             Of course, backwards compatibility is kept, so the following would also work:
220              
221             #!/usr/bin/perl
222             use warnings;
223             use strict;
224             use DBIx::Config;
225              
226             my $dbh = DBIx::Config->connect(
227             "dbi:Pg:host=localhost;database=blog",
228             "TheDoctor",
229             "dnoPydoleM",
230             {
231             TraceLevel => 1,
232             },
233             );
234              
235             For cases where you may use something like C, a
236             method is provided that will simply return the connection credentials:
237              
238              
239             !/usr/bin/perl
240             use warnings;
241             use strict;
242             use DBIx::Connector;
243             use DBIx::Config;
244              
245             my $conn = DBIx::Connector->new(DBIx::Config->connect_info("MY_DATABASE"));
246              
247             =head1 CONFIG FILES
248              
249             By default the following configuration files are examined, in order listed,
250             for credentials. Configuration files are loaded with L. You
251             should append the extention that Config::Any will recognize your file in
252             to the list below. For instance ./dbic will look for files such as
253             C<./dbic.yaml>, C<./dbic.conf>, etc. For documentation on acceptable files
254             please see L. The first file which has the given credentials
255             is used.
256              
257             =over 4
258              
259             =item * C<$ENV{DBIX_CONFIG_DIR}> . '/dbic',
260              
261             C<$ENV{DBIX_CONFIG_DIR}> can be configured at run-time, for instance:
262              
263             DBIX_CONFIG_DIR="/var/local/" ./my_program.pl
264              
265             =item * C<$ENV{DBIX_CONFIG_DIR}> . '/dbi',
266              
267             C<$ENV{DBIX_CONFIG_DIR}> can be configured at run-time, for instance:
268              
269             DBIX_CONFIG_DIR="/var/local/" ./my_program.pl
270              
271             =item * ./dbic
272              
273             =item * ./dbi
274              
275             =item * $HOME/.dbic
276              
277             =item * $HOME/.dbi
278              
279             =item * /etc/dbic
280              
281             =item * /etc/dbi
282              
283             =item * /etc/dbi
284              
285             =back
286              
287             =head1 USE SPECIFIC CONFIG FILES
288              
289             If you would rather explicitly state the configuration files you
290             want loaded, you can use the class accessor C
291             instead.
292              
293             #!/usr/bin/perl
294             use warnings;
295             use strict;
296             use DBIx::Config
297              
298             my $DBI = DBIx::Config->new( config_files => [
299             '/var/www/secret/dbic.yaml',
300             '/opt/database.yaml',
301             ]);
302             my $dbh = $DBI->connect( "MY_DATABASE" );
303              
304             This will check the files, C,
305             and C in the same way as C,
306             however it will only check the specific files, instead of checking
307             for each extension that L supports. You MUST use the
308             extension that corresponds to the file type you are loading.
309             See L for information on supported file types and
310             extension mapping.
311              
312             =head1 OVERRIDING
313              
314             =head2 config_files
315              
316             The configuration files may be changed by setting an accessor:
317              
318             #!/usr/bin/perl
319             use warnings;
320             use strict;
321             use DBIx::Config
322              
323             my $DBI = DBIx::Config->new(config_paths => ['./dbcreds', '/etc/dbcreds']);
324             my $dbh = $DBI->connect( "MY_DATABASE" );
325              
326             This would check, in order, C in the current directory, and then C,
327             checking for valid configuration file extentions appended to the given file.
328              
329             =head2 filter_loaded_credentials
330              
331             You may want to change the credentials that have been loaded, before they are used
332             to connect to the DB. A coderef is taken that will allow you to make programatic
333             changes to the loaded credentials, while giving you access to the origional data
334             structure used to connect.
335              
336             DBIx::Config->new(
337             filter_loaded_credentials => sub {
338             my ( $self, $loaded_credentials, $connect_args ) = @_;
339             ...
340             return $loaded_credentials;
341             }
342             )
343              
344             Your coderef will take three arguments.
345              
346             =over 4
347              
348             =item * C<$self>, the instance of DBIx::Config your code was called from. C
349              
350             =item * C<$loaded_credentials>, the credentials loaded from the config file.
351              
352             =item * C<$connect_args>, the normalized data structure of the inital C call.
353              
354             =back
355              
356             Your coderef should return the same structure given by C<$loaded_credentials>.
357              
358             As an example, the following code will use the credentials from C, but
359             use its a hostname defined in the code itself.
360              
361             C (note C):
362              
363             MY_DATABASE:
364             dsn: "DBI:mysql:database=students;host=%s;port=3306"
365             user: "WalterWhite"
366             password: "relykS"
367              
368             The Perl script:
369              
370             #!/usr/bin/perl
371             use warnings;
372             use strict;
373             use DBIx::Config;
374              
375             my $dbh = DBIx::Config->new(
376             # If we have %s, replace it with a hostname.
377             filter_loaded_credentials => sub {
378             my ( $self, $loaded_credentials, $connect_args ) = @_;
379              
380             if ( $loaded_credentials->{dsn} =~ /\%s/ ) {
381             $loaded_credentials->{dsn} = sprintf(
382             $loaded_credentials->{dsn}, $connect_args->{hostname}
383             );
384             }
385             return $loaded_credentials;
386             }
387             )->connect( "MY_DATABASE", { hostname => "127.0.0.1" } );
388              
389             =head2 load_credentials
390              
391             Override this function to change the way that DBIx::Config loads credentials.
392             The function takes the class name, as well as a hashref.
393              
394             If you take the route of having ->connect('DATABASE') used as a key for whatever
395             configuration you are loading, DATABASE would be $config->{dsn}
396              
397             $obj->connect(
398             "SomeTarget",
399             "Yuri",
400             "Yawny",
401             {
402             TraceLevel => 1
403             }
404             );
405              
406             Would result in the following data structure as $config in load_credentials($self, $config):
407              
408             {
409             dsn => "SomeTarget",
410             user => "Yuri",
411             password => "Yawny",
412             TraceLevel => 1,
413             }
414              
415             Currently, load_credentials will NOT be called if the first argument to ->connect()
416             looks like a valid DSN. This is determined by match the DSN with /^dbi:/i.
417              
418             The function should return the same structure. For instance:
419              
420             #!/usr/bin/perl
421             use warnings;
422             use strict;
423             use DBIx::Config;
424             use LWP::Simple;
425             use JSON;
426              
427             my $DBI = DBIx::Config->new(
428             load_credentials => sub {
429             my ( $self, $config ) = @_;
430            
431             return decode_json(
432             get( "http://someserver.com/v1.0/database?name=" . $config->{dsn} )
433             );
434             }
435             )
436              
437             my $dbh = $DBI->connect( "MAGIC_DATABASE" );
438              
439             =head1 SEE ALSO
440              
441             =over 4
442              
443             =item * L
444              
445             =back
446              
447             =head1 AUTHOR
448              
449             =over 4
450              
451             =item * Kaitlyn Parkhurst (SymKat) Isymkat@symkat.comE> (L)
452              
453             =back
454              
455             =head1 CONTRIBUTORS
456              
457             =over 4
458              
459             =item * Matt S. Trout (mst) Imst@shadowcat.co.ukE>
460              
461             =back
462              
463             =head1 COPYRIGHT
464              
465             Copyright (c) 2012 the DBIx::Config L and L as listed
466             above.
467              
468             =head1 LICENSE
469              
470             This library is free software and may be distributed under the same terms as
471             perl itself.
472              
473             =head1 AVAILABILITY
474              
475             The latest version of this software is available at
476             L
477