dev release
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader.pm
index 75318cf..53ccd66 100644 (file)
@@ -1,9 +1,19 @@
 package DBIx::Class::Schema::Loader;
 
 use strict;
-use UNIVERSAL::require;
+use warnings;
+use base qw/DBIx::Class::Schema Class::Data::Accessor/;
+use Carp::Clan qw/^DBIx::Class/;
+use Class::C3;
+use Scalar::Util qw/ weaken /;
 
-our $VERSION = '0.01';
+# Always remember to do all digits for the version even if they're 0
+# i.e. first release of 0.XX *must* be 0.XX000. This avoids fBSD ports
+# brain damage and presumably various other packaging systems too
+our $VERSION = '0.04999_08';
+
+__PACKAGE__->mk_classaccessor('_loader_args' => {});
+__PACKAGE__->mk_classaccessors(qw/dump_to_dir _loader_invoked _loader loader_class/);
 
 =head1 NAME
 
@@ -11,104 +21,386 @@ DBIx::Class::Schema::Loader - Dynamic definition of a DBIx::Class::Schema
 
 =head1 SYNOPSIS
 
-  use DBIx::Class::Schema::Loader;
-
-  my $loader = DBIx::Class::Schema::Loader->new(
-    dsn                     => "dbi:mysql:dbname",
-    user                    => "root",
-    password                => "",
-    namespace               => "Data",
-    additional_classes      => [qw/DBIx::Class::Foo/],
-    additional_base_classes => [qw/My::Stuff/],
-    left_base_classes       => [qw/DBIx::Class::Bar/],
-    constraint              => '^foo.*',
-    relationships           => 1,
-    options                 => { AutoCommit => 1 }, 
-    inflect                 => { child => 'children' },
-    debug                   => 1,
+  ### use this module to generate a set of class files
+
+  # in a script
+  use DBIx::Class::Schema::Loader qw/ make_schema_at /;
+  make_schema_at(
+      'My::Schema',
+      { debug => 1,
+        dump_directory => './lib',
+      },
+      [ 'dbi:Pg:dbname="foo"', 'myuser', 'mypassword' ],
   );
 
-  my $conn = $loader->connection($dsn, $user, $password); #
-  my $conn = $loader->connection(); # uses same dsn as ->new();
+  # from the command line or a shell script with dbicdump (distributed
+  # with this module).  Do `perldoc dbicdump` for usage.
+  dbicdump -o dump_directory=./lib \
+           -o debug=1 \
+           My::Schema \
+           'dbi:Pg:dbname=foo' \
+           myuser \
+           mypassword
+
+  ### or generate and load classes at runtime
+  # note: this technique is not recommended
+  # for use in production code
+
+  package My::Schema;
+  use base qw/DBIx::Class::Schema::Loader/;
+
+  __PACKAGE__->loader_options(
+      constraint              => '^foo.*',
+      # debug                 => 1,
+  );
 
-use with mod_perl
+  #### in application code elsewhere:
 
-in your startup.pl
+  use My::Schema;
 
-  # load all tables
-  use DBIx::Class::Loader;
-  my $loader = DBIx::Class::Loader->new(
-    dsn       => "dbi:mysql:dbname",
-    user      => "root",
-    password  => "",
-    namespace => "Data",
-  );
+  my $schema1 = My::Schema->connect( $dsn, $user, $password, $attrs);
+  # -or-
+  my $schema1 = "My::Schema"; $schema1->connection(as above);
 
-in your web application.
+=head1 DESCRIPTION 
 
-  use strict;
+DBIx::Class::Schema::Loader automates the definition of a
+L<DBIx::Class::Schema> by scanning database table definitions and
+setting up the columns, primary keys, and relationships.
 
-  # you can use Data::Film directly
-  my $conn = $loader->connection();
-  my $film_moniker = $loader->moniker('film');
-  my $a_film = $conn->resultset($film_moniker)->find($id);
+DBIx::Class::Schema::Loader currently supports only the DBI storage type.
+It has explicit support for L<DBD::Pg>, L<DBD::mysql>, L<DBD::DB2>,
+L<DBD::SQLite>, and L<DBD::Oracle>.  Other DBI drivers may function to
+a greater or lesser degree with this loader, depending on how much of the
+DBI spec they implement, and how standard their implementation is.
 
-=head1 DESCRIPTION
+Patches to make other DBDs work correctly welcome.
 
-DBIx::Class::Schema::Loader automate the definition of a
-DBIx::Class::Schema by scanning table schemas and setting up
-columns and primary keys.
+See L<DBIx::Class::Schema::Loader::DBI::Writing> for notes on writing
+your own vendor-specific subclass for an unsupported DBD driver.
 
-DBIx::Class::Schema::Loader supports MySQL, Postgres, SQLite and DB2.  See
-L<DBIx::Class::Schema::Loader::Generic> for more, and
-L<DBIx::Class::Schema::Loader::Writing> for notes on writing your own
-db-specific subclass for an unsupported db.
+This module requires L<DBIx::Class> 0.07006 or later, and obsoletes
+the older L<DBIx::Class::Loader>.
 
-L<Class::DBI::Loader>, L<Class::DBI>, and L<DBIx::Class::Loader> are now
-obsolete, use L<DBIx::Class> and this module instead. ;)
+This module is designed more to get you up and running quickly against
+an existing database, or to be effective for simple situations, rather
+than to be what you use in the long term for a complex database/project.
 
-=cut
+That being said, transitioning your code from a Schema generated by this
+module to one that doesn't use this module should be straightforward and
+painless, so don't shy away from it just for fears of the transition down
+the road.
 
 =head1 METHODS
 
-=head2 new
+=head2 loader_class
+
+=over 4
+
+=item Argument: $loader_class
+
+=back
+
+Set the loader class to be instantiated when L</connection> is called.
+If the classname starts with "::", "DBIx::Class::Schema::Loader" is
+prepended. Defaults to L<DBIx::Class::Schema/storage_type> (which must
+start with "::" when using L<DBIx::Class::Schema::Loader>).
+
+This is mostly useful for subclassing existing loaders or in conjunction
+with L</dump_to_dir>.
+
+=head2 loader_options
+
+=over 4
+
+=item Argument: \%loader_options
+
+=back
+
+Example in Synopsis above demonstrates a few common arguments.  For
+detailed information on all of the arguments, most of which are
+only useful in fairly complex scenarios, see the
+L<DBIx::Class::Schema::Loader::Base> documentation.
+
+If you intend to use C<loader_options>, you must call
+C<loader_options> before any connection is made, or embed the
+C<loader_options> in the connection information itself as shown
+below.  Setting C<loader_options> after the connection has
+already been made is useless.
+
+=cut
+
+sub loader_options {
+    my $self = shift;
+    
+    my %args = (ref $_[0] eq 'HASH') ? %{$_[0]} : @_;
+    $self->_loader_args(\%args);
+
+    $self;
+}
+
+sub _invoke_loader {
+    my $self = shift;
+    my $class = ref $self || $self;
+
+    my $args = $self->_loader_args;
+
+    # set up the schema/schema_class arguments
+    $args->{schema} = $self;
+    $args->{schema_class} = $class;
+    weaken($args->{schema}) if ref $self;
+    $args->{dump_directory} ||= $self->dump_to_dir;
 
-Example in Synopsis above demonstrates the available arguments.  For
-detailed information on the arguments, see the
-L<DBIx::Class::Schema::Loader::Generic> documentation.
+    # XXX this only works for relative storage_type, like ::DBI ...
+    my $impl = $self->loader_class
+      || "DBIx::Class::Schema::Loader" . $self->storage_type;
+    $impl = "DBIx::Class::Schema::Loader${impl}" if $impl =~ /^::/;
+    eval { $self->ensure_class_loaded($impl) };
+    croak qq/Could not load storage_type loader "$impl": "$@"/ if $@;
+
+    $self->_loader($impl->new(%$args));
+    $self->_loader->load;
+    $self->_loader_invoked(1);
+
+    $self;
+}
+
+=head2 connection
+
+=over 4
+
+=item Arguments: @args
+
+=item Return Value: $new_schema
+
+=back
+
+See L<DBIx::Class::Schema/connection> for basic usage.
+
+If the final argument is a hashref, and it contains the keys C<loader_options>
+or C<loader_class>, those keys will be deleted, and their values value will be
+used for the loader options or class, respectively, just as if set via the
+L</loader_options> or L</loader_class> methods above.
+
+The actual auto-loading operation (the heart of this module) will be invoked
+as soon as the connection information is defined.
+
+=cut
+
+sub connection {
+    my $self = shift;
+
+    if($_[-1] && ref $_[-1] eq 'HASH') {
+        for my $option (qw/ loader_class loader_options result_base_class schema_base_class/) {
+            if(my $value = delete $_[-1]->{$option}) {
+                $self->$option($value);
+            }
+        }
+        pop @_ if !keys %{$_[-1]};
+    }
+
+    $self = $self->next::method(@_);
+
+    my $class = ref $self || $self;
+    if(!$class->_loader_invoked) {
+        $self->_invoke_loader
+    }
+
+    return $self;
+}
+
+=head2 clone
+
+See L<DBIx::Class::Schema/clone>.
 
 =cut
 
-sub new {
-    my ( $class, %args ) = @_;
+sub clone {
+    my $self = shift;
+
+    my $clone = $self->next::method(@_);
 
-    foreach (qw/namespace dsn/) {
-       die qq/Argument $_ is required/ if ! $args{$_};
+    if($clone->_loader_args) {
+        $clone->_loader_args->{schema} = $clone;
+        weaken($clone->_loader_args->{schema});
     }
 
-    $args{namespace} =~ s/(.*)::$/$1/;
+    $clone;
+}
+
+=head2 dump_to_dir
+
+=over 4
+
+=item Argument: $directory
+
+=back
+
+Calling this as a class method on either L<DBIx::Class::Schema::Loader>
+or any derived schema class will cause all schemas to dump
+manual versions of themselves to the named directory when they are
+loaded.  In order to be effective, this must be set before defining a
+connection on this schema class or any derived object (as the loading
+happens as soon as both a connection and loader_options are set, and
+only once per class).
+
+See L<DBIx::Class::Schema::Loader::Base/dump_directory> for more
+details on the dumping mechanism.
+
+This can also be set at module import time via the import option
+C<dump_to_dir:/foo/bar> to L<DBIx::Class::Schema::Loader>, where
+C</foo/bar> is the target directory.
+
+Examples:
+
+    # My::Schema isa DBIx::Class::Schema::Loader, and has connection info
+    #   hardcoded in the class itself:
+    perl -MDBIx::Class::Schema::Loader=dump_to_dir:/foo/bar -MMy::Schema -e1
+
+    # Same, but no hard-coded connection, so we must provide one:
+    perl -MDBIx::Class::Schema::Loader=dump_to_dir:/foo/bar -MMy::Schema -e 'My::Schema->connection("dbi:Pg:dbname=foo", ...)'
+
+    # Or as a class method, as long as you get it done *before* defining a
+    #  connection on this schema class or any derived object:
+    use My::Schema;
+    My::Schema->dump_to_dir('/foo/bar');
+    My::Schema->connection(........);
+
+    # Or as a class method on the DBIx::Class::Schema::Loader itself, which affects all
+    #   derived schemas
+    use My::Schema;
+    use My::OtherSchema;
+    DBIx::Class::Schema::Loader->dump_to_dir('/foo/bar');
+    My::Schema->connection(.......);
+    My::OtherSchema->connection(.......);
+
+    # Another alternative to the above:
+    use DBIx::Class::Schema::Loader qw| dump_to_dir:/foo/bar |;
+    use My::Schema;
+    use My::OtherSchema;
+    My::Schema->connection(.......);
+    My::OtherSchema->connection(.......);
+
+=cut
+
+sub import {
+    my $self = shift;
+    return if !@_;
+    foreach my $opt (@_) {
+        if($opt =~ m{^dump_to_dir:(.*)$}) {
+            $self->dump_to_dir($1)
+        }
+        elsif($opt eq 'make_schema_at') {
+            no strict 'refs';
+            my $cpkg = (caller)[0];
+            *{"${cpkg}::make_schema_at"} = \&make_schema_at;
+        }
+    }
+}
+
+=head2 make_schema_at
+
+=over 4
+
+=item Arguments: $schema_class_name, \%loader_options, \@connect_info
+
+=item Return Value: $schema_class_name
+
+=back
 
-    my $dsn = $args{dsn};
-    my ($driver) = $dsn =~ m/^dbi:(\w*?)(?:\((.*?)\))?:/i;
-    $driver = 'SQLite' if $driver eq 'SQLite2';
-    my $impl = "DBIx::Class::Schema::Loader::" . $driver;
+This function creates a DBIx::Class schema from an existing RDBMS
+schema.  With the C<dump_directory> option, generates a set of
+DBIx::Class classes from an existing database schema read from the
+given dsn.  Without a C<dump_directory>, creates schema classes in
+memory at runtime without generating on-disk class files.
 
-    $impl->require or
-    die qq/Couldn't require loader class "$impl", "$UNIVERSAL::require::ERROR"/;
+For a complete list of supported loader_options, see
+L<DBIx::Class::Schema::Loader::Base>
 
-    return $impl->new(%args);
+This function can be imported in the usual way, as illustrated in
+these Examples:
+
+    # Simple example, creates as a new class 'New::Schema::Name' in
+    #  memory in the running perl interpreter.
+    use DBIx::Class::Schema::Loader qw/ make_schema_at /;
+    make_schema_at(
+        'New::Schema::Name',
+        { debug => 1 },
+        [ 'dbi:Pg:dbname="foo"','postgres' ],
+    );
+
+    # Inside a script, specifying a dump directory in which to write
+    # class files
+    use DBIx::Class::Schema::Loader qw/ make_schema_at /;
+    make_schema_at(
+        'New::Schema::Name',
+        { debug => 1, dump_directory => './lib' },
+        [ 'dbi:Pg:dbname="foo"','postgres' ],
+    );
+
+=cut
+
+sub make_schema_at {
+    my ($target, $opts, $connect_info) = @_;
+
+    {
+        no strict 'refs';
+        @{$target . '::ISA'} = qw/DBIx::Class::Schema::Loader/;
+    }
+
+    $target->loader_options($opts);
+    $target->connection(@$connect_info);
 }
 
+=head2 rescan
+
+=over 4
+
+=item Return Value: @new_monikers
+
+=back
+
+Re-scans the database for newly added tables since the initial
+load, and adds them to the schema at runtime, including relationships,
+etc.  Does not process drops or changes.
+
+Returns a list of the new monikers added.
+
+=cut
+
+sub rescan { my $self = shift; $self->_loader->rescan($self) }
+
+
+=head1 KNOWN ISSUES
+
+=head2 Multiple Database Schemas
+
+Currently the loader is limited to working within a single schema
+(using the underlying RDBMS's definition of "schema").  If you have a
+multi-schema database with inter-schema relationships (which is easy
+to do in PostgreSQL or DB2 for instance), you currently can only
+automatically load the tables of one schema, and relationships to
+tables in other schemas will be silently ignored.
+
+At some point in the future, an intelligent way around this might be
+devised, probably by allowing the C<db_schema> option to be an
+arrayref of schemas to load.
+
+In "normal" L<DBIx::Class::Schema> usage, manually-defined
+source classes and relationships have no problems crossing vendor schemas.
+
 =head1 AUTHOR
 
-Sebastian Riedel, C<sri@oook.de>
+Brandon Black, C<blblack@gmail.com>
+
+Based on L<DBIx::Class::Loader> by Sebastian Riedel
 
 Based upon the work of IKEBE Tomohiro
 
 =head1 THANK YOU
 
-Adam Anderson, Andy Grundman, Autrijus Tang, Dan Kubb, David Naughton,
-Randal Schwartz, Simon Flack and all the others who've helped.
+Matt S Trout, all of the #dbix-class folks, and everyone who's ever sent
+in a bug report or suggestion.
 
 =head1 LICENSE
 
@@ -117,7 +409,7 @@ the same terms as Perl itself.
 
 =head1 SEE ALSO
 
-L<DBIx::Class>
+L<DBIx::Class>, L<DBIx::Class::Manual::ExampleSchema>
 
 =cut