release 0.07023
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader.pm
CommitLineData
18fca96a 1package DBIx::Class::Schema::Loader;
a78e3fed 2
3use strict;
a4a19f3c 4use warnings;
65e705c3 5use base qw/DBIx::Class::Schema Class::Accessor::Grouped/;
cc15b78f 6use MRO::Compat;
942bd5e0 7use mro 'c3';
39d5612f 8use Carp::Clan qw/^DBIx::Class/;
9use Scalar::Util 'weaken';
cc15b78f 10use Sub::Name 'subname';
50b95db6 11use DBIx::Class::Schema::Loader::Utils 'array_eq';
23fd9773 12use Try::Tiny;
13use Hash::Merge 'merge';
39d5612f 14use namespace::clean;
3980d69c 15
a4a19f3c 16# Always remember to do all digits for the version even if they're 0
17# i.e. first release of 0.XX *must* be 0.XX000. This avoids fBSD ports
18# brain damage and presumably various other packaging systems too
835a650f 19our $VERSION = '0.07023';
457eb8a6 20
65e705c3 21__PACKAGE__->mk_group_accessors('inherited', qw/
22 _loader_args
23 dump_to_dir
24 _loader_invoked
25 _loader
26 loader_class
27 naming
f22644d7 28 use_namespaces
a8d229ff 29/);
65e705c3 30__PACKAGE__->_loader_args({});
a78e3fed 31
32=head1 NAME
33
227cea92 34DBIx::Class::Schema::Loader - Create a DBIx::Class::Schema based on a database
a78e3fed 35
36=head1 SYNOPSIS
37
707fb247 38 ### use this module to generate a set of class files
39
40 # in a script
41 use DBIx::Class::Schema::Loader qw/ make_schema_at /;
42 make_schema_at(
43 'My::Schema',
44 { debug => 1,
45 dump_directory => './lib',
46 },
35a87f06 47 [ 'dbi:Pg:dbname="foo"', 'myuser', 'mypassword',
48 { loader_class => 'MyLoader' } # optionally
49 ],
707fb247 50 );
51
52 # from the command line or a shell script with dbicdump (distributed
53 # with this module). Do `perldoc dbicdump` for usage.
54 dbicdump -o dump_directory=./lib \
227cea92 55 -o components='["InflateColumn::DateTime"]' \
707fb247 56 -o debug=1 \
57 My::Schema \
58 'dbi:Pg:dbname=foo' \
59 myuser \
60 mypassword
61
62 ### or generate and load classes at runtime
63 # note: this technique is not recommended
64 # for use in production code
65
a4a19f3c 66 package My::Schema;
67 use base qw/DBIx::Class::Schema::Loader/;
a78e3fed 68
996be9ee 69 __PACKAGE__->loader_options(
996be9ee 70 constraint => '^foo.*',
71 # debug => 1,
a78e3fed 72 );
af6c2665 73
707fb247 74 #### in application code elsewhere:
a78e3fed 75
a4a19f3c 76 use My::Schema;
a78e3fed 77
a4a19f3c 78 my $schema1 = My::Schema->connect( $dsn, $user, $password, $attrs);
79 # -or-
996be9ee 80 my $schema1 = "My::Schema"; $schema1->connection(as above);
074e81cd 81
996be9ee 82=head1 DESCRIPTION
074e81cd 83
fbd83464 84DBIx::Class::Schema::Loader automates the definition of a
227cea92 85L<DBIx::Class::Schema> by scanning database table definitions and setting up
86the columns, primary keys, unique constraints and relationships.
a78e3fed 87
700658a5 88See L<dbicdump> for the C<dbicdump> utility.
89
227cea92 90DBIx::Class::Schema::Loader currently supports only the DBI storage type. It
1065db64 91has explicit support for L<DBD::Pg>, L<DBD::mysql>, L<DBD::DB2>,
227cea92 92L<DBD::Firebird>, L<DBD::InterBase>, L<DBD::Informix>, L<DBD::SQLAnywhere>,
6b0e47fc 93L<DBD::SQLite>, L<DBD::Sybase> (for Sybase ASE and MSSSQL), L<DBD::ODBC> (for
227cea92 94MSSQL, MSAccess, Firebird and SQL Anywhere) L<DBD::ADO> (for MSSQL and
95MSAccess) and L<DBD::Oracle>. Other DBI drivers may function to a greater or
6b0e47fc 96lesser degree with this loader, depending on how much of the DBI spec they
97implement, and how standard their implementation is.
3fe9c5d9 98
99Patches to make other DBDs work correctly welcome.
a78e3fed 100
996be9ee 101See L<DBIx::Class::Schema::Loader::DBI::Writing> for notes on writing
102your own vendor-specific subclass for an unsupported DBD driver.
a78e3fed 103
227cea92 104This module requires L<DBIx::Class> 0.08127 or later, and obsoletes the older
105L<DBIx::Class::Loader>.
89ecd854 106
227cea92 107See L<DBIx::Class::Schema::Loader::Base> for available options.
89ecd854 108
a78e3fed 109=head1 METHODS
110
39d5612f 111=head2 loader
112
227cea92 113The loader object, as class data on your Schema. For methods available see
114L<DBIx::Class::Schema::Loader::Base> and L<DBIx::Class::Schema::Loader::DBI>.
39d5612f 115
116=cut
117
118sub loader {
119 my $self = shift;
120 $self->_loader(@_);
121}
122
29ddb54c 123=head2 loader_class
124
530e0bf6 125=over 4
126
127=item Argument: $loader_class
128
129=back
130
29ddb54c 131Set the loader class to be instantiated when L</connection> is called.
132If the classname starts with "::", "DBIx::Class::Schema::Loader" is
133prepended. Defaults to L<DBIx::Class::Schema/storage_type> (which must
134start with "::" when using L<DBIx::Class::Schema::Loader>).
135
136This is mostly useful for subclassing existing loaders or in conjunction
137with L</dump_to_dir>.
138
996be9ee 139=head2 loader_options
a78e3fed 140
530e0bf6 141=over 4
142
143=item Argument: \%loader_options
144
145=back
146
996be9ee 147Example in Synopsis above demonstrates a few common arguments. For
148detailed information on all of the arguments, most of which are
149only useful in fairly complex scenarios, see the
150L<DBIx::Class::Schema::Loader::Base> documentation.
a78e3fed 151
3fe9c5d9 152If you intend to use C<loader_options>, you must call
153C<loader_options> before any connection is made, or embed the
154C<loader_options> in the connection information itself as shown
155below. Setting C<loader_options> after the connection has
59cfa251 156already been made is useless.
a78e3fed 157
996be9ee 158=cut
1031d4f6 159
996be9ee 160sub loader_options {
161 my $self = shift;
65e705c3 162
d65cda9e 163 my %args = (ref $_[0] eq 'HASH') ? %{$_[0]} : @_;
996be9ee 164 $self->_loader_args(\%args);
996be9ee 165
166 $self;
167}
168
169sub _invoke_loader {
170 my $self = shift;
171 my $class = ref $self || $self;
172
59cfa251 173 my $args = $self->_loader_args;
174
23fd9773 175 # temporarily copy $self's storage to class
176 my $class_storage = $class->storage;
177 if (ref $self) {
178 $class->storage($self->storage);
179 $class->storage->set_schema($class);
180 }
181
182 $args->{schema} = $class;
59cfa251 183 $args->{schema_class} = $class;
59cfa251 184 $args->{dump_directory} ||= $self->dump_to_dir;
a0e0a56a 185 $args->{naming} = $self->naming if $self->naming;
42ea7b88 186 $args->{use_namespaces} = $self->use_namespaces if defined $self->use_namespaces;
af6c2665 187
996be9ee 188 # XXX this only works for relative storage_type, like ::DBI ...
71a6e88a 189 my $loader_class = $self->loader_class;
190 if ($loader_class) {
191 $loader_class = "DBIx::Class::Schema::Loader${loader_class}" if $loader_class =~ /^::/;
192 $args->{loader_class} = $loader_class;
193 };
194
195 my $impl = $loader_class || "DBIx::Class::Schema::Loader" . $self->storage_type;
23fd9773 196 try {
197 $self->ensure_class_loaded($impl)
198 }
199 catch {
200 croak qq/Could not load loader_class "$impl": "$_"/;
201 };
af6c2665 202
23fd9773 203 $class->loader($impl->new(%$args));
204 $class->loader->load;
205 $class->_loader_invoked(1);
996be9ee 206
23fd9773 207 # copy to $self
208 if (ref $self) {
209 $self->loader($class->loader);
210 $self->_loader_invoked(1);
211
212 $self->_merge_state_from($class);
213 }
214
215 # restore $class's storage
216 $class->storage($class_storage);
217
218 return $self;
219}
220
221# FIXME This needs to be moved into DBIC at some point, otherwise we are
222# maintaining things to do with DBIC guts, which we have no business of
223# maintaining. But at the moment it would be just dead code in DBIC, so we'll
224# maintain it here.
225sub _merge_state_from {
226 my ($self, $from) = @_;
227
228 my $orig_class_mappings = $self->class_mappings;
229 my $orig_source_registrations = $self->source_registrations;
230
231 $self->_copy_state_from($from);
232
233 $self->class_mappings(merge($orig_class_mappings, $self->class_mappings))
234 if $orig_class_mappings;
235
236 $self->source_registrations(merge($orig_source_registrations, $self->source_registrations))
237 if $orig_source_registrations;
238}
239
240sub _copy_state_from {
241 my $self = shift;
242 my ($from) = @_;
243
244 # older DBIC's do not have this method
245 if (try { DBIx::Class->VERSION('0.08197'); 1 }) {
246 return $self->next::method(@_);
247 }
248 else {
249 # this is a copy from DBIC git master pre 0.08197
250 $self->class_mappings({ %{$from->class_mappings} });
251 $self->source_registrations({ %{$from->source_registrations} });
252
253 foreach my $moniker ($from->sources) {
254 my $source = $from->source($moniker);
255 my $new = $source->new($source);
256 # we use extra here as we want to leave the class_mappings as they are
257 # but overwrite the source_registrations entry with the new source
258 $self->register_extra_source($moniker => $new);
259 }
260
261 if ($from->storage) {
262 $self->storage($from->storage);
263 $self->storage->set_schema($self);
264 }
265 }
996be9ee 266}
267
268=head2 connection
269
530e0bf6 270=over 4
271
272=item Arguments: @args
273
274=item Return Value: $new_schema
275
276=back
277
278See L<DBIx::Class::Schema/connection> for basic usage.
d65cda9e 279
29ddb54c 280If the final argument is a hashref, and it contains the keys C<loader_options>
281or C<loader_class>, those keys will be deleted, and their values value will be
282used for the loader options or class, respectively, just as if set via the
283L</loader_options> or L</loader_class> methods above.
d65cda9e 284
285The actual auto-loading operation (the heart of this module) will be invoked
286as soon as the connection information is defined.
996be9ee 287
288=cut
289
290sub connection {
de89deba 291 my $self = shift;
292 my $class = ref $self || $self;
d65cda9e 293
294 if($_[-1] && ref $_[-1] eq 'HASH') {
cc15b78f 295 for my $option (qw/loader_class loader_options/) {
29ddb54c 296 if(my $value = delete $_[-1]->{$option}) {
297 $self->$option($value);
298 }
d65cda9e 299 }
29ddb54c 300 pop @_ if !keys %{$_[-1]};
d65cda9e 301 }
302
cc15b78f 303 # Make sure we inherit from schema_base_class and load schema_components
304 # before connecting.
305 require DBIx::Class::Schema::Loader::Base;
306 my $temp_loader = DBIx::Class::Schema::Loader::Base->new(
a2c2cf69 307 %{ $self->_loader_args },
308 schema => $self,
b7b8c970 309 naming => 'current',
310 use_namespaces => 1,
cc15b78f 311 );
312
50b95db6 313 my $modify_isa = 0;
314 my @components;
315
cc15b78f 316 if ($temp_loader->schema_base_class || $temp_loader->schema_components) {
50b95db6 317 @components = @{ $temp_loader->schema_components }
cc15b78f 318 if $temp_loader->schema_components;
319
320 push @components, ('+'.$temp_loader->schema_base_class)
321 if $temp_loader->schema_base_class;
322
50b95db6 323 my $class_isa = do {
324 no strict 'refs';
325 \@{"${class}::ISA"};
326 };
327
328 my @component_classes = map {
329 /^\+/ ? substr($_, 1, length($_) - 1) : "DBIx::Class::$_"
330 } @components;
331
332 $modify_isa++ if not array_eq([ @$class_isa[0..(@components-1)] ], \@component_classes)
333 }
334
335 if ($modify_isa) {
de89deba 336 $class->load_components(@components);
cc15b78f 337
a1781f7f 338 # This hack is necessary because we changed @ISA of $self through
de89deba 339 # ->load_components and we are now in a different place in the mro.
cc15b78f 340 no warnings 'redefine';
341
342 local *connection = subname __PACKAGE__.'::connection' => sub {
343 my $self = shift;
344 $self->next::method(@_);
345 };
346
de89deba 347 my @linear_isa = @{ mro::get_linear_isa($class) };
348
349 my $next_method;
350
351 foreach my $i (1..$#linear_isa) {
352 no strict 'refs';
353 $next_method = *{$linear_isa[$i].'::connection'}{CODE};
354 last if $next_method;
355 }
356
357 $self = $self->$next_method(@_);
cc15b78f 358 }
a1781f7f 359 else {
360 $self = $self->next::method(@_);
361 }
996be9ee 362
59cfa251 363 if(!$class->_loader_invoked) {
fa994d3c 364 $self->_invoke_loader
365 }
996be9ee 366
367 return $self;
368}
369
370=head2 clone
371
530e0bf6 372See L<DBIx::Class::Schema/clone>.
996be9ee 373
374=cut
375
376sub clone {
377 my $self = shift;
378
379 my $clone = $self->next::method(@_);
380
fa994d3c 381 if($clone->_loader_args) {
382 $clone->_loader_args->{schema} = $clone;
383 weaken($clone->_loader_args->{schema});
384 }
996be9ee 385
386 $clone;
387}
388
389=head2 dump_to_dir
390
530e0bf6 391=over 4
392
393=item Argument: $directory
394
395=back
996be9ee 396
397Calling this as a class method on either L<DBIx::Class::Schema::Loader>
707fb247 398or any derived schema class will cause all schemas to dump
996be9ee 399manual versions of themselves to the named directory when they are
400loaded. In order to be effective, this must be set before defining a
401connection on this schema class or any derived object (as the loading
074e81cd 402happens as soon as both a connection and loader_options are set, and
403only once per class).
996be9ee 404
405See L<DBIx::Class::Schema::Loader::Base/dump_directory> for more
406details on the dumping mechanism.
407
408This can also be set at module import time via the import option
409C<dump_to_dir:/foo/bar> to L<DBIx::Class::Schema::Loader>, where
410C</foo/bar> is the target directory.
411
412Examples:
413
414 # My::Schema isa DBIx::Class::Schema::Loader, and has connection info
415 # hardcoded in the class itself:
416 perl -MDBIx::Class::Schema::Loader=dump_to_dir:/foo/bar -MMy::Schema -e1
417
418 # Same, but no hard-coded connection, so we must provide one:
419 perl -MDBIx::Class::Schema::Loader=dump_to_dir:/foo/bar -MMy::Schema -e 'My::Schema->connection("dbi:Pg:dbname=foo", ...)'
420
421 # Or as a class method, as long as you get it done *before* defining a
422 # connection on this schema class or any derived object:
423 use My::Schema;
424 My::Schema->dump_to_dir('/foo/bar');
425 My::Schema->connection(........);
426
427 # Or as a class method on the DBIx::Class::Schema::Loader itself, which affects all
428 # derived schemas
429 use My::Schema;
430 use My::OtherSchema;
431 DBIx::Class::Schema::Loader->dump_to_dir('/foo/bar');
432 My::Schema->connection(.......);
433 My::OtherSchema->connection(.......);
434
435 # Another alternative to the above:
436 use DBIx::Class::Schema::Loader qw| dump_to_dir:/foo/bar |;
437 use My::Schema;
438 use My::OtherSchema;
439 My::Schema->connection(.......);
440 My::OtherSchema->connection(.......);
441
442=cut
443
444sub import {
445 my $self = shift;
a8d229ff 446
996be9ee 447 return if !@_;
a8d229ff 448
449 my $cpkg = (caller)[0];
450
996be9ee 451 foreach my $opt (@_) {
452 if($opt =~ m{^dump_to_dir:(.*)$}) {
453 $self->dump_to_dir($1)
454 }
455 elsif($opt eq 'make_schema_at') {
456 no strict 'refs';
996be9ee 457 *{"${cpkg}::make_schema_at"} = \&make_schema_at;
458 }
a8d229ff 459 elsif($opt eq 'naming') {
460 no strict 'refs';
461 *{"${cpkg}::naming"} = sub { $self->naming(@_) };
462 }
f22644d7 463 elsif($opt eq 'use_namespaces') {
464 no strict 'refs';
465 *{"${cpkg}::use_namespaces"} = sub { $self->use_namespaces(@_) };
466 }
996be9ee 467 }
468}
469
470=head2 make_schema_at
471
530e0bf6 472=over 4
473
707fb247 474=item Arguments: $schema_class_name, \%loader_options, \@connect_info
530e0bf6 475
707fb247 476=item Return Value: $schema_class_name
530e0bf6 477
478=back
479
707fb247 480This function creates a DBIx::Class schema from an existing RDBMS
481schema. With the C<dump_directory> option, generates a set of
482DBIx::Class classes from an existing database schema read from the
483given dsn. Without a C<dump_directory>, creates schema classes in
484memory at runtime without generating on-disk class files.
996be9ee 485
707fb247 486For a complete list of supported loader_options, see
487L<DBIx::Class::Schema::Loader::Base>
483987b9 488
35a87f06 489The last hashref in the C<\@connect_info> can specify the L</loader_class>.
490
707fb247 491This function can be imported in the usual way, as illustrated in
492these Examples:
996be9ee 493
5223f24a 494 # Simple example, creates as a new class 'New::Schema::Name' in
495 # memory in the running perl interpreter.
996be9ee 496 use DBIx::Class::Schema::Loader qw/ make_schema_at /;
497 make_schema_at(
498 'New::Schema::Name',
59cfa251 499 { debug => 1 },
35a87f06 500 [ 'dbi:Pg:dbname="foo"','postgres','',
501 { loader_class => 'MyLoader' } # optionally
502 ],
996be9ee 503 );
504
707fb247 505 # Inside a script, specifying a dump directory in which to write
506 # class files
996be9ee 507 use DBIx::Class::Schema::Loader qw/ make_schema_at /;
508 make_schema_at(
509 'New::Schema::Name',
59cfa251 510 { debug => 1, dump_directory => './lib' },
35a87f06 511 [ 'dbi:Pg:dbname="foo"','postgres','',
512 { loader_class => 'MyLoader' } # optionally
513 ],
996be9ee 514 );
515
b486b265 516The last hashref in the C<\@connect_info> is checked for loader arguments such
517as C<loader_options> and C<loader_class>, see L</connection> for more details.
518
996be9ee 519=cut
520
521sub make_schema_at {
522 my ($target, $opts, $connect_info) = @_;
523
483987b9 524 {
525 no strict 'refs';
526 @{$target . '::ISA'} = qw/DBIx::Class::Schema::Loader/;
527 }
528
23fd9773 529 $target->_loader_invoked(0);
71a6e88a 530
483987b9 531 $target->loader_options($opts);
23fd9773 532
533 my $temp_schema = $target->connect(@$connect_info);
534
535 $target->storage($temp_schema->storage);
536 $target->storage->set_schema($target);
537
538 return $target;
996be9ee 539}
540
b97c2c1e 541=head2 rescan
542
530e0bf6 543=over 4
544
545=item Return Value: @new_monikers
546
547=back
548
b97c2c1e 549Re-scans the database for newly added tables since the initial
550load, and adds them to the schema at runtime, including relationships,
551etc. Does not process drops or changes.
552
a60b5b8d 553Returns a list of the new monikers added.
554
b97c2c1e 555=cut
556
39d5612f 557sub rescan { my $self = shift; $self->loader->rescan($self) }
b97c2c1e 558
a8d229ff 559=head2 naming
560
561=over 4
562
563=item Arguments: \%opts | $ver
564
565=back
566
567Controls the naming options for backward compatibility, see
568L<DBIx::Class::Schema::Loader::Base/naming> for details.
569
570To upgrade a dynamic schema, use:
571
572 __PACKAGE__->naming('current');
573
574Can be imported into your dump script and called as a function as well:
575
576 naming('v4');
996be9ee 577
f22644d7 578=head2 use_namespaces
579
580=over 4
581
582=item Arguments: 1|0
583
584=back
585
586Controls the use_namespaces options for backward compatibility, see
587L<DBIx::Class::Schema::Loader::Base/use_namespaces> for details.
588
589To upgrade a dynamic schema, use:
590
591 __PACKAGE__->use_namespaces(1);
592
593Can be imported into your dump script and called as a function as well:
594
595 use_namespaces(1);
596
996be9ee 597=head1 KNOWN ISSUES
598
599=head2 Multiple Database Schemas
600
c4a69b87 601See L<DBIx::Class::Schema::Loader::Base/db_schema>.
89ecd854 602
be80bba7 603=head1 ACKNOWLEDGEMENTS
a78e3fed 604
be80bba7 605Matt S Trout, all of the #dbix-class folks, and everyone who's ever sent
606in a bug report or suggestion.
fbd83464 607
8a6b44ef 608Based on L<DBIx::Class::Loader> by Sebastian Riedel
a78e3fed 609
610Based upon the work of IKEBE Tomohiro
611
be80bba7 612=head1 AUTHOR
a78e3fed 613
be80bba7 614blblack: Brandon Black <blblack@gmail.com>
615
616=head1 CONTRIBUTORS
617
a41f1fd4 618ilmari: Dagfinn Ilmari MannsE<aring>ker <ilmari@ilmari.org>
be80bba7 619
620arcanez: Justin Hunter <justin.d.hunter@gmail.com>
621
622ash: Ash Berlin <ash@cpan.org>
623
412638fa 624btilly: Ben Tilly <btilly@gmail.com>
59388920 625
be80bba7 626Caelum: Rafael Kitover <rkitover@cpan.org>
627
628TSUNODA Kazuya <drk@drk7.jp>
629
f84a7413 630rbo: Robert Bohne <rbo@cpan.org>
be80bba7 631
69fca474 632ribasushi: Peter Rabbitson <ribasushi@cpan.org>
1f625792 633
fdd8ff16 634gugu: Andrey Kostenko <a.kostenko@rambler-co.ru>
635
65e705c3 636jhannah: Jay Hannah <jay@jays.net>
637
07307014 638jnap: John Napiorkowski <jjn1056@yahoo.com>
639
7b505bbd 640rbuels: Robert Buels <rmb32@cornell.edu>
641
accc9e96 642timbunce: Tim Bunce <timb@cpan.org>
da21e0cf 643
c21bfb92 644mst: Matt S. Trout <mst@shadowcatsystems.co.uk>
645
d36c8734 646mstratman: Mark A. Stratman <stratman@gmail.com>
647
827dff19 648kane: Jos Boumans <kane@cpan.org>
649
43b982ea 650waawaamilk: Nigel McNie <nigel@mcnie.name>
651
96f68869 652acmoore: Andrew Moore <amoore@cpan.org>
653
2a5dcfb3 654bphillips: Brian Phillips <bphillips@cpan.org>
655
8763ffda 656schwern: Michael G. Schwern <mschwern@cpan.org>
657
c899395b 658SineSwiper: Brendan Byrd <byrd.b@insightcom.com>
659
9fd0726a 660hobbs: Andrew Rodland <arodland@cpan.org>
661
c9cf9b4d 662domm: Thomas Klausner <domm@plix.at>
663
12333562 664spb: Stephen Bennett <spb@exherbo.org>
665
71687093 666Matias E. Fernandez <mfernandez@pisco.ch>
667
3bb5544f 668alnewkirk: Al Newkirk <awncorp@cpan.org>
07f39b47 669
006c8ed3 670angelixd: Paul C. Mantz <pcmantz@cpan.org>
671
be80bba7 672... and lots of other folks. If we forgot you, please write the current
673maintainer or RT.
a78e3fed 674
9cc8e7e1 675=head1 COPYRIGHT & LICENSE
676
677Copyright (c) 2006 - 2009 by the aforementioned
678L<DBIx::Class::Schema::Loader/AUTHOR> and
679L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
a78e3fed 680
681This library is free software; you can redistribute it and/or modify it under
682the same terms as Perl itself.
683
684=head1 SEE ALSO
685
cb6407d7 686L<DBIx::Class>, L<DBIx::Class::Manual::Intro>, L<DBIx::Class::Tutorial>,
687L<DBIx::Class::Schema::Loader::Base>
a78e3fed 688
689=cut
690
6911;
71a6e88a 692# vim:et sts=4 sw=4 tw=0: