File Coverage

blib/lib/DBIx/ORM/Declarative.pm
Criterion Covered Total %
statement 128 166 77.1
branch 22 58 37.9
condition 14 40 35.0
subroutine 28 32 87.5
pod 3 7 42.8
total 195 303 64.3


line stmt bran cond sub pod time code
1             package DBIx::ORM::Declarative;
2              
3 1     1   12580 use strict;
  1         2  
  1         35  
4 1     1   6 use Carp;
  1         4  
  1         478  
5 1     1   1002 use DBIx::ORM::Declarative::Schema;
  1         4  
  1         38  
6 1     1   1284 use DBIx::ORM::Declarative::Table;
  1         2  
  1         36  
7 1     1   1056 use DBIx::ORM::Declarative::Join;
  1         5  
  1         46  
8 1     1   1041 use DBIx::ORM::Declarative::Row;
  1         3  
  1         40  
9 1     1   1079 use DBIx::ORM::Declarative::JRow;
  1         3  
  1         115  
10              
11 1     1   7 use vars qw($VERSION);
  1         2  
  1         180  
12             $VERSION = '0.22';
13              
14 1     1   7 use constant BASE_CLASS => 'DBIx::ORM::Declarative';
  1         1  
  1         239  
15 1     1   7 use constant SCHEMA_CLASS => 'DBIx::ORM::Declarative::Schema';
  1         1  
  1         61  
16 1     1   26 use constant TABLE_CLASS => 'DBIx::ORM::Declarative::Table';
  1         1  
  1         264  
17 1     1   5 use constant JOIN_CLASS => 'DBIx::ORM::Declarative::Join';
  1         2  
  1         52  
18 1     1   4 use constant ROW_CLASS => 'DBIx::ORM::Declarative::Row';
  1         2  
  1         51  
19 1     1   70 use constant JROW_CLASS => 'DBIx::ORM::Declarative::JRow';
  1         2  
  1         85  
20              
21             # Use this to /really/ supress warnings
22 1     1   6 use constant w__noop => sub { };
  1         2  
  1         62  
  0         0  
23              
24             # The error we return when we have an embarassment of riches
25 1     1   5 use constant E_TOOMANYROWS => 'Database error: underdetermined data set';
  1         1  
  1         60  
26              
27             # The error we return when we've lost the row we just inserted
28 1     1   5 use constant E_NOROWSFOUND => 'Database error: inserted data not found';
  1         1  
  1         1723  
29              
30             # We need to register table & join creation methods; otherwise, we
31             # may wind up blowing up when we try to deal with a table that has
32             # a column with the same name as the table itself
33             my %table_methods = ();
34             my %join_methods = ();
35              
36             sub table_method
37             {
38 5     5 0 17 my ($self, $table, $method) = @_;
39 5 100 66     30 return $table_methods{$table} if $table_methods{$table} and not $method;
40 3         11 $table_methods{$table} = $method;
41             }
42              
43             sub join_method
44             {
45 1     1 0 3 my ($self, $join, $method) = @_;
46 1 50 33     6 return $join_methods{$join} if $join_methods{$join} and not $method;
47 1         3 $join_methods{$join} = $method;
48             }
49              
50             # This applies a method by name - necessary for perl < 5.6
51             sub apply_method
52             {
53 0     0 0 0 my ($obj, $method, $wantarray, @args) = @_;
54             # Check to see if we can apply it directly
55 0         0 my @rv;
56             eval
57 0         0 {
58             # We don't need any warnings here
59 0         0 local $SIG{__WARN__} = __PACKAGE__->w__noop;
60 0 0       0 if($wantarray)
61             {
62 0         0 @rv = $obj->$method(@args);
63             }
64             else
65             {
66 0         0 $rv[0] = $obj->$method(@args);
67             }
68             } ;
69 0 0       0 if(not $@)
70             {
71 0 0       0 return $wantarray?@rv:$rv[0];
72             }
73 0         0 my $res = UNIVERSAL::can($obj, $method);
74 0 0       0 if($res)
75             {
76 0 0       0 return $wantarray?($res->($obj, @args)):scalar($res->($obj, @args));
77             }
78 0         0 $res = UNIVERSAL::can($obj, 'AUTOLOAD');
79 0 0       0 if($res)
80             {
81             # We can't directly use the result in $res, because we need to know
82             # which AUTOLOAD it found. Just use eval for now. *sigh*.
83 0 0       0 if($wantarray)
84             {
85 0         0 eval "\@rv = \$obj->$method(\@args)";
86             }
87             else
88             {
89 0         0 eval "\$rv[0] = \$obj->$method(\@args)";
90             }
91 0 0       0 carp $@ if $@;
92 0 0       0 return $wantarray?@rv:$rv[0];
93             }
94 0   0     0 my $class = ref $obj || $obj;
95 0         0 carp qq(Can't locate object method "$method" via package "$class");
96             }
97              
98             # Create a new DBIx::ORM::Declarative object
99             # Accepts args as a hash
100             # Recognized args are "handle" and "debug"
101             # Unrecognized args are ignored
102             # If used as an object method, copy the handle and debug status, if available
103             sub new
104             {
105 13     13 1 39 my ($self, %args) = @_;
106 13   66     45 my $class = ref $self || $self;
107 13 50       68 my $handle = exists $args{handle}?$args{handle}:$self->handle;
108 13   50     70 my $debug = delete $args{debug} || $self->debug_level || 0;
109 13 50 33     122 if(not exists $args{handle} and DBI->can('connect') and $args{dsn})
      33        
110             {
111 0         0 $handle = DBI->connect(@args{qw(dsn username password)},
112             { RaiseError => 0, PrintError => 0, AutoCommit => 0 });
113             }
114 13         275 my $rv = bless { __handle => $handle, __debug_level => $debug }, $class;
115 13         109 return $rv;
116             }
117              
118             # Custom import method to create schemas during the "use" clause.
119             sub import
120             {
121 2     2   1364 my ($package, @args) = @_;
122 2 100       17 if(not ref $args[0])
123             {
124 1         6 $package->schema(@args);
125 1         4345 return;
126             }
127 1         5 for my $arg (@args)
128             {
129 1 50       6 if(not ref $arg)
130             {
131 0         0 carp "Can't import '$arg' in '$package'";
132 0         0 next;
133             }
134 1         8 $package->schema(%$arg);
135             }
136             }
137              
138             # Get or set the DBI handle
139             sub handle
140             {
141 13     13 1 170 my $self = shift;
142 13 100       33 return unless ref $self;
143 6 50       16 if(@_)
144             {
145 0         0 delete $self->{__handle};
146 0 0       0 $self->{__handle} = $_[0] if $_[0];
147 0         0 return $self;
148             }
149 6 50       45 return unless exists $self->{__handle};
150 6         14 return $self->{__handle};
151             }
152              
153             # Get or set the debug level
154             sub debug_level
155             {
156 13     13 0 16 my $self = shift;
157 13 100       65 return 0 unless ref $self;
158 6 50       11 if(@_)
159             {
160 0   0     0 $self->{__debug_level} = $_[0] || 0;
161 0         0 return $self;
162             }
163 6   50     48 return $self->{__debug_level} || 0;
164             }
165              
166             # Get the current schema name, or switch to a new schema, or create a
167             # new schema class.
168             sub schema
169             {
170 2     2 1 6 my ($self, @args) = @_;
171 2 100       8 if(@args<2)
172             {
173 1 50       5 if(@args==1)
174             {
175 0         0 my $schema = shift @args;
176 0 0 0     0 return $self->apply_method($schema,wantarray)
177             if $schema and $self->can($schema);
178 0         0 return $self;
179             }
180 1         2 my $schema;
181 1         2 eval { $schema = $self->_schema; };
  1         21  
182 1         5 return $schema;
183             }
184              
185             # Creating/updating a schema class - process the args
186 1         5 my %args = @args;
187 1         5 my $schema = delete $args{schema};
188 1         3 my $from_dual = delete $args{from_dual};
189 1   50     9 my $limit_clause = delete $args{limit_clause} || 'LIMIT %offset%,%count%';
190              
191 1 50 0     4 carp "missing schema argument" and return unless $schema;
192 1         9 my $schema_class = $self->SCHEMA_CLASS . "::$schema";
193              
194             # The meat of the declarations
195 1   50     5 my $tables = delete $args{tables} || [ ];
196 1   50     123 my $joins = delete $args{joins} || [ ];
197 1   50     9 my $aliases = delete $args{table_aliases} || { };
198              
199             # We're gonna do a whole mess of symbolic references...
200 1     1   9 no strict 'refs';
  1         1  
  1         913  
201 1         9 my $schema_method_name = $self->BASE_CLASS . "::$schema";
202 1 50       3 if(not @{$schema_class . '::ISA'})
  1         15  
203             {
204             # Create the class heirarchy
205 1         13 @{$schema_class . '::ISA'} = ($self->SCHEMA_CLASS);
  1         25  
206              
207             # Let's see if we're called from import...
208 1         8 my ($pkg, $file, $line, $sub) = caller(1);
209 1 50 33     10 if($sub eq __PACKAGE__ . '::import' and $pkg ne 'main')
210             {
211             # Yep - insert ourselves in the upstream @ISA...
212 0         0 my $isaref = \@{$pkg . '::ISA'};
  0         0  
213 0 0       0 push @$isaref, $schema_class unless $pkg->isa($schema_class);
214             }
215              
216             # Information methods
217 1     4   8 *{$schema_class . '::_schema' } = sub { $schema; };
  1         9  
  4         305  
218 1         8 *{$schema_class . '::_schema_class' } =
  1         13  
219 1     4   7 *{$schema_class . '::_class' } = sub { $schema_class; };
  4         12  
220 1     0   6 *{$schema_class . '::_limit_clause' } = sub { $limit_clause; };
  1         12  
  0         0  
221 1     0   5 *{$schema_class . '::_from_dual' } = sub { $from_dual; };
  1     0   6  
  0         0  
222              
223             # A constructor/mutator
224 1         20 *{$schema_method_name} = sub
225             {
226 2     2   3 my ($self) = @_;
227 2         6 my $rv = $self->new;
228 2 50       23 bless $rv, $schema_class unless $rv->isa($schema_class);
229 2         28 return $rv;
230 1         5 } ;
231             }
232              
233             # Create the tables
234 1         18 $schema_class->table(%$_) foreach @$tables;
235              
236             # Create the aliases, if we have any
237 1         6 $schema_class->alias($_, $aliases->{$_}) foreach keys %$aliases;
238            
239             # Create any joins we might have
240 1         13 $schema_class->join(%$_) foreach @$joins;
241              
242 1         3 return &{$schema_method_name}($self);
  1         5  
243             }
244              
245             1;