Merge 'load_namespaces' into 'DBIx-Class-current'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema.pm
1 package DBIx::Class::Schema;
2
3 use strict;
4 use warnings;
5
6 use Carp::Clan qw/^DBIx::Class/;
7 use Scalar::Util qw/weaken/;
8 require Module::Find;
9
10 use base qw/DBIx::Class/;
11
12 __PACKAGE__->mk_classdata('class_mappings' => {});
13 __PACKAGE__->mk_classdata('source_registrations' => {});
14 __PACKAGE__->mk_classdata('storage_type' => '::DBI');
15 __PACKAGE__->mk_classdata('storage');
16 __PACKAGE__->mk_classdata('exception_action');
17
18 =head1 NAME
19
20 DBIx::Class::Schema - composable schemas
21
22 =head1 SYNOPSIS
23
24   package Library::Schema;
25   use base qw/DBIx::Class::Schema/;
26
27   # load Library::Schema::CD, Library::Schema::Book, Library::Schema::DVD
28   __PACKAGE__->load_classes(qw/CD Book DVD/);
29
30   package Library::Schema::CD;
31   use base qw/DBIx::Class/;
32   __PACKAGE__->load_components(qw/PK::Auto Core/); # for example
33   __PACKAGE__->table('cd');
34
35   # Elsewhere in your code:
36   my $schema1 = Library::Schema->connect(
37     $dsn,
38     $user,
39     $password,
40     { AutoCommit => 0 },
41   );
42
43   my $schema2 = Library::Schema->connect($coderef_returning_dbh);
44
45   # fetch objects using Library::Schema::DVD
46   my $resultset = $schema1->resultset('DVD')->search( ... );
47   my @dvd_objects = $schema2->resultset('DVD')->search( ... );
48
49 =head1 DESCRIPTION
50
51 Creates database classes based on a schema. This is the recommended way to
52 use L<DBIx::Class> and allows you to use more than one concurrent connection
53 with your classes.
54
55 NB: If you're used to L<Class::DBI> it's worth reading the L</SYNOPSIS>
56 carefully, as DBIx::Class does things a little differently. Note in
57 particular which module inherits off which.
58
59 =head1 METHODS
60
61 =head2 register_class
62
63 =over 4
64
65 =item Arguments: $moniker, $component_class
66
67 =back
68
69 Registers a class which isa DBIx::Class::ResultSourceProxy. Equivalent to
70 calling:
71
72   $schema->register_source($moniker, $component_class->result_source_instance);
73
74 =cut
75
76 sub register_class {
77   my ($self, $moniker, $to_register) = @_;
78   $self->register_source($moniker => $to_register->result_source_instance);
79 }
80
81 =head2 register_source
82
83 =over 4
84
85 =item Arguments: $moniker, $result_source
86
87 =back
88
89 Registers the L<DBIx::Class::ResultSource> in the schema with the given
90 moniker.
91
92 =cut
93
94 sub register_source {
95   my ($self, $moniker, $source) = @_;
96   my %reg = %{$self->source_registrations};
97   $reg{$moniker} = $source;
98   $self->source_registrations(\%reg);
99   $source->schema($self);
100   weaken($source->{schema}) if ref($self);
101   if ($source->result_class) {
102     my %map = %{$self->class_mappings};
103     $map{$source->result_class} = $moniker;
104     $self->class_mappings(\%map);
105   }
106 }
107
108 =head2 class
109
110 =over 4
111
112 =item Arguments: $moniker
113
114 =item Return Value: $classname
115
116 =back
117
118 Retrieves the result class name for the given moniker. For example:
119
120   my $class = $schema->class('CD');
121
122 =cut
123
124 sub class {
125   my ($self, $moniker) = @_;
126   return $self->source($moniker)->result_class;
127 }
128
129 =head2 source
130
131 =over 4
132
133 =item Arguments: $moniker
134
135 =item Return Value: $result_source
136
137 =back
138
139   my $source = $schema->source('Book');
140
141 Returns the L<DBIx::Class::ResultSource> object for the registered moniker.
142
143 =cut
144
145 sub source {
146   my ($self, $moniker) = @_;
147   my $sreg = $self->source_registrations;
148   return $sreg->{$moniker} if exists $sreg->{$moniker};
149
150   # if we got here, they probably passed a full class name
151   my $mapped = $self->class_mappings->{$moniker};
152   $self->throw_exception("Can't find source for ${moniker}")
153     unless $mapped && exists $sreg->{$mapped};
154   return $sreg->{$mapped};
155 }
156
157 =head2 sources
158
159 =over 4
160
161 =item Return Value: @source_monikers
162
163 =back
164
165 Returns the source monikers of all source registrations on this schema.
166 For example:
167
168   my @source_monikers = $schema->sources;
169
170 =cut
171
172 sub sources { return keys %{shift->source_registrations}; }
173
174 =head2 resultset
175
176 =over 4
177
178 =item Arguments: $moniker
179
180 =item Return Value: $result_set
181
182 =back
183
184   my $rs = $schema->resultset('DVD');
185
186 Returns the L<DBIx::Class::ResultSet> object for the registered moniker.
187
188 =cut
189
190 sub resultset {
191   my ($self, $moniker) = @_;
192   return $self->source($moniker)->resultset;
193 }
194
195 =head2 load_classes
196
197 =over 4
198
199 =item Arguments: @classes?, { $namespace => [ @classes ] }+
200
201 =back
202
203 With no arguments, this method uses L<Module::Find> to find all classes under
204 the schema's namespace. Otherwise, this method loads the classes you specify
205 (using L<use>), and registers them (using L</"register_class">).
206
207 It is possible to comment out classes with a leading C<#>, but note that perl
208 will think it's a mistake (trying to use a comment in a qw list), so you'll
209 need to add C<no warnings 'qw';> before your load_classes call.
210
211 Example:
212
213   My::Schema->load_classes(); # loads My::Schema::CD, My::Schema::Artist,
214                               # etc. (anything under the My::Schema namespace)
215
216   # loads My::Schema::CD, My::Schema::Artist, Other::Namespace::Producer but
217   # not Other::Namespace::LinerNotes nor My::Schema::Track
218   My::Schema->load_classes(qw/ CD Artist #Track /, {
219     Other::Namespace => [qw/ Producer #LinerNotes /],
220   });
221
222 =cut
223
224 sub load_classes {
225   my ($class, @params) = @_;
226
227   my %comps_for;
228
229   if (@params) {
230     foreach my $param (@params) {
231       if (ref $param eq 'ARRAY') {
232         # filter out commented entries
233         my @modules = grep { $_ !~ /^#/ } @$param;
234
235         push (@{$comps_for{$class}}, @modules);
236       }
237       elsif (ref $param eq 'HASH') {
238         # more than one namespace possible
239         for my $comp ( keys %$param ) {
240           # filter out commented entries
241           my @modules = grep { $_ !~ /^#/ } @{$param->{$comp}};
242
243           push (@{$comps_for{$comp}}, @modules);
244         }
245       }
246       else {
247         # filter out commented entries
248         push (@{$comps_for{$class}}, $param) if $param !~ /^#/;
249       }
250     }
251   } else {
252     my @comp = map { substr $_, length "${class}::"  }
253                  Module::Find::findallmod($class);
254     $comps_for{$class} = \@comp;
255   }
256
257   my @to_register;
258   {
259     no warnings qw/redefine/;
260     local *Class::C3::reinitialize = sub { };
261     foreach my $prefix (keys %comps_for) {
262       foreach my $comp (@{$comps_for{$prefix}||[]}) {
263         my $comp_class = "${prefix}::${comp}";
264         $class->ensure_class_loaded($comp_class);
265         $comp_class->source_name($comp) unless $comp_class->source_name;
266
267         push(@to_register, [ $comp_class->source_name, $comp_class ]);
268       }
269     }
270   }
271   Class::C3->reinitialize;
272
273   foreach my $to (@to_register) {
274     $class->register_class(@$to);
275     #  if $class->can('result_source_instance');
276   }
277 }
278
279 =head2 load_namespaces
280
281 =over 4
282
283 =item Arguments: %options?
284
285 =back
286
287 This is an alternative to L</load_classes> above which assumes an alternative
288 layout for automatic class loading.  It assumes that all result
289 classes are underneath a sub-namespace of the schema called C<Result>, any
290 corresponding ResultSet classes are underneath a sub-namespace of the schema
291 called C<ResultSet>.
292
293 Both of the sub-namespaces are configurable if you don't like the defaults,
294 via the options C<result_namespace> and C<resultset_namespace>.
295
296 If (and only if) you specify the option C<default_resultset_class>, any found
297 Result classes for which we do not find a corresponding
298 ResultSet class will have their C<resultset_class> set to
299 C<default_resultset_class>.
300
301 C<load_namespaces> takes care of calling C<resultset_class> for you where
302 neccessary if you didn't do it for yourself.
303
304 All of the namespace and classname options to this method are relative to
305 the schema classname by default.  To specify a fully-qualified name, prefix
306 it with a literal C<+>.
307
308 Examples:
309
310   # load My::Schema::Result::CD, My::Schema::Result::Artist,
311   #    My::Schema::ResultSet::CD, etc...
312   My::Schema->load_namespaces;
313
314   # Override everything to use ugly names.
315   # In this example, if there is a My::Schema::Res::Foo, but no matching
316   #   My::Schema::RSets::Foo, then Foo will have its
317   #   resultset_class set to My::Schema::RSetBase
318   My::Schema->load_namespaces(
319     result_namespace => 'Res',
320     resultset_namespace => 'RSets',
321     default_resultset_class => 'RSetBase',
322   );
323
324   # Put things in other namespaces
325   My::Schema->load_namespaces(
326     result_namespace => '+Some::Place::Results',
327     resultset_namespace => '+Another::Place::RSets',
328   );
329
330 If you'd like to use multiple namespaces of each type, simply use an arrayref
331 of namespaces for that option.  In the case that the same result
332 (or resultset) class exists in multiple namespaces, the latter entries in
333 your list of namespaces will override earlier ones.
334
335   My::Schema->load_namespaces(
336     # My::Schema::Results_C::Foo takes precedence over My::Schema::Results_B::Foo :
337     result_namespace => [ 'Results_A', 'Results_B', 'Results_C' ],
338     resultset_namespace => [ '+Some::Place::RSets', 'RSets' ],
339   );
340
341 =cut
342
343 # Pre-pends our classname to the given relative classname or
344 #   class namespace, unless there is a '+' prefix, which will
345 #   be stripped.
346 sub _expand_relative_name {
347   my ($class, $name) = @_;
348   return if !$name;
349   $name = $class . '::' . $name if ! ($name =~ s/^\+//);
350   return $name;
351 }
352
353 # returns a hash of $shortname => $fullname for every package
354 #  found in the given namespaces ($shortname is with the $fullname's
355 #  namespace stripped off)
356 sub _map_namespaces {
357   my ($class, @namespaces) = @_;
358
359   my @results_hash;
360   foreach my $namespace (@namespaces) {
361     push(
362       @results_hash,
363       map { (substr($_, length "${namespace}::"), $_) }
364       Module::Find::findallmod($namespace)
365     );
366   }
367
368   @results_hash;
369 }
370
371 sub load_namespaces {
372   my ($class, %args) = @_;
373
374   my $result_namespace = delete $args{result_namespace} || 'Result';
375   my $resultset_namespace = delete $args{resultset_namespace} || 'ResultSet';
376   my $default_resultset_class = delete $args{default_resultset_class};
377
378   $class->throw_exception('load_namespaces: unknown option(s): '
379     . join(q{,}, map { qq{'$_'} } keys %args))
380       if scalar keys %args;
381
382   $default_resultset_class
383     = $class->_expand_relative_name($default_resultset_class);
384
385   for my $arg ($result_namespace, $resultset_namespace) {
386     $arg = [ $arg ] if !ref($arg) && $arg;
387
388     $class->throw_exception('load_namespaces: namespace arguments must be '
389       . 'a simple string or an arrayref')
390         if ref($arg) ne 'ARRAY';
391
392     $_ = $class->_expand_relative_name($_) for (@$arg);
393   }
394
395   my %results = $class->_map_namespaces(@$result_namespace);
396   my %resultsets = $class->_map_namespaces(@$resultset_namespace);
397
398   my @to_register;
399   {
400     no warnings 'redefine';
401     local *Class::C3::reinitialize = sub { };
402     use warnings 'redefine';
403
404     foreach my $result (keys %results) {
405       my $result_class = $results{$result};
406       $class->ensure_class_loaded($result_class);
407       $result_class->source_name($result) unless $result_class->source_name;
408
409       my $rs_class = delete $resultsets{$result};
410       my $rs_set = $result_class->resultset_class;
411       if($rs_set && $rs_set ne 'DBIx::Class::ResultSet') {
412         if($rs_class && $rs_class ne $rs_set) {
413           warn "We found ResultSet class '$rs_class' for '$result', but it seems "
414              . "that you had already set '$result' to use '$rs_set' instead";
415         }
416       }
417       elsif($rs_class ||= $default_resultset_class) {
418         $class->ensure_class_loaded($rs_class);
419         $result_class->resultset_class($rs_class);
420       }
421
422       push(@to_register, [ $result_class->source_name, $result_class ]);
423     }
424   }
425
426   foreach (sort keys %resultsets) {
427     warn "load_namespaces found ResultSet class $_ with no "
428       . 'corresponding Result class';
429   }
430
431   Class::C3->reinitialize;
432   $class->register_class(@$_) for (@to_register);
433
434   return;
435 }
436
437 =head2 compose_connection
438
439 =over 4
440
441 =item Arguments: $target_namespace, @db_info
442
443 =item Return Value: $new_schema
444
445 =back
446
447 Calls L<DBIx::Class::Schema/"compose_namespace"> to the target namespace,
448 calls L<DBIx::Class::Schema/connection> with @db_info on the new schema,
449 then injects the L<DBix::Class::ResultSetProxy> component and a
450 resultset_instance classdata entry on all the new classes, in order to support
451 $target_namespaces::$class->search(...) method calls.
452
453 This is primarily useful when you have a specific need for class method access
454 to a connection. In normal usage it is preferred to call
455 L<DBIx::Class::Schema/connect> and use the resulting schema object to operate
456 on L<DBIx::Class::ResultSet> objects with L<DBIx::Class::Schema/resultset> for
457 more information.
458
459 =cut
460
461 sub compose_connection {
462   my ($self, $target, @info) = @_;
463   my $base = 'DBIx::Class::ResultSetProxy';
464   eval "require ${base};";
465   $self->throw_exception
466     ("No arguments to load_classes and couldn't load ${base} ($@)")
467       if $@;
468
469   if ($self eq $target) {
470     # Pathological case, largely caused by the docs on early C::M::DBIC::Plain
471     foreach my $moniker ($self->sources) {
472       my $source = $self->source($moniker);
473       my $class = $source->result_class;
474       $self->inject_base($class, $base);
475       $class->mk_classdata(resultset_instance => $source->resultset);
476       $class->mk_classdata(class_resolver => $self);
477     }
478     $self->connection(@info);
479     return $self;
480   }
481
482   my $schema = $self->compose_namespace($target, $base);
483   {
484     no strict 'refs';
485     *{"${target}::schema"} = sub { $schema };
486   }
487
488   $schema->connection(@info);
489   foreach my $moniker ($schema->sources) {
490     my $source = $schema->source($moniker);
491     my $class = $source->result_class;
492     #warn "$moniker $class $source ".$source->storage;
493     $class->mk_classdata(result_source_instance => $source);
494     $class->mk_classdata(resultset_instance => $source->resultset);
495     $class->mk_classdata(class_resolver => $schema);
496   }
497   return $schema;
498 }
499
500 =head2 compose_namespace
501
502 =over 4
503
504 =item Arguments: $target_namespace, $additional_base_class?
505
506 =item Return Value: $new_schema
507
508 =back
509
510 For each L<DBIx::Class::ResultSource> in the schema, this method creates a
511 class in the target namespace (e.g. $target_namespace::CD,
512 $target_namespace::Artist) that inherits from the corresponding classes
513 attached to the current schema.
514
515 It also attaches a corresponding L<DBIx::Class::ResultSource> object to the
516 new $schema object. If C<$additional_base_class> is given, the new composed
517 classes will inherit from first the corresponding classe from the current
518 schema then the base class.
519
520 For example, for a schema with My::Schema::CD and My::Schema::Artist classes,
521
522   $schema->compose_namespace('My::DB', 'Base::Class');
523   print join (', ', @My::DB::CD::ISA) . "\n";
524   print join (', ', @My::DB::Artist::ISA) ."\n";
525
526 will produce the output
527
528   My::Schema::CD, Base::Class
529   My::Schema::Artist, Base::Class
530
531 =cut
532
533 sub compose_namespace {
534   my ($self, $target, $base) = @_;
535   my %reg = %{ $self->source_registrations };
536   my %target;
537   my %map;
538   my $schema = $self->clone;
539   {
540     no warnings qw/redefine/;
541     local *Class::C3::reinitialize = sub { };
542     foreach my $moniker ($schema->sources) {
543       my $source = $schema->source($moniker);
544       my $target_class = "${target}::${moniker}";
545       $self->inject_base(
546         $target_class => $source->result_class, ($base ? $base : ())
547       );
548       $source->result_class($target_class);
549       $target_class->result_source_instance($source)
550         if $target_class->can('result_source_instance');
551     }
552   }
553   Class::C3->reinitialize();
554   {
555     no strict 'refs';
556     foreach my $meth (qw/class source resultset/) {
557       *{"${target}::${meth}"} =
558         sub { shift->schema->$meth(@_) };
559     }
560   }
561   return $schema;
562 }
563
564 =head2 setup_connection_class
565
566 =over 4
567
568 =item Arguments: $target, @info
569
570 =back
571
572 Sets up a database connection class to inject between the schema and the
573 subclasses that the schema creates.
574
575 =cut
576
577 sub setup_connection_class {
578   my ($class, $target, @info) = @_;
579   $class->inject_base($target => 'DBIx::Class::DB');
580   #$target->load_components('DB');
581   $target->connection(@info);
582 }
583
584 =head2 connection
585
586 =over 4
587
588 =item Arguments: @args
589
590 =item Return Value: $new_schema
591
592 =back
593
594 Instantiates a new Storage object of type
595 L<DBIx::Class::Schema/"storage_type"> and passes the arguments to
596 $storage->connect_info. Sets the connection in-place on the schema.
597
598 See L<DBIx::Class::Storage::DBI/"connect_info"> for DBI-specific syntax,
599 or L<DBIx::Class::Storage> in general.
600
601 =cut
602
603 sub connection {
604   my ($self, @info) = @_;
605   return $self if !@info && $self->storage;
606   my $storage_class = $self->storage_type;
607   $storage_class = 'DBIx::Class::Storage'.$storage_class
608     if $storage_class =~ m/^::/;
609   eval "require ${storage_class};";
610   $self->throw_exception(
611     "No arguments to load_classes and couldn't load ${storage_class} ($@)"
612   ) if $@;
613   my $storage = $storage_class->new($self);
614   $storage->connect_info(\@info);
615   $self->storage($storage);
616   return $self;
617 }
618
619 =head2 connect
620
621 =over 4
622
623 =item Arguments: @info
624
625 =item Return Value: $new_schema
626
627 =back
628
629 This is a convenience method. It is equivalent to calling
630 $schema->clone->connection(@info). See L</connection> and L</clone> for more
631 information.
632
633 =cut
634
635 sub connect { shift->clone->connection(@_) }
636
637 =head2 txn_do
638
639 =over 4
640
641 =item Arguments: C<$coderef>, @coderef_args?
642
643 =item Return Value: The return value of $coderef
644
645 =back
646
647 Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
648 returning its result (if any). Equivalent to calling $schema->storage->txn_do.
649 See L<DBIx::Class::Storage/"txn_do"> for more information.
650
651 This interface is preferred over using the individual methods L</txn_begin>,
652 L</txn_commit>, and L</txn_rollback> below.
653
654 =cut
655
656 sub txn_do {
657   my $self = shift;
658
659   $self->storage or $self->throw_exception
660     ('txn_do called on $schema without storage');
661
662   $self->storage->txn_do(@_);
663 }
664
665 =head2 txn_begin
666
667 Begins a transaction (does nothing if AutoCommit is off). Equivalent to
668 calling $schema->storage->txn_begin. See
669 L<DBIx::Class::Storage::DBI/"txn_begin"> for more information.
670
671 =cut
672
673 sub txn_begin {
674   my $self = shift;
675
676   $self->storage or $self->throw_exception
677     ('txn_begin called on $schema without storage');
678
679   $self->storage->txn_begin;
680 }
681
682 =head2 txn_commit
683
684 Commits the current transaction. Equivalent to calling
685 $schema->storage->txn_commit. See L<DBIx::Class::Storage::DBI/"txn_commit">
686 for more information.
687
688 =cut
689
690 sub txn_commit {
691   my $self = shift;
692
693   $self->storage or $self->throw_exception
694     ('txn_commit called on $schema without storage');
695
696   $self->storage->txn_commit;
697 }
698
699 =head2 txn_rollback
700
701 Rolls back the current transaction. Equivalent to calling
702 $schema->storage->txn_rollback. See
703 L<DBIx::Class::Storage::DBI/"txn_rollback"> for more information.
704
705 =cut
706
707 sub txn_rollback {
708   my $self = shift;
709
710   $self->storage or $self->throw_exception
711     ('txn_rollback called on $schema without storage');
712
713   $self->storage->txn_rollback;
714 }
715
716 =head2 clone
717
718 =over 4
719
720 =item Return Value: $new_schema
721
722 =back
723
724 Clones the schema and its associated result_source objects and returns the
725 copy.
726
727 =cut
728
729 sub clone {
730   my ($self) = @_;
731   my $clone = { (ref $self ? %$self : ()) };
732   bless $clone, (ref $self || $self);
733
734   foreach my $moniker ($self->sources) {
735     my $source = $self->source($moniker);
736     my $new = $source->new($source);
737     $clone->register_source($moniker => $new);
738   }
739   $clone->storage->set_schema($clone) if $clone->storage;
740   return $clone;
741 }
742
743 =head2 populate
744
745 =over 4
746
747 =item Arguments: $moniker, \@data;
748
749 =back
750
751 Populates the source registered with the given moniker with the supplied data.
752 @data should be a list of listrefs -- the first containing column names, the
753 second matching values.
754
755 i.e.,
756
757   $schema->populate('Artist', [
758     [ qw/artistid name/ ],
759     [ 1, 'Popular Band' ],
760     [ 2, 'Indie Band' ],
761     ...
762   ]);
763
764 =cut
765
766 sub populate {
767   my ($self, $name, $data) = @_;
768   my $rs = $self->resultset($name);
769   my @names = @{shift(@$data)};
770   my @created;
771   foreach my $item (@$data) {
772     my %create;
773     @create{@names} = @$item;
774     push(@created, $rs->create(\%create));
775   }
776   return @created;
777 }
778
779 =head2 exception_action
780
781 =over 4
782
783 =item Arguments: $code_reference
784
785 =back
786
787 If C<exception_action> is set for this class/object, L</throw_exception>
788 will prefer to call this code reference with the exception as an argument,
789 rather than its normal <croak> action.
790
791 Your subroutine should probably just wrap the error in the exception
792 object/class of your choosing and rethrow.  If, against all sage advice,
793 you'd like your C<exception_action> to suppress a particular exception
794 completely, simply have it return true.
795
796 Example:
797
798    package My::Schema;
799    use base qw/DBIx::Class::Schema/;
800    use My::ExceptionClass;
801    __PACKAGE__->exception_action(sub { My::ExceptionClass->throw(@_) });
802    __PACKAGE__->load_classes;
803
804    # or:
805    my $schema_obj = My::Schema->connect( .... );
806    $schema_obj->exception_action(sub { My::ExceptionClass->throw(@_) });
807
808    # suppress all exceptions, like a moron:
809    $schema_obj->exception_action(sub { 1 });
810
811 =head2 throw_exception
812
813 =over 4
814
815 =item Arguments: $message
816
817 =back
818
819 Throws an exception. Defaults to using L<Carp::Clan> to report errors from
820 user's perspective.  See L</exception_action> for details on overriding
821 this method's behavior.
822
823 =cut
824
825 sub throw_exception {
826   my $self = shift;
827   croak @_ if !$self->exception_action || !$self->exception_action->(@_);
828 }
829
830 =head2 deploy (EXPERIMENTAL)
831
832 =over 4
833
834 =item Arguments: $sqlt_args, $dir
835
836 =back
837
838 Attempts to deploy the schema to the current storage using L<SQL::Translator>.
839
840 Note that this feature is currently EXPERIMENTAL and may not work correctly
841 across all databases, or fully handle complex relationships.
842
843 See L<SQL::Translator/METHODS> for a list of values for C<$sqlt_args>. The most
844 common value for this would be C<< { add_drop_table => 1, } >> to have the SQL
845 produced include a DROP TABLE statement for each table created.
846
847 =cut
848
849 sub deploy {
850   my ($self, $sqltargs, $dir) = @_;
851   $self->throw_exception("Can't deploy without storage") unless $self->storage;
852   $self->storage->deploy($self, undef, $sqltargs, $dir);
853 }
854
855 =head2 create_ddl_dir (EXPERIMENTAL)
856
857 =over 4
858
859 =item Arguments: \@databases, $version, $directory, $sqlt_args
860
861 =back
862
863 Creates an SQL file based on the Schema, for each of the specified
864 database types, in the given directory.
865
866 Note that this feature is currently EXPERIMENTAL and may not work correctly
867 across all databases, or fully handle complex relationships.
868
869 =cut
870
871 sub create_ddl_dir {
872   my $self = shift;
873
874   $self->throw_exception("Can't create_ddl_dir without storage") unless $self->storage;
875   $self->storage->create_ddl_dir($self, @_);
876 }
877
878 =head2 ddl_filename (EXPERIMENTAL)
879
880   my $filename = $table->ddl_filename($type, $dir, $version)
881
882 Creates a filename for a SQL file based on the table class name.  Not
883 intended for direct end user use.
884
885 =cut
886
887 sub ddl_filename {
888     my ($self, $type, $dir, $version) = @_;
889
890     my $filename = ref($self);
891     $filename =~ s/::/-/;
892     $filename = "$dir$filename-$version-$type.sql";
893
894     return $filename;
895 }
896
897 1;
898
899 =head1 AUTHORS
900
901 Matt S. Trout <mst@shadowcatsystems.co.uk>
902
903 =head1 LICENSE
904
905 You may distribute this code under the same terms as Perl itself.
906
907 =cut