File Coverage

blib/lib/Rose/DBx/Bouquet.pm
Criterion Covered Total %
statement 18 91 19.7
branch 0 12 0.0
condition 0 2 0.0
subroutine 6 11 54.5
pod 0 3 0.0
total 24 119 20.1


line stmt bran cond sub pod time code
1             package Rose::DBx::Bouquet;
2              
3             # Author:
4             # Ron Savage
5             #
6             # Note:
7             # \t = 4 spaces || die.
8              
9 1     1   26700 use strict;
  1         1  
  1         39  
10 1     1   6 use warnings;
  1         2  
  1         37  
11              
12             require 5.005_62;
13              
14             require Exporter;
15              
16 1     1   6 use Carp;
  1         2  
  1         91  
17 1     1   5 use File::Path; # For mkpath and rmtree.
  1         1  
  1         53  
18 1     1   1691 use HTML::Template;
  1         22080  
  1         44  
19 1     1   669 use Rose::DBx::Bouquet::Config;
  1         4  
  1         1338  
20              
21             our @ISA = qw(Exporter);
22              
23             # Items to export into callers namespace by default. Note: do not export
24             # names by default without a very good reason. Use EXPORT_OK instead.
25             # Do not simply export all your public functions/methods/constants.
26              
27             # This allows declaration use Rose::DBx::Bouquet ':all';
28             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
29             # will save memory.
30             our %EXPORT_TAGS = ( 'all' => [ qw(
31              
32             ) ] );
33              
34             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
35              
36             our @EXPORT = qw(
37              
38             );
39              
40             our $VERSION = '1.04';
41              
42             # -----------------------------------------------
43              
44             # Preloaded methods go here.
45              
46             # -----------------------------------------------
47              
48             # Encapsulated class data.
49              
50             {
51             my(%_attr_data) =
52             (
53             # These values are the defaults for rose.app.gen.pl's command line options.
54             # If the default value here is undef, and the user did not provide a value via
55             # command line options to rose.app.gen.pl, then get the value from the config file.
56              
57             _exclude => undef,
58             _module => 'Local::Wine',
59             _output_dir => undef,
60             _remove => 0,
61             _tmpl_path => undef,
62             _verbose => undef,
63             );
64              
65             sub _default_for
66             {
67 0     0     my($self, $attr_name) = @_;
68              
69 0           $_attr_data{$attr_name};
70             }
71              
72             sub _standard_keys
73             {
74 0     0     keys %_attr_data;
75             }
76             }
77              
78             # -----------------------------------------------
79              
80             sub log
81             {
82 0     0 0   my($self, $message) = @_;
83              
84 0 0         if ($$self{'_verbose'})
85             {
86 0           print STDERR "$message\n";
87             }
88              
89             } # End of log.
90              
91             # -----------------------------------------------
92              
93             sub new
94             {
95 0     0 0   my($class, $arg) = @_;
96 0           my($self) = bless({}, $class);
97 0           my($config) = Rose::DBx::Bouquet::Config -> new();
98              
99 0           for my $attr_name ($self -> _standard_keys() )
100             {
101 0           my($arg_name) = $attr_name =~ /^_(.*)/;
102              
103 0 0         if (exists($$arg{$arg_name}) )
104             {
105 0           $$self{$attr_name} = $$arg{$arg_name};
106             }
107             else
108             {
109 0           $$self{$attr_name} = $self -> _default_for($attr_name);
110             }
111              
112 0 0         if (! defined $$self{$attr_name})
113             {
114             # The '' is for when the user chops the option out of the config file,
115             # and also refuses to specify a value on the command line.
116              
117 0           my($method) = "get_$arg_name";
118 0   0       $$self{$attr_name} = $config -> $method() || '';
119             }
120             }
121              
122 0           $$self{'_dir_name'} = "$$self{'_output_dir'}\::$$self{'_module'}\::Rose";
123 0           $$self{'_dir_name'} = File::Spec -> catdir(split(/::/, $$self{'_dir_name'}) );
124 0           $$self{'_db_module'} = "$$self{'_module'}\::Base\::DB";;
125 0           my($file) = $$self{'_db_module'};
126 0           $file = File::Spec -> catdir(split(/::/, $file) );
127              
128 0           $self -> log("exclude: $$self{'_exclude'}");
129 0           $self -> log("module: $$self{'_module'}");
130 0           $self -> log("output_dir: $$self{'_output_dir'}");
131 0           $self -> log("remove: $$self{'_remove'}");
132 0           $self -> log("tmpl_path: $$self{'_tmpl_path'}");
133 0           $self -> log("verbose: $$self{'_verbose'}");
134 0           $self -> log("Working dir: $$self{'_dir_name'}");
135 0           $self -> log("Rose::DB module: $$self{'_db_module'}");
136              
137             # Ensure we can load the user's Rose::DB-based module.
138              
139 0           eval "require '$file.pm'";
140 0 0         croak $@ if $@;
141              
142 0           return $self;
143              
144             } # End of new.
145              
146             # -----------------------------------------------
147              
148             sub run
149             {
150 0     0 0   my($self) = @_;
151              
152 0 0         if ($$self{'_remove'})
153             {
154 0           $self -> log("Removing: $$self{'_dir_name'}");
155 0           $self -> log('Success');
156              
157 0           rmtree([$$self{'_dir_name'}]);
158              
159 0           return 0;
160             }
161              
162 0           my($rose_db) = $$self{'_db_module'} -> new();
163 0           my($dbh) = $rose_db -> retain_dbh();
164 0           my($sth) = $dbh -> table_info(undef, undef, '%', 'TABLE');
165              
166 0           my($data);
167             my(@module);
168 0           my($name);
169              
170 0           $self -> log('Processing tables:');
171              
172 0           while ($data = $sth -> fetchrow_hashref() )
173             {
174 0 0         next if ($$data{'TABLE_NAME'} =~ /$$self{'_exclude'}/);
175              
176 0           $self -> log($$data{'TABLE_NAME'});
177              
178 0           $name = ucfirst $$data{'TABLE_NAME'};
179 0           $name =~ s/(.+?)_(.)/$1\u$2/g;
180              
181 0           push @module,
182             {
183             module_name => $name,
184             table_name => $$data{'TABLE_NAME'},
185             }
186             }
187              
188 0           $self -> log('Processing modules:');
189              
190 0           @module = sort{$$a{'module_name'} cmp $$b{'module_name'} } @module;
  0            
191              
192 0           my($module, @module_loop);
193 0           my(@package_loop);
194              
195 0           for $module (@module)
196             {
197 0           $self -> log($$module{'module_name'});
198              
199 0           push @module_loop,
200             {
201             module => $$module{'module_name'},
202             };
203              
204 0           push @package_loop,
205             {
206             module => $$module{'module_name'},
207             prefix => $$self{'_module'},
208             table => $$module{'table_name'},
209             };
210             }
211              
212 0           mkpath([$$self{'_dir_name'}], 0, 0744);
213              
214 0           $self -> log('Processing template generator.pl.tmpl');
215              
216 0           my($template) = HTML::Template -> new(filename => File::Spec -> catfile($$self{'_tmpl_path'}, 'generator.pl.tmpl') );
217              
218 0           $template -> param(dir_name => $$self{'_dir_name'});
219 0           $template -> param(module_loop => \@module_loop);
220 0           $template -> param(package_loop => \@package_loop);
221 0           $template -> param(prefix => $$self{'_module'});
222 0           $template -> param(remove => $$self{'_remove'});
223 0           $template -> param(tmpl_path => $$self{'_tmpl_path'});
224 0           $template -> param(verbose => $$self{'_verbose'});
225              
226 0           print $template -> output();
227              
228 0           $self -> log('Success');
229              
230 0           return 0;
231              
232             } # End of run.
233              
234             # -----------------------------------------------
235              
236             1;
237              
238             =head1 NAME
239              
240             C - Use a database schema to generate Rose-based source code
241              
242             =head1 Synopsis
243              
244             Step 1: Unpack the distros:
245             shell> tar xvzf Rose-DBx-Bouquet-1.00.tgz (from CPAN)
246             shell> tar xvzf Local-Wine-1.06.tgz (see FAQ)
247              
248             Step 2: Check for (and install) the pre-requisites:
249             shell> cd Rose-DBx-Bouquet-1.00
250             shell> perl Build.PL
251             shell> cd ../Local-Wine-1.06
252             shell> perl Build.PL
253              
254             Note: You /must/ now be in Local-Wine-1.06/.
255              
256             Step 3: Create and optionally populate the database:
257             Edit lib/Local/Wine/.htwine.conf
258             and then
259             shell> scripts/create.tables.pl
260             shell> scripts/populate.tables.pl
261              
262             Step 4: Edit:
263             o lib/Rose/DBx/Bouquet/.htrose.bouquet.conf
264             o lib/Local/Wine/.htwine.conf
265              
266             Step 5: Run the first code generator (see scripts/rosy for an overview):
267             shell> scripts/run.rose.app.gen.pl > scripts/run.rose.pl
268              
269             Step 6: This is the log from run.rose.app.gen.pl:
270             exclude: ^(?:pg_|sql_)
271             module: Local::Wine
272             output_dir: ./lib
273             remove: 0
274             tmpl_path: /home/ron/perl.modules/Rose-DBx-Bouquet-1.00/templates
275             verbose: 1
276             Working dir: lib/Local/Wine/Rose
277             Rose::DB module: Local::Wine::Base::DB
278             Processing tables:
279             grape
280             vineyard
281             wine
282             wine_maker
283             Processing modules:
284             Grape
285             Vineyard
286             Wine
287             WineMaker
288             Processing template generator.pl.tmpl
289             Success
290              
291             Step 7: Run the second code generator:
292             shell> perl -Ilib scripts/run.rose.pl
293              
294             Step 8: This is the log from run.rose.pl:
295             Processing Rose::DB-based modules:
296             Generated lib/Local/Wine/Rose/Grape.pm
297             Generated lib/Local/Wine/Rose/Vineyard.pm
298             Generated lib/Local/Wine/Rose/Wine.pm
299             Generated lib/Local/Wine/Rose/WineMaker.pm
300             Processing */Manager.pm modules:
301             Generated lib/Local/Wine/Rose/Grape/Manager.pm
302             Generated lib/Local/Wine/Rose/Vineyard/Manager.pm
303             Generated lib/Local/Wine/Rose/Wine/Manager.pm
304             Generated lib/Local/Wine/Rose/WineMaker/Manager.pm
305             Processing */Form.pm modules:
306             Module: Grape. Columns: id, name
307             Generated lib/Local/Wine/Rose/Grape/Form.pm
308             Module: Vineyard. Columns: id, name
309             Generated lib/Local/Wine/Rose/Vineyard/Form.pm
310             Module: Wine. Columns: grape_id, id, rating, review_date, vineyard_id, vintage, wine_maker_id
311             Generated lib/Local/Wine/Rose/Wine/Form.pm
312             Module: WineMaker. Columns: id, name
313             Generated lib/Local/Wine/Rose/WineMaker/Form.pm
314             Success
315              
316             You can see this generated 12 files.
317             These files are used by CGI::Application::Bouquet::Rose (on CPAN), and by test.rose.pl.
318              
319             Step 9: Test the generated code:
320             shell> scripts/test.rose.pl
321              
322             Step 10: This is the log (12 lines) from test.rose.pl:
323             Total grape record count: 63.
324             Page: 1 of 'name like S%'.
325             1: Sangiovese,Shiraz.
326             2: Sauvignon,Semillon.
327             3: Sav Blanc.
328             4: Sav Blanc,Semillon.
329             Page: 2 of 'name like S%'.
330             1: Sav Blanc,Verdelho.
331             2: Semillon.
332             3: Shiraz.
333             4: Sparkling Shiraz.
334             Page: 3 of 'name like S%'.
335              
336             Step 11: Switch to the instructions for CGI::Application::Bouquet::Rose.
337              
338             =head1 Description
339              
340             C is a pure Perl module.
341              
342             It uses a database schema to generate Rose-based source code.
343              
344             This documentation uses Local::Wine as the basis for all discussions. See the FAQ for the availability
345             of the Local::Wine distro.
346              
347             The generated code can be used as-is, or it can be used by C.
348              
349             This module is actually a very simple version of C, and was inspired by the latter.
350              
351             The main difference, apart from its lack of sophistication of course, is that C uses
352             C-style templates to control the format of all generated code.
353              
354             C contains just enough code to be usable by C.
355              
356             If you wish to use C instead of C, there are a couple of places
357             in the templates which have to be changed.
358              
359             =head1 Distributions
360              
361             This module is available as a Unix-style distro (*.tgz).
362              
363             See http://savage.net.au/Perl-modules.html for details.
364              
365             See http://savage.net.au/Perl-modules/html/installing-a-module.html for
366             help on unpacking and installing.
367              
368             =head1 Constructor and initialization
369              
370             new(...) returns an object of type C.
371              
372             This is the class's contructor.
373              
374             Usage: C<< Rose::DBx::Bouquet -> new() >>.
375              
376             This method takes a hashref of options.
377              
378             Call C as C<< new({option_1 => value_1, option_2 => value_2, ...}) >>.
379              
380             Available options:
381              
382             =over 4
383              
384             =item exclude
385              
386             This takes a regexp (without the //) of table names to exclude.
387              
388             Code is generated for each table which is I excluded.
389              
390             If not specified, the value defaults to the value in lib/Rose/DBx/Bouquet/.htrose.bouquet.conf.
391              
392             The default value is ^(?:information_schema|pg_|sql_), which suits users of C.
393              
394             =item module
395              
396             This takes the name of a module to be used in the prefix of the namespace of the generated modules.
397              
398             Generate a set of modules under this name. So, C would result in:
399              
400             =over 4
401              
402             =item ./lib/Local/Wine/Rose/*.pm (1 per table)
403              
404             =item ./lib/Local/Wine/Rose/*/Form.pm (1 per table)
405              
406             =item ./lib/Local/Wine/Rose/*/Manager.pm (1 per table)
407              
408             =back
409              
410             These examples assume -output_dir is defaulting to ./lib.
411              
412             The default value for 'module' is C, because this document uses C for all examples,
413             and because you can download the C distro from my website, as explained in the FAQ, for testing.
414              
415             =item output_dir
416              
417             This takes the path where the output modules are to be written.
418              
419             If not specified, the value defaults to the value in lib/Rose/DBx/Bouquet/.htrose.bouquet.conf.
420              
421             See the discussion of the 'module' option above for more information.
422              
423             The default value is ./lib.
424              
425             =item remove
426              
427             This takes either a 0 or a 1.
428              
429             Removes files generated by an earlier run of this program.
430              
431             For instance, given the output listed under the 'module' option above, it removes
432             the directory ./lib/Local/Wine/Rose/.
433              
434             The default value is 0, meaning do not remove files.
435              
436             =item tmpl_path
437              
438             This is the path to C template directory.
439              
440             These templates are input to the code generation process.
441              
442             If not specified, the value defaults to the value in lib/Rose/DBx/Bouquet/.htrose.bouquet.conf.
443              
444             The default value is ../Rose-DBx-Bouquet-1.00/templates.
445              
446             Note: The point of the '../' is because I assume you have done 'cd Local-Wine-1.06'
447             or the equivalent for whatever module you are working with.
448              
449             =item verbose
450              
451             This takes either a 0 or a 1.
452              
453             Write more or less progress messages to STDERR, during code generation.
454              
455             The default value is 0.
456              
457             =back
458              
459             =head1 FAQ
460              
461             =over 4
462              
463             =item Availability of Local::Wine
464              
465             Download Local::Wine from http://savage.net.au/Perl-modules/Local-Wine-1.06.tgz
466              
467             The schema is at: http://savage.net.au/Perl-modules/wine.png
468              
469             C ships with C in the bin/ directory, whereas
470             C ships with various programs in the scripts/ directory.
471              
472             Files in the /bin directory get installed via 'make install'. Files in the scripts/ directory
473             are not intended to be installed; they are only used during the code-generation process.
474              
475             Note also that 'make install' installs lib/Rose/DBx/Bouquet/.htrose.bouquet.conf, and - depending
476             on your OS - you may need to change its permissions in order to edit it.
477              
478             =item Minimum modules required when replacing Local::Wine with your own code
479              
480             Short answer:
481              
482             =over 4
483              
484             =item Local::Wine
485              
486             =item Local::Wine::Config
487              
488             You can implement this module any way you want. It just has to provide the same methods.
489              
490             Note specifically that even if you re-write C, rather than just copying all the code
491             into your new module, I believe you should still provide to the end user a config file of options equivalent
492             to those in .htwine.conf.
493              
494             =item Local::Wine::Base::Create
495              
496             =item Local::Wine::DB
497              
498             This module will use the default type and domain, where 'type' and 'domain' are Rose concepts.
499              
500             =item Local::Wine::Object
501              
502             =back
503              
504             Long answer:
505              
506             See the docs for Local::Wine.
507              
508             =item Why isn't Local::Wine on CPAN?
509              
510             To avoid the problem of people assuming it can be downloaded and used just like any other module.
511              
512             =item Do you support DBIx::Class besides Rose?
513              
514             I did not try, but I assume it would be easy to do.
515              
516             =item How does C handle rows with a great many columns?
517              
518             All columns are processed.
519              
520             Future versions of either or both of C and C
521             will support a 'little language' (http://en.wikipedia.org/wiki/Little_language) which will allow you to
522             specify the columns to be displayed from the current table.
523              
524             =item How does C handle foreign keys?
525              
526             When C displays a HTML form containing a foreign key input field,
527             you must enter a value (optionally with SQL wild cards) for the foreign key, if you wish to use that field
528             as a search key.
529              
530             Future versions of either or both of C and C
531             will support a 'little language' which will allow you to specify the columns to be displayed from the
532             foreign table via the value of the foreign key.
533              
534             =item A note on option management
535              
536             You'll see a list of option names and default values near the top of this file, in the hash %_attr_data.
537              
538             Some default values are undef, and some are scalars.
539              
540             My policy is this:
541              
542             =over 4
543              
544             =item If the default is undef...
545              
546             Then the real default comes from a config file, and is obtained via the *::Config module.
547              
548             =item If the default is a scalar...
549              
550             Then that scalar is the default, and cannot be over-ridden by a value from a conf file.
551              
552             =back
553              
554             =item But why have such a method of handling options?
555              
556             Because I believe it makes sense for the end user (you, dear reader), to have the power to change
557             configuration values without patching the source code. Hence the conf file.
558              
559             However, for some values, I don't think it makes sense to do that. So, for those options, the default
560             value is a scalar in the source code of this module.
561              
562             =item Is this option arrangement permanent?
563              
564             No. Options whose defaults are already in the config file will never be deleted from that file.
565              
566             However, options not currently in the config file may be made available via the config file,
567             depending on feedback.
568              
569             Also, the config file is an easy way of preparing for more user-editable options.
570              
571             =back
572              
573             =head1 Method: log($message)
574              
575             If C was called as C<< new({verbose => 1}) >>, write the message to STDERR.
576              
577             If C was called as C<< new({verbose => 0}) >> (the default), do nothing.
578              
579             =head1 Method: run()
580              
581             Do everything.
582              
583             See C for an example of how to call C.
584              
585             =head1 See also
586              
587             C.
588              
589             =head1 Author
590              
591             C was written by Ron Savage Iron@savage.net.auE> in 2008.
592              
593             Home page: http://savage.net.au/index.html
594              
595             =head1 Copyright
596              
597             Australian copyright (c) 2008, Ron Savage.
598              
599             All Programs of mine are 'OSI Certified Open Source Software';
600             you can redistribute them and/or modify them under the terms of
601             The Artistic License, a copy of which is available at:
602             http://www.opensource.org/licenses/index.html
603              
604             =cut