File Coverage

blib/lib/Data/Phrasebook/Loader/DBI.pm
Criterion Covered Total %
statement 68 68 100.0
branch 30 30 100.0
condition 11 12 91.6
subroutine 10 10 100.0
pod 4 4 100.0
total 123 124 99.1


line stmt bran cond sub pod time code
1             package Data::Phrasebook::Loader::DBI;
2 6     6   136801 use strict;
  6         16  
  6         269  
3 6     6   36 use warnings FATAL => 'all';
  6         12  
  6         307  
4 6     6   34 use base qw( Data::Phrasebook::Loader::Base Data::Phrasebook::Debug );
  6         15  
  6         10305  
5 6     6   11758 use Carp qw( croak );
  6         12  
  6         250  
6 6     6   13126 use DBI;
  6         114866  
  6         6310  
7              
8             our $VERSION = '0.16';
9              
10             =head1 NAME
11              
12             Data::Phrasebook::Loader::DBI - Absract your phrases with a DBI driver.
13              
14             =head1 SYNOPSIS
15              
16             use Data::Phrasebook;
17              
18             my $q = Data::Phrasebook->new(
19             class => 'Fnerk',
20             loader => 'DBI',
21             file => {
22             dsn => 'dbi:mysql:database=test',
23             dbuser => 'user',
24             dbpass => 'pass',
25             dbtable => 'phrasebook',
26             dbcolumns => ['keyword','phrase','dictionary'],
27             }
28             );
29              
30             OR
31              
32             my $q = Data::Phrasebook->new(
33             class => 'Fnerk',
34             loader => 'DBI',
35             file => {
36             dbh => $dbh,
37             dbtable => 'phrasebook',
38             dbcolumns => ['keyword','phrase','dictionary'],
39             }
40             );
41              
42             $q->delimiters( qr{ \[% \s* (\w+) \s* %\] }x );
43             my $phrase = $q->fetch($keyword);
44              
45             =head1 DESCRIPTION
46              
47             This class loader implements phrasebook patterns using DBI.
48              
49             Phrases can be contained within one or more dictionaries, with each phrase
50             accessible via a unique key. Phrases may contain placeholders, please see
51             L for an explanation of how to use these. Groups of phrases
52             are kept in a dictionary. The first dictionary is used as the default, unless
53             a specific dictionary is requested.
54              
55             This module provides a base class for phrasebook implementations via a database.
56             Note that the order of table columns is significant. If there is no dictionary
57             field, all entries are assumed to be part of the default dictionary.
58              
59             =head1 DICTIONARIES
60              
61             In the instance where a dictionary column is specified, but no dictionary name
62             is set, all dictionaries are searched, returned in lexical order native to the
63             DB.
64              
65             =head1 INHERITANCE
66              
67             L inherits from the base class
68             L.
69             See that module for other available methods and documentation.
70              
71             =head1 METHODS
72              
73             =head2 load
74              
75             Given the appropriate settings, connects to the designated database. Note that
76             for consistency, the connection string and other database specific settings,
77             are passed via a hashref.
78              
79             $loader->load( $file );
80              
81             The hashref can be either:
82              
83             my $file => {
84             dsn => 'dbi:mysql:database=test',
85             dbuser => 'user',
86             dbpass => 'pass',
87             dbtable => 'phrasebook',
88             dbcolumns => ['keyword','phrase','dictionary'],
89             };
90              
91             which will create a connection to the specified database. Or:
92              
93             my $file => {
94             dbh => $dbh,
95             dbtable => 'phrasebook',
96             dbcolumns => ['keyword','phrase','dictionary'],
97             };
98              
99             which will reuse and already established connection.
100              
101             This method is used internally by L's
102             C method, to initialise the data store.
103              
104             =cut
105              
106             sub load
107             {
108 14     14 1 9253 my ($self, $file, $dict) = @_;
109              
110 14 100       39 $self->{file} = $file if($file);
111 14 100       30 $self->{dict} = $dict if($dict);
112              
113 14 100       252 croak "Phrasebook file definition missing"
114             unless($self->{file});
115 13 100       229 croak "Phrasebook table name missing"
116             unless($self->{file}{dbtable});
117 11         146 croak "Phrasebook column names missing"
118             unless($self->{file}{dbcolumns} &&
119 12 100 100     187 scalar(@{$self->{file}{dbcolumns}}) >= 2);
120              
121 10 100       31 $self->{dbh} = $self->{file}{dbh} if(defined $self->{file}{dbh});
122              
123 10   66     38 $self->{dbh} ||= do {
124 4 100       122 croak "No DSN specified for a database connection"
125             unless($self->{file}{dsn});
126 3 100 100     267 croak "DB user details missing"
127             unless($self->{file}{dbuser} && $self->{file}{dbpass});
128              
129 1         9 DBI->connect( $self->{file}{dsn},
130             $self->{file}{dbuser}, $self->{file}{dbpass},
131             { RaiseError => 1, AutoCommit => 1 });
132             };
133             };
134              
135             =head2 get
136              
137             Returns the phrase stored in the phrasebook, for a given keyword.
138              
139             my $value = $loader->get( $key );
140              
141             =cut
142              
143             sub get {
144 15     15 1 5225 my ($self,$key) = @_;
145 15         19 my (@dicts,$sth,@row);
146              
147 15 100       36 return unless($key);
148              
149 10         38 my $sql =
150             'SELECT '.$self->{file}{dbcolumns}[1].
151             ' FROM '.$self->{file}{dbtable}.
152             ' WHERE '.$self->{file}{dbcolumns}[0].'=?';
153              
154 10 100 100     49 if($self->{file}{dbcolumns}[2] && $self->{dict}) {
155 6 100       17 push @dicts, ref($self->{dict}) eq 'ARRAY' ? @{$self->{dict}} : $self->{dict};
  2         5  
156 6         13 my $query = $sql . ' AND '.$self->{file}{dbcolumns}[2].'=?';
157 6         16 $sth = $self->{dbh}->prepare($sql);
158              
159 6         29 for my $dict (@dicts) {
160 8         19 $sth->execute($key,$dict);
161 8         114 @row = $sth->fetchrow_array;
162 8         36 $sth->finish;
163              
164 8 100       36 return $row[0] if(@row);
165             }
166             }
167              
168             # not in a named dictionary, or no dictionary specified
169 7         19 $sth = $self->{dbh}->prepare($sql);
170 7         44 $sth->execute($key);
171 7         99 @row = $sth->fetchrow_array;
172 7         31 $sth->finish;
173              
174 7 100       27 return $row[0] if(@row);
175 5         11 return;
176             }
177              
178             =head2 dicts
179              
180             Returns the list of dictionaries available.
181              
182             my @dicts = $loader->dicts();
183              
184             =cut
185              
186             sub dicts {
187 2     2 1 484 my $self = shift;
188              
189 2 100       8 return () unless($self->{file}{dbcolumns}[2]);
190              
191 1         5 my $sql =
192             'SELECT '.$self->{file}{dbcolumns}[2].
193             ' FROM '.$self->{file}{dbtable};
194              
195 1         4 my $sth = $self->{dbh}->prepare($sql);
196 1         7 $sth->execute;
197 1         19 my $row = $sth->fetchall_arrayref;
198 1         6 $sth->finish;
199              
200 1         3 return map {$_->[0]} @$row;
  2         24  
201             }
202              
203             =head2 keywords
204              
205             Returns the list of keywords available. List is lexically sorted.
206              
207             my @keywords = $loader->keywords();
208              
209             =cut
210              
211             sub keywords {
212 1     1 1 681 my $self = shift;
213 1         3 my $dict_set = 0;
214              
215             # note that we don't need to worry about dictionaries as the default
216             # is to search all available dictionaries
217              
218 1         4 my $sql =
219             'SELECT '.$self->{file}{dbcolumns}[0].
220             ' FROM '.$self->{file}{dbtable};
221              
222 1         4 my $sth = $self->{dbh}->prepare($sql);
223 1         7 $sth->execute();
224 1         17 my $rows = $sth->fetchall_arrayref;
225 1         7 $sth->finish;
226              
227 1         4 my @keywords = sort map {$_->[0]} @$rows;
  2         9  
228 1         4 return @keywords;
229             }
230              
231             sub DESTROY {
232 10     10   4888 my $self = shift;
233 10 100       58 $self->{dbh}->disconnect if defined $self->{dbh};
234             }
235              
236             1;
237              
238             __END__