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