... | ... |
@@ -8,3 +8,4 @@ _build/* |
8 | 8 |
blib/* |
9 | 9 |
*.tar.gz |
10 | 10 |
cover_db/* |
11 |
+*.tmp |
... | ... |
@@ -0,0 +1,15 @@ |
1 |
+\bRCS\b |
|
2 |
+\bCVS\b |
|
3 |
+^MANIFEST\. |
|
4 |
+^Makefile$ |
|
5 |
+^Build$ |
|
6 |
+^Build.bat$ |
|
7 |
+^_build/ |
|
8 |
+\.(bak|tdy|old|tmp)$ |
|
9 |
+~$ |
|
10 |
+^blib/ |
|
11 |
+^pm_to_blib |
|
12 |
+\.cvsignore |
|
13 |
+\.gz$ |
|
14 |
+^\.git |
|
15 |
+^cover_db/ |
... | ... |
@@ -1,14 +1,17 @@ |
1 | 1 |
package DBI::Custom; |
2 | 2 |
use Object::Simple; |
3 |
+ |
|
4 |
+our $VERSION = '0.0101'; |
|
5 |
+ |
|
6 |
+use Carp 'croak'; |
|
3 | 7 |
use DBI; |
4 |
-use SQL::Abstract; |
|
5 | 8 |
|
6 | 9 |
# Model |
7 | 10 |
sub model : ClassAttr { auto_build => \&_inherit_model } |
8 | 11 |
|
9 | 12 |
# Inherit super class model |
10 | 13 |
sub _inherit_model { |
11 |
- $class = shict; |
|
14 |
+ my $class = shift; |
|
12 | 15 |
my $super = do { |
13 | 16 |
no strict 'refs'; |
14 | 17 |
${"${class}::ISA"}[0]; |
... | ... |
@@ -39,62 +42,35 @@ sub initialize_model { |
39 | 42 |
sub clone { |
40 | 43 |
my $self = shift; |
41 | 44 |
my $new = $self->Object::Simple::new; |
42 |
- $new->connect_infos(%{$self->connect_infos || {}}); |
|
45 |
+ $new->connect_info(%{$self->connect_info || {}}); |
|
43 | 46 |
$new->filters(%{$self->filters || {}}); |
44 |
- |
|
45 |
- $new->global_bind_rules(@{$self->global_bind_rules || []}); |
|
46 |
- $new->global_fetch_rules(@{$self->global_fetch_rules || []}); |
|
47 |
- |
|
48 |
- foreach my $method (qw/bind_rules fetch_rules/) { |
|
49 |
- my $new_rules = []; |
|
50 |
- foreach my $rule (@{$self->method}) { |
|
51 |
- my $new_rule = {}; |
|
52 |
- foreach my $key ($rule) { |
|
53 |
- if ($key eq 'filter') { |
|
54 |
- my $new_filters = []; |
|
55 |
- foreach my $filter (@{$rule->{$key} || []}) { |
|
56 |
- push @$new_filters, $filter |
|
57 |
- } |
|
58 |
- $new_rule->{$key} = $new_filters; |
|
59 |
- } |
|
60 |
- else { |
|
61 |
- $new_rule->{$key} = $rule->{$key}; |
|
62 |
- } |
|
63 |
- } |
|
64 |
- push @$new_rules, $new_rule; |
|
65 |
- } |
|
66 |
- $self->$method($new_rules); |
|
67 |
- } |
|
47 |
+ $new->bind_filter($self->bind_filter); |
|
48 |
+ $new->fetch_filter($self->fetch_filter); |
|
68 | 49 |
} |
69 | 50 |
|
70 | 51 |
# Attribute |
71 | 52 |
sub connect_info : Attr { type => 'hash', auto_build => sub { shift->connect_info({}) } } |
72 | 53 |
|
73 |
-sub global_bind_rules : Attr { type => 'array', auto_build => sub { shift->global_bind_rules([]) } } |
|
74 |
-sub add_global_bind_rule { shift->global_bind_rules(@_) } |
|
54 |
+sub bind_filter : Attr {} |
|
55 |
+sub fetch_filter : Attr {} |
|
75 | 56 |
|
76 |
-sub global_fetch_rules : Attr { type => 'array', auto_build => sub { shift->global_fetch_rules([]) } } |
|
77 |
-sub add_global_fetch_rules { shift->global_fetch_rules(@_) } |
|
78 |
- |
|
79 |
-sub bind_rules : Attr { type => 'hash', auto_build => sub { shift->bind_rules({}) } |
|
80 |
-sub add_bind_rule { shift->bind_rules(@_) } |
|
81 |
- |
|
82 |
-sub fetch_rules : Attr { type => 'hash', auto_build => sub { shift->fetch_rules({}) } |
|
83 |
-sub add_fetch_rule { shift->fetch_rules(@_) } |
|
84 |
- |
|
85 |
-sub filters : Attr { type => 'hash', deref => 1, default => sub { {} } } |
|
57 |
+sub filters : Attr { type => 'hash', deref => 1, auto_build => sub { shift->filters({}) } } |
|
86 | 58 |
sub add_filter { shift->filters(@_) } |
87 | 59 |
|
88 |
- |
|
89 | 60 |
sub dbh : Attr { auto_build => sub { shift->connect } } |
90 |
-sub sql_abstract : Attr { auto_build => sub { shift->sql_abstract(SQL::Abstract->new) }} |
|
61 |
+ |
|
62 |
+our %VALID_CONNECT_INFO = map {$_ => 1} qw/data_source user password options/; |
|
91 | 63 |
|
92 | 64 |
sub connect { |
93 | 65 |
my $self = shift; |
94 | 66 |
my $connect_info = $self->connect_info; |
95 | 67 |
|
68 |
+ foreach my $key (keys %{$self->connect_info}) { |
|
69 |
+ |
|
70 |
+ } |
|
71 |
+ |
|
96 | 72 |
my $dbh = DBI->connect( |
97 |
- $connect_info->{dsn}, |
|
73 |
+ $connect_info->{data_source}, |
|
98 | 74 |
$connect_info->{user}, |
99 | 75 |
$connect_info->{password}, |
100 | 76 |
{ |
... | ... |
@@ -108,99 +84,10 @@ sub connect { |
108 | 84 |
$self->dbh($dbh); |
109 | 85 |
} |
110 | 86 |
|
111 |
-sub reconnect { |
|
112 |
- my $self = shift; |
|
113 |
- $self->dbh(undef); |
|
114 |
- $self->connect; |
|
115 |
-} |
|
116 |
- |
|
117 |
-sub query { |
|
118 |
- my ($self, $query, @binds) = @_; |
|
119 |
- $self->{success} = 0; |
|
120 |
- |
|
121 |
- $self->_replace_omniholder(\$query, \@binds); |
|
122 |
- |
|
123 |
- my $st; |
|
124 |
- my $sth; |
|
125 |
- |
|
126 |
- my $old = $old_statements{$self}; |
|
127 |
- |
|
128 |
- if (my $i = (grep $old->[$_][0] eq $query, 0..$#$old)[0]) { |
|
129 |
- $st = splice(@$old, $i, 1)->[1]; |
|
130 |
- $sth = $st->{sth}; |
|
131 |
- } else { |
|
132 |
- eval { $sth = $self->{dbh}->prepare($query) } or do { |
|
133 |
- if ($@) { |
|
134 |
- $@ =~ s/ at \S+ line \d+\.\n\z//; |
|
135 |
- Carp::croak($@); |
|
136 |
- } |
|
137 |
- $self->{reason} = "Prepare failed ($DBI::errstr)"; |
|
138 |
- return _dummy; |
|
139 |
- }; |
|
140 |
- |
|
141 |
- # $self is quoted on purpose, to pass along the stringified version, |
|
142 |
- # and avoid increasing reference count. |
|
143 |
- $st = bless { |
|
144 |
- db => "$self", |
|
145 |
- sth => $sth, |
|
146 |
- query => $query |
|
147 |
- }, 'DBIx::Simple::Statement'; |
|
148 |
- $statements{$self}{$st} = $st; |
|
149 |
- } |
|
150 |
- |
|
151 |
- eval { $sth->execute(@binds) } or do { |
|
152 |
- if ($@) { |
|
153 |
- $@ =~ s/ at \S+ line \d+\.\n\z//; |
|
154 |
- Carp::croak($@); |
|
155 |
- } |
|
156 |
- |
|
157 |
- $self->{reason} = "Execute failed ($DBI::errstr)"; |
|
158 |
- return _dummy; |
|
159 |
- }; |
|
160 |
- |
|
161 |
- $self->{success} = 1; |
|
162 |
- |
|
163 |
- return bless { st => $st, lc_columns => $self->{lc_columns} }, $self->{result_class}; |
|
164 |
-} |
|
165 |
- |
|
166 | 87 |
sub query { |
167 |
- my ($self, $sql) = @_; |
|
168 |
- my $sth = $self->dbh->prepare($sql); |
|
169 |
- $sth->execute(@bind); |
|
170 |
-} |
|
171 |
- |
|
172 |
-sub select { |
|
173 |
- my ($table, $column_names, $where, $order) = @_; |
|
174 | 88 |
|
175 |
- my ($stmt, @bind) = $self->sql_abstract->select($table, $column_names, $where, $order); |
|
176 |
- my $sth = $self->dbh->prepare($stmt); |
|
177 |
- $sth->execute(@bind); |
|
178 |
-} |
|
179 |
- |
|
180 |
-sub insert { |
|
181 |
- my ($self, $table, $values) = @_; |
|
182 |
- |
|
183 |
- my ($stmt, @bind) = $self->sql_abstract->insert($table, $values); |
|
184 |
- my $sth = $self->dbh->prepare($stmt); |
|
185 |
- $sth->execute(@bind); |
|
186 |
-} |
|
187 |
- |
|
188 |
-sub update { |
|
189 |
- my ($self, $values, $where) = @_; |
|
190 |
- my ($stmt, @bind) = $self->sql_abstract->update($table, $values, $where); |
|
191 |
- my $sth = $self->dbh->prepare($stmt); |
|
192 |
- $sth->execute(@bind); |
|
193 | 89 |
} |
194 | 90 |
|
195 |
-sub delete { |
|
196 |
- my ($self, $where) = @_; |
|
197 |
- my ($stmt, @bind) = $self->sql_abstract->delete($table, $where); |
|
198 |
- my $sth = $self->dbh->prepare($stmt); |
|
199 |
- $sth->execute(@bind); |
|
200 |
-} |
|
201 |
- |
|
202 |
- |
|
203 |
- |
|
204 | 91 |
Object::Simple->build_class; |
205 | 92 |
|
206 | 93 |
=head1 NAME |
... | ... |
@@ -209,44 +96,39 @@ DBI::Custom - The great new DBI::Custom! |
209 | 96 |
|
210 | 97 |
=head1 VERSION |
211 | 98 |
|
212 |
-Version 0.01 |
|
99 |
+Version 0.0101 |
|
213 | 100 |
|
214 | 101 |
=cut |
215 | 102 |
|
216 |
-our $VERSION = '0.01'; |
|
217 |
- |
|
218 |
- |
|
219 | 103 |
=head1 SYNOPSIS |
220 | 104 |
|
221 |
-Quick summary of what the module does. |
|
105 |
+ my $dbi = DBI::Custom->new; |
|
222 | 106 |
|
223 |
-Perhaps a little code snippet. |
|
107 |
+=head1 METHODS |
|
224 | 108 |
|
225 |
- use DBI::Custom; |
|
109 |
+=head2 add_filter |
|
226 | 110 |
|
227 |
- my $foo = DBI::Custom->new(); |
|
228 |
- ... |
|
111 |
+=head2 bind_filter |
|
229 | 112 |
|
230 |
-=head1 EXPORT |
|
113 |
+=head2 clone |
|
231 | 114 |
|
232 |
-A list of functions that can be exported. You can delete this section |
|
233 |
-if you don't export anything, such as for a purely object-oriented module. |
|
115 |
+=head2 connect |
|
234 | 116 |
|
235 |
-=head1 FUNCTIONS |
|
117 |
+=head2 connect_info |
|
236 | 118 |
|
237 |
-=head2 function1 |
|
119 |
+=head2 dbh |
|
238 | 120 |
|
239 |
-=cut |
|
121 |
+=head2 fetch_filter |
|
240 | 122 |
|
241 |
-sub function1 { |
|
242 |
-} |
|
123 |
+=head2 filters |
|
243 | 124 |
|
244 |
-=head2 function2 |
|
125 |
+=head2 initialize_model |
|
245 | 126 |
|
246 |
-=cut |
|
127 |
+=head2 model |
|
247 | 128 |
|
248 |
-sub function2 { |
|
249 |
-} |
|
129 |
+=head2 new |
|
130 |
+ |
|
131 |
+=head2 query |
|
250 | 132 |
|
251 | 133 |
=head1 AUTHOR |
252 | 134 |
|
... | ... |
@@ -0,0 +1,176 @@ |
1 |
+use Test::More 'no_plan'; |
|
2 |
+use strict; |
|
3 |
+use warnings; |
|
4 |
+ |
|
5 |
+use DBI::Custom; |
|
6 |
+use Scalar::Util qw/blessed/; |
|
7 |
+ |
|
8 |
+# user password database |
|
9 |
+our ($U, $P, $D) = connect_info(); |
|
10 |
+ |
|
11 |
+ |
|
12 |
+{ |
|
13 |
+ my $dbi = DBI::Custom->new( |
|
14 |
+ connect_info => { |
|
15 |
+ user => 'a', |
|
16 |
+ password => 'b', |
|
17 |
+ data_source => 'c', |
|
18 |
+ options => {d => 1, e => 2} |
|
19 |
+ }, |
|
20 |
+ filters => { |
|
21 |
+ f => 3, |
|
22 |
+ }, |
|
23 |
+ bind_filter => 'f', |
|
24 |
+ fetch_filter => 'g', |
|
25 |
+ dbh => 'e', |
|
26 |
+ ); |
|
27 |
+ |
|
28 |
+ is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}} ,filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', dbh => 'e'}, 'new'); |
|
29 |
+ |
|
30 |
+ isa_ok($dbi, 'DBI::Custom'); |
|
31 |
+} |
|
32 |
+ |
|
33 |
+{ |
|
34 |
+ package DBI::Custom::T1; |
|
35 |
+ use base 'DBI::Custom'; |
|
36 |
+ |
|
37 |
+ __PACKAGE__->initialize_model(sub { |
|
38 |
+ my $model = shift; |
|
39 |
+ |
|
40 |
+ $model |
|
41 |
+ ->connect_info( |
|
42 |
+ user => 'a', |
|
43 |
+ password => 'b', |
|
44 |
+ data_source => 'c', |
|
45 |
+ options => {d => 1, e => 2} |
|
46 |
+ ) |
|
47 |
+ ->filters( |
|
48 |
+ f => 3 |
|
49 |
+ ) |
|
50 |
+ ->bind_filter('f') |
|
51 |
+ ->fetch_filter('g') |
|
52 |
+ ->dbh('e') |
|
53 |
+ }); |
|
54 |
+} |
|
55 |
+{ |
|
56 |
+ my $dbi = DBI::Custom::T1->new( |
|
57 |
+ connect_info => { |
|
58 |
+ user => 'ao', |
|
59 |
+ password => 'bo', |
|
60 |
+ data_source => 'co', |
|
61 |
+ options => {do => 10, eo => 20} |
|
62 |
+ }, |
|
63 |
+ filters => { |
|
64 |
+ fo => 30, |
|
65 |
+ }, |
|
66 |
+ bind_filter => 'fo', |
|
67 |
+ fetch_filter => 'go', |
|
68 |
+ ); |
|
69 |
+ |
|
70 |
+ is_deeply($dbi,{connect_info => {user => 'ao', password => 'bo', data_source => 'co', options => {do => 10, eo => 20}} ,filters => {fo => 30}, bind_filter => 'fo', fetch_filter => 'go'}, 'new arguments'); |
|
71 |
+ |
|
72 |
+ isa_ok($dbi, 'DBI::Custom::T1'); |
|
73 |
+} |
|
74 |
+ |
|
75 |
+{ |
|
76 |
+ my $dbi = DBI::Custom::T1->new; |
|
77 |
+ |
|
78 |
+ is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}} ,filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g'}, 'new custom class'); |
|
79 |
+ |
|
80 |
+ isa_ok($dbi, 'DBI::Custom::T1'); |
|
81 |
+ |
|
82 |
+} |
|
83 |
+ |
|
84 |
+{ |
|
85 |
+ package DBI::Custom::T1_2; |
|
86 |
+ use base 'DBI::Custom::T1'; |
|
87 |
+} |
|
88 |
+ |
|
89 |
+{ |
|
90 |
+ my $dbi = DBI::Custom::T1_2->new; |
|
91 |
+ |
|
92 |
+ is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}} ,filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g'}, 'new custom class inherit'); |
|
93 |
+ |
|
94 |
+ isa_ok($dbi, 'DBI::Custom::T1_2'); |
|
95 |
+} |
|
96 |
+ |
|
97 |
+{ |
|
98 |
+ package DBI::Custom::T1_3; |
|
99 |
+ use base 'DBI::Custom::T1'; |
|
100 |
+ |
|
101 |
+ __PACKAGE__->initialize_model(sub { |
|
102 |
+ my $model = shift; |
|
103 |
+ |
|
104 |
+ $model |
|
105 |
+ ->connect_info( |
|
106 |
+ user => 'ao', |
|
107 |
+ password => 'bo', |
|
108 |
+ data_source => 'co', |
|
109 |
+ options => {do => 10, eo => 20} |
|
110 |
+ ) |
|
111 |
+ ->filters( |
|
112 |
+ fo => 30 |
|
113 |
+ ) |
|
114 |
+ ->bind_filter('fo') |
|
115 |
+ ->fetch_filter('go') |
|
116 |
+ ->dbh('eo') |
|
117 |
+ }); |
|
118 |
+ |
|
119 |
+} |
|
120 |
+ |
|
121 |
+{ |
|
122 |
+ my $dbi = DBI::Custom::T1_3->new; |
|
123 |
+ |
|
124 |
+ is_deeply($dbi,{connect_info => {user => 'ao', password => 'bo', data_source => 'co', options => {do => 10, eo => 20}} ,filters => {fo => 30}, bind_filter => 'fo', fetch_filter => 'go'}, 'new custom class'); |
|
125 |
+ |
|
126 |
+ isa_ok($dbi, 'DBI::Custom::T1_3'); |
|
127 |
+} |
|
128 |
+ |
|
129 |
+{ |
|
130 |
+ my $dbi = DBI::Custom::T1_3->new( |
|
131 |
+ connect_info => { |
|
132 |
+ user => 'a', |
|
133 |
+ password => 'b', |
|
134 |
+ data_source => 'c', |
|
135 |
+ options => {d => 1, e => 2} |
|
136 |
+ }, |
|
137 |
+ filters => { |
|
138 |
+ f => 3, |
|
139 |
+ }, |
|
140 |
+ bind_filter => 'f', |
|
141 |
+ fetch_filter => 'g', |
|
142 |
+ dbh => 'e', |
|
143 |
+ ); |
|
144 |
+ |
|
145 |
+ is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}} ,filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', dbh => 'e'}, 'new'); |
|
146 |
+ |
|
147 |
+ isa_ok($dbi, 'DBI::Custom'); |
|
148 |
+} |
|
149 |
+ |
|
150 |
+{ |
|
151 |
+ my $dbi = DBI::Custom->new( |
|
152 |
+ connect_info => { |
|
153 |
+ user => $U, |
|
154 |
+ password => $P, |
|
155 |
+ data_source => "dbi:mysql:$D" |
|
156 |
+ } |
|
157 |
+ ); |
|
158 |
+ $dbi->connect; |
|
159 |
+ |
|
160 |
+ ok(blessed $dbi->dbh); |
|
161 |
+ can_ok($dbi->dbh, qw/prepare/); |
|
162 |
+ |
|
163 |
+ |
|
164 |
+} |
|
165 |
+ |
|
166 |
+sub connect_info { |
|
167 |
+ my $file = 'password.tmp'; |
|
168 |
+ open my $fh, '<', $file |
|
169 |
+ or return; |
|
170 |
+ |
|
171 |
+ my ($user, $password, $database) = split(/\s/, (<$fh>)[0]); |
|
172 |
+ |
|
173 |
+ close $fh; |
|
174 |
+ |
|
175 |
+ return ($user, $password, $database); |
|
176 |
+} |