... | ... |
@@ -10,6 +10,9 @@ my $builder = Module::Build->new( |
10 | 10 |
build_requires => { |
11 | 11 |
'Test::More' => 0, |
12 | 12 |
}, |
13 |
+ requires => { |
|
14 |
+ 'Object::Simple' => 0.0602, |
|
15 |
+ }, |
|
13 | 16 |
add_to_cleanup => [ 'DBI-Custom-*' ], |
14 | 17 |
create_makefile_pl => 'traditional', |
15 | 18 |
); |
... | ... |
@@ -5,64 +5,62 @@ our $VERSION = '0.0101'; |
5 | 5 |
|
6 | 6 |
use Carp 'croak'; |
7 | 7 |
use DBI; |
8 |
+use DBI::Custom::SQL::Template; |
|
9 |
+ |
|
10 |
+### Class-Object Accessors |
|
11 |
+sub connect_info : ClassObjectAttr { type => 'hash', auto_build => sub { |
|
12 |
+ shift->Object::Simple::initialize_class_object_attr( |
|
13 |
+ default => sub { {} }, clone => sub { |
|
14 |
+ my $value = shift; |
|
15 |
+ my $new_value = \%{$value || {}}; |
|
16 |
+ $new_value->{options} = $value->{options} if $value->{options}; |
|
17 |
+ return $new_value; |
|
18 |
+ } |
|
19 |
+ ) |
|
20 |
+}} |
|
8 | 21 |
|
9 |
-# Model |
|
10 |
-sub prototype : ClassAttr { auto_build => sub { |
|
11 |
- my $class = shift; |
|
12 |
- my $super = do { |
|
13 |
- no strict 'refs'; |
|
14 |
- ${"${class}::ISA"}[0]; |
|
15 |
- }; |
|
16 |
- my $prototype = eval{$super->can('prototype')} |
|
17 |
- ? $super->prototype->clone |
|
18 |
- : $class->Object::Simple::new; |
|
19 |
- |
|
20 |
- $class->prototype(bless $prototype, $class); |
|
22 |
+sub bind_filter : ClassObjectAttr { auto_build => sub { |
|
23 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'scalar') |
|
24 |
+}} |
|
25 |
+sub fetch_filter : ClassObjectAttr { auto_build => sub { |
|
26 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'scalar') |
|
21 | 27 |
}} |
22 | 28 |
|
23 |
-# New |
|
24 |
-sub new { |
|
25 |
- my $invocant = shift; |
|
26 |
- my $class = ref $invocant || $invocant; |
|
27 |
- my $prototype = $class->prototype; |
|
28 |
- my $self = $class->Object::Simple::new(%{$prototype->clone}, @_); |
|
29 |
- return bless $self, $class; |
|
30 |
-} |
|
29 |
+sub filters : ClassObjectAttr { type => 'hash', deref => 1, auto_build => sub { |
|
30 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'hash') |
|
31 |
+}} |
|
31 | 32 |
|
32 |
-# Clone |
|
33 |
-sub clone { |
|
34 |
- my $self = shift; |
|
35 |
- my $new = $self->Object::Simple::new; |
|
36 |
- |
|
37 |
- # Scalar copy |
|
38 |
- foreach my $attr (qw/bind_filter fetch_filter result_class/) { |
|
39 |
- $new->$attr($self->$attr); |
|
40 |
- } |
|
41 |
- |
|
42 |
- # Hash ref copy |
|
43 |
- foreach my $attr (qw/connect_info filters valid_connect_info/) { |
|
44 |
- $new->$attr(\%{$self->$attr || {}}); |
|
45 |
- } |
|
46 |
- |
|
47 |
- # Other |
|
48 |
- $new->connect_info->{options} = \%{$self->connect_info->{options}}; |
|
49 |
- $new->sql_template($self->sql_template->clone); |
|
50 |
-} |
|
33 |
+sub result_class : ClassObjectAttr { auto_build => sub { |
|
34 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'scalar') |
|
35 |
+}} |
|
51 | 36 |
|
52 |
-# Attribute |
|
53 |
-sub connect_info : Attr { type => 'hash', default => sub { {} } } |
|
54 |
-sub bind_filter : Attr {} |
|
55 |
-sub fetch_filter : Attr {} |
|
37 |
+sub sql_template : ClassObjectAttr { auto_build => sub { |
|
38 |
+ shift->Object::Simple::initialize_class_object_attr( |
|
39 |
+ clone => sub {my $value = shift; $value ? $value->clone : undef}, |
|
40 |
+ default => sub { DBI::Custom::SQL::Template->new } |
|
41 |
+ ) |
|
42 |
+}} |
|
56 | 43 |
|
57 |
-sub filters : Attr { type => 'hash', deref => 1, default => sub { {} } } |
|
58 |
-sub add_filter { shift->filters(@_) } |
|
44 |
+sub valid_connect_info : ClassObjectAttr { type => 'hash', deref => 1, auto_build => sub { |
|
45 |
+ shift->Object::Simple::initialize_class_object_attr( |
|
46 |
+ default => sub { return {map {$_ => 1} qw/data_source user password options/} }, |
|
47 |
+ clone => 'hash' |
|
48 |
+ ) |
|
49 |
+}} |
|
59 | 50 |
|
60 |
-sub result_class : Attr { default => 'DBI::Custom::Result' } |
|
51 |
+### Object Accessor |
|
61 | 52 |
sub dbh : Attr {} |
62 |
-sub sql_template : Attr { default => sub { DBI::Custom::SQL::Template->new } } |
|
63 |
-sub valid_connect_info : Attr { type => 'hash', deref => 1, default => sub { |
|
64 |
- return {map {$_ => 1} qw/data_source user password options/} |
|
65 |
-}} |
|
53 |
+ |
|
54 |
+ |
|
55 |
+### Methods |
|
56 |
+# Add filter |
|
57 |
+sub add_filter { |
|
58 |
+ my $invocant = shift; |
|
59 |
+ |
|
60 |
+ my %old_filters = $invocant->filters; |
|
61 |
+ my %new_filters = ref $_[0] eq 'HASH' ? %{$_[0]} : @_; |
|
62 |
+ $invocant->filters(%old_filters, %new_filters); |
|
63 |
+} |
|
66 | 64 |
|
67 | 65 |
# Auto commit |
68 | 66 |
sub auto_commit { |
... | ... |
@@ -104,6 +102,7 @@ sub connect { |
104 | 102 |
return $self; |
105 | 103 |
} |
106 | 104 |
|
105 |
+# DESTROY |
|
107 | 106 |
sub DESTROY { |
108 | 107 |
my $self = shift; |
109 | 108 |
$self->disconnect if $self->connected; |
... | ... |
@@ -223,404 +222,6 @@ sub query_raw_sql { |
223 | 222 |
|
224 | 223 |
Object::Simple->build_class; |
225 | 224 |
|
226 |
- |
|
227 |
-package DBI::Custom::Result; |
|
228 |
-use Object::Simple; |
|
229 |
- |
|
230 |
-# Attributes |
|
231 |
-sub sth : Attr {} |
|
232 |
-sub fetch_filter : Attr {} |
|
233 |
- |
|
234 |
- |
|
235 |
-# Fetch (array) |
|
236 |
-sub fetch { |
|
237 |
- my ($self, $type) = @_; |
|
238 |
- my $sth = $self->sth; |
|
239 |
- my $fetch_filter = $self->fetch_filter; |
|
240 |
- |
|
241 |
- # Fetch |
|
242 |
- my $row = $sth->fetchrow_arrayref; |
|
243 |
- |
|
244 |
- # Cannot fetch |
|
245 |
- return unless $row; |
|
246 |
- |
|
247 |
- # Filter |
|
248 |
- if ($fetch_filter) { |
|
249 |
- my $keys = $sth->{NAME_lc}; |
|
250 |
- my $types = $sth->{TYPE}; |
|
251 |
- for (my $i = 0; $i < @$keys; $i++) { |
|
252 |
- $row->[$i]= $fetch_filter->($keys->[$i], $row->[$i], $types->[$i], |
|
253 |
- $sth, $i); |
|
254 |
- } |
|
255 |
- } |
|
256 |
- return wantarray ? @$row : $row; |
|
257 |
-} |
|
258 |
- |
|
259 |
-# Fetch (hash) |
|
260 |
-sub fetch_hash { |
|
261 |
- my $self = shift; |
|
262 |
- my $sth = $self->sth; |
|
263 |
- my $fetch_filter = $self->fetch_filter; |
|
264 |
- |
|
265 |
- # Fetch |
|
266 |
- my $row = $sth->fetchrow_arrayref; |
|
267 |
- |
|
268 |
- # Cannot fetch |
|
269 |
- return unless $row; |
|
270 |
- |
|
271 |
- # Keys |
|
272 |
- my $keys = $sth->{NAME_lc}; |
|
273 |
- |
|
274 |
- # Filter |
|
275 |
- my $row_hash = {}; |
|
276 |
- if ($fetch_filter) { |
|
277 |
- my $types = $sth->{TYPE}; |
|
278 |
- for (my $i = 0; $i < @$keys; $i++) { |
|
279 |
- $row_hash->{$keys->[$i]} = $fetch_filter->($keys->[$i], $row->[$i], |
|
280 |
- $types->[$i], $sth, $i); |
|
281 |
- } |
|
282 |
- } |
|
283 |
- |
|
284 |
- # No filter |
|
285 |
- else { |
|
286 |
- for (my $i = 0; $i < @$keys; $i++) { |
|
287 |
- $row_hash->{$keys->[$i]} = $row->[$i]; |
|
288 |
- } |
|
289 |
- } |
|
290 |
- return wantarray ? %$row_hash : $row_hash; |
|
291 |
-} |
|
292 |
- |
|
293 |
-# Fetch all (array) |
|
294 |
-sub fetch_all { |
|
295 |
- my $self = shift; |
|
296 |
- |
|
297 |
- my $rows = []; |
|
298 |
- while(my @row = $self->fetch) { |
|
299 |
- push @$rows, [@row]; |
|
300 |
- } |
|
301 |
- return wantarray ? @$rows : $rows; |
|
302 |
-} |
|
303 |
- |
|
304 |
-# Fetch all (hash) |
|
305 |
-sub fetch_all_hash { |
|
306 |
- my $self = shift; |
|
307 |
- |
|
308 |
- my $rows = []; |
|
309 |
- while(my %row = $self->fetch_hash) { |
|
310 |
- push @$rows, {%row}; |
|
311 |
- } |
|
312 |
- return wantarray ? @$rows : $rows; |
|
313 |
-} |
|
314 |
- |
|
315 |
-# Finish |
|
316 |
-sub finish { shift->sth->finish } |
|
317 |
- |
|
318 |
-# Error |
|
319 |
-sub error { |
|
320 |
- my $self = shift; |
|
321 |
- my $sth = $self->sth; |
|
322 |
- return wantarray ? ($sth->errstr, $sth->err, $sth->state) : $sth->errstr; |
|
323 |
-} |
|
324 |
- |
|
325 |
-Object::Simple->build_class; |
|
326 |
- |
|
327 |
- |
|
328 |
-package DBI::Custom::SQL::Template; |
|
329 |
-use Object::Simple; |
|
330 |
-use Carp 'croak'; |
|
331 |
- |
|
332 |
-# Clone |
|
333 |
-sub clone { |
|
334 |
- my $self = shift; |
|
335 |
- my $new = $self->Object::Simple::new; |
|
336 |
- |
|
337 |
- # Scalar copy |
|
338 |
- foreach my $attr (qw/tag_start tag_end bind_filter upper_case tag_syntax template/) { |
|
339 |
- $new->$attr($self->$attr); |
|
340 |
- } |
|
341 |
- |
|
342 |
- # Hash ref copy |
|
343 |
- foreach my $attr (qw/tag_processors/) { |
|
344 |
- $new->$attr(\%{$self->$attr || {}}); |
|
345 |
- } |
|
346 |
- |
|
347 |
- # Other |
|
348 |
- $new->tree([]); |
|
349 |
- |
|
350 |
- return $new; |
|
351 |
-} |
|
352 |
- |
|
353 |
- |
|
354 |
-### Attributes; |
|
355 |
-sub tag_start : Attr { default => '{' } |
|
356 |
-sub tag_end : Attr { default => '}' } |
|
357 |
-sub template : Attr {}; |
|
358 |
-sub tree : Attr { default => sub { [] } } |
|
359 |
-sub bind_filter : Attr {} |
|
360 |
-sub upper_case : Attr {default => 0} |
|
361 |
- |
|
362 |
-sub tag_syntax : Attr { default => <<'EOS' }; |
|
363 |
-{? name} ? |
|
364 |
-{= name} name = ? |
|
365 |
-{<> name} name <> ? |
|
366 |
- |
|
367 |
-{< name} name < ? |
|
368 |
-{> name} name > ? |
|
369 |
-{>= name} name >= ? |
|
370 |
-{<= name} name <= ? |
|
371 |
- |
|
372 |
-{like name} name like ? |
|
373 |
-{in name} name in [?, ?, ..] |
|
374 |
- |
|
375 |
-{insert_values} (key1, key2, key3) values (?, ?, ?) |
|
376 |
-{update_values} set key1 = ?, key2 = ?, key3 = ? |
|
377 |
-EOS |
|
378 |
- |
|
379 |
-sub tag_processors : Attr {type => 'hash', deref => 1, auto_build => sub { |
|
380 |
- shift->tag_processors( |
|
381 |
- '?' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
382 |
- '=' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
383 |
- '<>' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
384 |
- '>' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
385 |
- '<' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
386 |
- '>=' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
387 |
- '<=' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
388 |
- 'like' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
389 |
- 'in' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
390 |
- 'insert_values' => \&DBI::Custom::SQL::Template::TagProcessor::expand_insert_values, |
|
391 |
- 'update_set' => \&DBI::Custom::SQL::Template::TagProcessor::expand_update_set |
|
392 |
- ); |
|
393 |
-}} |
|
394 |
- |
|
395 |
-sub add_tag_processor { |
|
396 |
- my $class = shift; |
|
397 |
- my $tag_processors = ref $_[0] eq 'HASH' ? $_[0] : {@_}; |
|
398 |
- $class->tag_processor(%{$class->tag_processor}, %{$tag_processors}); |
|
399 |
-} |
|
400 |
- |
|
401 |
-sub create_sql { |
|
402 |
- my ($self, $template, $values, $filter) = @_; |
|
403 |
- |
|
404 |
- $filter ||= $self->bind_filter; |
|
405 |
- |
|
406 |
- $self->parse($template); |
|
407 |
- |
|
408 |
- my ($sql, @bind) = $self->build_sql({bind_filter => $filter, values => $values}); |
|
409 |
- |
|
410 |
- return ($sql, @bind); |
|
411 |
-} |
|
412 |
- |
|
413 |
-sub parse { |
|
414 |
- my ($self, $template) = @_; |
|
415 |
- $self->template($template); |
|
416 |
- |
|
417 |
- # Clean start; |
|
418 |
- $self->tree([]); |
|
419 |
- |
|
420 |
- # Tags |
|
421 |
- my $tag_start = quotemeta $self->tag_start; |
|
422 |
- my $tag_end = quotemeta $self->tag_end; |
|
423 |
- |
|
424 |
- # Tokenize |
|
425 |
- my $state = 'text'; |
|
426 |
- |
|
427 |
- # Save original template |
|
428 |
- my $original_template = $template; |
|
429 |
- |
|
430 |
- # Text |
|
431 |
- while ($template =~ s/([^$tag_start]*?)$tag_start([^$tag_end].*?)$tag_end//sm) { |
|
432 |
- my $text = $1; |
|
433 |
- my $tag = $2; |
|
434 |
- |
|
435 |
- push @{$self->tree}, {type => 'text', args => [$text]} if $text; |
|
436 |
- |
|
437 |
- if ($tag) { |
|
438 |
- |
|
439 |
- my ($tag_name, @args) = split /\s+/, $tag; |
|
440 |
- |
|
441 |
- $tag ||= ''; |
|
442 |
- unless ($self->tag_processors->{$tag_name}) { |
|
443 |
- my $tag_syntax = $self->tag_syntax; |
|
444 |
- croak("Tag '{$tag}' in SQL template is not exist.\n\n" . |
|
445 |
- "SQL template tag syntax\n" . |
|
446 |
- "$tag_syntax\n\n" . |
|
447 |
- "Your SQL template is \n$original_template\n\n"); |
|
448 |
- } |
|
449 |
- |
|
450 |
- push @{$self->tree}, {type => 'tag', tag_name => $tag_name, args => [@args]}; |
|
451 |
- } |
|
452 |
- } |
|
453 |
- |
|
454 |
- push @{$self->tree}, {type => 'text', args => [$template]} if $template; |
|
455 |
-} |
|
456 |
- |
|
457 |
-sub build_sql { |
|
458 |
- my ($self, $args) = @_; |
|
459 |
- |
|
460 |
- my $tree = $args->{tree} || $self->tree; |
|
461 |
- my $bind_filter = $args->{bind_filter} || $self->bind_filter; |
|
462 |
- my $values = $args->{values} || {}; |
|
463 |
- |
|
464 |
- my @bind_values_all; |
|
465 |
- my $sql = ''; |
|
466 |
- foreach my $node (@$tree) { |
|
467 |
- my $type = $node->{type}; |
|
468 |
- my $tag_name = $node->{tag_name}; |
|
469 |
- my $args = $node->{args}; |
|
470 |
- |
|
471 |
- if ($type eq 'text') { |
|
472 |
- # Join text |
|
473 |
- $sql .= $args->[0]; |
|
474 |
- } |
|
475 |
- elsif ($type eq 'tag') { |
|
476 |
- my $tag_processor = $self->tag_processors->{$tag_name}; |
|
477 |
- |
|
478 |
- croak("Tag processor '$type' must be code reference") |
|
479 |
- unless ref $tag_processor eq 'CODE'; |
|
480 |
- |
|
481 |
- my ($expand, @bind_values) |
|
482 |
- = $tag_processor->($tag_name, $args, $values, |
|
483 |
- $bind_filter, $self); |
|
484 |
- |
|
485 |
- $DB::single = 1; |
|
486 |
- unless ($self->_placeholder_count($expand) == @bind_values) { |
|
487 |
- require Data::Dumper; |
|
488 |
- |
|
489 |
- my $bind_values_dump |
|
490 |
- = Data::Dumper->Dump([\@bind_values], ['*bind_values']); |
|
491 |
- |
|
492 |
- croak("Place holder count must be same as bind value count\n" . |
|
493 |
- "Tag : $tag_name\n" . |
|
494 |
- "Expand : $expand\n" . |
|
495 |
- "Bind values: $bind_values_dump\n"); |
|
496 |
- } |
|
497 |
- push @bind_values_all, @bind_values; |
|
498 |
- $sql .= $expand; |
|
499 |
- } |
|
500 |
- } |
|
501 |
- $sql .= ';' unless $sql =~ /;$/; |
|
502 |
- return ($sql, @bind_values_all); |
|
503 |
-} |
|
504 |
- |
|
505 |
-sub _placeholder_count { |
|
506 |
- my ($self, $expand) = @_; |
|
507 |
- $expand ||= ''; |
|
508 |
- |
|
509 |
- my $count = 0; |
|
510 |
- my $pos = -1; |
|
511 |
- while (($pos = index($expand, '?', $pos + 1)) != -1) { |
|
512 |
- $count++; |
|
513 |
- } |
|
514 |
- return $count; |
|
515 |
-} |
|
516 |
- |
|
517 |
-Object::Simple->build_class; |
|
518 |
- |
|
519 |
- |
|
520 |
-package DBI::Custom::SQL::Template::TagProcessor; |
|
521 |
-use strict; |
|
522 |
-use warnings; |
|
523 |
- |
|
524 |
-sub expand_place_holder { |
|
525 |
- my ($tag_name, $args, $values, $bind_filter, $sql_tmpl_obj) = @_; |
|
526 |
- |
|
527 |
- my $key = $args->[0]; |
|
528 |
- |
|
529 |
- my @bind_values; |
|
530 |
- # Filter Value |
|
531 |
- if ($tag_name eq 'in') { |
|
532 |
- $values->{$key} = [$values->{$key}] unless ref $values->{$key} eq 'ARRAY'; |
|
533 |
- if ($bind_filter) { |
|
534 |
- for (my $i = 0; $i < @$values; $i++) { |
|
535 |
- push @bind_values, scalar $bind_filter->($key, $values->{$key}->[$i]); |
|
536 |
- } |
|
537 |
- } |
|
538 |
- else { |
|
539 |
- for (my $i = 0; $i < @$values; $i++) { |
|
540 |
- push @bind_values, $values->{$key}->[$i]; |
|
541 |
- } |
|
542 |
- } |
|
543 |
- } |
|
544 |
- else { |
|
545 |
- if ($bind_filter) { |
|
546 |
- push @bind_values, scalar $bind_filter->($key, $values->{$key}); |
|
547 |
- } |
|
548 |
- else { |
|
549 |
- push @bind_values, $values->{$key}; |
|
550 |
- } |
|
551 |
- } |
|
552 |
- |
|
553 |
- $tag_name = uc $tag_name if $sql_tmpl_obj->upper_case; |
|
554 |
- |
|
555 |
- my $expand; |
|
556 |
- if ($tag_name eq '?') { |
|
557 |
- $expand = '?'; |
|
558 |
- } |
|
559 |
- elsif ($tag_name eq 'in') { |
|
560 |
- $expand = '('; |
|
561 |
- for (my $i = 0; $i < @$values; $i++) { |
|
562 |
- $expand .= '?, '; |
|
563 |
- } |
|
564 |
- $expand =~ s/, $'//; |
|
565 |
- $expand .= ')'; |
|
566 |
- } |
|
567 |
- else { |
|
568 |
- $expand = "$key $tag_name ?"; |
|
569 |
- } |
|
570 |
- |
|
571 |
- return ($expand, @bind_values); |
|
572 |
-} |
|
573 |
- |
|
574 |
-sub expand_insert_values { |
|
575 |
- my ($tag_name, $args, $values, $bind_filter, $sql_tmpl_obj) = @_; |
|
576 |
- |
|
577 |
- my $insert_keys = '('; |
|
578 |
- my $place_holders = '('; |
|
579 |
- |
|
580 |
- $values = $args->[0] ? $values->{$args->[0]} : $values->{insert_values}; |
|
581 |
- |
|
582 |
- my @bind_values; |
|
583 |
- foreach my $key (sort keys %$values) { |
|
584 |
- $bind_filter ? push @bind_values, scalar $bind_filter->($key, $values->{$key}) |
|
585 |
- : push @bind_values, $values->{$key}; |
|
586 |
- |
|
587 |
- $insert_keys .= "$key, "; |
|
588 |
- $place_holders .= "?, "; |
|
589 |
- } |
|
590 |
- |
|
591 |
- $insert_keys =~ s/, $//; |
|
592 |
- $insert_keys .= ')'; |
|
593 |
- |
|
594 |
- $place_holders =~ s/, $//; |
|
595 |
- $place_holders .= ')'; |
|
596 |
- |
|
597 |
- my $expand = $sql_tmpl_obj->upper_case ? "$insert_keys VALUES $place_holders" |
|
598 |
- : "$insert_keys values $place_holders"; |
|
599 |
- |
|
600 |
- return ($expand, @bind_values); |
|
601 |
-} |
|
602 |
- |
|
603 |
-sub expand_update_set { |
|
604 |
- my ($tag_name, $args, $values, $bind_filter, $sql_tmpl_obj) = @_; |
|
605 |
- |
|
606 |
- my $expand = $sql_tmpl_obj->upper_case ? 'SET ' : 'set '; |
|
607 |
- $values = $args->[0] ? $values->{$args->[0]} : $values->{update_set}; |
|
608 |
- |
|
609 |
- my @bind_values; |
|
610 |
- foreach my $key (sort keys %$values) { |
|
611 |
- $bind_filter ? push @bind_values, scalar $bind_filter->($key, $values->{$key}) |
|
612 |
- : push @bind_values, $values->{$key}; |
|
613 |
- |
|
614 |
- $expand .= "$key = ?, "; |
|
615 |
- } |
|
616 |
- $expand =~ s/, $//; |
|
617 |
- return ($expand, @bind_values); |
|
618 |
-} |
|
619 |
- |
|
620 |
- |
|
621 |
-package DBI::Custom; |
|
622 |
-1; |
|
623 |
- |
|
624 | 225 |
=head1 NAME |
625 | 226 |
|
626 | 227 |
DBI::Custom - Customizable simple DBI |
... | ... |
@@ -4,6 +4,7 @@ use warnings; |
4 | 4 |
|
5 | 5 |
use DBI::Custom; |
6 | 6 |
use Scalar::Util qw/blessed/; |
7 |
+use DBI::Custom::SQL::Template; |
|
7 | 8 |
|
8 | 9 |
my $sql_tmpl1 = DBI::Custom::SQL::Template->new->upper_case(0); |
9 | 10 |
my $sql_tmpl2 = DBI::Custom::SQL::Template->new->upper_case(1); |
... | ... |
@@ -22,14 +23,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
22 | 23 |
}, |
23 | 24 |
bind_filter => 'f', |
24 | 25 |
fetch_filter => 'g', |
25 |
- dbh => 'e', |
|
26 | 26 |
result_class => 'g', |
27 | 27 |
sql_template => $sql_tmpl1, |
28 | 28 |
valid_connect_info => {i => 1} |
29 | 29 |
); |
30 | 30 |
is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', |
31 | 31 |
options => {d => 1, e => 2}}, filters => {f => 3}, bind_filter => 'f', |
32 |
- fetch_filter => 'g', dbh => 'e', result_class => 'g', |
|
32 |
+ fetch_filter => 'g', result_class => 'g', |
|
33 | 33 |
sql_template => $sql_tmpl1, valid_connect_info => {i => 1}}, 'new'); |
34 | 34 |
|
35 | 35 |
isa_ok($dbi, 'DBI::Custom'); |
... | ... |
@@ -40,9 +40,9 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
40 | 40 |
package DBI::Custom::T1; |
41 | 41 |
use base 'DBI::Custom'; |
42 | 42 |
|
43 |
- my $prototype = __PACKAGE__->prototype; |
|
43 |
+ my $class = __PACKAGE__; |
|
44 | 44 |
|
45 |
- $prototype |
|
45 |
+ $class |
|
46 | 46 |
->connect_info( |
47 | 47 |
user => 'a', |
48 | 48 |
password => 'b', |
... | ... |
@@ -54,7 +54,6 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
54 | 54 |
) |
55 | 55 |
->bind_filter('f') |
56 | 56 |
->fetch_filter('g') |
57 |
- ->dbh('e') |
|
58 | 57 |
->result_class('DBI::Custom::Result') |
59 | 58 |
->sql_template($sql_tmpl1) |
60 | 59 |
->valid_connect_info({p => 1}) |
... | ... |
@@ -90,11 +89,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
90 | 89 |
{ |
91 | 90 |
my $dbi = DBI::Custom::T1->new; |
92 | 91 |
|
93 |
- my $sql_tmpl = delete $dbi->{sql_template}; |
|
94 |
- is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}}, |
|
95 |
- filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', result_class => 'DBI::Custom::Result', |
|
96 |
- valid_connect_info => {p => 1}}, 'new custom class'); |
|
97 |
- |
|
92 |
+ is_deeply($dbi->connect_info, {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}}); |
|
93 |
+ is_deeply({$dbi->filters}, {f => 3}); |
|
94 |
+ is($dbi->bind_filter, 'f'); |
|
95 |
+ is($dbi->fetch_filter, 'g'); |
|
96 |
+ is($dbi->result_class, 'DBI::Custom::Result'); |
|
97 |
+ is_deeply({$dbi->valid_connect_info},{p => 1}); |
|
98 |
+ is($dbi->sql_template->upper_case, 0); |
|
98 | 99 |
isa_ok($dbi, 'DBI::Custom::T1'); |
99 | 100 |
|
100 | 101 |
} |
... | ... |
@@ -107,11 +108,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
107 | 108 |
{ |
108 | 109 |
my $dbi = DBI::Custom::T1_2->new; |
109 | 110 |
|
110 |
- my $sql_tmpl = delete $dbi->{sql_template}; |
|
111 |
- is($sql_tmpl->upper_case, 0); |
|
112 |
- is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}}, |
|
113 |
- filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', result_class => 'DBI::Custom::Result', |
|
114 |
- valid_connect_info => {p => 1}}, 'new custom class inherit'); |
|
111 |
+ is_deeply($dbi->connect_info, {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}}); |
|
112 |
+ is_deeply(scalar $dbi->filters, {f => 3}); |
|
113 |
+ is($dbi->bind_filter, 'f'); |
|
114 |
+ is($dbi->fetch_filter, 'g'); |
|
115 |
+ is($dbi->result_class, 'DBI::Custom::Result'); |
|
116 |
+ is_deeply({$dbi->valid_connect_info}, {p => 1}); |
|
117 |
+ is($dbi->sql_template->upper_case, 0); |
|
115 | 118 |
|
116 | 119 |
isa_ok($dbi, 'DBI::Custom::T1_2'); |
117 | 120 |
} |
... | ... |
@@ -120,9 +123,9 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
120 | 123 |
package DBI::Custom::T1_3; |
121 | 124 |
use base 'DBI::Custom::T1'; |
122 | 125 |
|
123 |
- my $prototype = __PACKAGE__->prototype; |
|
126 |
+ my $class = __PACKAGE__; |
|
124 | 127 |
|
125 |
- $prototype |
|
128 |
+ $class |
|
126 | 129 |
->connect_info( |
127 | 130 |
user => 'ao', |
128 | 131 |
password => 'bo', |
... | ... |
@@ -134,7 +137,6 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
134 | 137 |
) |
135 | 138 |
->bind_filter('fo') |
136 | 139 |
->fetch_filter('go') |
137 |
- ->dbh('eo') |
|
138 | 140 |
->result_class('ho') |
139 | 141 |
->sql_template($sql_tmpl2) |
140 | 142 |
->valid_connect_info({p => 3}) |
... | ... |
@@ -144,11 +146,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
144 | 146 |
{ |
145 | 147 |
my $dbi = DBI::Custom::T1_3->new; |
146 | 148 |
|
147 |
- my $sql_tmpl = delete $dbi->{sql_template}; |
|
148 |
- is($sql_tmpl->upper_case, 1); |
|
149 |
- is_deeply($dbi,{connect_info => {user => 'ao', password => 'bo', data_source => 'co', options => {do => 10, eo => 20}}, |
|
150 |
- filters => {fo => 30}, bind_filter => 'fo', fetch_filter => 'go', result_class => 'ho', |
|
151 |
- valid_connect_info => {p => 3}}, 'new custom class'); |
|
149 |
+ is_deeply($dbi->connect_info, {user => 'ao', password => 'bo', data_source => 'co', options => {do => 10, eo => 20}}); |
|
150 |
+ is_deeply(scalar $dbi->filters, {fo => 30}); |
|
151 |
+ is($dbi->bind_filter, 'fo'); |
|
152 |
+ is($dbi->fetch_filter, 'go'); |
|
153 |
+ is($dbi->result_class, 'ho'); |
|
154 |
+ is_deeply(scalar $dbi->valid_connect_info, {p => 3}); |
|
155 |
+ is($dbi->sql_template->upper_case, 1); |
|
152 | 156 |
|
153 | 157 |
isa_ok($dbi, 'DBI::Custom::T1_3'); |
154 | 158 |
} |
... | ... |
@@ -166,7 +170,6 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
166 | 170 |
}, |
167 | 171 |
bind_filter => 'f', |
168 | 172 |
fetch_filter => 'g', |
169 |
- dbh => 'e', |
|
170 | 173 |
result_class => 'h', |
171 | 174 |
sql_template => $sql_tmpl3, |
172 | 175 |
valid_connect_info => {p => 4} |
... | ... |
@@ -175,7 +178,7 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
175 | 178 |
my $sql_tmpl = delete $dbi->{sql_template}; |
176 | 179 |
is($sql_tmpl->upper_case, 2); |
177 | 180 |
is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}}, |
178 |
- filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', dbh => 'e', result_class => 'h', |
|
181 |
+ filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', result_class => 'h', |
|
179 | 182 |
valid_connect_info => {p => 4}}, 'new'); |
180 | 183 |
|
181 | 184 |
isa_ok($dbi, 'DBI::Custom'); |