File Coverage

blib/lib/YATT/Lite/WebMVC0/DBSchema/DBIC.pm
Criterion Covered Total %
statement 174 254 68.5
branch 35 90 38.8
condition 11 26 42.3
subroutine 34 43 79.0
pod 0 12 0.0
total 254 425 59.7


line stmt bran cond sub pod time code
1             package YATT::Lite::WebMVC0::DBSchema::DBIC; sub MY () {__PACKAGE__}
2 2     2   134924 use strict;
  2         7  
  2         57  
3 2     2   11 use warnings qw(FATAL all NONFATAL misc);
  2         4  
  2         80  
4 2     2   11 use Carp;
  2         4  
  2         105  
5              
6 2     2   15 use base qw(YATT::Lite::WebMVC0::DBSchema);
  2         4  
  2         668  
7 2     2   14 use fields qw(DBIC cf_DBIC);
  2         5  
  2         15  
8              
9 2     2   468 use YATT::Lite::Util::AsBase qw/_import_as_base/;
  2         5  
  2         19  
10              
11 2     2   14 use Scalar::Util qw/weaken/;
  2         5  
  2         153  
12             require DBIx::Class::Core;
13              
14 4     4 0 12 sub DBIC_SCHEMA {'YATT::Lite::WebMVC0::DBSchema::DBIC::DBIC_SCHEMA'}
15              
16             use YATT::Lite::Types
17 2         19 ([Table => -fields => [qw(cf_package cf_components)]]
18             , [Column => -fields => [qw(cf_dbic_opts)]]
19 2     2   12 );
  2         5  
20              
21 2     2   12 use YATT::Lite::Util qw(globref define_const lexpand terse_dump);
  2         5  
  2         3007  
22              
23             sub dbic {
24 5     5 0 15 (my MY $schema) = @_;
25 5   66     34 $schema->{DBIC} //= do {
26 1 50       4 if ($schema->{cf_debug}) {
27 0         0 print STDERR "INFO: DBSchema($schema) DBIC->connect"
28             , ", class = $schema->{cf_DBIC}\n";
29             }
30 1         6 weaken($schema); # !! This is very important to avoid memleak!
31 1     1   11 $schema->{cf_DBIC}->connect(sub {$schema->make_connection});
  1         3436  
32             };
33             }
34              
35             sub connect {
36 0 0   0 0 0 my MY $schema = ref $_[0] ? shift->clone : shift->new;
37 0         0 $schema->{DBIC} = $schema->{cf_DBIC}->connect(@_);
38 0 0       0 if ($schema->{cf_debug}) {
39 0         0 print STDERR "INFO: DBSchema($schema)::connect"
40             ,", class = $schema->{cf_DBIC}\n";
41             }
42 0         0 $schema;
43             }
44              
45             sub disconnect {
46 1     1 0 4 (my MY $schema, my ($msg)) = @_;
47 1         5 $schema->reset;
48 1         7 $schema->SUPER::disconnect($msg);
49             }
50              
51             sub reset {
52 2     2 0 6 (my MY $self) = @_;
53 2         13 $self->SUPER::reset;
54 2         7 delete $self->{DBIC};
55             }
56              
57             sub txn_do {
58 0     0 0 0 (my MY $schema, my $sub) = splice @_, 0, 2;
59 0 0       0 $schema->dbic->txn_do($sub, @_ ? @_ : $schema->{DBIC});
60             }
61              
62             sub startup {
63 0     0 0 0 (my MY $schema, my (@apps)) = @_;
64 0 0       0 unless ($schema->{cf_DBIC}) {
65 0         0 croak "DBIC classname parameter is empty!";
66             }
67 0 0       0 unless ($schema->{cf_DBIC} =~ /::/) {
68 0         0 croak "DBIC classname MUST has '::'! $schema->{cf_DBIC}";
69             }
70 0         0 $schema->SUPER::startup(@apps);
71 0         0 $schema->build_dbic($schema->{cf_DBIC});
72 0 0 0     0 if (my (@args) = $schema->{cf_DBH}
73             || lexpand($schema->{cf_connection_spec})) {
74 0         0 $schema->connect(@args);
75             }
76             }
77              
78             sub default_dbi_attr {
79 1     1 0 7 (shift->SUPER::default_dbi_attr, AutoCommit => 1);
80             }
81              
82             sub import {
83 4     4   106 YATT::Lite::Util::AsBase::parse_args(\@_, scalar caller);
84 4 50       15 return unless @_ >= 2;
85 4         15 goto &build_dbic;
86             }
87              
88             # use YATT::Lite::WebMVC0::DBSchema::DBIC $pkg => @desc;
89             #
90             # $pkg ISA DBIC_SCHEMA (ISA DBIx::Class::Schema)
91             # ${pkg}::Result::$tab ISA DBIx::Class::Core
92              
93             # XXX: Make sure build_dbic is callable many times.
94             sub build_dbic {
95 4     4 0 15 my ($class_or_obj, $DBIC) = splice @_, 0, 2;
96 4         9 (my $myPkg, my MY $schema) = do {
97 4 50       13 if (ref $class_or_obj) {
98 0         0 (ref $class_or_obj, $class_or_obj);
99             } else {
100 4         21 ($class_or_obj, $class_or_obj->new(@_));
101             }
102             };
103              
104 4   33     12 $DBIC //= caller() . '::DBIC';
105             {
106 4         13 my $sym = globref($DBIC, undef);
107 4 50       7 unless (*{$sym}{CODE}) {
  4         13  
108 4         14 define_const($sym, $DBIC);
109             }
110             }
111              
112             # DBIC->YATT_DBSchema holds YATT::Lite::WebMVC0::DBSchema::DBIC instance.
113             {
114 4         8 my $sym = globref($DBIC, 'YATT_DBSchema');
  4         9  
  4         12  
115 4 50       9 unless (*{$sym}{CODE}) {
  4         14  
116             *$sym = sub {
117 10     10   115133 my $dbic = shift;
118             print STDERR "DEBUG: DBIC->YATT_DBSchema is called\n"
119 10 50       65 if $schema->{cf_debug};
120             # Class method として呼んだときは, schema に set しない。
121 10 100 66     83 $schema->{DBIC} ||= $dbic
      66        
122             if defined $dbic and ref $dbic; # XXX: weaken??
123 10         64 $schema;
124 4         22 };
125             }
126             }
127 4         11 $schema->{cf_DBIC} = $DBIC;
128              
129 4         14 *{globref($DBIC, 'ISA')} = [$myPkg->DBIC_SCHEMA];
  4         11  
130 4         23 $myPkg->add_inc($DBIC);
131              
132 4         8 foreach my Table $tab (@{$schema->{table_list}}) {
  4         11  
133             # XXX: 正確には rowClass よね、これって。
134             # XXX: じゃぁ ResultSet の方は作らなくてよいのか?
135             my $tabClass = $tab->{cf_package}
136 11         53 = join('::', $DBIC, Result => $tab->{cf_name});
137 11         32 *{globref($tabClass, 'ISA')} = ['DBIx::Class::Core'];
  11         38  
138 11         57 $myPkg->add_inc($tabClass);
139              
140 11         22 my Column $pk;
141 11         47 my @comp = (qw/Core/, lexpand($tab->{cf_components}));
142              
143 11 100       34 if ($tab->{cf_view}) {
144 1         20 $tabClass->load_components(@comp);
145 1         179 $tabClass->table_class('DBIx::Class::ResultSource::View');
146             # ------------- (order is important!) ----------------
147 1         58 $tabClass->table($tab->{cf_name});
148 1         2068 $tabClass->result_source_instance->view_definition($tab->{cf_view});
149 1 50       222 $tabClass->result_source_instance->is_virtual($tab->{cf_virtual} ? 1 : 0);
150             } else {
151 10         36 $pk = $schema->get_table_pk($tab);
152 10 100 66     61 push @comp, qw(PK::Auto) if $pk and $pk->{cf_autoincrement};
153 10         110 $tabClass->load_components(@comp);
154 10         1762 $tabClass->table($tab->{cf_name});
155             }
156              
157 11         5608 my @constraints = lexpand($tab->{chk_unique});
158             {
159 11         25 my @colSpecs;
  11         19  
160 11         16 foreach my Column $col (@{$tab->{col_list}}) {
  11         29  
161             # dbic_opts;
162             my %dbic_opts = (data_type => $col->{cf_type}
163 42 50       158 , map(defined $_ ? %$_ : (), $col->{cf_dbic_opts}));
164 42         97 push @colSpecs, $col->{cf_name} => \%dbic_opts;
165 42 50       110 push @constraints, [$col->{cf_name}] if $col->{cf_unique};
166             }
167 11         145 $tabClass->add_columns(@colSpecs);
168             }
169 11 100       16067 $tabClass->set_primary_key($schema->get_table_pk($tab)) if $pk;
170 11         2062 foreach my $uniq (@constraints) {
171 0         0 my $ixname = join("_", $tabClass, @$uniq);
172 0 0       0 print STDERR <{cf_verbose};
173 0         0 -- $tabClass->add_unique_constraint($ixname, [@{[join ", ", @$uniq]}])
174             END
175 0         0 $tabClass->add_unique_constraint($ixname, $uniq);
176             }
177             }
178             # Relationship の設定と、 register_class の呼び出し。
179 4         9 foreach my Table $tab (@{$schema->{table_list}}) {
  4         15  
180 11         3027 my $tabClass = $tab->{cf_package};
181 11         50 foreach my $rel ($schema->list_relations($tab->{cf_name})) {
182 12         40 my ($relType, @relOpts) = @$rel;
183 12 100       96 if (my $sub = $myPkg->can("add_relation_$relType")) {
184 2         9 $sub->($myPkg, $schema, $tab, @relOpts);
185 2         7 next;
186             }
187              
188 10         26 my ($relName, $fkName, $fTabName) = @relOpts;
189 10 50       24 unless (defined $fTabName) {
190 0         0 croak "Foreign table is empty for $tab->{cf_name} $relType $relName $fkName";
191             }
192 10         24 my $fTab = $schema->{table_dict}{$fTabName};
193             # table の package 名が確定するまで、relation の設定を遅延させたいから。
194 10 50       23 print STDERR <{cf_verbose};
195 0         0 -- $tabClass->$relType($relName, $fTab->{cf_package}, @{[terse_dump($fkName)]})
196             END
197 10         20 eval {
198 10         81 $tabClass->$relType($relName, $fTab->{cf_package}, $fkName);
199             };
200 10 50       8189 if ($@) {
201             die "Relationship Error in: $relType $relName, foreign="
202 0         0 .$fTab->{cf_package}.": $@";
203             }
204             }
205             # register_class は Relationship 設定が済んでからじゃないとダメ?
206 11         69 $DBIC->register_class($tab->{cf_name}, $tabClass);
207             }
208              
209 4         4479 $schema;
210             }
211              
212             # XXX: 上と被っているので、まとめるべし。
213             sub add_relation_many_to_many {
214 2     2 0 5 (my $myPkg, my MY $schema, my Table $tab
215             , my ($relName, $fkName, $tabName)) = @_;
216 2         4 my $relType = 'many_to_many';
217 2         5 my $tabClass = $tab->{cf_package};
218 2 50       13 print STDERR <{cf_verbose};
219 0         0 -- $tabClass->$relType($relName, $tabName, @{[terse_dump($fkName)]})
220             END
221 2         5 eval {
222 2         23 $tabClass->$relType($relName, $tabName, $fkName)
223             };
224 2 50       239 if ($@) {
225 0         0 die "Relationship Error in: $relType ($relName, $tabName, $fkName)".$@;
226             }
227             }
228              
229             *deploy = *ensure_created; *deploy = *ensure_created;
230             sub ensure_created {
231 4     4 0 14 (my MY $self, my $dbic) = @_;
232 4   33     22 $dbic ||= $self->{DBIC};
233             $dbic->storage->dbh_do
234             (sub {
235 4     4   11521 (my ($storage, $dbh), my MY $self) = @_;
236 4         29 $self->ensure_created_on($dbh);
237 4         77 }, $self)
238             }
239              
240             # XXX: delegate は、やりすぎだったかもしれない。
241             sub add_delegate {
242 76     76 0 132 my ($pack) = shift;
243 76         109 my ($alias, $dbic_method) = do {
244 76 100       153 if (@_ == 2) {
245 2         7 @_;
246             } else {
247 74         154 ($_[0], $_[0]);
248             }
249             };
250 76         197 *{globref($pack, $alias)} = sub {
251 4     4   8623 my MY $self = shift;
252 4         16 $self->dbic->$dbic_method(@_);
253 76         242 };
254             }
255              
256             foreach my $name (keys %DBIx::Class::Schema::) {
257             next unless $name =~ /^[a-z]\w*$/;
258             next unless *{$DBIx::Class::Schema::{$name}}{CODE};
259             next if MY->can($name);
260             MY->add_delegate($name);
261             }
262              
263             MY->add_delegate(model => 'resultset');
264              
265             {
266             package YATT::Lite::WebMVC0::DBSchema::DBIC::DBIC_SCHEMA;
267 2     2   15 use base qw(DBIx::Class::Schema);
  2         4  
  2         150  
268 2     2   13 use Carp;
  2         4  
  2         1739  
269             # XXX: Should this hold (weakened) ref to DBSchema?
270              
271             # Aid to migrate from YATT_DBSchema->to_zzz methods.
272             sub to_find {
273 3     3   15577 my ($dbic, $tabName, $keyCol, $rowidCol) = @_;
274 3         15 my $rs = $dbic->resultset($tabName);
275 3         988 my $dbh = $dbic->storage->dbh;
276 3 50       678 unless (defined $keyCol) {
    50          
    50          
277 0     0   0 sub { $rs->find(@_) }
278 0         0 } elsif (ref $keyCol eq 'ARRAY') {
  0         0  
279 0         0 my (@sql, @cols, @atts);
280 0         0 foreach (@$keyCol) {
281 0 0 0     0 push @atts, $_ and next if ref $_ eq 'HASH';
282 0         0 push @sql, do {
283 0 0       0 if (ref $_) {
    0          
284 0         0 my ($col, @comp) = @$_;
285 0 0       0 unless (@comp) {
    0          
    0          
286 0         0 push @cols, $col;
287 0         0 $col . ' = ?';
288 0         0 } elsif (@comp == 1) {
289 0         0 $dbh->quote_identifier($col) . ' = ' . $dbh->quote($comp[0]);
290 0         0 } elsif (@comp % 2 == 0) {
291 0         0 my @sql = $dbh->quote_identifier($col);
292 0         0 while (my ($kw, $value) = splice @comp, 0, 2) {
293 0         0 push @sql, $kw, $dbh->quote($value);
294             }
295 0         0 join " ", @sql;
296             } else {
297 0         0 croak "Invalid column spec: $col, @comp";
298             }
299             } elsif (/\?/) {
300 0 0       0 my ($col) = /^(\w+)/
301             or croak "Can't extract colname from colspec: $_";
302 0         0 push @cols, $col;
303 0         0 $_;
304             } else {
305 0         0 push @cols, $_;
306 0         0 $dbh->quote_identifier($_) . ' = ?';
307             }
308             }
309             }
310 0         0 my $sql = join " AND ", @sql;
311             sub {
312 0     0   0 my (@value) = @_;
313 0 0       0 unless (@value == @cols) {
314 0         0 croak "bind param length mismatch for $sql!(@{[scalar @value]})";
  0         0  
315             }
316 0 0       0 my $row = $rs->search(\ [$sql, zip(\@cols, \@value)], @atts)->single
317             or return undef;
318 0         0 $row->id;
319 0         0 };
320 0         0 } elsif (not defined $rowidCol) {
321             sub {
322 4     4   446 my ($value) = @_;
323 4 100       22 my $row = $rs->find({$keyCol => $value})
324             or return undef;
325 3         9421 $row->id;
326 3         21 };
327             } else {
328             sub {
329 0     0   0 my ($value) = @_;
330 0 0       0 my $row = $rs->find({$keyCol => $value})
331             or return undef;
332 0         0 $row->get_column($rowidCol);
333 0         0 };
334             }
335             }
336              
337             sub to_insert {
338 3     3   29446 my ($dbic, $tabName, @fields) = @_;
339 3         11 my $rs = $dbic->resultset($tabName);
340 3 50       959 unless (my ($pkCol, @morePkCol) = $rs->result_source->primary_columns) {
    50          
341             # If primary key is not defined, row obj is returned.
342 0         0 $dbic->to_insert_obj($tabName, @fields);
343 0         0 } elsif (@morePkCol) {
344 0         0 croak "table '$tabName' has multiple pk col, use to_insert_obj() please!";
345             } else {
346             sub {
347 4     4   3864 my %rec;
348 4         15 @rec{@fields} = @_;
349 4         18 my $row = $rs->new(\%rec)->insert;
350 4         5633 $row->get_column($pkCol);
351             }
352 3         46 }
353             }
354              
355             # This returns row object, not primary key.
356             sub to_insert_obj {
357 0     0   0 my ($dbic, $tabName, @fields) = @_;
358 0         0 my $rs = $dbic->resultset($tabName);
359             sub {
360 0     0   0 my %rec;
361 0         0 @rec{@fields} = @_;
362 0         0 $rs->new(\%rec)->insert;
363 0         0 };
364             }
365              
366             sub to_encode {
367 1     1   6 my ($dbic, $tabName, $keyCol, @otherCols) = @_;
368 1         4 my $to_find = $dbic->to_find($tabName, $keyCol);
369 1         6 my $to_ins = $dbic->to_insert($tabName, $keyCol, @otherCols);
370              
371             sub {
372 2     2   461 my ($value, @rest) = @_;
373 2 100       6 $to_find->($value) || $to_ins->($value, @rest);
374 1         6 };
375             }
376              
377             sub to_fetch {
378 2     2   517 my ($dbic, $tabName, $keyColList, $resColList, @rest) = @_;
379 2         8 my $sql = $dbic->YATT_DBSchema
380             ->sql_to_fetch($tabName, $keyColList, $resColList, @rest);
381 2         49 my $storage = $dbic->storage;
382             # XXX: dbh_do
383 2         31 my $sth;
384             sub {
385 2     2   7 my (@value) = @_;
386 2   33     12 $sth ||= $storage->dbh->prepare($sql);
387 2         528 $sth->execute(@value);
388 2         36 $sth;
389             }
390 2         12 }
391              
392             sub zip {
393 0     0     my @res;
394 0           for (my $i = 0; $i < @{$_[0]}; $i++) {
  0            
395 0           push @res, [map {$_->[$i]} @_];
  0            
396             }
397 0 0         wantarray ? @res : \@res;
398             }
399             }
400              
401 2     2   16 use YATT::Lite::Breakpoint ();
  2         5  
  2         94  
402             YATT::Lite::Breakpoint::break_load_dbschema_dbic();
403              
404             1;