Revision 7dce1e6b
Von Sven Schöling vor etwa 13 Jahren hinzugefügt
scripts/rose_auto_create_model.pl | ||
---|---|---|
10 | 10 |
use CGI qw( -no_xhtml); |
11 | 11 |
use Config::Std; |
12 | 12 |
use Data::Dumper; |
13 |
use Digest::MD5 qw(md5_hex); |
|
13 | 14 |
use English qw( -no_match_vars ); |
15 |
use Getopt::Long; |
|
14 | 16 |
use List::MoreUtils qw(any); |
17 |
use Pod::Usage; |
|
18 |
use Term::ANSIColor; |
|
15 | 19 |
|
16 | 20 |
use SL::Auth; |
17 | 21 |
use SL::DBUtils; |
... | ... | |
23 | 27 |
use SL::DB::Helper::ALL; |
24 | 28 |
use SL::DB::Helper::Mappings; |
25 | 29 |
|
30 |
my %blacklist = SL::DB::Helper::Mappings->get_blacklist; |
|
31 |
my %package_names = SL::DB::Helper::Mappings->get_package_names; |
|
32 |
|
|
26 | 33 |
our $form; |
27 | 34 |
our $cgi; |
28 | 35 |
our $auth; |
... | ... | |
36 | 43 |
|
37 | 44 |
our $meta_path = "SL/DB/MetaSetup"; |
38 | 45 |
|
46 |
my %config; |
|
47 |
|
|
39 | 48 |
sub setup { |
40 |
if (@ARGV < 2) { |
|
41 |
print "Usage: $PROGRAM_NAME login table1[=package1] [table2[=package2] ...]\n"; |
|
42 |
print " or $PROGRAM_NAME login [--all|-a] [--sugar|-s]\n"; |
|
43 |
exit 1; |
|
44 |
} |
|
45 | 49 |
|
46 | 50 |
SL::LxOfficeConf->read; |
47 | 51 |
|
48 |
my $login = shift @ARGV;
|
|
52 |
my $login = $config{login} || $::lx_office_conf{devel}{login};
|
|
49 | 53 |
|
50 |
$::lxdebug = LXDebug->new(); |
|
54 |
if (!$login) { |
|
55 |
error("No login found in config. Please provide a login:"); |
|
56 |
usage(); |
|
57 |
} |
|
51 | 58 |
|
52 |
# locale messages
|
|
59 |
$::lxdebug = LXDebug->new();
|
|
53 | 60 |
$::locale = Locale->new("de"); |
54 | 61 |
$::form = new Form; |
55 | 62 |
$::cgi = new CGI(''); |
56 | 63 |
$::auth = SL::Auth->new(); |
57 |
|
|
58 | 64 |
$::user = User->new($login); |
59 |
|
|
60 | 65 |
%::myconfig = $auth->read_user($login); |
61 | 66 |
$form->{script} = 'rose_meta_data.pl'; |
62 | 67 |
$form->{login} = $login; |
... | ... | |
93 | 98 |
CODE |
94 | 99 |
|
95 | 100 |
if ($EVAL_ERROR) { |
96 |
print STDERR "Error in execution for table '$table': $EVAL_ERROR"; |
|
101 |
error("Error in execution for table '$table'"); |
|
102 |
error("'$EVAL_ERROR'") if $config{verbose}; |
|
97 | 103 |
return; |
98 | 104 |
} |
99 | 105 |
|
100 | 106 |
$definition =~ s/::AUTO::/::/g; |
101 |
|
|
102 |
my $file_exists = -f $meta_file; |
|
103 |
|
|
104 |
open(OUT, ">$meta_file") || die; |
|
105 |
print OUT <<CODE; |
|
107 |
my $full_definition = <<CODE; |
|
106 | 108 |
# This file has been auto-generated. Do not modify it; it will be overwritten |
107 | 109 |
# by $::script automatically. |
108 | 110 |
$definition; |
109 | 111 |
CODE |
110 |
close OUT; |
|
111 | 112 |
|
112 |
print "File '$meta_file' " . ($file_exists ? 'updated' : 'created') . " for table '$table'\n"; |
|
113 |
|
|
114 |
if (! -f $file) { |
|
115 |
open(OUT, ">$file") || die; |
|
116 |
print OUT <<CODE; |
|
113 |
my $meta_definition = <<CODE; |
|
117 | 114 |
# This file has been auto-generated only because it didn't exist. |
118 | 115 |
# Feel free to modify it at will; it will not be overwritten automatically. |
119 | 116 |
|
... | ... | |
128 | 125 |
|
129 | 126 |
1; |
130 | 127 |
CODE |
131 |
close OUT; |
|
132 | 128 |
|
133 |
print "File '$file' created as well.\n"; |
|
129 |
my $file_exists = -f $meta_file; |
|
130 |
if ($file_exists) { |
|
131 |
my $old_size = -s $meta_file; |
|
132 |
my $old_md5 = md5_hex(do { local(@ARGV, $/) = ($meta_file); <> }); |
|
133 |
my $new_size = length $full_definition; |
|
134 |
my $new_md5 = md5_hex($full_definition); |
|
135 |
if ($old_size == $new_size && $old_md5 == $new_md5) { |
|
136 |
notice("No changes in $meta_file, skipping.") if $config{verbose}; |
|
137 |
return; |
|
138 |
} |
|
139 |
} |
|
140 |
|
|
141 |
if (!$config{nocommit}) { |
|
142 |
open my $out, ">", $meta_file || die; |
|
143 |
print $out $full_definition; |
|
144 |
} |
|
145 |
|
|
146 |
notice("File '$meta_file' " . ($file_exists ? 'updated' : 'created') . " for table '$table'"); |
|
147 |
|
|
148 |
if (! -f $file) { |
|
149 |
if (!$config{nocommit}) { |
|
150 |
open my $out, ">", $file || die; |
|
151 |
print $out $meta_definition; |
|
152 |
} |
|
153 |
|
|
154 |
notice("File '$file' created as well."); |
|
134 | 155 |
} |
135 | 156 |
} |
136 | 157 |
|
137 |
setup(); |
|
158 |
sub parse_args { |
|
159 |
my ($options) = @_; |
|
160 |
GetOptions( |
|
161 |
'login|user=s' => \ my $login, |
|
162 |
all => \ my $all, |
|
163 |
sugar => \ my $sugar, |
|
164 |
'no-commit' => \ my $nocommit, |
|
165 |
help => sub { pod2usage(verbose => 99, sections => 'NAME|SYNOPSIS|OPTIONS') }, |
|
166 |
verbose => \ my $verbose, |
|
167 |
); |
|
168 |
|
|
169 |
$options->{login} = $login if $login; |
|
170 |
$options->{sugar} = $sugar; |
|
171 |
$options->{all} = $all; |
|
172 |
$options->{nocommit} = $nocommit; |
|
173 |
$options->{verbose} = $verbose; |
|
174 |
} |
|
138 | 175 |
|
139 |
my %blacklist = SL::DB::Helper::Mappings->get_blacklist; |
|
140 |
my %package_names = SL::DB::Helper::Mappings->get_package_names; |
|
176 |
sub usage { |
|
177 |
pod2usage(verbose => 99, sections => 'SYNOPSIS'); |
|
178 |
} |
|
141 | 179 |
|
142 |
my @tables = (); |
|
143 |
if (($ARGV[0] eq '--all') || ($ARGV[0] eq '-a') || ($ARGV[0] eq '--sugar') || ($ARGV[0] eq '-s')) { |
|
144 |
my ($type, $prefix) = ($ARGV[0] eq '--sugar') || ($ARGV[0] eq '-s') ? ('SUGAR', 'sugar_') : ('LXOFFICE', ''); |
|
145 |
my $db = SL::DB::create(undef, $type); |
|
146 |
@tables = map { $package_names{$type}->{$_} ? "${_}=" . $package_names{$type}->{$_} : $prefix ? "${_}=${prefix}${_}" : $_ } |
|
147 |
grep { my $table = $_; !any { $_ eq $table } @{ $blacklist{$type} } } |
|
148 |
$db->list_tables; |
|
180 |
sub make_tables { |
|
181 |
my @tables; |
|
182 |
if ($config{all} || $config{sugar}) { |
|
183 |
my ($type, $prefix) = $config{sugar} ? ('SUGAR', 'sugar_') : ('LXOFFICE', ''); |
|
184 |
my $db = SL::DB::create(undef, $type); |
|
185 |
@tables = |
|
186 |
map { $package_names{$type}->{$_} ? "$_=" . $package_names{$type}->{$_} : $prefix ? "$_=$prefix$_" : $_ } |
|
187 |
grep { my $table = $_; !any { $_ eq $table } @{ $blacklist{$type} } } |
|
188 |
$db->list_tables; |
|
189 |
} elsif (@ARGV) { |
|
190 |
@tables = @ARGV; |
|
191 |
} else { |
|
192 |
error("You specified neither --sugar nor --all nor any specific tables."); |
|
193 |
usage(); |
|
194 |
} |
|
149 | 195 |
|
150 |
} else { |
|
151 |
@tables = @ARGV; |
|
196 |
@tables; |
|
152 | 197 |
} |
153 | 198 |
|
154 |
foreach my $table (@tables) { |
|
199 |
sub error { |
|
200 |
print STDERR colored(shift, 'red'), $/; |
|
201 |
} |
|
202 |
|
|
203 |
sub notice { |
|
204 |
print @_, $/; |
|
205 |
} |
|
206 |
|
|
207 |
parse_args(\%config); |
|
208 |
setup(); |
|
209 |
my @tables = make_tables(); |
|
210 |
|
|
211 |
for my $table (@tables) { |
|
155 | 212 |
# add default model name unless model name is given or no defaults exists |
156 | 213 |
$table .= '=' . $package_names{LXOFFICE}->{lc $table} if $table !~ /=/ && $package_names{LXOFFICE}->{lc $table}; |
157 | 214 |
|
158 | 215 |
process_table($table); |
159 | 216 |
} |
217 |
|
|
218 |
1; |
|
219 |
|
|
220 |
__END__ |
|
221 |
|
|
222 |
=encoding utf-8 |
|
223 |
|
|
224 |
=head1 NAME |
|
225 |
|
|
226 |
rose_auto_create_model - mana Rose::DB::Object classes for Lx-Office |
|
227 |
|
|
228 |
=head1 SYNOPSIS |
|
229 |
|
|
230 |
scripts/rose_create_model.pl --login login table1[=package1] [table2[=package2] ...] |
|
231 |
scripts/rose_create_model.pl --login login [--all|-a] [--sugar|-s] |
|
232 |
|
|
233 |
# updates all models |
|
234 |
scripts/rose_create_model.pl --login login --all |
|
235 |
|
|
236 |
# updates only customer table, login taken from config |
|
237 |
scripts/rose_create_model.pl customer |
|
238 |
|
|
239 |
# updates only parts table, package will be Part |
|
240 |
scripts/rose_create_model.pl parts=Part |
|
241 |
|
|
242 |
# try to update parts, but don't do it. tell what would happen in detail |
|
243 |
scripts/rose_create_model.pl --no-commit --verbose parts |
|
244 |
|
|
245 |
=head1 DESCRIPTION |
|
246 |
|
|
247 |
Rose::DB::Object comes with a nice function named auto initialization with code |
|
248 |
generation. The documentation of Rose describes it like this: |
|
249 |
|
|
250 |
I<[...] auto-initializing metadata at runtime by querying the database has many |
|
251 |
caveats. An alternate approach is to query the database for metadata just once, |
|
252 |
and then generate the equivalent Perl code which can be pasted directly into |
|
253 |
the class definition in place of the call to auto_initialize.> |
|
254 |
|
|
255 |
I<Like the auto-initialization process itself, perl code generation has a |
|
256 |
convenient wrapper method as well as separate methods for the individual parts. |
|
257 |
All of the perl code generation methods begin with "perl_", and they support |
|
258 |
some rudimentary code formatting options to help the code conform to you |
|
259 |
preferred style. Examples can be found with the documentation for each perl_* |
|
260 |
method.> |
|
261 |
|
|
262 |
I<This hybrid approach to metadata population strikes a good balance between |
|
263 |
upfront effort and ongoing maintenance. Auto-generating the Perl code for the |
|
264 |
initial class definition saves a lot of tedious typing. From that point on, |
|
265 |
manually correcting and maintaining the definition is a small price to pay for |
|
266 |
the decreased start-up cost, the ability to use the class in the absence of a |
|
267 |
database connection, and the piece of mind that comes from knowing that your |
|
268 |
class is stable, and won't change behind your back in response to an "action at |
|
269 |
a distance" (i.e., a database schema update).> |
|
270 |
|
|
271 |
Unfortunately this ready easier than it is, since classes need to get in the |
|
272 |
right package and directory, certain stuff need to be adjusted and table names |
|
273 |
need to be translated into their class names. This script will wrap all that |
|
274 |
behind a few simple options. |
|
275 |
|
|
276 |
In the most basic version, just give it a login and |
|
277 |
|
|
278 |
=head1 OPTIONS |
|
279 |
|
|
280 |
=over 4 |
|
281 |
|
|
282 |
=item C<--login, -l LOGIN> |
|
283 |
|
|
284 |
=item C<--user, -u LOGIN> |
|
285 |
|
|
286 |
Provide a login. If not present the login is loaded from the config key |
|
287 |
C<devel/login>. If that too is not found, an error is thrown. |
|
288 |
|
|
289 |
=item C<--all, -a> |
|
290 |
|
|
291 |
Process all tables from the database. Only those that are blacklistes in |
|
292 |
L<SL::DB::Helper::Mappings> are excluded. |
|
293 |
|
|
294 |
=item C<--sugar, -s> |
|
295 |
|
|
296 |
Process tables in sugar schema instead of standard schema. Rarely useful unless |
|
297 |
you debug schema awareness of the RDBO layer. |
|
298 |
|
|
299 |
=item C<--no-commit, -n> |
|
300 |
|
|
301 |
Do not write back generated files. This will do everything as usual but not |
|
302 |
actually modify any files. |
|
303 |
|
|
304 |
=item C<--help, -h> |
|
305 |
|
|
306 |
Print this help. |
|
307 |
|
|
308 |
=item C<--verbose, -v> |
|
309 |
|
|
310 |
Prints extra information, such as skipped files that were not changed and |
|
311 |
errors where the auto initialization failed. |
|
312 |
|
|
313 |
=back |
|
314 |
|
|
315 |
=head1 BUGS |
|
316 |
|
|
317 |
None yet. |
|
318 |
|
|
319 |
=head1 AUTHOR |
|
320 |
|
|
321 |
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt> |
|
322 |
|
|
323 |
=cut |
Auch abrufbar als: Unified diff
rose_auto_create_model.pl überarbeitet.
- nocommit option
- farbige ausgabe
- verbose
- login aus config laden
- bessere doku