File Coverage

blib/lib/DBIx/Dictionary.pm
Criterion Covered Total %
statement 15 15 100.0
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 20 20 100.0


line stmt bran cond sub pod time code
1             package DBIx::Dictionary;
2              
3             =head1 NAME
4              
5             DBIx::Dictionary - Support for query dictionaries in DBI
6              
7             =head1 SYNOPSIS
8            
9             use DBIx::Dictionary;
10            
11             my $dbh = DBIx::Dictionary->connect(
12             $dsn, $user,
13             $password,
14             {
15             DictionaryFile => 'dictionary.ini',
16             DictionaryName => 'mysql',
17             }
18             ) or die DBIx::Dictionary->errstr;
19            
20             # Will prepare query 'insert' from dictionary
21             my $sth = $dbh->prepare('insert')
22             or die $dbh->errstr;
23            
24             $sth->execute(@bind);
25              
26             =head1 ABSTRACT
27              
28             The C implements a common practice of database
29             development called "query dictionary". It makes the programmer's life
30             easier by adding support to those query dictionaries directly into
31             L.
32              
33             Instead of add common SQL queries inside a program using heredocs or
34             concatenating strings, the programmer can put those queries into a
35             query dictionary and use it all over the application.
36              
37             This can be also a substitute for underlying smart code, allowing one
38             to construct applications which can be deployed to different database
39             queries without relying on heavy logic to maintain the SQL queries
40             compatible.
41              
42             With C, one can have a C, C and
43             C sections with specific queries translated to each database
44             dialect.
45              
46             A typical C content, in L
47             format would be like this:
48              
49            
50            
51             insert <
52             INSERT INTO sometable VALUES (?, ?)
53             SQL
54              
55            
56              
57            
58              
59             insert <
60             INSERT INTO sometable VALUES (?, ?)
61             SQL
62              
63            
64              
65             Please be advised that it is planned to support more than one
66             dictionary storage format.
67              
68             =cut
69              
70 4     4   31431 use warnings;
  4         10  
  4         139  
71 4     4   22 use strict;
  4         7  
  4         132  
72              
73 4     4   21 use Carp;
  4         13  
  4         1534  
74 4     4   12199 use Config::General;
  4         189879  
  4         309  
75              
76 4     4   52 use base 'DBI';
  4         10  
  4         422145  
77              
78             our $VERSION = '0.01';
79             our @ISA;
80              
81             use constant ATTR => 3;
82              
83             =head1 API
84              
85             =head2 DBIx::Dictionary
86              
87             This class provides the necessary glue to add support for query
88             dictionaries on top of L.
89              
90             =over 4
91              
92             =cut
93              
94             #
95             # __load_dictionary( $file, $name )
96             #
97             # This function loads the given dictionary $file and assure there's a
98             # section with the given $name.
99             #
100             sub __load_dictionary {
101             my ( $file, $name ) = @_;
102              
103             croak "File '$file' doesn't exist!"
104             unless -e $file;
105              
106             my $config = Config::General->new(
107             -AllowMultiOptions => 'no',
108             -ConfigFile => $file,
109             );
110              
111             my %config = $config->getall;
112              
113             croak "Dictionary '$name' not found!"
114             unless exists $config{Dictionary}{$name};
115              
116             return $config{Dictionary}{$name};
117             }
118              
119             =item connect( $dsn, $username, $password, \%attributes )
120              
121             This method overloads L C method. It can parse two
122             more attributes other than L: C which is a
123             valid path to some L file and
124             C is the section name we want to load. Default value
125             for C is C.
126              
127              
128             my $dbh = DBIx::Dictionary->connect(
129             $dsn,
130             $username,
131             $password,
132             {
133             DictionaryFile => 'dictionary.ini',
134             DictionaryName => 'mysql',
135             }
136             );
137              
138             The above example loads the dictionary file C and will
139             look up the named queries in the section C.
140              
141             C can also receive a C attribute as L
142             does. It will load the module, and make C and
143             C subclasses of the given C as well.
144              
145             This is useful for using named placeholders with
146             L, like:
147              
148             my $dbh = DBIx::Dictionary->connect(
149             $dsn,
150             $username,
151             $password,
152             {
153             DictionaryFile => 'dictionary.ini',
154             RootClass => 'DBIx::Placeholder::Named',
155             }
156             );
157            
158             =cut
159              
160             sub connect {
161             my ( $class, @args ) = @_;
162              
163             my $dictionary_file;
164             my $dictionary_name = 'default';
165              
166             if ( $args[ATTR] and ref( $args[ATTR] ) eq 'HASH' ) {
167             $dictionary_file = delete $args[ATTR]{DictionaryFile}
168             if ( exists $args[ATTR]{DictionaryFile} );
169             $dictionary_name = delete $args[ATTR]{DictionaryName}
170             if ( exists $args[ATTR]{DictionaryName} );
171              
172             if ( exists $args[ATTR]{RootClass} ) {
173             my $root_class = delete $args[ATTR]{RootClass};
174              
175             # Loads root class
176             eval "require $root_class;";
177              
178             croak $@ if $@;
179              
180             # install user's given RootClass as base class for
181             # DBIx::Dictionary, DBIx::Dictionary::db and
182             # DBIx::Dictionary::st
183             unshift @DBIx::Dictionary::ISA, $root_class;
184             unshift @DBIx::Dictionary::db::ISA, $root_class . "::db";
185             unshift @DBIx::Dictionary::st::ISA, $root_class . "::st";
186             }
187             }
188              
189             # what is the purpose of this module if we don't have a dictionary?!
190             croak "DictionaryFile attribute is obligatory!"
191             unless $dictionary_file;
192              
193             my $self = $class->SUPER::connect(@args);
194              
195             my $dictionary = __load_dictionary( $dictionary_file, $dictionary_name );
196             $self->{private_dbix_dictionary_info}{dictionary} = $dictionary;
197              
198             return $self;
199             }
200              
201             =back
202              
203             =cut
204              
205             package DBIx::Dictionary::db;
206              
207             =head2 DBIx::Dictionary::db
208              
209             This is a L subclass.
210              
211             =cut
212              
213             use Carp;
214              
215             use base 'DBI::db';
216              
217             our @ISA;
218              
219             =over 4
220              
221             =item prepare( $query )
222              
223             C accepts the a query name from dictionary as parameter,
224             otherwise will assume it is a SQL query.
225              
226             my $sth;
227             $sth = $dbh->prepare('insert_customer');
228             $sth = $dbh->prepare($some_dynamic_query);
229              
230             On the above example, both C statements should work as
231             expected.
232              
233             =cut
234              
235             sub prepare {
236             my ( $dbh, $query_name ) = @_;
237              
238             my $query;
239              
240             if ( exists $dbh->{private_dbix_dictionary_info}{dictionary}{$query_name} )
241             {
242              
243             # $query_name is a key on dictionary.
244             $query = $dbh->{private_dbix_dictionary_info}{dictionary}{$query_name};
245             }
246             else {
247              
248             # $query_name is not a key on our dictionary, so let's assume
249             # it *is* the query.
250             $query = $query_name;
251             }
252              
253             my $sth = $dbh->SUPER::prepare($query)
254             or return;
255              
256             return $sth;
257             }
258              
259             =back
260              
261             =cut
262              
263             package DBIx::Dictionary::st;
264              
265             =head2 DBIx::Dictionary::st
266              
267             This is a L subclass.
268              
269             =cut
270              
271             use base 'DBI::st';
272              
273             our @ISA;
274              
275             1;
276              
277             =head1 CAVEATS
278              
279             =over 4
280              
281             =item
282              
283             C won't work if you use it using the C
284             attribute with L. It is expect not to work, because when
285             connecting to database like this:
286              
287             my $dbh = DBI->connect(
288             $dsn,
289             $username,
290             $password,
291             {
292             RootClass => 'DBIx::Dictionary,
293             }
294             );
295              
296             L won't call C. Calling
297             C is important because the dictionary
298             setup is done there. This can change in the future, but not for now.
299              
300             =back
301              
302             =head1 AUTHOR
303              
304             Copyright (c) 2008, Igor Sutton Lopes C<< >>. All rights
305             reserved.
306              
307             This module is free software; you can redistribute it and/or modify it
308             under the same terms as Perl itself.
309              
310             =head1 SEE ALSO
311              
312             L, L,
313             L