only _load_external for the classes we are supposed to
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / Base.pm
1 package DBIx::Class::Schema::Loader::Base;
2
3 use strict;
4 use warnings;
5 use base qw/Class::Accessor::Fast/;
6 use Class::C3;
7 use Carp::Clan qw/^DBIx::Class/;
8 use UNIVERSAL::require;
9 use DBIx::Class::Schema::Loader::RelBuilder;
10 use Data::Dump qw/ dump /;
11 use POSIX qw//;
12 use File::Spec qw//;
13 use Cwd qw//;
14 use Digest::MD5 qw//;
15 require DBIx::Class;
16
17 our $VERSION = '0.03999_01';
18
19 __PACKAGE__->mk_ro_accessors(qw/
20                                 schema
21                                 schema_class
22
23                                 exclude
24                                 constraint
25                                 additional_classes
26                                 additional_base_classes
27                                 left_base_classes
28                                 components
29                                 resultset_components
30                                 skip_relationships
31                                 moniker_map
32                                 inflect_singular
33                                 inflect_plural
34                                 debug
35                                 dump_directory
36                                 dump_overwrite
37
38                                 db_schema
39                                 _tables
40                                 classes
41                                 monikers
42                              /);
43
44 =head1 NAME
45
46 DBIx::Class::Schema::Loader::Base - Base DBIx::Class::Schema::Loader Implementation.
47
48 =head1 SYNOPSIS
49
50 See L<DBIx::Class::Schema::Loader>
51
52 =head1 DESCRIPTION
53
54 This is the base class for the storage-specific C<DBIx::Class::Schema::*>
55 classes, and implements the common functionality between them.
56
57 =head1 CONSTRUCTOR OPTIONS
58
59 These constructor options are the base options for
60 L<DBIx::Class::Schema::Loader/loader_opts>.  Available constructor options are:
61
62 =head2 skip_relationships
63
64 Skip setting up relationships.  The default is to attempt the loading
65 of relationships.
66
67 =head2 debug
68
69 If set to true, each constructive L<DBIx::Class> statement the loader
70 decides to execute will be C<warn>-ed before execution.
71
72 =head2 db_schema
73
74 Set the name of the schema to load (schema in the sense that your database
75 vendor means it).  Does not currently support loading more than one schema
76 name.
77
78 =head2 constraint
79
80 Only load tables matching regex.  Best specified as a qr// regex.
81
82 =head2 exclude
83
84 Exclude tables matching regex.  Best specified as a qr// regex.
85
86 =head2 moniker_map
87
88 Overrides the default table name to moniker translation.  Can be either
89 a hashref of table keys and moniker values, or a coderef for a translator
90 function taking a single scalar table name argument and returning
91 a scalar moniker.  If the hash entry does not exist, or the function
92 returns a false value, the code falls back to default behavior
93 for that table name.
94
95 The default behavior is: C<join '', map ucfirst, split /[\W_]+/, lc $table>,
96 which is to say: lowercase everything, split up the table name into chunks
97 anywhere a non-alpha-numeric character occurs, change the case of first letter
98 of each chunk to upper case, and put the chunks back together.  Examples:
99
100     Table Name  | Moniker Name
101     ---------------------------
102     luser       | Luser
103     luser_group | LuserGroup
104     luser-opts  | LuserOpts
105
106 =head2 inflect_plural
107
108 Just like L</moniker_map> above (can be hash/code-ref, falls back to default
109 if hash key does not exist or coderef returns false), but acts as a map
110 for pluralizing relationship names.  The default behavior is to utilize
111 L<Lingua::EN::Inflect::Number/to_PL>.
112
113 =head2 inflect_singular
114
115 As L</inflect_plural> above, but for singularizing relationship names.
116 Default behavior is to utilize L<Lingua::EN::Inflect::Number/to_S>.
117
118 =head2 additional_base_classes
119
120 List of additional base classes all of your table classes will use.
121
122 =head2 left_base_classes
123
124 List of additional base classes all of your table classes will use
125 that need to be leftmost.
126
127 =head2 additional_classes
128
129 List of additional classes which all of your table classes will use.
130
131 =head2 components
132
133 List of additional components to be loaded into all of your table
134 classes.  A good example would be C<ResultSetManager>.
135
136 =head2 resultset_components
137
138 List of additional ResultSet components to be loaded into your table
139 classes.  A good example would be C<AlwaysRS>.  Component
140 C<ResultSetManager> will be automatically added to the above
141 C<components> list if this option is set.
142
143 =head2 dump_directory
144
145 This option is designed to be a tool to help you transition from this
146 loader to a manually-defined schema when you decide it's time to do so.
147
148 The value of this option is a perl libdir pathname.  Within
149 that directory this module will create a baseline manual
150 L<DBIx::Class::Schema> module set, based on what it creates at runtime
151 in memory.
152
153 The created schema class will have the same classname as the one on
154 which you are setting this option (and the ResultSource classes will be
155 based on this name as well).
156
157 Normally you wouldn't hard-code this setting in your schema class, as it
158 is meant for one-time manual usage.
159
160 See L<DBIx::Class::Schema::Loader/dump_to_dir> for examples of the
161 recommended way to access this functionality.
162
163 =head2 dump_overwrite
164
165 Default false.  If true, Loader will unconditionally delete any existing
166 files before creating the new ones from scratch when dumping a schema to disk.
167
168 The default behavior is instead to only replace the top portion of the
169 file, up to and including the final stanza which contains
170 C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
171 leaving any customizations you placed after that as they were.
172
173 When C<dump_overwrite> is not set, if the output file already exists,
174 but the aforementioned final stanza is not found, or the checksum
175 contained there does not match the generated contents, Loader will
176 croak and not touch the file.
177
178 =head1 METHODS
179
180 None of these methods are intended for direct invocation by regular
181 users of L<DBIx::Class::Schema::Loader>.  Anything you can find here
182 can also be found via standard L<DBIx::Class::Schema> methods somehow.
183
184 =cut
185
186 # ensure that a peice of object data is a valid arrayref, creating
187 # an empty one or encapsulating whatever's there.
188 sub _ensure_arrayref {
189     my $self = shift;
190
191     foreach (@_) {
192         $self->{$_} ||= [];
193         $self->{$_} = [ $self->{$_} ]
194             unless ref $self->{$_} eq 'ARRAY';
195     }
196 }
197
198 =head2 new
199
200 Constructor for L<DBIx::Class::Schema::Loader::Base>, used internally
201 by L<DBIx::Class::Schema::Loader>.
202
203 =cut
204
205 sub new {
206     my ( $class, %args ) = @_;
207
208     my $self = { %args };
209
210     bless $self => $class;
211
212     $self->{db_schema}  ||= '';
213     $self->_ensure_arrayref(qw/additional_classes
214                                additional_base_classes
215                                left_base_classes
216                                components
217                                resultset_components
218                               /);
219
220     push(@{$self->{components}}, 'ResultSetManager')
221         if @{$self->{resultset_components}};
222
223     $self->{monikers} = {};
224     $self->{classes} = {};
225
226     $self->{schema_class} ||= ( ref $self->{schema} || $self->{schema} );
227     $self->{schema} ||= $self->{schema_class};
228
229     $self->{relbuilder} = DBIx::Class::Schema::Loader::RelBuilder->new(
230         $self->schema_class, $self->inflect_plural, $self->inflect_singular
231     ) if !$self->{skip_relationships};
232
233     $self;
234 }
235
236 sub _find_file_in_inc {
237     my ($self, $file) = @_;
238
239     foreach my $prefix (@INC) {
240         my $fullpath = $prefix . '/' . $file;
241         return $fullpath if -f $fullpath;
242     }
243
244     return;
245 }
246
247 sub _load_external {
248     my ($self, $class) = @_;
249
250     my $class_path = $class;
251     $class_path =~ s{::}{/}g;
252     $class_path .= '.pm';
253
254     my $inc_path = $self->_find_file_in_inc($class_path);
255
256     return if !$inc_path;
257
258     my $real_dump_path = $self->dump_directory
259         ? Cwd::abs_path(
260               File::Spec->catfile($self->dump_directory, $class_path)
261           )
262         : '';
263     my $real_inc_path = Cwd::abs_path($inc_path);
264     return if $real_inc_path eq $real_dump_path;
265
266     $class->require;
267     croak "Failed to load external class definition"
268         . " for '$class': $@"
269             if $@;
270
271     # If we make it to here, we loaded an external definition
272     warn qq/# Loaded external class definition for '$class'\n/
273         if $self->debug;
274
275     # The rest is only relevant when dumping
276     return if !$self->dump_directory;
277
278     croak 'Failed to locate actual external module file for '
279           . "'$class'"
280               if !$real_inc_path;
281     open(my $fh, '<', $real_inc_path)
282         or croak "Failed to open '$real_inc_path' for reading: $!";
283     $self->_ext_stmt($class,
284         qq|# These lines were loaded from '$real_inc_path' found in \@INC.|
285         .q|# They are now part of the custom portion of this file|
286         .q|# for you to hand-edit.  If you do not either delete|
287         .q|# this section or remove that file from @INC, this section|
288         .q|# will be repeated redundantly when you re-create this|
289         .q|# file again via Loader!|
290     );
291     while(<$fh>) {
292         chomp;
293         $self->_ext_stmt($class, $_);
294     }
295     $self->_ext_stmt($class,
296         q|# End of lines loaded from '$real_inc_path' |
297     );
298     close($fh)
299         or croak "Failed to close $real_inc_path: $!";
300 }
301
302 =head2 load
303
304 Does the actual schema-construction work.
305
306 =cut
307
308 sub load {
309     my $self = shift;
310
311     $self->_load_tables($self->_tables_list);
312 }
313
314 =head2 rescan
315
316 Rescan the database for newly added tables.  Does
317 not process drops or changes.
318
319 =cut
320
321 sub rescan {
322     my $self = shift;
323
324     my @created;
325     my @current = $self->_tables_list;
326     foreach my $table ($self->_tables_list) {
327         if(!exists $self->{_tables}->{$table}) {
328             push(@created, $table);
329         }
330     }
331
332     $self->_load_tables(@created);
333 }
334
335 sub _load_tables {
336     my ($self, @tables) = @_;
337
338     # First, use _tables_list with constraint and exclude
339     #  to get a list of tables to operate on
340
341     my $constraint   = $self->constraint;
342     my $exclude      = $self->exclude;
343
344     @tables = grep { /$constraint/ } @tables if $constraint;
345     @tables = grep { ! /$exclude/ } @tables if $exclude;
346
347     # Save the new tables to the tables list
348     push(@{$self->{_tables}}, @tables);
349
350     # Set up classes/monikers
351     {
352         no warnings 'redefine';
353         local *Class::C3::reinitialize = sub { };
354         use warnings;
355
356         $self->_make_src_class($_) for @tables;
357     }
358
359     Class::C3::reinitialize;
360
361     $self->_setup_src_meta($_) for @tables;
362
363     if(!$self->skip_relationships) {
364         $self->_load_relationships($_) for @tables;
365     }
366
367     $self->_load_external($_)
368         for map { $self->classes->{$_} } @tables;
369
370     $self->_dump_to_dir if $self->dump_directory;
371
372     # Drop temporary cache
373     delete $self->{_cache};
374
375     1;
376 }
377
378 sub _get_dump_filename {
379     my ($self, $class) = (@_);
380
381     $class =~ s{::}{/}g;
382     return $self->dump_directory . q{/} . $class . q{.pm};
383 }
384
385 sub _ensure_dump_subdirs {
386     my ($self, $class) = (@_);
387
388     my @name_parts = split(/::/, $class);
389     pop @name_parts; # we don't care about the very last element,
390                      # which is a filename
391
392     my $dir = $self->dump_directory;
393     while (1) {
394         if(!-d $dir) {
395             mkdir($dir) or croak "mkdir('$dir') failed: $!";
396         }
397         last if !@name_parts;
398         $dir = File::Spec->catdir($dir, shift @name_parts);
399     }
400 }
401
402 sub _dump_to_dir {
403     my ($self) = @_;
404
405     my $target_dir = $self->dump_directory;
406
407     my $schema_class = $self->schema_class;
408
409     croak "Must specify target directory for dumping!" if ! $target_dir;
410
411     warn "Dumping manual schema for $schema_class to directory $target_dir ...\n";
412
413     my $schema_text =
414           qq|package $schema_class;\n\n|
415         . qq|use strict;\nuse warnings;\n\n|
416         . qq|use base 'DBIx::Class::Schema';\n\n|
417         . qq|__PACKAGE__->load_classes;\n|;
418
419     $self->_write_classfile($schema_class, $schema_text);
420
421     foreach my $src_class (sort keys %{$self->{_dump_storage}}) {
422         my $src_text = 
423               qq|package $src_class;\n\n|
424             . qq|use strict;\nuse warnings;\n\n|
425             . qq|use base 'DBIx::Class';\n\n|;
426
427         $self->_write_classfile($src_class, $src_text);
428     }
429
430     warn "Schema dump completed.\n";
431 }
432
433 sub _write_classfile {
434     my ($self, $class, $text) = @_;
435
436     my $filename = $self->_get_dump_filename($class);
437     $self->_ensure_dump_subdirs($class);
438
439     if (-f $filename && $self->dump_overwrite) {
440         warn "Deleting existing file '$filename' due to "
441             . "'dump_overwrite' setting\n";
442         unlink($filename);
443     }    
444
445     my $custom_content = $self->_get_custom_content($class, $filename);
446
447     $custom_content ||= qq|\n# You can replace this text with custom|
448         . qq| content, and it will be preserved on regeneration|
449         . qq|\n1;\n|;
450
451     $text .= qq|$_\n|
452         for @{$self->{_dump_storage}->{$class} || []};
453
454     $text .= qq|\n\n# Created by DBIx::Class::Schema::Loader|
455         . qq| v| . $DBIx::Class::Schema::Loader::VERSION
456         . q| @ | . POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime)
457         . qq|\n# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:|;
458
459     open(my $fh, '>', $filename)
460         or croak "Cannot open '$filename' for writing: $!";
461
462     # Write the top half and its MD5 sum
463     print $fh $text . Digest::MD5::md5_base64($text) . "\n\n";
464
465     # Write out anything loaded via external partial class file in @INC
466     print $fh qq|$_\n|
467         for @{$self->{_ext_storage}->{$class} || []};
468
469     print $fh $custom_content;
470
471     close($fh)
472         or croak "Cannot close '$filename': $!";
473 }
474
475 sub _get_custom_content {
476     my ($self, $class, $filename) = @_;
477
478     return if ! -f $filename;
479     open(my $fh, '<', $filename)
480         or croak "Cannot open '$filename' for reading: $!";
481
482     my $mark_re = 
483         qr{^(# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:)([A-Za-z0-9/+]{22})\n};
484
485     my $found = 0;
486     my $buffer = '';
487     while(<$fh>) {
488         if(!$found && /$mark_re/) {
489             $found = 1;
490             $buffer .= $1;
491             croak "Checksum mismatch in '$filename'"
492                 if Digest::MD5::md5_base64($buffer) ne $2;
493
494             $buffer = '';
495         }
496         else {
497             $buffer .= $_;
498         }
499     }
500
501     croak "Cannot not overwrite '$filename' without 'dump_overwrite',"
502         . " it does not appear to have been generated by Loader"
503             if !$found;
504
505     return $buffer;
506 }
507
508 sub _use {
509     my $self = shift;
510     my $target = shift;
511     my $evalstr;
512
513     foreach (@_) {
514         warn "$target: use $_;" if $self->debug;
515         $self->_raw_stmt($target, "use $_;");
516         $_->require or croak ($_ . "->require: $@");
517         $evalstr .= "package $target; use $_;";
518     }
519     eval $evalstr if $evalstr;
520     croak $@ if $@;
521 }
522
523 sub _inject {
524     my $self = shift;
525     my $target = shift;
526     my $schema_class = $self->schema_class;
527
528     my $blist = join(q{ }, @_);
529     warn "$target: use base qw/ $blist /;" if $self->debug && @_;
530     $self->_raw_stmt($target, "use base qw/ $blist /;") if @_;
531     foreach (@_) {
532         $_->require or croak ($_ . "->require: $@");
533         $schema_class->inject_base($target, $_);
534     }
535 }
536
537 # Create class with applicable bases, setup monikers, etc
538 sub _make_src_class {
539     my ($self, $table) = @_;
540
541     my $schema       = $self->schema;
542     my $schema_class = $self->schema_class;
543
544     my $table_moniker = $self->_table2moniker($table);
545     my $table_class = $schema_class . q{::} . $table_moniker;
546
547     my $table_normalized = lc $table;
548     $self->classes->{$table} = $table_class;
549     $self->classes->{$table_normalized} = $table_class;
550     $self->monikers->{$table} = $table_moniker;
551     $self->monikers->{$table_normalized} = $table_moniker;
552
553     { no strict 'refs'; @{"${table_class}::ISA"} = qw/DBIx::Class/ }
554
555     $self->_use   ($table_class, @{$self->additional_classes});
556     $self->_inject($table_class, @{$self->additional_base_classes});
557
558     $self->_dbic_stmt($table_class, 'load_components', @{$self->components}, qw/PK::Auto Core/);
559
560     $self->_dbic_stmt($table_class, 'load_resultset_components', @{$self->resultset_components})
561         if @{$self->resultset_components};
562     $self->_inject($table_class, @{$self->left_base_classes});
563 }
564
565 # Set up metadata (cols, pks, etc) and register the class with the schema
566 sub _setup_src_meta {
567     my ($self, $table) = @_;
568
569     my $schema       = $self->schema;
570     my $schema_class = $self->schema_class;
571
572     my $table_class = $self->classes->{$table};
573     my $table_moniker = $self->monikers->{$table};
574
575     $self->_dbic_stmt($table_class,'table',$table);
576
577     my $cols = $self->_table_columns($table);
578     my $col_info;
579     eval { $col_info = $self->_columns_info_for($table) };
580     if($@) {
581         $self->_dbic_stmt($table_class,'add_columns',@$cols);
582     }
583     else {
584         my %col_info_lc = map { lc($_), $col_info->{$_} } keys %$col_info;
585         $self->_dbic_stmt(
586             $table_class,
587             'add_columns',
588             map { $_, ($col_info_lc{$_}||{}) } @$cols
589         );
590     }
591
592     my $pks = $self->_table_pk_info($table) || [];
593     @$pks ? $self->_dbic_stmt($table_class,'set_primary_key',@$pks)
594           : carp("$table has no primary key");
595
596     my $uniqs = $self->_table_uniq_info($table) || [];
597     $self->_dbic_stmt($table_class,'add_unique_constraint',@$_) for (@$uniqs);
598
599     $schema_class->register_class($table_moniker, $table_class);
600     $schema->register_class($table_moniker, $table_class) if $schema ne $schema_class;
601 }
602
603 =head2 tables
604
605 Returns a sorted list of loaded tables, using the original database table
606 names.
607
608 =cut
609
610 sub tables {
611     my $self = shift;
612
613     return keys %{$self->_tables};
614 }
615
616 # Make a moniker from a table
617 sub _table2moniker {
618     my ( $self, $table ) = @_;
619
620     my $moniker;
621
622     if( ref $self->moniker_map eq 'HASH' ) {
623         $moniker = $self->moniker_map->{$table};
624     }
625     elsif( ref $self->moniker_map eq 'CODE' ) {
626         $moniker = $self->moniker_map->($table);
627     }
628
629     $moniker ||= join '', map ucfirst, split /[\W_]+/, lc $table;
630
631     return $moniker;
632 }
633
634 sub _load_relationships {
635     my ($self, $table) = @_;
636
637     my $tbl_fk_info = $self->_table_fk_info($table);
638     foreach my $fkdef (@$tbl_fk_info) {
639         $fkdef->{remote_source} =
640             $self->monikers->{delete $fkdef->{remote_table}};
641     }
642
643     my $local_moniker = $self->monikers->{$table};
644     my $rel_stmts = $self->{relbuilder}->generate_code($local_moniker, $tbl_fk_info);
645
646     foreach my $src_class (sort keys %$rel_stmts) {
647         my $src_stmts = $rel_stmts->{$src_class};
648         foreach my $stmt (@$src_stmts) {
649             $self->_dbic_stmt($src_class,$stmt->{method},@{$stmt->{args}});
650         }
651     }
652 }
653
654 # Overload these in driver class:
655
656 # Returns an arrayref of column names
657 sub _table_columns { croak "ABSTRACT METHOD" }
658
659 # Returns arrayref of pk col names
660 sub _table_pk_info { croak "ABSTRACT METHOD" }
661
662 # Returns an arrayref of uniqs [ [ foo => [ col1, col2 ] ], [ bar => [ ... ] ] ]
663 sub _table_uniq_info { croak "ABSTRACT METHOD" }
664
665 # Returns an arrayref of foreign key constraints, each
666 #   being a hashref with 3 keys:
667 #   local_columns (arrayref), remote_columns (arrayref), remote_table
668 sub _table_fk_info { croak "ABSTRACT METHOD" }
669
670 # Returns an array of lower case table names
671 sub _tables_list { croak "ABSTRACT METHOD" }
672
673 # Execute a constructive DBIC class method, with debug/dump_to_dir hooks.
674 sub _dbic_stmt {
675     my $self = shift;
676     my $class = shift;
677     my $method = shift;
678
679     if(!$self->debug && !$self->dump_directory) {
680         $class->$method(@_);
681         return;
682     }
683
684     my $args = dump(@_);
685     $args = '(' . $args . ')' if @_ < 2;
686     my $stmt = $method . $args . q{;};
687
688     warn qq|$class\->$stmt\n| if $self->debug;
689     $class->$method(@_);
690     $self->_raw_stmt($class, '__PACKAGE__->' . $stmt);
691 }
692
693 # Store a raw source line for a class (for dumping purposes)
694 sub _raw_stmt {
695     my ($self, $class, $stmt) = @_;
696     push(@{$self->{_dump_storage}->{$class}}, $stmt) if $self->dump_directory;
697 }
698
699 # Like above, but separately for the externally loaded stuff
700 sub _ext_stmt {
701     my ($self, $class, $stmt) = @_;
702     push(@{$self->{_ext_storage}->{$class}}, $stmt) if $self->dump_directory;
703 }
704
705 =head2 monikers
706
707 Returns a hashref of loaded table to moniker mappings.  There will
708 be two entries for each table, the original name and the "normalized"
709 name, in the case that the two are different (such as databases
710 that like uppercase table names, or preserve your original mixed-case
711 definitions, or what-have-you).
712
713 =head2 classes
714
715 Returns a hashref of table to class mappings.  In some cases it will
716 contain multiple entries per table for the original and normalized table
717 names, as above in L</monikers>.
718
719 =head1 SEE ALSO
720
721 L<DBIx::Class::Schema::Loader>
722
723 =cut
724
725 1;