File Coverage

blib/lib/DBIx/Array/Connect.pm
Criterion Covered Total %
statement 92 101 91.0
branch 32 50 64.0
condition 5 11 45.4
subroutine 16 16 100.0
pod 8 8 100.0
total 153 186 82.2


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