DBIx-Custom / lib / DBI / Custom.pm /
Newer Older
344 lines | 7.777kb
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;
first commit
yuki-kimoto authored on 2009-10-13
8

            
cleanup
yuki-kimoto authored on 2009-10-15
9
# Model
10
sub model : ClassAttr { auto_build => \&_inherit_model }
first commit
yuki-kimoto authored on 2009-10-13
11

            
cleanup
yuki-kimoto authored on 2009-10-15
12
# Inherit super class model
13
sub _inherit_model {
add test
yuki-kimoto authored on 2009-10-16
14
    my $class = shift;
cleanup
yuki-kimoto authored on 2009-10-15
15
    my $super = do {
16
        no strict 'refs';
17
        ${"${class}::ISA"}[0];
18
    };
19
    my $model = eval{$super->can('model')}
20
                         ? $super->model->clone
21
                         : $class->Object::Simple::new;
cleanup
yuki-kimoto authored on 2009-10-14
22
    
23
    $class->model($model);
first commit
yuki-kimoto authored on 2009-10-13
24
}
25

            
cleanup
yuki-kimoto authored on 2009-10-15
26
# New
27
sub new {
28
    my $self = shift->Object::Simple::new(@_);
29
    my $class = ref $self;
30
    return bless {%{$class->model->clone}, %{$self}}, $class;
first commit
yuki-kimoto authored on 2009-10-13
31
}
32

            
cleanup
yuki-kimoto authored on 2009-10-15
33
# Initialize modle
34
sub initialize_model {
35
    my ($class, $callback) = @_;
first commit
yuki-kimoto authored on 2009-10-13
36
    
cleanup
yuki-kimoto authored on 2009-10-15
37
    # Callback to initialize model
38
    $callback->($class->model);
first commit
yuki-kimoto authored on 2009-10-13
39
}
40

            
cleanup
yuki-kimoto authored on 2009-10-15
41
# Clone
42
sub clone {
cleanup
yuki-kimoto authored on 2009-10-14
43
    my $self = shift;
cleanup
yuki-kimoto authored on 2009-10-15
44
    my $new = $self->Object::Simple::new;
add test
yuki-kimoto authored on 2009-10-16
45
    $new->connect_info(%{$self->connect_info || {}});
cleanup
yuki-kimoto authored on 2009-10-15
46
    $new->filters(%{$self->filters || {}});
add test
yuki-kimoto authored on 2009-10-16
47
    $new->bind_filter($self->bind_filter);
48
    $new->fetch_filter($self->fetch_filter);
cleanup
yuki-kimoto authored on 2009-10-14
49
}
50

            
cleanup
yuki-kimoto authored on 2009-10-15
51
# Attribute
52
sub connect_info       : Attr { type => 'hash',  auto_build => sub { shift->connect_info({}) } }
cleanup
yuki-kimoto authored on 2009-10-15
53

            
add test
yuki-kimoto authored on 2009-10-16
54
sub bind_filter : Attr {}
55
sub fetch_filter : Attr {}
cleanup
yuki-kimoto authored on 2009-10-15
56

            
add test
yuki-kimoto authored on 2009-10-16
57
sub filters : Attr { type => 'hash', deref => 1, auto_build => sub { shift->filters({}) } }
cleanup
yuki-kimoto authored on 2009-10-15
58
sub add_filter { shift->filters(@_) }
59

            
cleanup
yuki-kimoto authored on 2009-10-15
60
sub dbh          : Attr { auto_build => sub { shift->connect } }
add test
yuki-kimoto authored on 2009-10-17
61
sub sql_template : Attr { auto_build => sub { shift->sql_template(DBI::Custom::SQLTemplate->new) } }
add test
yuki-kimoto authored on 2009-10-16
62

            
63
our %VALID_CONNECT_INFO = map {$_ => 1} qw/data_source user password options/;
cleanup
yuki-kimoto authored on 2009-10-14
64

            
add some method
yuki-kimoto authored on 2009-10-14
65
sub connect {
66
    my $self = shift;
67
    my $connect_info = $self->connect_info;
68
    
add test
yuki-kimoto authored on 2009-10-16
69
    foreach my $key (keys %{$self->connect_info}) {
add test
yuki-kimoto authored on 2009-10-17
70
        croak("connect_info '$key' is invald")
71
          unless $VALID_CONNECT_INFO{$key};
add test
yuki-kimoto authored on 2009-10-16
72
    }
73
    
add some method
yuki-kimoto authored on 2009-10-14
74
    my $dbh = DBI->connect(
add test
yuki-kimoto authored on 2009-10-16
75
        $connect_info->{data_source},
add some method
yuki-kimoto authored on 2009-10-14
76
        $connect_info->{user},
77
        $connect_info->{password},
78
        {
79
            RaiseError => 1,
80
            PrintError => 0,
81
            AutoCommit => 1,
82
            %{$connect_info->{options} || {} }
83
        }
84
    );
85
    
86
    $self->dbh($dbh);
87
}
first commit
yuki-kimoto authored on 2009-10-13
88

            
add test
yuki-kimoto authored on 2009-10-17
89
sub create_sql {
90
    my $self = shift;
91
    
92
    my ($sql, @bind) = $self->sql_template->create_sql(@_);
93
    
94
    return ($sql, @bind);
95
}
96

            
add some method
yuki-kimoto authored on 2009-10-14
97
sub query {
try varioud way
yuki-kimoto authored on 2009-10-17
98
    my ($self, $template, $values, $filter)  = @_;
99
    
100
    $filter ||= $self->bind_filter;
101
    
102
    my ($sql, @bind) = $self->creqte_sql($template, $values, $filter);
add test
yuki-kimoto authored on 2009-10-17
103
    $self->prepare($sql);
104
    $self->execute(@bind);
105
}
106

            
107
sub query_raw_sql {
108
    my ($self, $sql, @bind) = @_;
109
    $self->prepare($sql);
110
    $self->execute(@bind);
111
}
112

            
113
Object::Simple->build_class;
114

            
115
package DBI::Custom::SQLTemplate;
116
use Object::Simple;
try various way
yuki-kimoto authored on 2009-10-17
117
use Carp 'croak';
add test
yuki-kimoto authored on 2009-10-17
118

            
try varioud way
yuki-kimoto authored on 2009-10-17
119
### Attributes;
try various way
yuki-kimoto authored on 2009-10-17
120
sub tag_start   : Attr { default => '{' }
121
sub tag_end     : Attr { default => '}' }
122
sub template    : Attr {};
123
sub tree        : Attr { auto_build => sub { shift->tree([]) } }
124
sub bind_filter : Attr {}
125
sub values      : Attr {}
126
sub upper_case  : Attr {default => 0}
try varioud way
yuki-kimoto authored on 2009-10-17
127

            
add test
yuki-kimoto authored on 2009-10-17
128
sub create_sql {
try varioud way
yuki-kimoto authored on 2009-10-17
129
    my ($self, $template, $values, $filter)  = @_;
130
    
try various way
yuki-kimoto authored on 2009-10-17
131
    $filter ||= $self->bind_filter;
132
    
try varioud way
yuki-kimoto authored on 2009-10-17
133
    $self->parse($template);
134
    
try various way
yuki-kimoto authored on 2009-10-17
135
    my ($sql, @bind) = $self->build_sql({bind_filter => $filter, values => $values});
try varioud way
yuki-kimoto authored on 2009-10-17
136
    
137
    return ($sql, @bind);
138
}
139

            
140
our $TAG_SYNTAX = <<'EOS';
141
[tag]            [expand]
142
{= name}         name = ?
try various way
yuki-kimoto authored on 2009-10-17
143
{<> name}        name <> ?
try varioud way
yuki-kimoto authored on 2009-10-17
144

            
145
{< name}         name < ?
146
{> name}         name > ?
147
{>= name}        name >= ?
148
{<= name}        name <= ?
149

            
150
{like name}      name like ?
151
{in name}        name in [?, ?, ..]
152

            
153
{insert_values}  (key1, key2, key3) values (?, ?, ?)
154
{update_values}  set key1 = ?, key2 = ?, key3 = ?
155
EOS
156

            
try various way
yuki-kimoto authored on 2009-10-17
157
our %VALID_TAG_NAMES = map {$_ => 1} qw/= <> < > >= <= like in insert_values update_values/;
try varioud way
yuki-kimoto authored on 2009-10-17
158
sub parse {
159
    my ($self, $template) = @_;
160
    $self->template($template);
161
    
162
    # Clean start;
163
    delete $self->{tree};
164
    
165
    # Tags
166
    my $tag_start = quotemeta $self->tag_start;
167
    my $tag_end   = quotemeta $self->tag_end;
first commit
yuki-kimoto authored on 2009-10-13
168
    
try varioud way
yuki-kimoto authored on 2009-10-17
169
    # Tokenize
170
    my $state = 'text';
171
    
172
    # Save original template
173
    my $original_template = $template;
174
    
175
    # Text
176
    while ($template =~ s/([^$tag_start]*?)$tag_start([^$tag_end].*?)$tag_end//sm) {
try various way
yuki-kimoto authored on 2009-10-17
177
        my $text = $1;
try varioud way
yuki-kimoto authored on 2009-10-17
178
        my $tag  = $2;
179
        
try various way
yuki-kimoto authored on 2009-10-17
180
        push @{$self->tree}, {type => 'text', args => [$text]} if $text;
try varioud way
yuki-kimoto authored on 2009-10-17
181
        
182
        if ($tag) {
183
            
try various way
yuki-kimoto authored on 2009-10-17
184
            my ($tag_name, @args) = split /\s+/, $tag;
try varioud way
yuki-kimoto authored on 2009-10-17
185
            
try various way
yuki-kimoto authored on 2009-10-17
186
            $tag ||= '';
187
            croak("Tag '$tag' in SQL template is invalid.\n\n" .
188
                  "SQL template tag syntax\n$TAG_SYNTAX\n\n" .
189
                  "Your SQL template is \n$original_template\n\n")
190
              unless $VALID_TAG_NAMES{$tag_name};
try varioud way
yuki-kimoto authored on 2009-10-17
191
            
try various way
yuki-kimoto authored on 2009-10-17
192
            push @{$self->tree}, {type => 'tag', tag_name => $tag_name, args => [@args]};
try varioud way
yuki-kimoto authored on 2009-10-17
193
        }
194
    }
195
    
try various way
yuki-kimoto authored on 2009-10-17
196
    push @{$self->tree}, {type => 'text', args => [$template]} if $template;
first commit
yuki-kimoto authored on 2009-10-13
197
}
198

            
try various way
yuki-kimoto authored on 2009-10-17
199
our %EXPAND_PLACE_HOLDER = map {$_ => 1} qw/= <> < > >= <= like/;
200
sub build_sql {
201
    my ($self, $args) = @_;
202
    
203
    my $tree        = $args->{tree} || $self->tree;
204
    my $bind_filter = $args->{bind_filter} || $self->bind_filter;
205
    my $values      = exists $args->{values} ? $args->{values} : $self->values;
206
    
207
    my @bind_values;
208
    my $sql = '';
209
    foreach my $node (@$tree) {
210
        my $type     = $node->{type};
211
        my $tag_name = $node->{tag_name};
212
        my $args     = $node->{args};
213
        
214
        if ($type eq 'text') {
215
            # Join text
216
            $sql .= $args->[0];
217
        }
218
        elsif ($type eq 'tag') {
219
            if ($EXPAND_PLACE_HOLDER{$tag_name}) {
220
                my $key = $args->[0];
221
                
222
                # Filter Value
223
                if ($bind_filter) {
224
                    push @bind_values, scalar $bind_filter->($values->{$key});
225
                }
226
                else {
227
                    push @bind_values, $values->{$key};
228
                }
229
                $tag_name = uc $tag_name if $self->upper_case;
230
                my $place_holder = "$key $tag_name ?";
231
                $sql .= $place_holder;
232
            }
233
        }
234
    }
235
    $sql .= ';' unless $sql =~ /;$/;
236
    return ($sql, @bind_values);
237
}
try varioud way
yuki-kimoto authored on 2009-10-17
238

            
239

            
first commit
yuki-kimoto authored on 2009-10-13
240
Object::Simple->build_class;
241

            
242
=head1 NAME
243

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

            
246
=head1 VERSION
247

            
add test
yuki-kimoto authored on 2009-10-16
248
Version 0.0101
first commit
yuki-kimoto authored on 2009-10-13
249

            
250
=cut
251

            
252
=head1 SYNOPSIS
253

            
add test
yuki-kimoto authored on 2009-10-16
254
  my $dbi = DBI::Custom->new;
first commit
yuki-kimoto authored on 2009-10-13
255

            
add test
yuki-kimoto authored on 2009-10-16
256
=head1 METHODS
first commit
yuki-kimoto authored on 2009-10-13
257

            
add test
yuki-kimoto authored on 2009-10-16
258
=head2 add_filter
first commit
yuki-kimoto authored on 2009-10-13
259

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

            
add test
yuki-kimoto authored on 2009-10-16
262
=head2 clone
first commit
yuki-kimoto authored on 2009-10-13
263

            
add test
yuki-kimoto authored on 2009-10-16
264
=head2 connect
first commit
yuki-kimoto authored on 2009-10-13
265

            
add test
yuki-kimoto authored on 2009-10-16
266
=head2 connect_info
first commit
yuki-kimoto authored on 2009-10-13
267

            
add test
yuki-kimoto authored on 2009-10-16
268
=head2 dbh
first commit
yuki-kimoto authored on 2009-10-13
269

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

            
add test
yuki-kimoto authored on 2009-10-16
272
=head2 filters
first commit
yuki-kimoto authored on 2009-10-13
273

            
add test
yuki-kimoto authored on 2009-10-16
274
=head2 initialize_model
first commit
yuki-kimoto authored on 2009-10-13
275

            
add test
yuki-kimoto authored on 2009-10-16
276
=head2 model
first commit
yuki-kimoto authored on 2009-10-13
277

            
add test
yuki-kimoto authored on 2009-10-16
278
=head2 new
279

            
280
=head2 query
first commit
yuki-kimoto authored on 2009-10-13
281

            
add test
yuki-kimoto authored on 2009-10-17
282
=head2 create_sql
283

            
284
=head2 query_raw_sql
285

            
286
=head2 sql_template
287

            
first commit
yuki-kimoto authored on 2009-10-13
288
=head1 AUTHOR
289

            
290
Yuki Kimoto, C<< <kimoto.yuki at gmail.com> >>
291

            
292
=head1 BUGS
293

            
294
Please report any bugs or feature requests to C<bug-dbi-custom at rt.cpan.org>, or through
295
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=DBI-Custom>.  I will be notified, and then you'll
296
automatically be notified of progress on your bug as I make changes.
297

            
298

            
299

            
300

            
301
=head1 SUPPORT
302

            
303
You can find documentation for this module with the perldoc command.
304

            
305
    perldoc DBI::Custom
306

            
307

            
308
You can also look for information at:
309

            
310
=over 4
311

            
312
=item * RT: CPAN's request tracker
313

            
314
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=DBI-Custom>
315

            
316
=item * AnnoCPAN: Annotated CPAN documentation
317

            
318
L<http://annocpan.org/dist/DBI-Custom>
319

            
320
=item * CPAN Ratings
321

            
322
L<http://cpanratings.perl.org/d/DBI-Custom>
323

            
324
=item * Search CPAN
325

            
326
L<http://search.cpan.org/dist/DBI-Custom/>
327

            
328
=back
329

            
330

            
331
=head1 ACKNOWLEDGEMENTS
332

            
333

            
334
=head1 COPYRIGHT & LICENSE
335

            
336
Copyright 2009 Yuki Kimoto, all rights reserved.
337

            
338
This program is free software; you can redistribute it and/or modify it
339
under the same terms as Perl itself.
340

            
341

            
342
=cut
343

            
344
1; # End of DBI::Custom