File Coverage

blib/lib/FabForce/DBDesigner4/DBIC.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package FabForce::DBDesigner4::DBIC;
2              
3 9     9   140666 use warnings;
  9         18  
  9         269  
4 9     9   35 use strict;
  9         11  
  9         216  
5 9     9   32 use Carp;
  9         18  
  9         520  
6 9     9   41 use File::Spec;
  9         15  
  9         179  
7 9     9   2858 use FabForce::DBDesigner4;
  0            
  0            
8              
9             # ABSTRACT: create DBIC scheme for DBDesigner4 xml file
10              
11             our $VERSION = '0.15';
12              
13             =head1 SYNOPSIS
14              
15             use FabForce::DBDesigner4::DBIC;
16              
17             my $foo = FabForce::DBDesigner4::DBIC->new();
18             $foo->output_path( $some_path );
19             $foo->namespace( 'MyApp::DB' );
20             $foo->version_add( 0.01 );
21             $foo->create_schema( $xml_document );
22              
23             =head1 METHODS
24              
25             =head2 new
26              
27             creates a new object of FabForce::DBDesigner4::DBIC. You can pass some parameters
28             to new (all parameters are optional)
29              
30             my $foo = FabForce::DBDesigner4::DBIC->new(
31             output_path => '/path/to/dir',
32             input_file => '/path/to/dbdesigner.file',
33             namespace => 'MyApp::Database',
34             version_add => 0.001,
35             schema_name => 'MySchema',
36             column_details => 1,
37             use_fake_dbic => 1, # default 0.
38             );
39              
40             C is helpful when C is not installed on the
41             machine where you use this module.
42            
43             =cut
44              
45             sub new {
46             my ($class,%args) = @_;
47            
48             my $self = {};
49             bless $self, $class;
50            
51             $self->output_path( $args{output_path} );
52             $self->input_file( $args{input_file} );
53             $self->namespace( $args{namespace} );
54             $self->schema_name( $args{schema_name} );
55             $self->version_add( $args{version_add} );
56             $self->column_details( $args{column_details} );
57              
58             if ( $args{use_fake_dbic} || !eval{ require DBIx::Class } ) {
59             require FabForce::DBDesigner4::DBIC::FakeDBIC;
60             }
61            
62             $self->prefix(
63             'belongs_to' => '',
64             'has_many' => '',
65             'has_one' => '',
66             'many_to_many' => '',
67             );
68            
69            
70             return $self;
71             }
72              
73             =head2 output_path
74              
75             sets / gets the output path for the scheme
76              
77             $foo->output_path( '/any/directory' );
78             print $foo->output_path;
79              
80             =cut
81              
82             sub output_path {
83             my ($self,$path) = @_;
84            
85             $self->{output_path} = $path if defined $path;
86             return $self->{output_path};
87             }
88              
89             =head2 input_file
90              
91             sets / gets the name of the DBDesigner file (XML format)
92              
93             $foo->input_file( 'dbdesigner.xml' );
94             print $foo->input_file;
95              
96             =cut
97              
98             sub input_file{
99             my ($self,$file) = @_;
100            
101             $self->{_input_file} = $file if defined $file;
102             return $self->{_input_file};
103             }
104              
105             =head2 column_details
106              
107             If enabled, the column definitions are more detailed. Default: disabled.
108              
109             $foo->column_details( 1 );
110              
111             Standard (excerpt from Result classes):
112              
113             __PACKAGE__->add_columns( qw/
114             cert_id
115             register_nr
116             state
117             );
118              
119             With enabled column details:
120              
121             __PACKAGE__->add_columns(
122             cert_id => {
123             data_type => 'integer',
124             is_nullable => 0,
125             is_auto_increment => 1,
126             },
127             register_nr => {
128             data_type => 'integer',
129             is_nullable => 0,
130             },
131             state => {
132             data_type => 'varchar',
133             size => 1,
134             is_nullable => 0,
135             default_value => 'done',
136             },
137             );
138              
139             This is useful when you use L to deploy the columns
140             correctly.
141              
142             =cut
143              
144             sub column_details {
145             my ($self,$bool) = @_;
146            
147             $self->{_column_details} = $bool if defined $bool;
148             return $self->{_column_details};
149             }
150              
151             =head2 version_add
152              
153             The files should be versioned (e.g. to deploy the DB via C). On the first run
154             the version is set to "0.01". When the schema file already exists, the version is increased by the value
155             of C (default: 0.01)
156              
157             $foo->version_add( 0.001 );
158              
159             =cut
160              
161             sub version_add{
162             my ($self,$inc) = @_;
163            
164             $self->{_version_add} = $inc if defined $inc;
165             return $self->{_version_add};
166             }
167              
168             =head2 create_schema
169              
170             creates all the files that are needed to work with DBIx::Class schema:
171              
172             The main module that loads all classes and one class per table. If you haven't
173             specified an input file, the module will croak.
174              
175             You can specify the input file either with input_file or as an parameter for
176             create_schema
177              
178             $foo->input_file( 'dbdesigner.xml' );
179             $foo->create_schema;
180            
181             # or
182            
183             $foo->create_schema( 'dbdesigner.xml' );
184              
185             =cut
186              
187             sub create_schema{
188             my ($self, $inputfile) = @_;
189            
190             $inputfile ||= $self->input_file;
191            
192             croak "no input file defined" unless defined $inputfile;
193            
194             my $output_path = $self->output_path || '.';
195             my $namespace = $self->namespace;
196            
197             my $fabforce = $self->dbdesigner;
198             $fabforce->parsefile( xml => $inputfile );
199             my @tables = $fabforce->getTables;
200            
201            
202             my @files;
203             my %relations;
204            
205             for my $table ( @tables ){
206             my $name = $table->name;
207             $self->_add_class( $name );
208             my $rels = $table->get_foreign_keys;
209             for my $to_table ( keys %$rels ){
210             $relations{$to_table}->{to}->{$name} = $rels->{$to_table};
211             $relations{$name}->{from}->{$to_table} = $rels->{$to_table};
212             }
213             }
214            
215             my @scheme = $self->_main_template;
216            
217             for my $table ( @tables ){
218             push @files, $self->_class_template( $table, $relations{$table->name} );
219             }
220            
221             push @files, @scheme;
222            
223             $self->_write_files( @files );
224             }
225              
226             =head2 create_scheme
227              
228             C is an alias for C for compatibility reasons
229              
230             =cut
231              
232             sub create_scheme {
233             &create_schema;
234             }
235              
236             =head2 schema_name
237              
238             sets a new name for the schema. By default on of these names is used:
239              
240             DBIC_Scheme Database DBIC MyScheme MyDatabase DBIxClass_Scheme
241              
242             $dbic->schema_name( 'MyNewSchema' );
243              
244             =cut
245              
246             sub schema_name {
247             my ($self,$name) = @_;
248            
249             if( @_ == 2 ){
250             $name =~ s![^A-Za-z0-9_]!!g if defined $name;
251             $self->_schema( $name );
252             }
253             }
254              
255             =head2 namespace
256              
257             sets / gets the name of the namespace. If you set the namespace to 'Test' and you
258             have a table named 'MyTable', the main module is named 'Test::DBIC_Scheme' and
259             the class for 'MyTable' is named 'Test::DBIC_Scheme::MyTable'
260              
261             $foo->namespace( 'MyApp::DB' );
262              
263             =cut
264              
265             sub namespace{
266             my ($self,$namespace) = @_;
267            
268             $self->{namespace} = '' unless defined $self->{namespace};
269            
270             #print "yes: $namespace\n" if defined $namespace and $namespace =~ /^[A-Z]\w*(::\w+)*$/;
271            
272             if( defined $namespace and $namespace !~ /^[A-Z]\w*(::\w+)*$/ ){
273             croak "no valid namespace given";
274             }
275             elsif( defined $namespace ){
276             $self->{namespace} = $namespace;
277             }
278              
279             return $self->{namespace};
280             }
281              
282             =head2 prefix
283              
284             In relationships the accessor for the objects of the "other" table shouldn't have the name of the column.
285             Otherwise it is very clumsy to get the orginial value of this table.
286              
287             $foo->prefix( 'belongs_to' => 'fk_' );
288             $foo->prefix( 'has_many' => 'has_' );
289              
290             creates (col1 is the column name of the foreign key)
291              
292             __PACKAGE__->belongs_to( 'fk_col1' => 'OtherTable', {'foreign.col1' => 'self.col1' } );
293              
294             =cut
295              
296             sub prefix{
297             if( @_ == 2 ){
298             my ($self,$key) = @_;
299             return $self->{prefixes}->{$key};
300             }
301              
302             if( @_ > 1 and @_ % 2 != 0 ){
303             my ($self,%prefixes) = @_;
304             while( my ($key,$val) = each %prefixes ){
305             $self->{prefixes}->{$key} = $val;
306             }
307             }
308             }
309              
310             =head2 dbdesigner
311              
312             returns the C object.
313              
314             =cut
315              
316             sub dbdesigner {
317             my ($self) = @_;
318            
319             unless( $self->{_dbdesigner} ){
320             $self->{_dbdesigner} = FabForce::DBDesigner4->new;
321             }
322            
323             $self->{_dbdesigner};
324             }
325              
326             sub _write_files{
327             my ($self, %files) = @_;
328            
329             for my $package ( keys %files ){
330             my @path;
331             push @path, $self->output_path if $self->output_path;
332             push @path, split /::/, $package;
333             my $file = pop @path;
334             my $dir = File::Spec->catdir( @path );
335            
336             $dir = $self->_untaint_path( $dir );
337            
338             unless( -e $dir ){
339             $self->_mkpath( $dir );
340             }
341              
342             if( open my $fh, '>', $dir . '/' . $file . '.pm' ){
343             print $fh $files{$package};
344             close $fh;
345             }
346             else{
347             croak "Couldn't create $file.pm";
348             }
349             }
350             }
351              
352             sub _untaint_path{
353             my ($self,$path) = @_;
354             ($path) = ( $path =~ /(.*)/ );
355             # win32 uses ';' for a path separator, assume others use ':'
356             my $sep = ($^O =~ /win32/i) ? ';' : ':';
357             # -T disallows relative directories in the PATH
358             $path = join $sep, grep !/^\./, split /$sep/, $path;
359             return $path;
360             }
361              
362             sub _mkpath{
363             my ($self, $path) = @_;
364            
365             my @parts = split /[\\\/]/, $path;
366            
367             for my $i ( 0..$#parts ){
368             my $dir = File::Spec->catdir( @parts[ 0..$i ] );
369             $dir = $self->_untaint_path( $dir );
370             mkdir $dir unless -e $dir;
371             }
372             }
373              
374             sub _add_class{
375             my ($self,$class) = @_;
376            
377             push @{ $self->{_classes} }, $class if defined $class;
378             }
379              
380             sub _get_classes{
381             my ($self) = @_;
382            
383             return @{ $self->{_classes} };
384             }
385              
386             sub _version{
387             my ($self,$version) = @_;
388            
389             $self->{_version} = $version if defined $version;
390             return $self->{_version};
391             }
392              
393             sub _schema{
394             my ($self,$name) = @_;
395            
396             $self->{_scheme} = $name if defined $name;
397             return $self->{_scheme};
398             }
399              
400             sub _has_many_template{
401             my ($self, $to, $arrayref) = @_;
402            
403             my $package = $self->namespace . '::' . $self->_schema . '::Result::' . $to;
404             $package =~ s/^:://;
405             my $name = (split /::/, $package)[-1];
406            
407             my $string = '';
408             for my $arref ( @$arrayref ){
409             my ($foreign_field,$field) = @$arref;
410             my $temp = $self->prefix( 'has_many' ) . $name;
411            
412             $string .= qq~
413             __PACKAGE__->has_many( $temp => '$package',
414             { 'foreign.$foreign_field' => 'self.$field' });
415             ~;
416             }
417              
418             return $string;
419             }
420              
421             sub _belongs_to_template{
422             my ($self, $from, $arrayref) = @_;
423            
424             my $package = $self->namespace . '::' . $self->_schema . '::Result::' . $from;
425             $package =~ s/^:://;
426             my $name = (split /::/, $package)[-1];
427            
428             my $string = '';
429             for my $arref ( @$arrayref ){
430             my ($field,$foreign_field) = @$arref;
431             my $temp_field = $self->prefix( 'belongs_to' ) . $name;
432            
433             $string .= qq~
434             __PACKAGE__->belongs_to($temp_field => '$package',
435             { 'foreign.$foreign_field' => 'self.$field' });
436             ~;
437             }
438              
439             return $string;
440             }
441              
442             sub _class_template{
443             my ($self,$table,$relations) = @_;
444            
445             my $name = $table->name;
446             my $package = $self->namespace . '::' . $self->_schema . '::Result::' . $name;
447             $package =~ s/^:://;
448            
449             my ($has_many, $belongs_to) = ('','');
450            
451             for my $to_table ( keys %{ $relations->{to} } ){
452             $has_many .= $self->_has_many_template( $to_table, $relations->{to}->{$to_table} );
453             }
454              
455             for my $from_table ( keys %{ $relations->{from} } ){
456             $belongs_to .= $self->_belongs_to_template( $from_table, $relations->{from}->{$from_table} );
457             }
458            
459             my @columns = $table->column_names;
460             my $column_string = '';
461              
462             if ( !$self->column_details ) {
463             $column_string = "qw/\n" . join "\n", map{ " " . $_ }@columns, " /";
464             }
465             else {
466             my @columns = @{ $table->column_details || [] };
467              
468             for my $column ( @columns ) {
469             $column->{DefaultValue} =~ s/'/\\'/g;
470              
471             if ( $column->{DataType} =~ /char/i && $column->{Width} <= 0 ) {
472             $column->{Width} = 255;
473             }
474              
475             my @options;
476              
477             my $name = $column->{ColName};
478              
479             push @options, "data_type => '" . $column->{DataType} . "',";
480             push @options, "is_auto_increment => 1," if $column->{AutoInc};
481             push @options, "is_nullable => 1," if !$column->{NotNull};
482             push @options, "size => " . $column->{Width} . "," if $column->{Width} > 0;
483             push @options, "default_value => '" . $column->{DefaultValue} . "'," if $column->{DefaultValue};
484              
485             my $option_string = join "\n ", @options;
486              
487             $column_string .= <<" COLUMN";
488             $name => {
489             $option_string
490             },
491             COLUMN
492             }
493             }
494              
495             my $primary_key = join " ", $table->key;
496             my $version = $self->_version;
497            
498             my $template = qq~package $package;
499            
500             use strict;
501             use warnings;
502             use base qw(DBIx::Class);
503              
504             our \$VERSION = $version;
505              
506             __PACKAGE__->load_components( qw/PK::Auto Core/ );
507             __PACKAGE__->table( '$name' );
508             __PACKAGE__->add_columns(
509             $column_string
510             );
511             __PACKAGE__->set_primary_key( qw/ $primary_key / );
512              
513             $has_many
514             $belongs_to
515              
516             1;~;
517              
518             return $package, $template;
519             }
520              
521             sub _main_template{
522             my ($self) = @_;
523            
524             my @class_names = $self->_get_classes;
525             my $classes = join "\n", map{ " " . $_ }@class_names;
526            
527             my $schema_name = $self->_schema;
528             my @schema_names = qw(DBIC_Schema Database DBIC MySchema MyDatabase DBIxClass_Schema);
529            
530             for my $schema ( @schema_names ){
531             last if $schema_name;
532             unless( grep{ $_ eq $schema }@class_names ){
533             $schema_name = $schema;
534             last;
535             }
536             }
537              
538             croak "couldn't determine a package name for the schema" unless $schema_name;
539            
540             $self->_schema( $schema_name );
541            
542             my $namespace = $self->namespace . '::' . $schema_name;
543             $namespace =~ s/^:://;
544              
545             my $version;
546             eval {
547             eval "require $namespace";
548             $version = $namespace->VERSION()
549             };
550              
551             if ( $version ) {
552             $version += ( $self->version_add || 0.01 );
553             }
554              
555             $version ||= '0.01';
556              
557             $self->_version( $version );
558            
559             my $template = qq~package $namespace;
560              
561             use base qw/DBIx::Class::Schema/;
562              
563             our \$VERSION = $version;
564              
565             __PACKAGE__->load_namespaces;
566              
567             1;~;
568              
569             return $namespace, $template;
570             }
571              
572             =head1 BUGS
573              
574             Please report any bugs or feature requests to
575             C, or through the web interface at
576             L.
577             I will be notified, and then you'll automatically be notified of progress on
578             your bug as I make changes.
579              
580             =head1 SUPPORT
581              
582             You can find documentation for this module with the perldoc command.
583              
584             perldoc FabForce::DBDesigner4::DBIC
585              
586             You can also look for information at:
587              
588             =over 4
589              
590             =item * AnnoCPAN: Annotated CPAN documentation
591              
592             L
593              
594             =item * CPAN Ratings
595              
596             L
597              
598             =item * RT: CPAN's request tracker
599              
600             L
601              
602             =item * Search CPAN
603              
604             L
605              
606             =back
607              
608             =head1 ACKNOWLEDGEMENTS
609              
610             =cut
611              
612             1; # End of FabForce::DBDesigner4::DBIC