line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::Simple::Class::Schema; |
2
|
1
|
|
|
1
|
|
46815
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
27
|
|
3
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
20
|
|
4
|
1
|
|
|
1
|
|
14
|
use 5.010001; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
19
|
|
5
|
1
|
|
|
1
|
|
3
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
42
|
|
6
|
1
|
|
|
1
|
|
3
|
use Data::Dumper; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
28
|
|
7
|
1
|
|
|
1
|
|
335
|
use parent 'DBIx::Simple::Class'; |
|
1
|
|
|
|
|
201
|
|
|
1
|
|
|
|
|
3
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.006'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
*_get_obj_args = \&DBIx::Simple::Class::_get_obj_args; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#struct to keep schemas while building |
15
|
|
|
|
|
|
|
my $schemas = {}; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
#for accessing schema structures during tests |
18
|
|
|
|
|
|
|
sub _schemas { |
19
|
6
|
50
|
|
6
|
|
16
|
$_[2] && ($schemas->{$_[1]} = $_[2]); |
20
|
6
|
100
|
66
|
|
|
43
|
return $_[1] && exists $schemas->{$_[1]} ? $schemas->{$_[1]} : $schemas; |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub _get_table_info { |
24
|
4
|
|
|
4
|
|
7
|
my ($class, $args) = _get_obj_args(@_); |
25
|
|
|
|
|
|
|
|
26
|
4
|
50
|
|
|
|
9
|
$args->{namespace} || Carp::croak('Please pass "namespace" argument'); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
#get tables from the current database |
29
|
|
|
|
|
|
|
#see https://metacpan.org/module/DBI#table_info |
30
|
4
|
|
100
|
|
|
10
|
return $schemas->{$args->{namespace}}{tables} = $class->dbh->table_info( |
|
|
|
100
|
|
|
|
|
31
|
|
|
|
|
|
|
undef, undef, |
32
|
|
|
|
|
|
|
$args->{table} || '%', |
33
|
|
|
|
|
|
|
$args->{type} || "'TABLE','VIEW'" |
34
|
|
|
|
|
|
|
)->fetchall_arrayref({}); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub _get_column_info { |
39
|
4
|
|
|
4
|
|
7
|
my ($class, $tables) = @_; |
40
|
4
|
|
|
|
|
9
|
my $dbh = $class->dbh; |
41
|
4
|
|
|
|
|
15
|
foreach my $t (@$tables) { |
42
|
8
|
|
|
|
|
405
|
$t->{column_info} = |
43
|
|
|
|
|
|
|
$dbh->column_info(undef, undef, $t->{TABLE_NAME}, '%')->fetchall_arrayref({}); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#TODO support multi_column primary keys.see DSC::find() |
46
|
8
|
|
100
|
|
|
7573
|
$t->{PRIMARY_KEY} = |
47
|
|
|
|
|
|
|
$dbh->primary_key_info(undef, undef, $t->{TABLE_NAME})->fetchall_arrayref({}) |
48
|
|
|
|
|
|
|
->[0]->{COLUMN_NAME} || ''; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
#as child table |
51
|
8
|
|
|
|
|
5494
|
my $sth = |
52
|
|
|
|
|
|
|
$dbh->foreign_key_info(undef, undef, undef, undef, undef, $t->{TABLE_NAME}); |
53
|
8
|
50
|
|
|
|
4649
|
$t->{FOREIGN_KEYS} = $sth->fetchall_arrayref({}) if $sth; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
} |
56
|
4
|
|
|
|
|
367
|
return $tables; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
#generates COLUMNS and PRIMARY_KEY |
60
|
|
|
|
|
|
|
sub _generate_COLUMNS_ALIASES_CHECKS { |
61
|
4
|
|
|
4
|
|
7
|
my ($class, $tables) = @_; |
62
|
|
|
|
|
|
|
|
63
|
4
|
|
|
|
|
7
|
foreach my $t (@$tables) { |
64
|
8
|
|
|
|
|
12
|
$t->{COLUMNS} = []; |
65
|
8
|
|
|
|
|
11
|
$t->{ALIASES} = {}; |
66
|
8
|
|
|
|
|
10
|
$t->{CHECKS} = {}; |
67
|
8
|
|
|
|
|
8
|
$t->{QUOTE_IDENTIFIERS} = 0; |
68
|
8
|
|
|
|
|
35
|
foreach my $col (sort { $a->{ORDINAL_POSITION} <=> $b->{ORDINAL_POSITION} } |
|
86
|
|
|
|
|
75
|
|
|
8
|
|
|
|
|
23
|
|
69
|
|
|
|
|
|
|
@{$t->{column_info}}) |
70
|
|
|
|
|
|
|
{ |
71
|
52
|
|
|
|
|
41
|
push @{$t->{COLUMNS}}, $col->{COLUMN_NAME}; |
|
52
|
|
|
|
|
63
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
#generate ALIASES |
74
|
52
|
100
|
|
|
|
327
|
if ($col->{COLUMN_NAME} =~ /\W/) { #not A-z0-9_ |
|
|
100
|
|
|
|
|
|
75
|
2
|
|
50
|
|
|
8
|
$t->{QUOTE_IDENTIFIERS} ||= 1; |
76
|
2
|
|
|
|
|
4
|
$t->{ALIASES}{$col->{COLUMN_NAME}} = $col->{COLUMN_NAME}; |
77
|
2
|
|
|
|
|
7
|
$t->{ALIASES}{$col->{COLUMN_NAME}} =~ s/\W/_/g; #foo-bar=>foo_bar |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
elsif ($class->SUPER::can($col->{COLUMN_NAME})) { |
80
|
2
|
|
|
|
|
5
|
$t->{ALIASES}{$col->{COLUMN_NAME}} = 'column_' . $col->{COLUMN_NAME}; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# generate CHECKS |
84
|
52
|
100
|
|
|
|
73
|
if ($col->{IS_NULLABLE} eq 'NO') { |
85
|
32
|
|
|
|
|
48
|
$t->{CHECKS}{$col->{COLUMN_NAME}}{required} = 1; |
86
|
32
|
|
|
|
|
35
|
$t->{CHECKS}{$col->{COLUMN_NAME}}{defined} = 1; |
87
|
|
|
|
|
|
|
} |
88
|
52
|
100
|
100
|
|
|
111
|
if ($col->{COLUMN_DEF} && $col->{COLUMN_DEF} !~ /NULL/i) { |
89
|
20
|
|
|
|
|
19
|
my $default = $col->{COLUMN_DEF}; |
90
|
20
|
|
|
|
|
38
|
$default =~ s|\'||g; |
91
|
20
|
|
|
|
|
30
|
$t->{CHECKS}{$col->{COLUMN_NAME}}{default} = $default; |
92
|
|
|
|
|
|
|
} |
93
|
52
|
|
100
|
|
|
103
|
my $size = $col->{COLUMN_SIZE} // 0; |
94
|
52
|
100
|
66
|
|
|
145
|
if ($size >= 65535 || $size == 0) { |
95
|
18
|
|
|
|
|
14
|
$size = ''; |
96
|
|
|
|
|
|
|
} |
97
|
52
|
100
|
|
|
|
187
|
if ($col->{TYPE_NAME} =~ /INT/i) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
98
|
16
|
|
|
|
|
238
|
$t->{CHECKS}{$col->{COLUMN_NAME}}{allow} = qr/^-?\d{1,$size}$/x; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
elsif ($col->{TYPE_NAME} =~ /FLOAT|DOUBLE|DECIMAL/i) { |
101
|
8
|
|
100
|
|
|
18
|
my $scale = $col->{DECIMAL_DIGITS} || 0; |
102
|
8
|
|
|
|
|
9
|
my $precision = $size - $scale; |
103
|
8
|
|
|
|
|
108
|
$t->{CHECKS}{$col->{COLUMN_NAME}}{allow} = |
104
|
|
|
|
|
|
|
qr/^-?\d{1,$precision}(?:\.\d{0,$scale})?$/x; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
elsif ($col->{TYPE_NAME} =~ /CHAR|TEXT|CLOB/i) { |
107
|
|
|
|
|
|
|
$t->{CHECKS}{$col->{COLUMN_NAME}}{allow} = |
108
|
5
|
100
|
|
5
|
|
968
|
sub { ($_[0] =~ /^.{1,$size}$/x) || ($_[0] eq '') } |
109
|
24
|
|
|
|
|
79
|
} |
110
|
|
|
|
|
|
|
} #end foreach @{$t->{column_info} |
111
|
|
|
|
|
|
|
} #end foreach $tables |
112
|
4
|
|
|
|
|
4
|
return $tables; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
my $_MAKE_SCHEMA; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub _MAKE_SCHEMA { |
118
|
31
|
100
|
|
31
|
|
610
|
$_MAKE_SCHEMA = $_[1] if defined $_[1]; |
119
|
31
|
|
|
|
|
105
|
return $_MAKE_SCHEMA; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub _generate_CODE { |
123
|
4
|
|
|
4
|
|
6
|
my ($class, $args) = @_; |
124
|
4
|
|
|
|
|
4
|
my $code = ''; |
125
|
4
|
|
|
|
|
5
|
my $namespace = $args->{namespace}; |
126
|
4
|
|
|
|
|
5
|
my $tables = $schemas->{$namespace}{tables}; |
127
|
4
|
|
|
|
|
10
|
$schemas->{$namespace}{code} = []; |
128
|
4
|
100
|
|
|
|
7
|
if ($class->_MAKE_SCHEMA) { |
129
|
2
|
|
|
|
|
2
|
push @{$schemas->{$namespace}{code}}, <<"BASE_CLASS"; |
|
2
|
|
|
|
|
15
|
|
130
|
|
|
|
|
|
|
package $namespace; #The schema/base class |
131
|
|
|
|
|
|
|
use 5.010001; |
132
|
|
|
|
|
|
|
use strict; |
133
|
|
|
|
|
|
|
use warnings; |
134
|
|
|
|
|
|
|
use utf8; |
135
|
|
|
|
|
|
|
use parent qw(DBIx::Simple::Class); |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
our \$VERSION = '0.01'; |
138
|
|
|
|
|
|
|
sub is_base_class{return 1} |
139
|
|
|
|
|
|
|
sub dbix { |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# Singleton DBIx::Simple instance |
142
|
|
|
|
|
|
|
state \$DBIx; |
143
|
|
|
|
|
|
|
return (\$_[1] ? (\$DBIx = \$_[1]) : \$DBIx) |
144
|
|
|
|
|
|
|
|| Carp::croak('DBIx::Simple is not instantiated. Please first do ' |
145
|
|
|
|
|
|
|
. \$_[0] |
146
|
|
|
|
|
|
|
. '->dbix(DBIx::Simple->connect(\$DSN,\$u,\$p,{...})'); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
1; |
150
|
|
|
|
|
|
|
$/$/=pod$/$/=encoding utf8$/$/=head1 NAME$/$/$namespace - the base schema class. |
151
|
|
|
|
|
|
|
$/=head1 DESCRIPTION |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
This is the base class for using table records as plain Perl objects. |
154
|
|
|
|
|
|
|
The subclassses are:$/$/=over |
155
|
|
|
|
|
|
|
BASE_CLASS |
156
|
|
|
|
|
|
|
} |
157
|
4
|
|
|
|
|
7
|
foreach my $t (@$tables) { |
158
|
10
|
|
|
|
|
24
|
my $package = |
159
|
8
|
|
|
|
|
983
|
$namespace . '::' . (join '', map { ucfirst lc } split /_/, $t->{TABLE_NAME}); |
160
|
8
|
|
|
|
|
37
|
my $COLUMNS = Data::Dumper->Dump([$t->{COLUMNS}], ['$COLUMNS']); |
161
|
8
|
|
|
|
|
393
|
my $ALIASES = Data::Dumper->Dump([$t->{ALIASES}], ['$ALIASES']); |
162
|
8
|
|
|
|
|
209
|
my $CHECKS = Data::Dumper->Dump([$t->{CHECKS}], ['$CHECKS']); |
163
|
8
|
|
|
|
|
718
|
my $TABLE = Data::Dumper->Dump([$t->{TABLE_NAME}], ['$TABLE_NAME']); |
164
|
8
|
|
|
|
|
185
|
my $name_description = |
165
|
|
|
|
|
|
|
"A class for $t->{TABLE_TYPE} $t->{TABLE_NAME} in schema $t->{TABLE_SCHEM}"; |
166
|
8
|
100
|
|
|
|
14
|
$schemas->{$namespace}{code}[0] .= qq|$/=item L<$package> - $name_description$/| |
167
|
|
|
|
|
|
|
if $class->_MAKE_SCHEMA; |
168
|
8
|
|
|
|
|
76
|
push @{$schemas->{$namespace}{code}}, qq|package $package; #A table/row class |
|
52
|
|
|
|
|
184
|
|
169
|
|
|
|
|
|
|
use 5.010001; |
170
|
|
|
|
|
|
|
use strict; |
171
|
|
|
|
|
|
|
use warnings; |
172
|
|
|
|
|
|
|
use utf8; |
173
|
|
|
|
|
|
|
use parent qw($namespace); |
174
|
|
|
|
|
|
|
| . qq| |
175
|
|
|
|
|
|
|
sub is_base_class{return 0} |
176
|
|
|
|
|
|
|
my $TABLE |
177
|
|
|
|
|
|
|
sub TABLE {return \$TABLE_NAME}| . qq| |
178
|
|
|
|
|
|
|
sub PRIMARY_KEY{return '$t->{PRIMARY_KEY}'} |
179
|
|
|
|
|
|
|
my $COLUMNS |
180
|
|
|
|
|
|
|
sub COLUMNS {return \$COLUMNS} |
181
|
|
|
|
|
|
|
my $ALIASES |
182
|
|
|
|
|
|
|
sub ALIASES {return \$ALIASES} |
183
|
|
|
|
|
|
|
my $CHECKS |
184
|
|
|
|
|
|
|
sub CHECKS {return \$CHECKS} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
__PACKAGE__->QUOTE_IDENTIFIERS($t->{QUOTE_IDENTIFIERS}); |
187
|
|
|
|
|
|
|
#__PACKAGE__->BUILD;#build accessors during load |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
1; |
190
|
|
|
|
|
|
|
| . qq|$/=pod$/$/=encoding utf8$/$/=head1 NAME$/$/$name_description |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
| . qq|=head1 SYNOPSIS$/$/=head1 DESCRIPTION$/$/=head1 COLUMNS$/ |
193
|
|
|
|
|
|
|
Each column from table C<$t->{TABLE_NAME}> has an accessor method in this class. |
194
|
|
|
|
|
|
|
| |
195
|
8
|
|
|
|
|
8
|
. (join '', map { $/ . '=head2 ' . $_ . $/ } @{$t->{COLUMNS}}) |
|
8
|
|
|
|
|
9
|
|
196
|
|
|
|
|
|
|
. qq|$/=head1 ALIASES$/$/=head1 GENERATOR$/$/L<$class>$/$/=head1 SEE ALSO$/| |
197
|
|
|
|
|
|
|
. qq|L<$namespace>, L, L<$class> |
198
|
|
|
|
|
|
|
$/=head1 AUTHOR$/$/$ENV{USER}$/$/=cut |
199
|
|
|
|
|
|
|
|; |
200
|
|
|
|
|
|
|
} # end foreach my $t (@$tables) |
201
|
|
|
|
|
|
|
|
202
|
4
|
100
|
|
|
|
1258
|
$schemas->{$namespace}{code}[0] .= qq|$/=back$/$/=head1 GENERATOR$/$/L<$class> |
203
|
|
|
|
|
|
|
$/$/=head1 SEE ALSO$/$/ |
204
|
|
|
|
|
|
|
L<$class>, L, L, L |
205
|
|
|
|
|
|
|
$/=head1 AUTHOR$/$/$ENV{USER}$/$/=cut |
206
|
|
|
|
|
|
|
| if $class->_MAKE_SCHEMA; |
207
|
4
|
100
|
|
|
|
455
|
if (defined wantarray) { |
208
|
3
|
100
|
|
|
|
8
|
if (wantarray) { |
209
|
1
|
|
|
|
|
1
|
return @{$schemas->{$namespace}{code}}; |
|
1
|
|
|
|
|
7
|
|
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
else { |
212
|
2
|
|
|
|
|
2
|
return join '', @{$schemas->{$namespace}{code}}; |
|
2
|
|
|
|
|
20
|
|
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
1
|
|
|
|
|
3
|
return; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub load_schema { |
219
|
4
|
|
|
4
|
1
|
16
|
my ($class, $args) = _get_obj_args(@_); |
220
|
4
|
100
|
|
|
|
12
|
unless ($args->{namespace}) { |
221
|
2
|
|
|
|
|
9
|
$args->{namespace} = $class->dbh->{Name}; |
222
|
2
|
50
|
|
|
|
28
|
if ($args->{namespace} =~ /(database|dbname|db)=([^;]+);?/x) { |
223
|
2
|
|
|
|
|
4
|
$args->{namespace} = $2; |
224
|
|
|
|
|
|
|
} |
225
|
2
|
|
|
|
|
8
|
$args->{namespace} =~ s/\W//xg; |
226
|
2
|
|
|
|
|
10
|
$args->{namespace} = |
227
|
2
|
|
|
|
|
7
|
'DSCS::' . (join '', map { ucfirst lc } split /_/, $args->{namespace}); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
4
|
|
|
|
|
9
|
my $tables = $class->_get_table_info($args); |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
#get table columns, PRIMARY_KEY, foreign keys |
233
|
4
|
|
|
|
|
1700
|
$class->_get_column_info($tables); |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
#generate COLUMNS, ALIASES, CHECKS |
236
|
4
|
|
|
|
|
15
|
$class->_generate_COLUMNS_ALIASES_CHECKS($tables); |
237
|
4
|
|
66
|
|
|
29
|
$class->_MAKE_SCHEMA(($args->{table} eq '%') or (not $args->{table})); |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
#generate code |
240
|
4
|
100
|
|
|
|
6
|
if (wantarray) { |
241
|
1
|
|
|
|
|
2
|
return ($class->_generate_CODE($args)); |
242
|
|
|
|
|
|
|
} |
243
|
3
|
|
|
|
|
13
|
return $class->_generate_CODE($args); |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub dump_schema_at { |
248
|
8
|
|
|
8
|
1
|
1602
|
my ($class, $args) = _get_obj_args(@_); |
249
|
8
|
|
66
|
|
|
41
|
$args->{lib_root} ||= $INC[0]; |
250
|
8
|
|
|
|
|
7
|
my ($namespace, @namespace, @base_path, $schema_path); |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
#_generate_CODE() should be called by now |
253
|
|
|
|
|
|
|
#we always have only one key |
254
|
8
|
|
66
|
|
|
168
|
$namespace = (keys %$schemas)[0] |
255
|
|
|
|
|
|
|
|| Carp::croak('Please first call ' . __PACKAGE__ . '->load_schema()!'); |
256
|
|
|
|
|
|
|
|
257
|
7
|
|
|
|
|
37
|
require File::Path; |
258
|
7
|
|
|
|
|
42
|
require File::Spec; |
259
|
7
|
|
|
|
|
1111
|
require IO::File; |
260
|
7
|
|
|
|
|
6601
|
@namespace = split /::/, $namespace; |
261
|
7
|
|
|
|
|
43
|
@base_path = File::Spec->splitdir($args->{lib_root}); |
262
|
|
|
|
|
|
|
|
263
|
7
|
|
|
|
|
46
|
$schema_path = File::Spec->catdir(@base_path, @namespace); |
264
|
|
|
|
|
|
|
|
265
|
7
|
100
|
66
|
|
|
395
|
if (eval "require $namespace" && $class->_MAKE_SCHEMA) { |
266
|
4
|
|
|
|
|
407
|
carp( "Module $namespace is already installed at " |
267
|
|
|
|
|
|
|
. $INC{join('/', @namespace) . '.pm'} |
268
|
|
|
|
|
|
|
. ". Please avoid namespace collisions..."); |
269
|
|
|
|
|
|
|
} |
270
|
7
|
|
|
|
|
2140
|
say('Will dump classes at ' . $args->{lib_root}); |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
#We should be able to continue safely now... |
273
|
7
|
|
|
|
|
22
|
my $tables = $schemas->{$namespace}{tables}; |
274
|
7
|
|
|
|
|
7
|
my $code = $schemas->{$namespace}{code}; |
275
|
7
|
100
|
|
|
|
104
|
if (!-d $schema_path) { |
276
|
2
|
50
|
0
|
|
|
2
|
eval { File::Path::make_path($schema_path); } |
|
2
|
|
|
|
|
407
|
|
277
|
|
|
|
|
|
|
|| carp("Can not make path $schema_path.$/$!. Quitting...") && return; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
7
|
100
|
|
|
|
63
|
if ($class->_MAKE_SCHEMA) { |
281
|
5
|
100
|
100
|
|
|
17
|
carp("Overwriting $schema_path.pm...") if $args->{overwrite} && $class->DEBUG; |
282
|
5
|
|
33
|
|
|
318
|
my $base_fh = IO::File->new("> $schema_path.pm") |
283
|
|
|
|
|
|
|
|| Carp::croak("Could not open $schema_path.pm for writing" . $!); |
284
|
5
|
|
|
|
|
520
|
print $base_fh $code->[0]; |
285
|
5
|
|
|
|
|
18
|
$base_fh->close; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
7
|
|
|
|
|
169
|
foreach my $i (0 .. @$tables - 1) { |
289
|
22
|
|
|
|
|
59
|
my $filename = |
290
|
17
|
|
|
|
|
231
|
(join '', map { ucfirst lc } split /_/, $tables->[$i]{TABLE_NAME}) . '.pm'; |
291
|
17
|
100
|
100
|
|
|
372
|
next if (-f "$schema_path/$filename" && !$args->{overwrite}); |
292
|
10
|
100
|
100
|
|
|
47
|
carp("Overwriting $schema_path/$filename...") |
293
|
|
|
|
|
|
|
if $args->{overwrite} && $class->DEBUG; |
294
|
10
|
|
|
|
|
1142
|
my $fh = IO::File->new("> $schema_path/$filename"); |
295
|
10
|
50
|
|
|
|
805
|
if (defined $fh) { |
296
|
10
|
|
|
|
|
46
|
print $fh $code->[$i + 1]; |
297
|
10
|
|
|
|
|
368
|
$fh->close; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
else { |
300
|
0
|
|
|
|
|
0
|
carp("$schema_path/$filename: $!. Quitting!"); |
301
|
0
|
|
|
|
|
0
|
return; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
} |
304
|
7
|
|
|
|
|
161
|
return 1; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
1; |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=encoding utf8 |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head1 NAME |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
DBIx::Simple::Class::Schema - Create and use classes representing tables from a database |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=head1 SYNOPSIS |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
#Somewhere in a utility script or startup() of your application. |
319
|
|
|
|
|
|
|
DBIx::Simple::Class::Schema->dbix(DBIx::Simple->connect(...)); |
320
|
|
|
|
|
|
|
my $perl_code = DBIx::Simple::Class::Schema->load_schema( |
321
|
|
|
|
|
|
|
namespace =>'My::Model', |
322
|
|
|
|
|
|
|
table => '%', #all tables from the current database |
323
|
|
|
|
|
|
|
type => "'TABLE','VIEW'", # make classes for tables and views |
324
|
|
|
|
|
|
|
); |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
#Now eval() to use your classes. |
327
|
|
|
|
|
|
|
eval $perl_code || Carp::croak($@); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
#Or load and save it for more customisations and later usage. |
331
|
|
|
|
|
|
|
DBIx::Simple::Class::Schema->load_schema( |
332
|
|
|
|
|
|
|
namespace =>'My::Model', |
333
|
|
|
|
|
|
|
table => '%', #all tables from the current database |
334
|
|
|
|
|
|
|
type => "'TABLE','VIEW'", # make classes for tables and views |
335
|
|
|
|
|
|
|
); |
336
|
|
|
|
|
|
|
DBIx::Simple::Class::Schema->dump_schema_at( |
337
|
|
|
|
|
|
|
lib_root => "$ENV{PERL_LOCAL_LIB_ROOT}/lib" |
338
|
|
|
|
|
|
|
overwrite =>1 #overwrite existing files |
339
|
|
|
|
|
|
|
) || Carp::croak 'Something went wrong! See above...'; |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head1 DESCRIPTION |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
DBIx::Simple::Class::Schema automates the creation of classes from |
345
|
|
|
|
|
|
|
database tables. You can use it when you want to prototype quickly |
346
|
|
|
|
|
|
|
your application. It is also very convenient as an initial generator and dumper of |
347
|
|
|
|
|
|
|
your classes representing your database tables. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head1 METHODS |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=head2 load_schema |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
Class method. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Params: |
356
|
|
|
|
|
|
|
namespace - String. The class name for your base class, |
357
|
|
|
|
|
|
|
default: 'DSCS::'.(join '', map { ucfirst lc } split /_/, $database) |
358
|
|
|
|
|
|
|
table - SQL string for a LIKE clause, |
359
|
|
|
|
|
|
|
default: '%' |
360
|
|
|
|
|
|
|
type - SQL String for an IN clause. |
361
|
|
|
|
|
|
|
default: "'TABLE','VIEW'" |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Extracts tables' information from the current connection and generates |
364
|
|
|
|
|
|
|
Perl classes representing those tables or/and views. |
365
|
|
|
|
|
|
|
If called in list context returns an array with perl code for each package. |
366
|
|
|
|
|
|
|
The first package is the base class. The base class is generated only the argument C is '%' or empty.
367
|
|
|
|
|
|
|
If called in scalar context returns all the generated code as a string. |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
The generated classes are saved internally and are available for use by |
370
|
|
|
|
|
|
|
L. |
371
|
|
|
|
|
|
|
This makes it very convenient for quickly prototyping applications |
372
|
|
|
|
|
|
|
by just modifying tables in your database. |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
my $perl_code = DBIx::Simple::Class::Schema->load_schema(); |
375
|
|
|
|
|
|
|
#concatenaded code as one string |
376
|
|
|
|
|
|
|
eval $perl_code || Carp::croak($@); |
377
|
|
|
|
|
|
|
#... |
378
|
|
|
|
|
|
|
my $user = Dbname::User->find(2345); |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
#or My::Schema, My::Schema::Table1, My::Schema::Table2,... |
381
|
|
|
|
|
|
|
my @perl_code = DBIx::Simple::Class::Schema->load_schema(); |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
#or just prepare code before dumping it to disk. |
384
|
|
|
|
|
|
|
DBIx::Simple::Class::Schema->load_schema(); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=head2 dump_schema_at |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
Class method. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
Params: |
391
|
|
|
|
|
|
|
lib_root: String - Where classes will be dumped. |
392
|
|
|
|
|
|
|
default: $INC[0] |
393
|
|
|
|
|
|
|
overwrite: boolean -1/0 Should it overwrite existing classes with the same name? |
394
|
|
|
|
|
|
|
default: 0 |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Uses the generated code by L and saves each class on the disk. |
397
|
|
|
|
|
|
|
Does several checks: |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=over |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=item * |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
Checks if a file with the name of your base class exists and exits |
404
|
|
|
|
|
|
|
if the flag C is not set. |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=item * |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
The base class is dumped to disk only if the argument C is '%' or empty.
409
|
|
|
|
|
|
|
It was not generated in L. |
410
|
|
|
|
|
|
|
In other words base/schema class is generated when no specific table class is |
411
|
|
|
|
|
|
|
required to be generated. This is convinient if you want to generate only specific table-classes and use them on-the-fly without dumping them to disk. |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=item * |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
Checks if there is a module with the same name as your base class installed |
416
|
|
|
|
|
|
|
and warns if there is such module. This is done to avoid namespace collisions. |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=item * |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
Checks if the files can be written to disk and exit immediately if there is a problem. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=back |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
For every check above issues a warning so you, the developer, can decide what to do. |
425
|
|
|
|
|
|
|
Returns true on success. |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=head1 SUPPORTED DATABASE DRIVERS |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
DBIx::Simple::Class::Schema strives to be DBD agnostic and |
430
|
|
|
|
|
|
|
uses only functionality specified by L. |
431
|
|
|
|
|
|
|
This means that if a driver implements the methods specifyed in L it is supported. |
432
|
|
|
|
|
|
|
However currently only tests for L and L are written. |
433
|
|
|
|
|
|
|
Feel free to contribute with tests for your prefered driver. |
434
|
|
|
|
|
|
|
The following methods are used to retreive information form the database: |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=over |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=item * L |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=item * L |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=item * L |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=back |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head1 SUPPORTED SQL TYPES |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
Currently some minimal L are automatically generated for TYPE_NAMEs |
449
|
|
|
|
|
|
|
matching C,C, C. |
450
|
|
|
|
|
|
|
You are supposed to write your own business-specific checks. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=head1 SEE ALSO |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
L, L, L, |
456
|
|
|
|
|
|
|
L |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
Copyright 2012-2013 Красимир Беров (Krasimir Berov). |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
This program is free software, you can redistribute it and/or modify it under |
463
|
|
|
|
|
|
|
the terms of the Artistic License version 2.0. |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
See http://www.opensource.org/licenses/artistic-license-2.0 for more information. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=cut |
468
|
|
|
|
|
|
|
|
| |