File Coverage

blib/lib/Alzabo/Schema.pm
Criterion Covered Total %
statement 42 134 31.3
branch 0 46 0.0
condition 0 3 0.0
subroutine 14 32 43.7
pod 11 13 84.6
total 67 228 29.3


line stmt bran cond sub pod time code
1             package Alzabo::Schema;
2              
3 11     11   67 use strict;
  11         22  
  11         1310  
4 11     11   59 use vars qw($VERSION %CACHE);
  11         22  
  11         687  
5              
6 11     11   57 use Alzabo;
  11         22  
  11         225  
7 11     11   624 use Alzabo::Config;
  11         17  
  11         607  
8 11     11   10579 use Alzabo::Driver;
  11         38  
  11         473  
9 11     11   77 use Alzabo::Exceptions ( abbr => 'params_exception' );
  11         21  
  11         60  
10 11     11   12477 use Alzabo::RDBMSRules;
  11         45  
  11         548  
11 11     11   9450 use Alzabo::SQLMaker;
  11         324  
  11         448  
12 11     11   76 use Alzabo::Utils;
  11         23  
  11         213  
13              
14 11     11   68 use File::Spec;
  11         23  
  11         278  
15              
16 11     11   59 use Params::Validate qw( :all );
  11         24  
  11         2436  
17             Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );
18              
19 11     11   16652 use Storable ();
  11         49408  
  11         457  
20 11     11   105 use Tie::IxHash ();
  11         26  
  11         16395  
21              
22             $VERSION = 2.0;
23              
24             1;
25              
26             sub _load_from_file
27             {
28 0     0     my $class = shift;
29              
30 0           my %p = validate( @_, { name => { type => SCALAR },
31             } );
32              
33             # Making these (particularly from files) is expensive.
34 0 0         return $class->_cached_schema($p{name}) if $class->_cached_schema($p{name});
35              
36 0           my $schema_dir = Alzabo::Config::schema_dir;
37 0           my $file = $class->_schema_filename( $p{name} );
38              
39 0 0         -e $file or Alzabo::Exception::Params->throw( error => "No saved schema named $p{name} ($file)" );
40              
41 0           my $version_file = File::Spec->catfile( $schema_dir, $p{name}, "$p{name}.version" );
42              
43 0           my $version = 0;
44              
45 0           my $fh = do { local *FH; };
  0            
46 0 0         if ( -e $version_file )
47             {
48 0 0         open $fh, "<$version_file"
49             or Alzabo::Exception::System->throw( error => "Unable to open $version_file: $!\n" );
50 0           $version = join '', <$fh>;
51 0 0         close $fh
52             or Alzabo::Exception::System->throw( error => "Unable to close $version_file: $!" );
53             }
54              
55 0 0         if ( $version < $Alzabo::VERSION )
56             {
57 0           require Alzabo::BackCompat;
58              
59 0           Alzabo::BackCompat::update_schema( name => $p{name},
60             version => $version );
61             }
62              
63 0 0         open $fh, "<$file"
64             or Alzabo::Exception::System->throw( error => "Unable to open $file: $!" );
65 0 0         my $schema = Storable::retrieve_fd($fh)
66             or Alzabo::Exception::System->throw( error => "Can't retrieve from filehandle" );
67 0 0         close $fh
68             or Alzabo::Exception::System->throw( error => "Unable to close $file: $!" );
69              
70 0           my $rdbms_file = File::Spec->catfile( $schema_dir, $p{name}, "$p{name}.rdbms" );
71 0 0         open $fh, "<$rdbms_file"
72             or Alzabo::Exception::System->throw( error => "Unable to open $rdbms_file: $!\n" );
73 0           my $rdbms = join '', <$fh>;
74 0 0         close $fh
75             or Alzabo::Exception::System->throw( error => "Unable to close $rdbms_file: $!" );
76              
77 0           $rdbms =~ s/\s//g;
78              
79 0           ($rdbms) = $rdbms =~ /(\w+)/;
80              
81             # This is important because if the user is using MethodMaker, they
82             # might be calling this as My::Schema->load_from_file ...
83 0           bless $schema, $class;
84              
85 0           $schema->{driver} = Alzabo::Driver->new( rdbms => $rdbms,
86             schema => $schema );
87              
88 0           $schema->{rules} = Alzabo::RDBMSRules->new( rdbms => $rdbms );
89              
90 0           $schema->{sql} = Alzabo::SQLMaker->load( rdbms => $rdbms );
91              
92 0           $schema->_save_to_cache;
93              
94 0           return $schema;
95             }
96              
97             sub _cached_schema
98             {
99 0 0   0     my $class = shift->isa('Alzabo::Runtime::Schema') ? 'Alzabo::Runtime::Schema' : 'Alzabo::Create::Schema';
100              
101 0           validate_pos( @_, { type => SCALAR } );
102 0           my $name = shift;
103              
104 0           my $schema_dir = Alzabo::Config::schema_dir();
105 0           my $file = $class->_schema_filename($name);
106              
107 0 0         if (exists $CACHE{$name}{$class}{object})
108             {
109 0 0         my $mtime = (stat($file))[9]
110             or Alzabo::Exception::System->throw( error => "can't stat $file: $!" );
111              
112 0 0         return $CACHE{$name}{$class}{object}
113             if $mtime <= $CACHE{$name}{$class}{mtime};
114             }
115             }
116              
117             sub _schema_filename
118             {
119 0     0     my $class = shift;
120              
121 0           return $class->_base_filename(shift) . '.' . $class->_schema_file_type . '.alz';
122             }
123              
124             sub _base_filename
125             {
126 0     0     shift;
127 0           my $name = shift;
128              
129 0           return File::Spec->catfile( Alzabo::Config::schema_dir(), $name, $name );
130             }
131              
132             sub _save_to_cache
133             {
134 0     0     my $self = shift;
135 0 0         my $class = $self->isa('Alzabo::Runtime::Schema') ? 'Alzabo::Runtime::Schema' : 'Alzabo::Create::Schema';
136 0           my $name = $self->name;
137              
138 0           $CACHE{$name}{$class} = { object => $self,
139             mtime => time };
140             }
141              
142             sub name
143             {
144 0     0 1   my $self = shift;
145              
146 0           return $self->{name};
147             }
148              
149             sub db_schema_name
150             {
151 0     0 0   my $self = shift;
152              
153             return
154 0 0         ( exists $self->{db_schema_name}
155             ? $self->{db_schema_name}
156             : $self->name
157             );
158             }
159              
160             sub has_table
161             {
162 0     0 1   my $self = shift;
163              
164 0           validate_pos( @_, { type => SCALAR } );
165              
166 0           return $self->{tables}->FETCH(shift);
167             }
168              
169 11     11   93 use constant TABLE_SPEC => { type => SCALAR };
  11         23  
  11         8324  
170              
171             sub table
172             {
173 0     0 1   my $self = shift;
174 0           my ($name) = validate_pos( @_, TABLE_SPEC );
175              
176             return
177 0   0       $self->{tables}->FETCH($name) ||
178             params_exception "Table $name doesn't exist in $self->{name}";
179             }
180              
181             sub tables
182             {
183 0     0 1   my $self = shift;
184              
185 0 0         return $self->table(@_) if @_ == 1;
186 0 0         return map { $self->table($_) } @_ if @_ > 1;
  0            
187 0           return $self->{tables}->Values;
188             }
189              
190             sub begin_work
191             {
192 0     0 1   shift->driver->begin_work;
193             }
194             *start_transaction = \&begin_work;
195              
196             sub rollback
197             {
198 0     0 1   shift->driver->rollback;
199             }
200              
201             sub commit
202             {
203 0     0 1   shift->driver->commit;
204             }
205             *finish_transaction = \&commit;
206              
207             sub run_in_transaction
208             {
209 0     0 1   my $self = shift;
210 0           my $code = shift;
211              
212 0           $self->begin_work;
213              
214 0           my @r;
215 0 0         if (wantarray)
216             {
217 0           @r = eval { $code->() };
  0            
218             }
219             else
220             {
221 0           $r[0] = eval { $code->() };
  0            
222             }
223              
224 0 0         if (my $e = $@)
225             {
226 0           eval { $self->rollback };
  0            
227 0 0         if ( Alzabo::Utils::safe_can( $e, 'rethrow' ) )
228             {
229 0           $e->rethrow;
230             }
231             else
232             {
233 0           Alzabo::Exception->throw( error => $e );
234             }
235             }
236              
237 0           $self->commit;
238              
239 0 0         return wantarray ? @r : $r[0];
240             }
241              
242             sub driver
243             {
244 0     0 1   my $self = shift;
245              
246 0           return $self->{driver};
247             }
248              
249             sub rules
250             {
251 0     0 1   my $self = shift;
252              
253 0           return $self->{rules};
254             }
255              
256 0     0 0   sub quote_identifiers { $_[0]->{quote_identifiers} }
257              
258             sub sqlmaker
259             {
260 0     0 1   my $self = shift;
261 0           my %p = validate( @_, { quote_identifiers =>
262             { type => BOOLEAN,
263             default => $self->{quote_identifiers},
264             },
265             },
266             );
267              
268 0           return $self->{sql}->new( driver => $self->driver,
269             quote_identifiers => $p{quote_identifiers},
270             );
271             }
272              
273             __END__