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 10     10   220502 use warnings;
  10         25  
  10         334  
4 10     10   49 use strict;
  10         24  
  10         299  
5 10     10   54 use Carp;
  10         24  
  10         634  
6 10     10   58 use File::Spec;
  10         15  
  10         238  
7 10     10   7128 use FabForce::DBDesigner4;
  0            
  0            
8              
9             # ABSTRACT: create DBIC scheme for DBDesigner4 xml file
10              
11             our $VERSION = '0.14';
12              
13              
14             sub new {
15             my ($class,%args) = @_;
16            
17             my $self = {};
18             bless $self, $class;
19            
20             $self->output_path( $args{output_path} );
21             $self->input_file( $args{input_file} );
22             $self->namespace( $args{namespace} );
23             $self->schema_name( $args{schema_name} );
24             $self->version_add( $args{version_add} );
25             $self->column_details( $args{column_details} );
26              
27             if ( $args{use_fake_dbic} || !eval{ require DBIx::Class } ) {
28             require FabForce::DBDesigner4::DBIC::FakeDBIC;
29             }
30            
31             $self->prefix(
32             'belongs_to' => '',
33             'has_many' => '',
34             'has_one' => '',
35             'many_to_many' => '',
36             );
37            
38            
39             return $self;
40             }
41              
42              
43             sub output_path {
44             my ($self,$path) = @_;
45            
46             $self->{output_path} = $path if defined $path;
47             return $self->{output_path};
48             }
49              
50              
51             sub input_file{
52             my ($self,$file) = @_;
53            
54             $self->{_input_file} = $file if defined $file;
55             return $self->{_input_file};
56             }
57              
58              
59             sub column_details {
60             my ($self,$bool) = @_;
61            
62             $self->{_column_details} = $bool if defined $bool;
63             return $self->{_column_details};
64             }
65              
66              
67             sub version_add{
68             my ($self,$inc) = @_;
69            
70             $self->{_version_add} = $inc if defined $inc;
71             return $self->{_version_add};
72             }
73              
74              
75             sub create_schema{
76             my ($self, $inputfile) = @_;
77            
78             $inputfile ||= $self->input_file;
79            
80             croak "no input file defined" unless defined $inputfile;
81            
82             my $output_path = $self->output_path || '.';
83             my $namespace = $self->namespace;
84            
85             my $fabforce = $self->dbdesigner;
86             $fabforce->parsefile( xml => $inputfile );
87             my @tables = $fabforce->getTables;
88            
89            
90             my @files;
91             my %relations;
92            
93             for my $table ( @tables ){
94             my $name = $table->name;
95             $self->_add_class( $name );
96             my $rels = $table->get_foreign_keys;
97             for my $to_table ( keys %$rels ){
98             $relations{$to_table}->{to}->{$name} = $rels->{$to_table};
99             $relations{$name}->{from}->{$to_table} = $rels->{$to_table};
100             }
101             }
102            
103             my @scheme = $self->_main_template;
104            
105             for my $table ( @tables ){
106             push @files, $self->_class_template( $table, $relations{$table->name} );
107             }
108            
109             push @files, @scheme;
110            
111             $self->_write_files( @files );
112             }
113              
114              
115             sub create_scheme {
116             &create_schema;
117             }
118              
119              
120             sub schema_name {
121             my ($self,$name) = @_;
122            
123             if( @_ == 2 ){
124             $name =~ s![^A-Za-z0-9_]!!g if defined $name;
125             $self->_schema( $name );
126             }
127             }
128              
129              
130             sub namespace{
131             my ($self,$namespace) = @_;
132            
133             $self->{namespace} = '' unless defined $self->{namespace};
134            
135             #print "yes: $namespace\n" if defined $namespace and $namespace =~ /^[A-Z]\w*(::\w+)*$/;
136            
137             if( defined $namespace and $namespace !~ /^[A-Z]\w*(::\w+)*$/ ){
138             croak "no valid namespace given";
139             }
140             elsif( defined $namespace ){
141             $self->{namespace} = $namespace;
142             }
143              
144             return $self->{namespace};
145             }
146              
147              
148             sub prefix{
149             if( @_ == 2 ){
150             my ($self,$key) = @_;
151             return $self->{prefixes}->{$key};
152             }
153              
154             if( @_ > 1 and @_ % 2 != 0 ){
155             my ($self,%prefixes) = @_;
156             while( my ($key,$val) = each %prefixes ){
157             $self->{prefixes}->{$key} = $val;
158             }
159             }
160             }
161              
162              
163             sub dbdesigner {
164             my ($self) = @_;
165            
166             unless( $self->{_dbdesigner} ){
167             $self->{_dbdesigner} = FabForce::DBDesigner4->new;
168             }
169            
170             $self->{_dbdesigner};
171             }
172              
173             sub _write_files{
174             my ($self, %files) = @_;
175            
176             for my $package ( keys %files ){
177             my @path;
178             push @path, $self->output_path if $self->output_path;
179             push @path, split /::/, $package;
180             my $file = pop @path;
181             my $dir = File::Spec->catdir( @path );
182            
183             $dir = $self->_untaint_path( $dir );
184            
185             unless( -e $dir ){
186             $self->_mkpath( $dir );
187             }
188              
189             if( open my $fh, '>', $dir . '/' . $file . '.pm' ){
190             print $fh $files{$package};
191             close $fh;
192             }
193             else{
194             croak "Couldn't create $file.pm";
195             }
196             }
197             }
198              
199             sub _untaint_path{
200             my ($self,$path) = @_;
201             ($path) = ( $path =~ /(.*)/ );
202             # win32 uses ';' for a path separator, assume others use ':'
203             my $sep = ($^O =~ /win32/i) ? ';' : ':';
204             # -T disallows relative directories in the PATH
205             $path = join $sep, grep !/^\./, split /$sep/, $path;
206             return $path;
207             }
208              
209             sub _mkpath{
210             my ($self, $path) = @_;
211            
212             my @parts = split /[\\\/]/, $path;
213            
214             for my $i ( 0..$#parts ){
215             my $dir = File::Spec->catdir( @parts[ 0..$i ] );
216             $dir = $self->_untaint_path( $dir );
217             mkdir $dir unless -e $dir;
218             }
219             }
220              
221             sub _add_class{
222             my ($self,$class) = @_;
223            
224             push @{ $self->{_classes} }, $class if defined $class;
225             }
226              
227             sub _get_classes{
228             my ($self) = @_;
229            
230             return @{ $self->{_classes} };
231             }
232              
233             sub _version{
234             my ($self,$version) = @_;
235            
236             $self->{_version} = $version if defined $version;
237             return $self->{_version};
238             }
239              
240             sub _schema{
241             my ($self,$name) = @_;
242            
243             $self->{_scheme} = $name if defined $name;
244             return $self->{_scheme};
245             }
246              
247             sub _has_many_template{
248             my ($self, $to, $arrayref) = @_;
249            
250             my $package = $self->namespace . '::' . $self->_schema . '::Result::' . $to;
251             $package =~ s/^:://;
252             my $name = (split /::/, $package)[-1];
253            
254             my $string = '';
255             for my $arref ( @$arrayref ){
256             my ($foreign_field,$field) = @$arref;
257             my $temp = $self->prefix( 'has_many' ) . $name;
258            
259             $string .= qq~
260             __PACKAGE__->has_many( $temp => '$package',
261             { 'foreign.$foreign_field' => 'self.$field' });
262             ~;
263             }
264              
265             return $string;
266             }
267              
268             sub _belongs_to_template{
269             my ($self, $from, $arrayref) = @_;
270            
271             my $package = $self->namespace . '::' . $self->_schema . '::Result::' . $from;
272             $package =~ s/^:://;
273             my $name = (split /::/, $package)[-1];
274            
275             my $string = '';
276             for my $arref ( @$arrayref ){
277             my ($field,$foreign_field) = @$arref;
278             my $temp_field = $self->prefix( 'belongs_to' ) . $name;
279            
280             $string .= qq~
281             __PACKAGE__->belongs_to($temp_field => '$package',
282             { 'foreign.$foreign_field' => 'self.$field' });
283             ~;
284             }
285              
286             return $string;
287             }
288              
289             sub _class_template{
290             my ($self,$table,$relations) = @_;
291            
292             my $name = $table->name;
293             my $package = $self->namespace . '::' . $self->_schema . '::Result::' . $name;
294             $package =~ s/^:://;
295            
296             my ($has_many, $belongs_to) = ('','');
297            
298             for my $to_table ( keys %{ $relations->{to} } ){
299             $has_many .= $self->_has_many_template( $to_table, $relations->{to}->{$to_table} );
300             }
301              
302             for my $from_table ( keys %{ $relations->{from} } ){
303             $belongs_to .= $self->_belongs_to_template( $from_table, $relations->{from}->{$from_table} );
304             }
305            
306             my @columns = $table->column_names;
307             my $column_string = '';
308              
309             if ( !$self->column_details ) {
310             $column_string = "qw/\n" . join "\n", map{ " " . $_ }@columns, " /";
311             }
312             else {
313             my @columns = @{ $table->column_details || [] };
314              
315             for my $column ( @columns ) {
316             $column->{DefaultValue} =~ s/'/\\'/g;
317              
318             if ( $column->{DataType} =~ /char/i && $column->{Width} <= 0 ) {
319             $column->{Width} = 255;
320             }
321              
322             my @options;
323              
324             my $name = $column->{ColName};
325              
326             push @options, "data_type => '" . $column->{DataType} . "',";
327             push @options, "is_auto_increment => 1," if $column->{AutoInc};
328             push @options, "is_nullable => 1," if !$column->{NotNull};
329             push @options, "size => " . $column->{Width} . "," if $column->{Width} > 0;
330             push @options, "default_value => '" . $column->{DefaultValue} . "'," if $column->{DefaultValue};
331              
332             my $option_string = join "\n ", @options;
333              
334             $column_string .= <<" COLUMN";
335             $name => {
336             $option_string
337             },
338             COLUMN
339             }
340             }
341              
342             my $primary_key = join " ", $table->key;
343             my $version = $self->_version;
344            
345             my $template = qq~package $package;
346            
347             use strict;
348             use warnings;
349             use base qw(DBIx::Class);
350              
351             our \$VERSION = $version;
352              
353             __PACKAGE__->load_components( qw/PK::Auto Core/ );
354             __PACKAGE__->table( '$name' );
355             __PACKAGE__->add_columns(
356             $column_string
357             );
358             __PACKAGE__->set_primary_key( qw/ $primary_key / );
359              
360             $has_many
361             $belongs_to
362              
363             1;~;
364              
365             return $package, $template;
366             }
367              
368             sub _main_template{
369             my ($self) = @_;
370            
371             my @class_names = $self->_get_classes;
372             my $classes = join "\n", map{ " " . $_ }@class_names;
373            
374             my $schema_name = $self->_schema;
375             my @schema_names = qw(DBIC_Schema Database DBIC MySchema MyDatabase DBIxClass_Schema);
376            
377             for my $schema ( @schema_names ){
378             last if $schema_name;
379             unless( grep{ $_ eq $schema }@class_names ){
380             $schema_name = $schema;
381             last;
382             }
383             }
384              
385             croak "couldn't determine a package name for the schema" unless $schema_name;
386            
387             $self->_schema( $schema_name );
388            
389             my $namespace = $self->namespace . '::' . $schema_name;
390             $namespace =~ s/^:://;
391              
392             my $version;
393             eval {
394             eval "require $namespace";
395             $version = $namespace->VERSION()
396             };
397              
398             if ( $version ) {
399             $version += ( $self->version_add || 0.01 );
400             }
401              
402             $version ||= '0.01';
403              
404             $self->_version( $version );
405            
406             my $template = qq~package $namespace;
407              
408             use base qw/DBIx::Class::Schema/;
409              
410             our \$VERSION = $version;
411              
412             __PACKAGE__->load_namespaces;
413              
414             1;~;
415              
416             return $namespace, $template;
417             }
418              
419              
420             1; # End of FabForce::DBDesigner4::DBIC
421              
422             __END__