NAME
DBIx::Class::Schema::Loader::Dynamic -- Really Dynamic Schema Generation for DBIx::Class
SYNOPSIS
# MySchema.pm
package MySchema;
use strict;
use warnings;
use base 'DBIx::Class::Schema';
use DBIx::Class::Schema::Loader::Dynamic;
sub connect_info { [ 'dbi:Pg:dbname="my_db, 'uid', 'pwd' ] }
sub setup {
my $class = shift;
my $schema = $class->connection(@{$class->connect_info});
DBIx::Class::Schema::Loader::Dynamic->new(
left_base_classes => 'MySchemaDB::Row',
naming => 'v8',
use_namespaces => 0,
schema => $schema,
)->load;
return $schema;
}
1;
# MySchema/Row.pm
package MySchema::Row;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->load_components('InflateColumn::DateTime');
sub hello { 'everybody gets me' }
1;
# Now, assuming the usual 'Music' sample database..
# MySchema/Artist.pm
package MySchema::Artist;
use strict;
use warnings;
sub hello { 'nobody gets me but me' }
1;
# finally, somewhere in my application
use MySchema;
my $schema = MySchema->setup;
# All table classes are now active. They are based on MySchema::Row.
my $artist = $schema->resultset('Artist')->first;
my $cd = $artist->cds()->first;
printf "%s but %s\n", $cd->hello, $artist->hello
DESCRIPTION
DBIx::Class::Schema::Loader::Dynamic is a faster and simpler driver for the dynamic schema generation feature of DBIx::Class::Schema::Loader.
It will make Perl classes for each table spring into existence and it runs the declarative statements (such as add_columns, has_many, ..) immediately at catalog discovery time, rather than code-generating Perl modules and then 'use'-ing those modules multiple times.
Manual customisation of table definition code is still achieved by optionally writing user-defined classes, which act as 'mix-ins' as expected by load_classes in DBIx::Class::Schema (except that you don't actually have to call load_classes).
If you want to generate static database definition code from your database, this module is not for you.
REASON
Design Goal
I consider dynamic schema discovery to have advantages over code-generation, especially as applied to agile techniques, software release management, database schema version management, and continuous delivery.
A useful design goal for application development in a continuous-integration environment is to insist that adding/removing tables, columns, or relationships, can be done without requiring any change to the code (except for removing references to dropped objects of course). If this goal is maintained then most of the pain and bureaucracy of schema version control goes away.
This module allows you to achieve that goal and still use the excellent DBIx::Class ORM.
Implementation
DBIx::Class::Schema::Loader already does outstanding work in catalog-discovery for many database products and in mapping names to the object model. We want to inherit this (literally), so we do. However to activate the results, even in 'dynamic' mode, the standard ::Loader uses a complex code-generation approach which generates Perl code to a temporary directory and then requires (pun intended) abstract Perl class manipulation to enable this code. Multiple passes are done in order to support relationship-discovery. It's fragile and cumbersome, and introduces a lot of Dark Code to the start of every production program, hence 'not recommended'.
This module enables a direct 'live' approach, as distinct from hidden-code-generation. It's faster, it removes a lot of Dark Code from production, and it's more familiar to users of Class::DBI::Loader and some other language ORMs.
LIMITATIONS
Loader Options
We expect most of the loader_options for DBIx::Class::Schema::Loader to be valid, but not all variations can be tested. In particular, all tests to date have been with use_namespaces=>0
and naming=>'v8'
.
Base 'Row' Class
As shown in the Synopsis code under 'MySchema::Row
' you really do want to manually create a base row class and nominate it in left_base_classes in the loader options.
This gives you one place to declare things like the ubiquitous load_components('InflateColumn::DateTime')
and to add/override other methods you wish to be inherited by all table classes in your object model. Make sure your base row class inherits from DBIx::Class::Core.
You can leave out left_base_classes
, in which case it will be defaulted automatically to DBIx::Class::Core.
Moniker Clash Logic removed
Vanilla DBIx::Class::Loader includes logic that checks for duplicates in the classnames generated for table names. That logic is removed in this release. Workaround: don't run this on a connect string that yields duplicate table names.
Private methods overriden
This module overrides some private methods (i.e. whose names =~ /^_\w+/) of DBIx::Class::Schema::Loader::Base. Ideally that module could be refactored to make these overrides more future-proof. I'll ask.
METHODS
You don't need to keep the $loader
object after running load
.
But if you do, then after 'setup' (for a Postgres database),
DBIx::Class::Schema::Loader::Dynamic inherits all methods from
DBIx::Class::Schema::Loader::DBI::Pg (*) which inherits all methods from
DBIx::Class::Schema::Loader::DBI::Component::QuotedDefault which inherits all methods from
DBIx::Class::Schema::Loader::DBI which inherits all methods from
DBIx::Class::Schema::Loader::DBI::Base which inherits all methods from
Class::Accessor::Grouped and Class::C3::Componentised.
(*or your engine-specific DBIx::Class::Schema::Loader::DBI::<subclass>)
.. but implements no new ones. Note that DBIx::Class::Schema itself is kept out of the inheritance chain, at all times.
CONNECTION HANDLING
In the Synopsis, the handling of the connect string and the introduction of a 'setup' method is just a suggestion. TMTOWTDI. Our suggestion allows a schema 'MySchema' itself to be sub-classed if required, with the opportunity to override the connect-string or the loader options.
Being standard DBIx::Class:Schema functionality, note the $schema handle will be just the literal class name (returned when you call connection
) or a true schema instance object ref (returned when you call connect
). See connect in DBIX::Schema::Class.
DEBUGGING
To trace the declarative DBIx::Class statements that are being run, set debug=>1
among the loader options.
REPOSITORY
Open-Sourced at Github: https://github.com/frank-carnovale/DBIx-Class-Schema-Loader-Dynamic. Please post issues there.
COPYRIGHT AND LICENSE
Copyright (C) 2016, Frank Carnovale <frankc@cpan.org>
This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.