File Coverage

blib/lib/DBIx/Mint.pm
Criterion Covered Total %
statement 52 53 98.1
branch 16 18 88.8
condition 3 4 75.0
subroutine 11 12 91.6
pod 4 5 80.0
total 86 92 93.4


line stmt bran cond sub pod time code
1             package DBIx::Mint;
2              
3 15     15   59243 use DBIx::Mint::Singleton;
  15         40  
  15         383  
4 15     15   7192 use DBIx::Connector;
  15         254166  
  15         366  
5 15     15   4873 use DBIx::Mint::Schema;
  15         41  
  15         392  
6 15     15   8175 use SQL::Abstract::More;
  15         387449  
  15         784  
7 15     15   112 use Carp;
  15         34  
  15         707  
8 15     15   62 use Moo;
  15         17  
  15         96  
9              
10             our $VERSION = 0.071;
11              
12             has name => ( is => 'ro', default => sub { '_DEFAULT' } );
13             has abstract => ( is => 'rw', default => sub { SQL::Abstract::More->new(); } );
14             has schema => ( is => 'rw', default => sub { return DBIx::Mint::Schema->new } );
15             has connector => ( is => 'rw', predicate => 1 );
16              
17             sub BUILD {
18 17     17 0 299 my $self = shift;
19 17         84 my $singleton = DBIx::Mint::Singleton->instance;
20 17 100       169 croak "DBIx::Mint object " . $self->name . " exists already"
21             if $singleton->exists($self->name);
22 16         59 $singleton->add_instance($self);
23             }
24              
25             sub instance {
26 174     174 1 3095312 my ($class, $name) = @_;
27 174   100     448 $name //= '_DEFAULT';
28 174         584 my $singleton = DBIx::Mint::Singleton->instance;
29 174 100       1472 if (!$singleton->exists($name)) {
30 14         53 $class->new( name => $name );
31             }
32 174         451 return $singleton->get_instance($name);
33             }
34              
35             sub dbh {
36 56     56 1 2293 my $self = shift;
37 56 50       247 return $self->has_connector ? $self->connector->dbh
38             : croak 'Please feed DBIx::Mint with a database connection';
39             };
40              
41             sub connect {
42 14     14 1 7096 my ($self, $dsn, $username, $passwd, $args) = @_;
43 14 100       113 if (ref $_[0]) {
44 2         3 $self = shift;
45             }
46             else {
47 12         20 my $class = shift;
48 12         40 $self = $class->instance();
49             }
50 14   50 0   106 $args->{HandleError} //= sub { croak $_[0] };
  0         0  
51 14         122 $self->connector( DBIx::Connector->new(
52             $dsn, $username, $passwd, $args ) );
53 14         263 $self->connector->mode('ping');
54              
55 14         183 return $self;
56             }
57              
58             sub do_transaction {
59 4     4 1 306 my ($self, $trans) = @_;
60              
61 4         8 my $auto = $self->dbh->{AutoCommit};
62 4 100       209 $self->dbh->{AutoCommit} = 0 if $auto;
63              
64 4         73 my @output;
65 4         5 eval { @output = $self->connector->txn( $trans ) };
  4         10  
66              
67 4 100       1666 if ($@) {
68 2         31 carp "Transaction failed: $@";
69 2         748 $self->dbh->rollback;
70 2 100       255 $self->dbh->{AutoCommit} = 1 if $auto;
71 2         53 return undef;
72             }
73 2 100       6 $self->dbh->{AutoCommit} = 1 if $auto;
74 2 50       4763 return @output ? @output : 1;
75             }
76              
77             1;
78              
79             =pod
80              
81             =head1 NAME
82              
83             DBIx::Mint - A mostly class-based ORM for Perl
84              
85             =head1 VERSION
86              
87             This documentation refers to DBIx::Mint 0.071
88              
89             =head1 SYNOPSIS
90              
91             Define your classes, which will play the role L:
92              
93             package Bloodbowl::Team;
94             use Moo;
95             with 'DBIx::Mint::Table';
96            
97             has id => (is => 'rw' );
98             has name => (is => 'rw' );
99             ...
100              
101             Nearby (probably in a module of its own), you define the schema for your classes:
102              
103             package Bloodbowl::Schema;
104              
105             my $schema = DBIx::Mint->instance->schema;
106             $schema->add_class(
107             class => 'Bloodbowl::Team',
108             table => 'teams',
109             pk => 'id',
110             auto_pk => 1,
111             );
112            
113             $schema->add_class(
114             class => 'Bloodbowl::Player',
115             table => 'players',
116             pk => 'id',
117             auto_pk => 1,
118             );
119            
120             # This is a one-to-many relationship
121             $schema->one_to_many(
122             conditions =>
123             ['Bloodbowl::Team', { id => 'team'}, 'Bloodbowl::Player'],
124             method => 'get_players',
125             inverse_method => 'get_team',
126             );
127              
128             And in your your scripts:
129            
130             use DBIx::Mint;
131             use My::Schema;
132            
133             # Connect to the database
134             DBIx::Mint->connect( $dsn, $user, $passwd, { dbi => 'options'} );
135            
136             my $team = Bloodbowl::Team->find(1);
137             my @players = $team->get_players;
138            
139             # Database modification methods include insert, update, and delete.
140             # They act on a single object when called as instance methods
141             # but over the whole table if called as class methods:
142             $team->name('Los Invencibles');
143             $team->update;
144            
145             Bloodbowl::Coach->update(
146             { status => 'suspended' },
147             { password => 'blocked' });
148            
149             Declaring the schema allows you to modify the data. To define a schema and to learn about data modification methods, look into L and L.
150              
151             If you only need to query the database, no schema is needed. L objects build database queries and fetch the resulting records:
152            
153             my $rs = DBIx::Mint::ResultSet->new( table => 'coaches' );
154            
155             # You can perform joins:
156             my @team_players = $rs->search( { 'me.id' => 1 } )
157             ->inner_join( 'teams', { 'me.id' => 'coach' })
158             ->inner_join( 'players', { 'teams.id' => 'team' })
159             ->all;
160            
161             =head1 DESCRIPTION
162              
163             DBIx::Mint is a mostly class-based, object-relational mapping module for Perl. It tries to be simple and flexible, and it is meant to integrate with your own custom classes.
164              
165             Since version 0.04, it allows for multiple database connections and it features L objects under the hood. This should make DBIx::Mint easy to use in persistent environments.
166              
167             There are many ORMs for Perl. Most notably, you should look at L and L which are two robust, proven offerings as of today. L is another light-weight alternative.
168              
169             =head1 DOCUMENTATION
170              
171             The documentation is split into four parts:
172              
173             =over
174              
175             =item * This general view, which documents the umbrella class DBIx::Mint. A DBIx::Mint object encapsulates a given database conection and its schema.
176              
177             =item * L documents the mapping between classes and database tables. It shows how to specify table names, primary keys and how to create associations between classes.
178              
179             =item * L is a role that allows you to modify or fetch data from a single table. It is meant to be applied to your custom classes using L or L.
180              
181             =item * L performs database queries using chainable methods. It does not know about the schema, so it can be used without one or without any external classes .
182              
183             =back
184              
185             =head1 GENERALITIES
186              
187             The basic idea is that, frequently, a class can be mapped to a database table. Records become objects that can be created, fetched, updated and deleted. With the help of a schema, classes know what database table they represent, as well as their primary keys and the relationships they have with other classes. Relationships between classes are represented as methods that act upon objects from other classes, for example, or that simply return data. Using such a schema and a table-accessing role, classes gain database persistence.
188              
189             Fetching data from joined tables is different, though. While you can have a class to represent records comming from a join, you cannot create, update or delete directly the objects from such a class. Using L objects, complex table joins and queries are encapsulated, along with different options to actually fetch data and possibly bless it into full-blown objects. In this case, DBIx::Mint uses the result set approach, as DBIx::Lite does.
190              
191             Finally, DBIx::Mint objects contain the database connection, the database schema and its SQL syntax details. Because each object encapsulates a database connection, you may create several objects to interact with different databases within your program. Mint objects are kept in a centralized pool so that they remain accessible without the need of passing them through explicitly.
192              
193             =head1 SUBROUTINES/METHODS IMPLEMENTED BY L
194              
195             =head2 new
196              
197             First of three constructors. All of them will save the newly created object into the connection pool, but they will croak if the object exists already. All the arguments to C are optional. If C or C are not given, the objects will be created for you with their default arguments.
198              
199             =over
200              
201             =item name
202              
203             The name of the new Mint object. Naming your objects allows for having more than one, and thus for having simultaneus connections to different databases. The object name will be used to fetch it from the connections pool (see the method L).
204              
205             =item schema
206              
207             A L object. You can create the DBIx::Mint::Schema yourself and feed it into different DBIx::Mint objects to use the same schema over different databases.
208              
209             =item abstract
210              
211             A L object.
212              
213             =item connector
214              
215             A L object.
216              
217             =back
218              
219             =head2 connect
220              
221             Connects the Mint object to the database. If called as a class method, C creates a Mint object with the default name first. It receives your database connection parameters per L's specifications:
222              
223             # Create the default Mint object and its connection:
224             DBIx::Mint->connect('dbi:SQLite:dbname=t/bloodbowl.db', $username, $password,
225             { AutoCommit => 1, RaiseError => 1 });
226              
227             # Create a named connection:
228             my $mint = DBIx::Mint->new( name => 'other' );
229             $mint->connect('dbi:SQLite:dbname=t/bloodbowl.db', '', '',
230             { AutoCommit => 1, RaiseError => 1 });
231              
232             =head2 instance
233              
234             Class method. It fetches an instance of L from the object pool:
235              
236             my $mint = DBIx::Mint->instance; # Default connection
237             my $mint2 = DBIx::Mint->instance('other'); # 'other' connection
238              
239             If the object does not exist, it will be created and so this is the third constructor. This method allows you to create mappings to different databases in the same program. The optional argument is used as the DBIx::Mint object name.
240              
241             =head2 connector
242              
243             This accessor/mutator will return the underlying L object.
244              
245             =head2 dbh
246              
247             This method will return the underlying database handle, which is guaranteed to be alive.
248            
249             =head2 abstract
250              
251             This is the accessor/mutator for the L subjacent object.
252              
253             =head2 schema
254              
255             This is the accessor/mutator for the L instance.
256              
257             =head2 do_transaction
258              
259             This instance method will take a code reference and execute it within a transaction block. In case the transaction fails (your code dies) it is rolled back and B. In this case, L will return C. If successful, the transaction will be commited and the method will return a true value.
260              
261             $mint->do_transaction( $code_ref ) || die "Transaction failed!";
262              
263             Note that it must be called as an intance method, not as a class method.
264            
265             =head1 USE OF L
266              
267             Under the hood, DBIx::Mint uses DBIx::Connector to hold the database handle and to make sure that the connection is well and alive when you need it. The database modification routines employ the 'fixup' mode for modifying the database at a very fine-grained level, so that no side-effects are visible. This allows us to use DBIx::Connector in an efficient way. If you choose to install method modifiers around database interactions, be careful to avoid unwanted secondary effects.
268              
269             The query routines offered by L use the 'fixup' mode while retrieving the statement holder with the SELECT query already prepared, but not while extracting information in the execution phase. If you fear that the database connection may have died in the meantime, you can always use Mint's C method to get a hold of the DBIx::Connector object and manage the whole query process yourself. This should not be necessary, though.
270              
271             =head1 DEPENDENCIES
272              
273             This distribution depends on the following external, non-core modules:
274              
275             =over
276              
277             =item Moo
278              
279             =item MooX::Singleton
280              
281             =item SQL::Abstract::More
282              
283             =item DBI
284              
285             =item DBIx::Connector
286              
287             =item List::MoreUtils
288              
289             =item Clone
290              
291             =back
292              
293             =head1 BUGS AND LIMITATIONS
294              
295             Testing is not complete; in particular, tests look mostly for the expected results and not for edge cases or plain incorrect input.
296              
297             Please report problems to the author. Patches are welcome. Tests are welcome also.
298              
299             =head1 ACKNOWLEDGEMENTS
300              
301             The ResultSet class was inspired by L, by Alessandro Ranellucci.
302              
303             =head1 AUTHOR
304              
305             Julio Fraire,
306              
307             =head1 LICENCE AND COPYRIGHT
308              
309             Copyright (c) 2015, Julio Fraire. All rights reserved.
310              
311             =head1 LICENSE
312              
313             This module is free software; you can redistribute it and/or
314             modify it under the same terms as Perl itself. See L.
315              
316             This program is distributed in the hope that it will be useful,
317             but WITHOUT ANY WARRANTY; without even the implied warranty of
318             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
319              
320             =cut