DBIx-Custom / lib / DBI / Custom.pm /
Newer Older
508 lines | 12.615kb
first commit
yuki-kimoto authored on 2009-10-13
1
package DBI::Custom;
2
use Object::Simple;
add test
yuki-kimoto authored on 2009-10-16
3

            
4
our $VERSION = '0.0101';
5

            
6
use Carp 'croak';
add some method
yuki-kimoto authored on 2009-10-14
7
use DBI;
add tests
yuki-kimoto authored on 2009-10-25
8
use DBI::Custom::SQL::Template;
add tests
yuki-kimoto authored on 2009-10-25
9
use DBI::Custom::Result;
add tests
yuki-kimoto authored on 2009-10-25
10

            
11
### Class-Object Accessors
update document
yuki-kimoto authored on 2009-10-27
12
sub user        : ClassObjectAttr { initialize => {clone => 'scalar'} }
13
sub password    : ClassObjectAttr { initialize => {clone => 'scalar'} }
14
sub data_source : ClassObjectAttr { initialize => {clone => 'scalar'} }
add various thins
yuki-kimoto authored on 2009-10-29
15
sub database    : ClassObjectAttr { initialize => {clone => 'scalar'} }
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
16

            
update document
yuki-kimoto authored on 2009-10-27
17
sub dbi_option : ClassObjectAttr { initialize => {clone => 'hash', 
18
                                                  default => sub { {} } } }
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
19

            
update document
yuki-kimoto authored on 2009-10-27
20
sub bind_filter  : ClassObjectAttr { initialize => {clone => 'scalar'} }
21
sub fetch_filter : ClassObjectAttr { initialize => {clone => 'scalar'} }
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
22

            
add various thins
yuki-kimoto authored on 2009-10-29
23
sub no_filters   : ClassObjectAttr { initialize => {clone => 'array'} }
24

            
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
25
sub filters : ClassObjectAttr {
26
    type => 'hash',
27
    deref => 1,
28
    initialize => {
add tests
yuki-kimoto authored on 2009-10-25
29
        clone   => 'hash',
30
        default => sub { {} }
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
31
    }
32
}
first commit
yuki-kimoto authored on 2009-10-13
33

            
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
34
sub result_class : ClassObjectAttr {
35
    initialize => {
add tests
yuki-kimoto authored on 2009-10-25
36
        clone   => 'scalar',
37
        default => 'DBI::Custom::Result'
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
38
    }
39
}
cleanup
yuki-kimoto authored on 2009-10-14
40

            
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
41
sub sql_template : ClassObjectAttr {
42
    initialize => {
update document
yuki-kimoto authored on 2009-10-27
43
        clone   => sub {$_[0] ? $_[0]->clone : undef},
catch up Object::Simple upda...
yuki-kimoto authored on 2009-10-26
44
        default => sub {DBI::Custom::SQL::Template->new}
45
    }
46
}
cleanup
yuki-kimoto authored on 2009-10-15
47

            
add tests
yuki-kimoto authored on 2009-10-25
48
### Object Accessor
add tests
yuki-kimoto authored on 2009-10-18
49
sub dbh          : Attr {}
add tests
yuki-kimoto authored on 2009-10-25
50

            
51

            
52
### Methods
add various thins
yuki-kimoto authored on 2009-10-29
53

            
add tests
yuki-kimoto authored on 2009-10-25
54
# Add filter
55
sub add_filter {
56
    my $invocant = shift;
57
    
58
    my %old_filters = $invocant->filters;
59
    my %new_filters = ref $_[0] eq 'HASH' ? %{$_[0]} : @_;
60
    $invocant->filters(%old_filters, %new_filters);
update document
yuki-kimoto authored on 2009-10-27
61
    return $invocant;
add tests
yuki-kimoto authored on 2009-10-25
62
}
add various
yuki-kimoto authored on 2009-10-18
63

            
64
# Auto commit
update document
yuki-kimoto authored on 2009-10-27
65
sub _auto_commit {
add various
yuki-kimoto authored on 2009-10-18
66
    my $self = shift;
67
    
68
    croak("Cannot change AutoCommit becouse of not connected")
69
        unless $self->dbh;
70
    
71
    if (@_) {
72
        $self->dbh->{AutoCommit} = $_[0];
73
        return $self;
74
    }
75
    return $self->dbh->{AutoCommit};
76
}
add test
yuki-kimoto authored on 2009-10-16
77

            
add various things
yuki-kimoto authored on 2009-10-17
78
# Connect
add some method
yuki-kimoto authored on 2009-10-14
79
sub connect {
80
    my $self = shift;
update document
yuki-kimoto authored on 2009-10-27
81
    my $data_source = $self->data_source;
82
    my $user        = $self->user;
83
    my $password    = $self->password;
84
    my $dbi_option  = $self->dbi_option;
add test
yuki-kimoto authored on 2009-10-16
85
    
add some method
yuki-kimoto authored on 2009-10-14
86
    my $dbh = DBI->connect(
update document
yuki-kimoto authored on 2009-10-27
87
        $data_source,
88
        $user,
89
        $password,
add some method
yuki-kimoto authored on 2009-10-14
90
        {
91
            RaiseError => 1,
92
            PrintError => 0,
93
            AutoCommit => 1,
update document
yuki-kimoto authored on 2009-10-27
94
            %{$dbi_option || {} }
add some method
yuki-kimoto authored on 2009-10-14
95
        }
96
    );
97
    
98
    $self->dbh($dbh);
add various
yuki-kimoto authored on 2009-10-18
99
    return $self;
add some method
yuki-kimoto authored on 2009-10-14
100
}
first commit
yuki-kimoto authored on 2009-10-13
101

            
add tests
yuki-kimoto authored on 2009-10-25
102
# DESTROY
add tests
yuki-kimoto authored on 2009-10-18
103
sub DESTROY {
104
    my $self = shift;
add tests
yuki-kimoto authored on 2009-10-18
105
    $self->disconnect if $self->connected;
add tests
yuki-kimoto authored on 2009-10-18
106
}
107

            
add various things
yuki-kimoto authored on 2009-10-17
108
# Is connected?
109
sub connected {
110
    my $self = shift;
add tests
yuki-kimoto authored on 2009-10-18
111
    return exists $self->{dbh} && eval {$self->{dbh}->can('prepare')};
add various things
yuki-kimoto authored on 2009-10-17
112
}
113

            
114
# Disconnect
115
sub disconnect {
116
    my $self = shift;
add tests
yuki-kimoto authored on 2009-10-18
117
    if ($self->connected) {
add various things
yuki-kimoto authored on 2009-10-17
118
        $self->dbh->disconnect;
119
        delete $self->{dbh};
120
    }
121
}
122

            
123
# Reconnect
124
sub reconnect {
125
    my $self = shift;
add tests
yuki-kimoto authored on 2009-10-18
126
    $self->disconnect if $self->connected;
add various things
yuki-kimoto authored on 2009-10-17
127
    $self->connect;
128
}
129

            
try various
yuki-kimoto authored on 2009-10-21
130
# Run tranzaction
131
sub run_tranzaction {
132
    my ($self, $tranzaction) = @_;
133
    
update document
yuki-kimoto authored on 2009-10-27
134
    $self->_auto_commit(0);
try various
yuki-kimoto authored on 2009-10-21
135
    
136
    eval {
137
        $tranzaction->();
138
        $self->dbh->commit;
139
    };
140
    
141
    if ($@) {
142
        my $tranzaction_error = $@;
143
        
144
        $self->dbh->rollback or croak("$@ and rollback also failed");
145
        croak("$tranzaction_error");
146
    }
update document
yuki-kimoto authored on 2009-10-27
147
    $self->_auto_commit(1);
add tests
yuki-kimoto authored on 2009-10-18
148
}
149

            
add various thins
yuki-kimoto authored on 2009-10-29
150
sub create_query {
151
    my ($self, $template) = @_;
add test
yuki-kimoto authored on 2009-10-17
152
    
add various thins
yuki-kimoto authored on 2009-10-29
153
    # Create query from SQL template
154
    my $query = $self->sql_template->create_query($template);
add test
yuki-kimoto authored on 2009-10-17
155
    
add various thins
yuki-kimoto authored on 2009-10-29
156
    # Create Query object;
157
    my $query = DBI::Custom::Query->new($query);
158
    
159
    # connect if not
160
    $self->connect unless $self->connected;
try varioud way
yuki-kimoto authored on 2009-10-17
161
    
add various thins
yuki-kimoto authored on 2009-10-29
162
    # Prepare statement handle
163
    my $sth = $self->dbh->prepare($query->{sql});
add tests
yuki-kimoto authored on 2009-10-18
164
    
add various thins
yuki-kimoto authored on 2009-10-29
165
    $query->sth($sth);
add tests
yuki-kimoto authored on 2009-10-18
166
    
add various thins
yuki-kimoto authored on 2009-10-29
167
    return $query;
168
}
169

            
170
sub execute {
171
    my ($self, $query, $params)  = @_;
try varioud way
yuki-kimoto authored on 2009-10-17
172
    
add various thins
yuki-kimoto authored on 2009-10-29
173
    # Create query if First argument is template
174
    if (!ref $query) {
175
        my $template = $query;
176
        $query = $sefl->create_query($tempalte);
177
    }
add tests
yuki-kimoto authored on 2009-10-18
178
    
add various thins
yuki-kimoto authored on 2009-10-29
179
    # Set bind filter
180
    $query->bind_filter($self->bind_filter) unless $query->bind_filter;
add tests
yuki-kimoto authored on 2009-10-18
181
    
add various thins
yuki-kimoto authored on 2009-10-29
182
    # Set no filter keys
183
    $query->no_filters($self->no_filters) unless $query->no_filters;
add tests
yuki-kimoto authored on 2009-10-18
184
    
add various thins
yuki-kimoto authored on 2009-10-29
185
    # Create bind value
186
    my $bind_values = $self->_build_bind_values($query, $params);
add tests
yuki-kimoto authored on 2009-10-18
187
    
cleanup
yuki-kimoto authored on 2009-10-18
188
    # Execute
add various thins
yuki-kimoto authored on 2009-10-29
189
    my $ret_val = $query->sth->execute(@$bind_values);
add various things
yuki-kimoto authored on 2009-10-17
190
    
cleanup
yuki-kimoto authored on 2009-10-18
191
    # Return resultset if select statement is executed
add various things
yuki-kimoto authored on 2009-10-17
192
    if ($sth->{NUM_OF_FIELDS}) {
193
        my $result_class = $self->result_class;
add various
yuki-kimoto authored on 2009-10-18
194
        my $result = $result_class->new({
195
            sth => $sth,
196
            fetch_filter => $self->fetch_filter
197
        });
add various things
yuki-kimoto authored on 2009-10-17
198
        return $result;
199
    }
add tests
yuki-kimoto authored on 2009-10-18
200
    return $ret_val;
add test
yuki-kimoto authored on 2009-10-17
201
}
202

            
add various thins
yuki-kimoto authored on 2009-10-29
203
sub _build_bind_values {
204
    my ($self, $query, $params) = @_;
205
    my $bind_filter = $query->bind_filter;
206
    my $no_filters_map  = $query->_no_filters_map || {};
cleanup
yuki-kimoto authored on 2009-10-18
207
    
add various thins
yuki-kimoto authored on 2009-10-29
208
    # binding values
209
    my @bind_values;
cleanup
yuki-kimoto authored on 2009-10-18
210
    
add various thins
yuki-kimoto authored on 2009-10-29
211
    # Filter and sdd bind values
212
    foreach my $param_key_info (@$param_key_infos) {
213
        my $filtering_key = $param_key_info->{key};
214
        my $access_keys = $param_key_info->{access_keys};
215
        
216
        my $original_key = $param_key_info->{original_key} || '';
217
        my $table        = $param_key_info->{table}        || '';
218
        my $column       = $param_key_info->{column}       || '';
219
        
220
        ACCESS_KEYS :
221
        foreach my $access_key (@$access_keys) {
222
            my $root_params = $params;
223
            for (my $i = 0; $i < @$access_key; $i++) {
224
                my $key = $access_key->[$i];
225
                
226
                croak("'access_keys' each value must be string or array reference")
227
                  unless (ref $key eq 'ARRAY' || ($key && !ref $key));
228
                
229
                if ($i == @$access_key - 1) {
230
                    if (ref $key eq 'ARRAY') {
231
                        if ($bind_filter && !$no_filters_map->{$original_key}) {
232
                            push @bind_values, $bind_filter->($root_params->[$key->[0]], $original_key, $table, $column);
233
                        }
234
                        else {
235
                            push @bind_values, scalar $root_params->[$key->[0]];
236
                        }
237
                    }
238
                    else {
239
                        next ACCESS_KEYS unless exists $root_params->{$key};
240
                        if ($bind_filter && !$no_filters_map->{$original_key}) {
241
                            push @bind_values, scalar $bind_filter->($root_params->{$key}, $original_key, $table, $column);
242
                        }
243
                        else {
244
                            push @bind_values, scalar $root_params->{$key};
245
                        }
246
                    }
247
                    return @bind_values;
248
                }
249
                
250
                if ($key eq 'ARRAY') {
251
                    $root_params = $root_params->[$key->[0]];
252
                }
253
                else {
254
                    next ACCESS_KEYS unless exists $root_params->{$key};
255
                    $root_params = $root_params->{$key};
256
                }
257
            }
258
        }
259
        croak("Cannot find key");
update document
yuki-kimoto authored on 2009-10-27
260
    }
add test
yuki-kimoto authored on 2009-10-17
261
}
262

            
add various thins
yuki-kimoto authored on 2009-10-29
263

            
add test
yuki-kimoto authored on 2009-10-17
264
Object::Simple->build_class;
265

            
first commit
yuki-kimoto authored on 2009-10-13
266
=head1 NAME
267

            
add test
yuki-kimoto authored on 2009-10-17
268
DBI::Custom - Customizable simple DBI
first commit
yuki-kimoto authored on 2009-10-13
269

            
270
=head1 VERSION
271

            
add test
yuki-kimoto authored on 2009-10-16
272
Version 0.0101
first commit
yuki-kimoto authored on 2009-10-13
273

            
274
=cut
275

            
276
=head1 SYNOPSIS
277

            
add test
yuki-kimoto authored on 2009-10-16
278
  my $dbi = DBI::Custom->new;
add various thins
yuki-kimoto authored on 2009-10-29
279
  
280
  my $query = $dbi->create_query($template);
281
  $dbi->execute($query);
first commit
yuki-kimoto authored on 2009-10-13
282

            
update document
yuki-kimoto authored on 2009-10-27
283
=head1 CLASS-OBJECT ACCESSORS
first commit
yuki-kimoto authored on 2009-10-13
284

            
update document
yuki-kimoto authored on 2009-10-27
285
=head2 user
286

            
287
    # Set and get database user name
288
    $self = $dbi->user($user);
289
    $user = $dbi->user;
290
    
291
    # Sample
292
    $dbi->user('taro');
293

            
294
=head2 password
295

            
296
    # Set and get database password
297
    $self     = $dbi->password($password);
298
    $password = $dbi->password;
299
    
300
    # Sample
301
    $dbi->password('lkj&le`@s');
302

            
303
=head2 data_source
304

            
305
    # Set and get database data source
306
    $self        = $dbi->data_source($data_soruce);
307
    $data_source = $dbi->data_source;
308
    
309
    # Sample(SQLite)
310
    $dbi->data_source(dbi:SQLite:dbname=$database);
311
    
312
    # Sample(MySQL);
313
    $dbi->data_source("dbi:mysql:dbname=$database");
314
    
315
    # Sample(PostgreSQL)
316
    $dbi->data_source("dbi:Pg:dbname=$database");
317

            
318
=head2 dbi_option
319

            
320
    # Set and get DBI option
321
    $self       = $dbi->dbi_option({$options => $value, ...});
322
    $dbi_option = $dbi->dbi_option;
323

            
324
    # Sample
325
    $dbi->dbi_option({PrintError => 0, RaiseError => 1});
326

            
327
dbi_option is used when you connect database by using connect.
328

            
329
=head2 sql_template
330

            
331
    # Set and get SQL::Template object
332
    $self         = $dbi->sql_template($sql_template);
333
    $sql_template = $dbi->sql_template;
334
    
335
    # Sample
336
    $dbi->sql_template(DBI::Cutom::SQL::Template->new);
337

            
338
=head2 filters
339

            
340
    # Set and get filters
341
    $self    = $dbi->filters($filters);
342
    $filters = $dbi->filters;
first commit
yuki-kimoto authored on 2009-10-13
343

            
add test
yuki-kimoto authored on 2009-10-16
344
=head2 bind_filter
first commit
yuki-kimoto authored on 2009-10-13
345

            
update document
yuki-kimoto authored on 2009-10-27
346
    # Set and get binding filter
347
    $self        = $dbi->bind_filter($bind_filter);
348
    $bind_filter = $dbi->bind_filter
first commit
yuki-kimoto authored on 2009-10-13
349

            
update document
yuki-kimoto authored on 2009-10-27
350
    # Sample
351
    $dbi->bind_filter($self->filters->{default_bind_filter});
352
    
first commit
yuki-kimoto authored on 2009-10-13
353

            
update document
yuki-kimoto authored on 2009-10-27
354
you can get DBI database handle if you need.
first commit
yuki-kimoto authored on 2009-10-13
355

            
add test
yuki-kimoto authored on 2009-10-16
356
=head2 fetch_filter
first commit
yuki-kimoto authored on 2009-10-13
357

            
update document
yuki-kimoto authored on 2009-10-27
358
    # Set and get Fetch filter
359
    $self         = $dbi->fetch_filter($fetch_filter);
360
    $fetch_filter = $dbi->fetch_filter;
first commit
yuki-kimoto authored on 2009-10-13
361

            
update document
yuki-kimoto authored on 2009-10-27
362
    # Sample
363
    $dbi->fetch_filter($self->filters->{default_fetch_filter});
add test
yuki-kimoto authored on 2009-10-16
364

            
update document
yuki-kimoto authored on 2009-10-27
365
=head2 result_class
first commit
yuki-kimoto authored on 2009-10-13
366

            
update document
yuki-kimoto authored on 2009-10-27
367
    # Set and get resultset class
368
    $self         = $dbi->result_class($result_class);
369
    $result_class = $dbi->result_class;
370
    
371
    # Sample
372
    $dbi->result_class('DBI::Custom::Result');
add test
yuki-kimoto authored on 2009-10-17
373

            
update document
yuki-kimoto authored on 2009-10-27
374
=head2 dbh
add test
yuki-kimoto authored on 2009-10-17
375

            
update document
yuki-kimoto authored on 2009-10-27
376
    # Get database handle
377
    $dbh = $self->dbh;
add test
yuki-kimoto authored on 2009-10-17
378

            
update document
yuki-kimoto authored on 2009-10-27
379
=head1 METHODS
add tests
yuki-kimoto authored on 2009-10-18
380

            
update document
yuki-kimoto authored on 2009-10-27
381
=head2 connect
382

            
383
    # Connect to database
384
    $self = $dbi->connect;
385
    
386
    # Sample
387
    $dbi = DBI::Custom->new(user => 'taro', password => 'lji8(', 
388
                            data_soruce => "dbi:mysql:dbname=$database");
389
    $dbi->connect;
add tests
yuki-kimoto authored on 2009-10-18
390

            
391
=head2 disconnect
392

            
update document
yuki-kimoto authored on 2009-10-27
393
    # Disconnect database
394
    $dbi->disconnect;
395

            
396
If database is already disconnected, this method do noting.
397

            
add tests
yuki-kimoto authored on 2009-10-18
398
=head2 reconnect
399

            
update document
yuki-kimoto authored on 2009-10-27
400
    # Reconnect
401
    $dbi->reconnect;
402

            
403
=head2 connected
404

            
405
    # Check connected
406
    $dbi->connected
407

            
408
=head2 add_filter
409

            
410
    # Add filter (hash ref or hash can be recieve)
411
    $self = $dbi->add_filter({$filter_name => $filter, ...});
412
    $self = $dbi->add_filter($filetr_name => $filter, ...);
413
    
414
    # Sample
415
    $dbi->add_filter(
416
        decode_utf8 => sub {
417
            my $value = shift;
418
            return Encode::decode('UTF-8', $value);
419
        },
420
        datetime_to_string => sub {
421
            my $value = shift;
422
            return $value->strftime('%Y-%m-%d %H:%M:%S')
423
        },
424
        default_bind_filter => sub {
425
            my ($value, $key, $filters) = @_;
426
            if (ref $value eq 'Time::Piece') {
427
                return $filters->{datetime_to_string}->($value);
428
            }
429
            else {
430
                return $filters->{decode_utf8}->($value);
431
            }
432
        },
433
        
434
        encode_utf8 => sub {
435
            my $value = shift;
436
            return Encode::encode('UTF-8', $value);
437
        },
438
        string_to_datetime => sub {
439
            my $value = shift;
440
            return DateTime::Format::MySQL->parse_datetime($value);
441
        },
442
        default_fetch_filter => sub {
443
            my ($value, $key, $filters, $type, $sth, $i) = @_;
444
            if ($type eq 'DATETIME') {
445
                return $self->filters->{string_to_datetime}->($value);
446
            }
447
            else {
448
                return $self->filters->{encode_utf8}->($value);
449
            }
450
        }
451
    );
452

            
453
add_filter add filter to filters
add tests
yuki-kimoto authored on 2009-10-18
454

            
update document
yuki-kimoto authored on 2009-10-27
455
=head2 query
456

            
457
    # Parse SQL template and execute SQL
458
    $result = $dbi->query($sql_template, $param);
459
    $result = $dbi->query($sql_template, $param, $bind_filter);
460
    
461
    # Sample
462
    $result = $dbi->query("select * from authors where {= name} && {= age}", 
463
                          {author => 'taro', age => 19});
464
    
465
    while (my @row = $result->fetch) {
466
        # do something
467
    }
468

            
469
See also L<DBI::Custom::SQL::Template>
470

            
471
=head2 query_raw_sql
472

            
473
    # Execute SQL
474
    $result = $dbi->query_raw_sql($sql, @bind_values);
475
    
476
    # Sample
477
    $result = $dbi->query("select * from table where name = ?, 
478
                          title = ?;", 'taro', 'perl');
479
    
480
    while (my @row = $result->fetch) {
481
        # do something
482
    }
483
    
cleanup
yuki-kimoto authored on 2009-10-22
484
=head2 run_tranzaction
first commit
yuki-kimoto authored on 2009-10-13
485

            
update document
yuki-kimoto authored on 2009-10-27
486
    # Run tranzaction
487
    $dbi->run_tranzaction(sub {
488
        # do something
489
    });
first commit
yuki-kimoto authored on 2009-10-13
490

            
update document
yuki-kimoto authored on 2009-10-27
491
If tranzaction is success, commit is execute. 
492
If tranzation is died, rollback is execute.
first commit
yuki-kimoto authored on 2009-10-13
493

            
add various
yuki-kimoto authored on 2009-10-18
494
=head1 AUTHOR
first commit
yuki-kimoto authored on 2009-10-13
495

            
add various
yuki-kimoto authored on 2009-10-18
496
Yuki Kimoto, C<< <kimoto.yuki at gmail.com> >>
first commit
yuki-kimoto authored on 2009-10-13
497

            
498
=head1 COPYRIGHT & LICENSE
499

            
500
Copyright 2009 Yuki Kimoto, all rights reserved.
501

            
502
This program is free software; you can redistribute it and/or modify it
503
under the same terms as Perl itself.
504

            
505

            
506
=cut
507

            
508
1; # End of DBI::Custom