File Coverage

blib/lib/DBIx/Array/Connect.pm
Criterion Covered Total %
statement 51 88 57.9
branch 19 44 43.1
condition 2 11 18.1
subroutine 11 13 84.6
pod 7 7 100.0
total 90 163 55.2


line stmt bran cond sub pod time code
1             package DBIx::Array::Connect;
2 3     3   166756 use strict;
  3         6  
  3         103  
3 3     3   16 use warnings;
  3         5  
  3         81  
4 3     3   15 use base qw{Package::New};
  3         10  
  3         3015  
5 3     3   4797 use Config::IniFiles qw{};
  3         68345  
  3         80  
6 3     3   829 use Path::Class qw{};
  3         24938  
  3         3367  
7              
8             our $VERSION='0.05';
9              
10             =head1 NAME
11              
12             DBIx::Array::Connect - Database Connections from an INI Configuration File
13              
14             =head1 SYNOPSIS
15              
16             use DBIx::Array::Connect;
17             my $dbx=DBIx::Array::Connect->new->connect("mydatabase"); #isa DBIx::Array
18              
19             my $dac=DBIx::Array::Connect->new(file=>"./my.ini"); #isa DBIx::Array::Connect
20             my $dbx=$dac->connect("mydatabase"); #isa DBIx::Array
21              
22             =head1 DESCRIPTION
23              
24             Provides an easy way to construct database objects and connect to databases while providing an easy way to centralize management of database connection strings.
25              
26             This package reads database connection information from an INI formatted configuration file and returns a connected database object.
27              
28             This module is used to connect to both Oracle 10g and 11g using L on both Linux and Win32, MySQL 4 and 5 using L on Linux, and Microsoft SQL Server using L on Linux and using L on Win32 systems in a 24x7 production environment.
29              
30             =head1 USAGE
31              
32             Create an INI configuration file with the following format. The default location for the INI file is /etc/database-connections-config.ini on Linux-like systems and C:\Windows\database-connections-config.ini on Windows-like systems.
33              
34             [mydatabase]
35             connection=DBI:mysql:database=mydb;host=myhost.mydomain.tld
36             user=myuser
37             password=mypassword
38             options=AutoCommit=>1, RaiseError=>1
39              
40             Connect to the database.
41              
42             my $dbx=DBIx::Array::Connect->new->connect("mydatabase"); #isa DBIx::Array
43             my $dbh=$dbx->dbh; #if you don't want to use DBIx::Array...
44              
45             Use the L object like you normally would.
46              
47             =head1 CONSTRUCTOR
48              
49             =head2 new
50              
51             my $dac=DBIx::Array::Connect->new; #Defaults
52             my $dac=DBIx::Array::Connect->new(file=>"path/my.ini"); #Override the INI location
53              
54             =head1 METHODS
55              
56             =head2 connect
57              
58             Returns a database object for the database nickname which is an INI section name.
59              
60             my $dbx=$dac->connect($nickname); #isa DBIx::Array
61              
62             my %overrides=(
63             connection => $connection,
64             user => $user,
65             password => $password,
66             options => {},
67             execute => [],
68             );
69             my $dbx=$dac->connect($nickname, \%overrides); #isa DBIx::Array
70              
71             =cut
72              
73             sub connect {
74 0     0 1 0 my $self=shift;
75 0 0       0 my $nickname=shift or die("Error: connect method requires a database nickname parameter.");
76 0         0 my $override=shift;
77 0 0       0 $override={} unless ref($override) eq "HASH";
78 0 0 0     0 my $connection= $override->{"connection"} || $self->cfg->val($nickname, "connection")
79             or die(qq{Error: connection not defined for nickname "$nickname"});
80 0   0     0 my $user = $override->{"user"} || $self->cfg->val($nickname, "user", "");
81 0   0     0 my $password = $override->{"password"} || $self->cfg->val($nickname, "password", "");
82 0         0 my %options=();
83 0 0       0 if (ref($override->{"options"}) eq "HASH") {
84 0         0 %options=%{$override->{"options"}};
  0         0  
85             } else {
86 0         0 my $options=$self->cfg->val($nickname, "options", "");
87 0         0 %options=map {s/^\s*//;s/\s*$//;$_} split(/[,=>]+/, $options);
  0         0  
  0         0  
  0         0  
88             }
89 0         0 my $class=$self->class;
90 0         0 eval("use $class");
91 0         0 my $dbx=$class->new(name=>$nickname);
92 0         0 $dbx->connect($connection, $user, $password, \%options);
93 0 0       0 if ($dbx->can("execute")) {
94 0         0 my @execute=();
95 0 0       0 if (ref($override->{"execute"}) eq "ARRAY") {
96 0         0 @execute=@{$override->{"execute"}};
  0         0  
97             } else {
98 0 0       0 @execute=grep {defined && length} $self->cfg->val($nickname, "execute");
  0         0  
99             }
100 0         0 $dbx->execute($_) foreach @execute;
101             }
102 0         0 return $dbx;
103             }
104              
105             =head2 sections
106              
107             Returns all of the "active" section names in the INI file with the given type.
108              
109             my $list=$dac->sections("db"); #[]
110             my @list=$dac->sections("db"); #()
111             my @list=$dac->sections; #All "active" sections in INI file
112              
113             Note: active=1 is assumed active=0 is inactive
114              
115             Example:
116              
117             my @dbx=map {$dac->connect($_)} $dac->sections("db"); #Connect to all active databases of type db
118              
119             =cut
120              
121             sub sections {
122 5     5 1 10457 my $self=shift;
123 5   100     24 my $type=shift || "";
124 5         16 my @list=grep {$self->cfg->val($_, "active", "1")} $self->cfg->Sections;
  25         549  
125 5 100       117 @list=grep {$self->cfg->val($_, "type", "") eq $type} @list if $type;
  16         263  
126 5 50       92 return wantarray ? @list : \@list;
127             }
128              
129             =head2 class
130              
131             Returns the class in to which to bless objects. The "class" is assumed to be a base DBIx::Array object. This package MAY work with other objects that have a connect method that pass directly to DBI->connect. The object must have a similar execute method to support the package's execute on connect capability.
132              
133             my $class=$dac->class; #$
134             $dac->class("DBIx::Array::Export"); #If you want the exports features of DBIx::Array
135              
136             Set on construction
137              
138             my $dac=DBIx::Array::Connect->new(class=>"DBIx::Array::Export");
139              
140             =cut
141              
142             sub class {
143 0     0 1 0 my $self=shift;
144 0 0       0 $self->{"class"}=shift if @_;
145 0 0       0 $self->{"class"}="DBIx::Array" unless defined $self->{"class"};
146 0         0 return $self->{"class"};
147             }
148              
149             =head2 file
150              
151             Sets or returns the profile INI filename
152              
153             my $file=$dac->file;
154             my $file=$dac->file("./my.ini");
155              
156             Set on construction
157              
158             my $dac=DBIx::Array::Connect->new(file=>"./my.ini");
159              
160             =cut
161              
162             sub file {
163 48     48 1 55 my $self=shift;
164 48 50       105 if (@_) {
165 0         0 $self->{'file'}=shift;
166 0 0       0 die(sprintf(qq{Error: Cannot read file "%s".}, $self->{'file'})) unless -r $self->{'file'};
167             }
168 48 100       108 unless (defined $self->{'file'}) {
169 1 50       4 die(sprintf(qq{Error: path method returned a "%s"; expecting an array reference.}, ref($self->path)))
170             unless ref($self->path) eq "ARRAY";
171 1         2 foreach my $path (@{$self->path}) {
  1         2  
172 1         3 $self->{"file"}=Path::Class::file($path, $self->basename);
173 1 50       194 last if -r $self->{"file"};
174             }
175             }
176             #We may not have a vaild file here? We'll let Config::IniFiles catch the error.
177 48         257 return $self->{'file'};
178             }
179              
180             =head2 path
181              
182             Sets and returns a list of search paths for the INI file.
183              
184             my $path=$dac->path; # []
185             my $path=$dac->path(".", ".."); # []
186              
187             Default: ["/etc"] on Linux-like systems
188             Default: ['C:\Windows'] on Windows-like systems
189              
190             Overloading path is a good way to migrate from one location to another over time.
191              
192             package My::Connect;
193             use base qw{DBIx::Array::Connect};
194             sub path {[".", "..", "/etc", "/home"]};
195              
196             Put INI file in the same folder as tnsnames.ora file.
197              
198             package My::Connect::Oracle;
199             use base qw{DBIx::Array::Connect};
200             use Path::Class qw{};
201             sub path {[Path::Class::dir($ENV{"ORACLE_HOME"}, qw{network admin})]}; #not taint safe
202              
203             =cut
204              
205             sub path {
206 12     12 1 3126 my $self=shift;
207 12 100       42 $self->{"path"}=[@_] if @_;
208 12 100       42 unless (ref($self->{"path"}) eq "ARRAY") {
209 1         2 my @path=();
210 1 50       7 if ($^O eq "MSWin32") {
211 0         0 eval("use Win32");
212 0         0 push @path, eval("Win32::GetFolderPath(Win32::CSIDL_WINDOWS)");
213             } else {
214 1     1   1132 eval("use Sys::Path");
  1         56037  
  1         21  
  1         102  
215 1         67 push @path, eval("Sys::Path->sysconfdir");
216             }
217 1         21 $self->{"path"}=\@path;
218             }
219 12         62 return $self->{"path"};
220             }
221              
222             =head2 basename
223              
224             Returns the INI basename.
225              
226             You may want to overload the basename property if you inherit this package.
227              
228             package My::Connect;
229             use base qw{DBIx::Array::Connect};
230             sub basename {"whatever.ini"};
231              
232             Default: database-connections-config.ini
233              
234             =cut
235              
236             sub basename {
237 5     5 1 687 my $self=shift;
238 5 100       17 $self->{"basename"}=shift if @_;
239 5 100       18 $self->{"basename"}="database-connections-config.ini"
240             unless $self->{"basename"};
241 5         22 return $self->{"basename"};
242             }
243              
244             =head2 cfg
245              
246             Returns the L object so that you can read additional information from the INI file.
247              
248             my $cfg=$dac->cfg; #isa Config::IniFiles
249              
250             Example
251              
252             my $connection_string=$dac->cfg->val($database, "connection");
253              
254             =cut
255              
256             sub cfg {
257 46     46 1 62 my $self=shift;
258 46         77 my $file=$self->file; #support for objects that can stringify paths.
259 46 100       119 $self->{'cfg'}=Config::IniFiles->new(-file=>"$file")
260             unless ref($self->{'cfg'}) eq "Config::IniFiles";
261 46         9728 return $self->{'cfg'};
262             }
263              
264             =head1 INI File Format
265              
266             =head2 Section
267              
268             The INI section is the value that needs to be passed in the connect method which is the database nickname.
269              
270             [section]
271              
272             my $dbx=DBIx::Array::Connect->new->connect("section");
273              
274             =head2 connection
275              
276             The string passed to DBI to connect to the database.
277              
278             Examples:
279              
280             connection=DBI:CSV:f_dir=.
281             connection=DBI:mysql:database=mydb;host=myhost.mydomain.tld
282             connection=DBI:Sybase:server=mssqlserver.mydomain.tld;datasbase=mydb
283             connection=DBI:Oracle:MYTNSNAME
284              
285             =head2 user
286              
287             The string passed to DBI as the user. Default is "" for user-less drivers.
288              
289             =head2 password
290              
291             The string passed to DBI as the password. Default is "" for password-less drivers.
292              
293             =head2 options
294              
295             Split and passed as a hash reference to DBI->connect.
296              
297             options=AutoCommit=>1, RaiseError=>1, ReadOnly=>1
298              
299             =head2 execute
300              
301             Connection settings that you want to execute every time you connect
302              
303             execute=ALTER SESSION SET NLS_DATE_FORMAT = 'MM/DD/YYYY HH24:MI:SS'
304             execute=INSERT INTO mylog (mycol) VALUES ('Me')
305              
306             =head2 type
307              
308             Allows grouping database connections in groups.
309              
310             type=group
311              
312             =head2 active
313              
314             This option is used by the sections method to filter out databases that may be temporarily down.
315              
316             active=1
317             active=0
318              
319             Default: 1
320              
321             =head1 LIMITATIONS
322              
323             Once the file method has cached a filename, basename and path are ignored. Once the Config::IniFiles is constructed the file method is ignored. If you want to use two different INI files, you should construct two different objects.
324              
325             The file, path and basename methods are common exports from other packages. Be wary!
326              
327             =head1 BUGS
328              
329             Send email to author and log on RT.
330              
331             =head1 SUPPORT
332              
333             DavisNetworks.com supports all Perl applications including this package.
334              
335             =head1 AUTHOR
336              
337             Michael R. Davis
338             CPAN ID: MRDVT
339             Satellite Tracking of People, LLC
340             mdavis@stopllc.com
341             http://www.stopllc.com/
342              
343             =head1 COPYRIGHT
344              
345             This program is free software licensed under the...
346              
347             The General Public License (GPL)
348             Version 2, June 1991
349              
350             The full text of the license can be found in the LICENSE file included with this module.
351              
352             =head1 SEE ALSO
353              
354             =head2 The Building Blocks
355              
356             L, L, L
357              
358             =head2 The Competition
359              
360             L uses a CSV file to store data. The constructor is wrapper around DBI->connect.
361              
362             my $dbh = DBIx::MyPassword->connect("user");
363              
364             L uses an INI file to store data. It uses encrypted passwords and the constructor returns array reference to feed into DBI->connect.
365              
366             my $dbh = DBI->connect(@{DBIx::PasswordIniFile->new(%arg)->getDBIConnectParams})
367              
368             L uses and internal hash reference to store data. The constructor is wrapper around DBI->connect.
369              
370             my $dbh = DBIx::Password->connect("user");
371              
372             =head2 The Comparison
373              
374             L uses an INI file to store data. The constructor returns a L object which is a wrapper around DBI.
375              
376             my $dbx = DBIx::Array::Connect->new->connect("nickname");
377             my $dbh = $dbx->dbh; #if you don't want to use DBIx::Array...
378              
379             =cut
380              
381             1;