use strict;
use warnings;
-use base qw/DBIx::Class::Schema/;
-use base qw/Class::Data::Accessor/;
-use Carp;
-use UNIVERSAL::require;
+use base qw/DBIx::Class::Schema Class::Accessor::Grouped/;
+use Carp::Clan qw/^DBIx::Class/;
+use Class::C3;
+use Scalar::Util qw/ weaken /;
# 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.02000';
-
-__PACKAGE__->mk_classaccessor('loader');
+our $VERSION = '0.05000';
+
+__PACKAGE__->mk_group_accessors('inherited', qw/
+ _loader_args
+ dump_to_dir
+ _loader_invoked
+ _loader
+ loader_class
+ naming
+ use_namespaces
+/);
+__PACKAGE__->_loader_args({});
=head1 NAME
=head1 SYNOPSIS
+ ### 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' ],
+ );
+
+ # 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__->load_from_connection(
- dsn => "dbi:mysql:dbname",
- user => "root",
- password => "",
- 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,
+ __PACKAGE__->loader_options(
+ constraint => '^foo.*',
+ # debug => 1,
);
- # in seperate application code ...
+ #### in application code elsewhere:
use My::Schema;
my $schema1 = My::Schema->connect( $dsn, $user, $password, $attrs);
# -or-
- my $schema1 = "My::Schema";
- # ^^ defaults to dsn/user/pass from load_from_connection()
-
- # Get a list of the original (database) names of the tables that
- # were loaded
- my @tables = $schema1->loader->tables;
-
- # Get a hashref of table_name => 'TableName' table-to-moniker
- # mappings.
- my $monikers = $schema1->loader->monikers;
-
- # Get a hashref of table_name => 'My::Schema::TableName'
- # table-to-classname mappings.
- my $classes = $schema1->loader->classes;
+ my $schema1 = "My::Schema"; $schema1->connection(as above);
- # Use the schema as per normal for DBIx::Class::Schema
- my $rs = $schema1->resultset($monikers->{foo_table})->search(...);
-
-=head1 DESCRIPTION
+=head1 DESCRIPTION
DBIx::Class::Schema::Loader automates the definition of a
-DBIx::Class::Schema by scanning table schemas and setting up
-columns and primary keys.
+L<DBIx::Class::Schema> by scanning database table definitions and
+setting up the columns, primary keys, and relationships.
+
+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>, L<DBD::Sybase> (for Sybase ASE and MSSSQL), L<DBD::ODBC> (for
+MSSQL) 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.
-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.
+Patches to make other DBDs work correctly welcome.
-This module requires L<DBIx::Class> 0.05 or later, and obsoletes
-L<DBIx::Class::Loader> for L<DBIx::Class> version 0.05 and later.
+See L<DBIx::Class::Schema::Loader::DBI::Writing> for notes on writing
+your own vendor-specific subclass for an unsupported DBD driver.
-While on the whole, the bare table definitions are fairly straightforward,
-relationship creation is somewhat heuristic, especially in the choosing
-of relationship types, join types, and relationship names. The relationships
-generated by this module will probably never be as well-defined as
-hand-generated ones. Because of this, over time a complex project will
-probably wish to migrate off of L<DBIx::Class::Schema::Loader>.
+This module requires L<DBIx::Class> 0.07006 or later, and obsoletes
+the older L<DBIx::Class::Loader>.
-It 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.
+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.
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
=head1 METHODS
-=head2 load_from_connection
+=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
-Example in Synopsis above demonstrates the available arguments. For
-detailed information on the arguments, see the
-L<DBIx::Class::Schema::Loader::Generic> documentation.
+=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 load_from_connection {
- my ( $class, %args ) = @_;
+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;
- croak 'dsn argument is required' if ! $args{dsn};
- my $dsn = $args{dsn};
- my ($driver) = $dsn =~ m/^dbi:(\w*?)(?:\((.*?)\))?:/i;
- $driver = 'SQLite' if $driver eq 'SQLite2';
- my $impl = "DBIx::Class::Schema::Loader::" . $driver;
+ my $args = $self->_loader_args;
- $impl->require or
- croak qq/Couldn't require loader class "$impl",/ .
- qq/"$UNIVERSAL::require::ERROR"/;
+ # 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;
+ $args->{naming} = $self->naming if $self->naming;
+ $args->{use_namespaces} = $self->use_namespaces if $self->use_namespaces;
- $args{schema} = $class;
+ # 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 $@;
- $class->loader($impl->new(%args));
- $class->loader->load;
+ $self->_loader($impl->new(%$args));
+ $self->_loader->load;
+ $self->_loader_invoked(1);
+
+ $self;
}
-=head2 loader
+=head2 connection
-This is an accessor in the generated Schema class for accessing
-the L<DBIx::Class::Schema::Loader::Generic> -based loader object
-that was used during construction. See the
-L<DBIx::Class::Schema::Loader::Generic> docs for more information
-on the available loader methods there.
+=over 4
-=head1 KNOWN BUGS
+=item Arguments: @args
-Aside from relationship definitions being less than ideal in general,
-this version is known not to handle the case of multiple relationships
-between the same pair of tables. All of the relationship code will
-be overhauled on the way to 0.03, at which time that bug will be
-addressed.
+=item Return Value: $new_schema
-=head1 AUTHOR
+=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 clone {
+ my $self = shift;
+
+ my $clone = $self->next::method(@_);
+
+ if($clone->_loader_args) {
+ $clone->_loader_args->{schema} = $clone;
+ weaken($clone->_loader_args->{schema});
+ }
+
+ $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 !@_;
+
+ my $cpkg = (caller)[0];
+
+ foreach my $opt (@_) {
+ if($opt =~ m{^dump_to_dir:(.*)$}) {
+ $self->dump_to_dir($1)
+ }
+ elsif($opt eq 'make_schema_at') {
+ no strict 'refs';
+ *{"${cpkg}::make_schema_at"} = \&make_schema_at;
+ }
+ elsif($opt eq 'naming') {
+ no strict 'refs';
+ *{"${cpkg}::naming"} = sub { $self->naming(@_) };
+ }
+ elsif($opt eq 'use_namespaces') {
+ no strict 'refs';
+ *{"${cpkg}::use_namespaces"} = sub { $self->use_namespaces(@_) };
+ }
+ }
+}
+
+=head2 make_schema_at
+
+=over 4
-Brandon Black, C<blblack@gmail.com>
+=item Arguments: $schema_class_name, \%loader_options, \@connect_info
+
+=item Return Value: $schema_class_name
+
+=back
+
+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.
+
+For a complete list of supported loader_options, see
+L<DBIx::Class::Schema::Loader::Base>
+
+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) }
+
+=head2 naming
+
+=over 4
+
+=item Arguments: \%opts | $ver
+
+=back
+
+Controls the naming options for backward compatibility, see
+L<DBIx::Class::Schema::Loader::Base/naming> for details.
+
+To upgrade a dynamic schema, use:
+
+ __PACKAGE__->naming('current');
+
+Can be imported into your dump script and called as a function as well:
+
+ naming('v4');
+
+=head2 use_namespaces
+
+=over 4
+
+=item Arguments: 1|0
+
+=back
+
+Controls the use_namespaces options for backward compatibility, see
+L<DBIx::Class::Schema::Loader::Base/use_namespaces> for details.
+
+To upgrade a dynamic schema, use:
+
+ __PACKAGE__->use_namespaces(1);
+
+Can be imported into your dump script and called as a function as well:
+
+ use_namespaces(1);
+
+=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 ACKNOWLEDGEMENTS
+
+Matt S Trout, all of the #dbix-class folks, and everyone who's ever sent
+in a bug report or suggestion.
Based on L<DBIx::Class::Loader> by Sebastian Riedel
Based upon the work of IKEBE Tomohiro
-=head1 THANK YOU
+=head1 AUTHOR
+
+blblack: Brandon Black <blblack@gmail.com>
+
+=head1 CONTRIBUTORS
+
+ilmari: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>
+
+arcanez: Justin Hunter <justin.d.hunter@gmail.com>
+
+ash: Ash Berlin <ash@cpan.org>
+
+Caelum: Rafael Kitover <rkitover@cpan.org>
+
+TSUNODA Kazuya <drk@drk7.jp>
+
+rbo: Robert Bohne <rbo@cpan.org>
+
+ribasushi: Peter Rabbitson <ribasushi@cpan.org>
+
+gugu: Andrey Kostenko <a.kostenko@rambler-co.ru>
+
+jhannah: Jay Hannah <jay@jays.net>
+
+rbuels: Robert Buels <rmb32@cornell.edu>
+
+timbunce: Tim Bunce <timb@cpan.org>
+
+mst: Matt S. Trout <mst@shadowcatsystems.co.uk>
+
+kane: Jos Boumans <kane@cpan.org>
+
+waawaamilk: Nigel McNie <nigel@mcnie.name>
+
+acmoore: Andrew Moore <amoore@cpan.org>
+
+... and lots of other folks. If we forgot you, please write the current
+maintainer or RT.
-Adam Anderson, Andy Grundman, Autrijus Tang, Dan Kubb, David Naughton,
-Randal Schwartz, Simon Flack and all the others who've helped.
+=head1 COPYRIGHT & LICENSE
-=head1 LICENSE
+Copyright (c) 2006 - 2009 by the aforementioned
+L<DBIx::Class::Schema::Loader/AUTHOR> and
+L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
This library is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
=head1 SEE ALSO
-L<DBIx::Class>
+L<DBIx::Class>, L<DBIx::Class::Manual::ExampleSchema>
=cut