File Coverage

blib/lib/DBIx/ParseDSN/Default.pm
Criterion Covered Total %
statement 94 95 98.9
branch 16 20 80.0
condition 13 21 61.9
subroutine 24 24 100.0
pod 11 11 100.0
total 158 171 92.4


line stmt bran cond sub pod time code
1             package DBIx::ParseDSN::Default;
2              
3 8     8   296246 use v5.8.8;
  8         29  
  8         555  
4              
5 8     8   1037 use utf8::all;
  8         81122  
  8         82  
6 8     8   15869 use strict;
  8         20  
  8         270  
7 8     8   6614 use autodie;
  8         143831  
  8         73  
8 8     8   49715 use warnings;
  8         17  
  8         330  
9 8     8   40 use Carp qw< carp croak confess cluck >;
  8         16  
  8         649  
10 8     8   19911 use DBI; # will use parse_dsn from here
  8         166353  
  8         584  
11 8     8   10735 use URI;
  8         49776  
  8         347  
12              
13 8     8   3662 use version; our $VERSION = qv('0.9.3');
  8         8586  
  8         71  
14              
15 8     8   11756 use Moo;
  8         104667  
  8         57  
16 8     8   20098 use namespace::clean;
  8         63487  
  8         66  
17 8     8   14104 use MooX::Aliases;
  8         39307  
  8         57  
18 8     8   11726 use MooX::HandlesVia;
  8         6485  
  8         49  
19              
20             has dsn => ( is => "ro", required => 1 );
21              
22             has database => ( is => "rw", alias => [qw/db dbname/] );
23             has host => ( is => "rw", alias => "server" );
24             has port => ( is => "rw" );
25             has driver => ( is => "rw" );
26             has scheme => ( is => "rw", default => "dbi" );
27              
28             has attr => (
29             handles_via => "Hash",
30             is => "ro",
31             default => sub {{}},
32             handles => {
33             set_attr => "set",
34             get_attr => "get",
35             delete_attr => "delete",
36             attributes => "elements",
37             }
38             );
39              
40             around host => sub {
41              
42             my $orig = shift;
43             my $self = shift;
44              
45             return $self->$orig unless my ($host) = @_;
46              
47             $host =~ s/^tcp://;
48              
49             if ( $host =~ s/:(\d+)// ) {
50             $self->port($1);
51             }
52              
53             return $self->$orig($host);
54              
55             };
56              
57             sub names_for_database {
58             return (
59 25     25 1 105 qw/database dbname name db/,
60             "sid", ## Oracle
61             "file name", "initialcatalog", ## from ADO, but generic
62             ## enough to allow in this
63             ## module
64             );
65             }
66             sub names_for_host {
67 26     26 1 71 return qw/host hostname server/;
68             }
69             sub names_for_port {
70 26     26 1 58 return qw/port/;
71             }
72             sub known_attribute_hash {
73              
74 26     26 1 50 my $self = shift;
75 26         43 my %h;
76              
77 26         102 my @db_names = map { lc $_ } $self->names_for_database;
  176         341  
78 26         248 @h{@db_names} = ("database") x @db_names;
79              
80 26         123 my @h_names = map { lc $_ } $self->names_for_host;
  78         250  
81 26         143 @h{@h_names} = ("host") x @h_names;
82              
83 26         112 my @p_names = map { lc $_ } $self->names_for_port;
  26         82  
84 26         114 @h{@p_names} = ("port") x @p_names;
85              
86 26         274 return %h;
87              
88             }
89              
90             sub dsn_parts {
91 106     106 1 673 my $self = shift;
92 106         584 return DBI->parse_dsn( $self->dsn );
93             }
94              
95             sub dbd_driver {
96 18     18 1 62111 my $self = shift;
97 18         84 my $driver = "DBD::" . $self->driver;
98 18         89 return $driver;
99             }
100             sub driver_attr {
101              
102 18     18 1 34 my $self = shift;
103 18         57 my ( $scheme, $driver, $attr, $attr_hash, $dsn ) = $self->dsn_parts;
104              
105 18         471 return $attr_hash;
106              
107             }
108             sub driver_dsn {
109 44     44 1 81 my $self = shift;
110 44         118 return ($self->dsn_parts)[4];
111             }
112              
113             sub is_remote {
114 4     4 1 3297 my $self = shift;
115 4         15 return not $self->is_local
116             }
117             sub is_local {
118 6     6 1 13909 my $self = shift;
119              
120             ## not much the default can do. if database exists as a file we
121             ## guess its a file based database and hence local
122 6 50 0     202 if ( defined $self->host and
    100 33        
123             (
124             lc $self->host eq "localhost" or
125             $self->host eq "127.0.0.1"
126             )
127             ) {
128 0         0 return 1;
129             }
130             elsif ( -f $self->database ) {
131 2         16 return 1;
132             }
133              
134 4         74 confess "Cannot determine if db is local";
135              
136             }
137              
138             sub parse {
139              
140             ## look for the following in the driver dsn:
141             ## 1: database: database dbname name db
142             ## 2: host: hostname host server
143             ## 3: port: port
144              
145             ## Assumes ";"-separated parameters in driver dsn
146             ## If driver dsn is one argument, its assumed to be the database
147              
148 26     26 1 67 my $self = shift;
149              
150 26         109 $self->driver( ($self->dsn_parts)[1] );
151              
152 26         771 my @pairs = split /;/, $self->driver_dsn;
153              
154 26         553 my %known_attr = $self->known_attribute_hash;
155              
156 26         93 for (@pairs) {
157              
158 46         4511 my($k,$v) = split /=/, $_, 2;
159              
160             ## An Oracle special case that would otherwise mess things up
161 46 100 100     262 if ( $self->driver eq "Oracle" and $k eq "SERVER" ) {
162             ## example: SERVER=POOLED
163 1         4 next;
164             }
165              
166             ## a //foo:xyz/bar type of uri, like Oracle
167 45 100 66     346 if ( $k =~ m|^//.+/.+| and not defined $v and @pairs == 1 ) {
    100 66        
      66        
168              
169             ## For this we offer something that works with oracle
170 2         21 my $u = URI->new;
171 2         5575 $u->opaque($k);
172              
173 2         203 my @p = $u->path_segments;
174              
175             ## 2nd part of path is database
176 2 50       84 if ( $p[1] ) {
177 2         13 $self->database($p[1]);
178             };
179              
180             ## host should be ok
181 2 50       9 if ( my $host = $u->authority ) {
182              
183             ## might contain port
184 2 50       77 if ( $host =~ s/:(\d+)// ) {
185 2         14 $self->port($1);
186             }
187              
188 2         65 $self->host( $host );
189              
190             }
191              
192             }
193             elsif (not defined $v and @pairs == 1) {
194 3         10 $self->database($k);
195             }
196              
197 45 100       156 if ( my $known_attr = $known_attr{lc $k} ) {
198 32         344 $self->$known_attr( $v );
199             }
200              
201 45         1023 $self->set_attr($k, $v);
202              
203             }
204              
205             ## Another Oracle speciality, strip ":POOLED" from db
206 26 100 100     8312 if ( $self->driver eq "Oracle" and
207             ( my $new_db = $self->database ) =~ s/:POOLED$// ) {
208 1         36 $self->database($new_db);
209             }
210              
211             }
212              
213             ## intercept constructor to allow 1st arg DSN and 2nd arg user string,
214             ## which may contain db name
215             around BUILDARGS => sub {
216              
217             my $orig = shift;
218             my $class = shift;
219              
220             my @args = @_;
221              
222             ## if first arg is a hash, work with that, otherwise start a new
223             ## empty hash
224             my %h = %{ ref $args[0] eq "HASH" ? $args[0] : {} };
225              
226             ## 1st arg can be dsn if not a hash
227             if ( defined $args[0] and ref $args[0] ne "HASH" ) {
228             $h{dsn} = $args[0];
229             }
230             ## look for db in user string - will not override one found in dsn
231             if ( defined $args[1] ) {
232             if ( $args[1] =~ /@(.+)$/ ) {
233             (my $db = $1) =~ s|/.*||;
234             $h{database} = $db;
235             }
236             }
237              
238             return $class->$orig(\%h)
239              
240             };
241              
242             sub BUILD {};
243              
244             ## call parse after build
245             after BUILD => sub {
246             my $self = shift;
247             $self->parse;
248             };
249              
250             1; # Magic true value required at end of module
251             __END__
252              
253             =encoding utf8
254              
255             =head1 NAME
256              
257             DBIx::ParseDSN::Default - A default DSN parser, moose based. You can
258             use this as is, or subclass it. DBIx::ParseDSN uses this class unless
259             it finds a better parser.
260              
261             It can be used directly to parse a DSN, but use instead
262             L<DBIx::ParseDSN/parse_dsn> which is the intended way to achieve this.
263              
264             =head1 VERSION
265              
266             This document describes DBIx::ParseDSN::Default version 0.9.3
267              
268             =head1 SYNOPSIS
269              
270             ## Use it directly:
271              
272             use DBIx::ParseDSN::Default;
273              
274             my $dsn = DBIx::ParseDSN::Default->new( "dbi:Foo:/bar/baz" );
275              
276             ## Subclass:
277             {
278             package DBIx::ParseDSN::OddBall;
279              
280             use Moo;
281             extends 'DBIx::ParseDSN::Default';
282              
283             sub names_for_database{ return qw/bucket/ }
284              
285             }
286              
287             package main;
288              
289             use DBIx::ParseDSN;
290              
291             my $dsn = parse_dsn( "dbi:OddBall:bucket=foo" )
292              
293             $dsn->database; ## "foo"
294              
295             =head1 DESCRIPTION
296              
297             This is a default DSN parser. It is not specific to any driver. It can
298             safely be used as a base for driver specfic parsers.
299              
300             It handles the most common database drivers. See test files for
301             details.
302              
303             =head1 DSN ATTIRIBUTES
304              
305             =head2 database
306              
307             =head2 dbname
308              
309             =head2 db
310              
311             Database attribute of the DSN. See L</names_for_database>.
312              
313             =head2 host
314              
315             =head2 server
316              
317             Server address of the connection. If any. See L</names_for_host>.
318              
319             =head2 port
320              
321             Port to connect to on the server. See L</names_for_port>
322              
323             =head1 OTHER METHODS
324              
325             =head2 parse( $dsn )
326              
327             A method used internally. Parses the DSN.
328              
329             =head2 driver_attr
330              
331             Any attributes to the driver, ie foo=bar in
332             dbi:SQLite(foo=bar):db.sqlite. See L<DBI/parse_dsn>.
333              
334             =head2 driver_dsn
335              
336             The 3rd part of the dsn string which is driver specific.
337              
338             =head2 dsn_parts
339              
340             The 5 values returned by DBI->parse_dsn
341              
342             =head2 is_local
343              
344             True if the dsn is local. File based db drivers are local, and network
345             connections to localhost or 127.0.0.1 are local.
346              
347             =head2 is_remote
348              
349             The oposite of is_local
350              
351             =head2 names_for_database
352              
353             Name variations for the database attribute. This class uses
354             qw/database dbname db/.
355              
356             =head2 names_for_host
357              
358             Name variations for the host attribute. This class uses qw/host
359             server/.
360              
361             =head2 names_for_port
362              
363             Name variations for the port attribute. This class uses C<port>. This
364             is included for completeness to follow the pattern used for
365             C<database> and C<host> but is likely never to be anything other than
366             just C<port>.
367              
368             =head2 known_attribute_hash
369              
370             Combines information for the three above methods to compose a hash
371             useful for translating names, eg:
372              
373             (
374             database => "database",
375             dbname => "database",
376             db => "database",
377             server => "host",
378             hostname => "host",
379             )
380              
381             This method is mainly for internal use.
382              
383             =head2 dbd_driver
384              
385             The perl module driver for this specific dsn. Currently the 2nd value
386             of the dsn string prefixed by DBD:: , ie DBD::SQLite.
387              
388             =head1 BUGS AND LIMITATIONS
389              
390             No bugs have been reported.
391              
392             Please report any bugs or feature requests to
393             C<bug-bug-dbix-parsedsn::parser::default@rt.cpan.org>, or through the web interface at
394             L<http://rt.cpan.org>.
395              
396             =head1 SEE ALSO
397              
398             L<DBIx::ParseDSN>
399              
400             =head1 AUTHOR
401              
402             Torbjørn Lindahl C<< <torbjorn.lindahl@gmail.com> >>
403              
404              
405             =head1 LICENCE AND COPYRIGHT
406              
407             Copyright (c) 2014, Torbjørn Lindahl C<< <torbjorn.lindahl@gmail.com> >>. All rights reserved.
408              
409             This module is free software; you can redistribute it and/or
410             modify it under the same terms as Perl itself. See L<perlartistic>.
411              
412              
413             =head1 DISCLAIMER OF WARRANTY
414              
415             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
416             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
417             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
418             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
419             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
420             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
421             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
422             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
423             NECESSARY SERVICING, REPAIR, OR CORRECTION.
424              
425             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
426             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
427             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
428             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
429             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
430             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
431             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
432             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
433             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
434             SUCH DAMAGES.