__PACKAGE__->mk_ro_accessors(
qw/
schema
schema_class
exclude
constraint
additional_classes
additional_base_classes
left_base_classes
components
resultset_components
relationships
moniker_map
inflect_singular
inflect_plural
debug
dump_directory
legacy_default_inflections
db_schema
_tables
classes
monikers
/
);
sub
_ensure_arrayref {
my
$self
=
shift
;
foreach
(
@_
) {
$self
->{
$_
} ||= [];
$self
->{
$_
} = [
$self
->{
$_
} ]
unless
ref
$self
->{
$_
} eq
'ARRAY'
;
}
}
sub
new {
my
(
$class
,
%args
) =
@_
;
my
$self
= {
%args
};
bless
$self
=>
$class
;
$self
->{db_schema} ||=
''
;
$self
->_ensure_arrayref(
qw/additional_classes
additional_base_classes
left_base_classes
components
resultset_components
/
);
push
(@{
$self
->{components}},
'ResultSetManager'
)
if
@{
$self
->{resultset_components}};
$self
->{monikers} = {};
$self
->{classes} = {};
for
(
qw/inflect_map inflect/
) {
warn
"Argument $_ is deprecated in favor of 'inflect_plural'"
if
$self
->{
$_
};
}
$self
->{inflect_plural} ||=
$self
->{inflect_map} ||
$self
->{inflect};
$self
->{schema_class} =
ref
$self
->{schema} ||
$self
->{schema};
$self
;
}
sub
_load_external {
my
$self
=
shift
;
foreach
my
$table_class
(
values
%{
$self
->classes}) {
$table_class
->
require
;
if
($@ && $@ !~ /^Can't locate /) {
croak
"Failed to load external class definition"
.
" for '$table_class': $@"
;
}
elsif
(!$@) {
warn
qq/# Loaded external class definition for '$table_class'\n/
if
$self
->debug;
}
}
}
sub
load {
my
$self
=
shift
;
$self
->_load_classes;
$self
->_load_relationships
if
$self
->relationships;
$self
->_load_external;
$self
->_dump_to_dir
if
$self
->dump_directory;
1;
}
sub
_get_dump_filename {
my
(
$self
,
$class
) = (
@_
);
$class
=~ s{::}{/}g;
return
$self
->dump_directory .
q{/}
.
$class
.
q{.pm}
;
}
sub
_ensure_dump_subdirs {
my
(
$self
,
$class
) = (
@_
);
my
@name_parts
=
split
(/::/,
$class
);
pop
@name_parts
;
my
$dir
=
$self
->dump_directory;
foreach
(
@name_parts
) {
$dir
.=
q{/}
.
$_
;
if
(! -d
$dir
) {
mkdir
(
$dir
) or
die
"mkdir('$dir') failed: $!"
;
}
}
}
sub
_dump_to_dir {
my
(
$self
) =
@_
;
my
$target_dir
=
$self
->dump_directory;
die
"Must specify target directory for dumping!"
if
!
$target_dir
;
warn
"Dumping manual schema to $target_dir ...\n"
;
if
(! -d
$target_dir
) {
mkdir
(
$target_dir
) or
die
"mkdir('$target_dir') failed: $!"
;
}
my
$schema_class
=
$self
->schema_class;
$self
->_ensure_dump_subdirs(
$schema_class
);
my
$schema_fn
=
$self
->_get_dump_filename(
$schema_class
);
open
(
my
$schema_fh
,
'>'
,
$schema_fn
)
or
die
"Cannot open $schema_fn for writing: $!"
;
print
$schema_fh
qq|package $schema_class;\n\n|
;
print
$schema_fh
qq|use strict;\nuse warnings;\n\n|
;
print
$schema_fh
qq|use base 'DBIx::Class::Schema';\n\n|
;
print
$schema_fh
qq|__PACKAGE__->load_classes;\n|
;
print
$schema_fh
qq|\n1;\n\n|
;
close
(
$schema_fh
)
or
die
"Cannot close $schema_fn: $!"
;
foreach
my
$src_class
(
sort
keys
%{
$self
->{_dump_storage}}) {
$self
->_ensure_dump_subdirs(
$src_class
);
my
$src_fn
=
$self
->_get_dump_filename(
$src_class
);
open
(
my
$src_fh
,
'>'
,
$src_fn
)
or
die
"Cannot open $src_fn for writing: $!"
;
print
$src_fh
qq|package $src_class;\n\n|
;
print
$src_fh
qq|use strict;\nuse warnings;\n\n|
;
print
$src_fh
qq|use base 'DBIx::Class';\n\n|
;
print
$src_fh
qq|\__PACKAGE__->$_\n|
for
@{
$self
->{_dump_storage}->{
$src_class
}};
print
$src_fh
qq|\n1;\n\n|
;
close
(
$src_fh
)
or
die
"Cannot close $src_fn: $!"
;
}
warn
"Schema dump completed.\n"
;
}
sub
_use {
my
$self
=
shift
;
my
$target
=
shift
;
foreach
(
@_
) {
$_
->
require
or croak (
$_
.
"->require: $@"
);
eval
"package $target; use $_;"
;
croak
"use $_: $@"
if
$@;
}
}
sub
_inject {
my
$self
=
shift
;
my
$target
=
shift
;
my
$schema_class
=
$self
->schema_class;
foreach
(
@_
) {
$_
->
require
or croak (
$_
.
"->require: $@"
);
$schema_class
->inject_base(
$target
,
$_
);
}
}
sub
_load_classes {
my
$self
=
shift
;
my
$schema_class
=
$self
->schema_class;
my
$constraint
=
$self
->constraint;
my
$exclude
=
$self
->exclude;
my
@tables
=
sort
$self
->_tables_list;
warn
"No tables found in database, nothing to load"
if
!
@tables
;
if
(
@tables
) {
@tables
=
grep
{ /
$constraint
/ }
@tables
if
$constraint
;
@tables
=
grep
{ ! /
$exclude
/ }
@tables
if
$exclude
;
warn
"All tables excluded by constraint/exclude, nothing to load"
if
!
@tables
;
}
$self
->{_tables} = \
@tables
;
foreach
my
$table
(
@tables
) {
my
$table_moniker
=
$self
->_table2moniker(
$table
);
my
$table_class
=
$schema_class
.
q{::}
.
$table_moniker
;
my
$table_normalized
=
lc
$table
;
$self
->classes->{
$table
} =
$table_class
;
$self
->classes->{
$table_normalized
} =
$table_class
;
$self
->monikers->{
$table
} =
$table_moniker
;
$self
->monikers->{
$table_normalized
} =
$table_moniker
;
no
warnings
'redefine'
;
local
*Class::C3::reinitialize
=
sub
{ };
{
no
strict
'refs'
;
@{
"${table_class}::ISA"
} =
qw/DBIx::Class/
;
}
$self
->_use (
$table_class
, @{
$self
->additional_classes});
$self
->_inject(
$table_class
, @{
$self
->additional_base_classes});
$self
->_dbic_stmt(
$table_class
,
'load_components'
, @{
$self
->components},
qw/PK::Auto Core/
);
$table_class
->load_resultset_components(@{
$self
->resultset_components})
if
@{
$self
->resultset_components};
$self
->_inject(
$table_class
, @{
$self
->left_base_classes});
}
Class::C3::reinitialize;
foreach
my
$table
(
@tables
) {
my
$table_class
=
$self
->classes->{
$table
};
my
$table_moniker
=
$self
->monikers->{
$table
};
$self
->_dbic_stmt(
$table_class
,
'table'
,
$table
);
my
$cols
=
$self
->_table_columns(
$table
);
$self
->_dbic_stmt(
$table_class
,
'add_columns'
,
@$cols
);
my
$pks
=
$self
->_table_pk_info(
$table
) || [];
@$pks
?
$self
->_dbic_stmt(
$table_class
,
'set_primary_key'
,
@$pks
)
: carp(
"$table has no primary key"
);
my
$uniqs
=
$self
->_table_uniq_info(
$table
) || [];
$self
->_dbic_stmt(
$table_class
,
'add_unique_constraint'
,
@$_
)
for
(
@$uniqs
);
$schema_class
->register_class(
$table_moniker
,
$table_class
);
}
}
sub
tables {
my
$self
=
shift
;
return
@{
$self
->_tables};
}
sub
_table2moniker {
my
(
$self
,
$table
) =
@_
;
my
$moniker
;
if
(
ref
$self
->moniker_map eq
'HASH'
) {
$moniker
=
$self
->moniker_map->{
$table
};
}
elsif
(
ref
$self
->moniker_map eq
'CODE'
) {
$moniker
=
$self
->moniker_map->(
$table
);
}
$moniker
||=
join
''
,
map
ucfirst
,
split
/[\W_]+/,
lc
$table
;
return
$moniker
;
}
sub
_load_relationships {
my
$self
=
shift
;
my
%fk_info
;
foreach
my
$table
(
$self
->tables) {
my
$tbl_fk_info
=
$self
->_table_fk_info(
$table
);
foreach
my
$fkdef
(
@$tbl_fk_info
) {
$fkdef
->{remote_source} =
$self
->monikers->{
delete
$fkdef
->{remote_table}};
}
my
$moniker
=
$self
->monikers->{
$table
};
$fk_info
{
$moniker
} =
$tbl_fk_info
;
}
my
$relbuilder
= DBIx::Class::Schema::Loader::RelBuilder->new(
$self
->schema_class, \
%fk_info
,
$self
->inflect_plural,
$self
->inflect_singular
);
my
$rel_stmts
=
$relbuilder
->generate_code;
foreach
my
$src_class
(
sort
keys
%$rel_stmts
) {
my
$src_stmts
=
$rel_stmts
->{
$src_class
};
foreach
my
$stmt
(
@$src_stmts
) {
$self
->_dbic_stmt(
$src_class
,
$stmt
->{method},@{
$stmt
->{args}});
}
}
}
sub
_table_columns { croak
"ABSTRACT METHOD"
}
sub
_table_pk_info { croak
"ABSTRACT METHOD"
}
sub
_table_uniq_info { croak
"ABSTRACT METHOD"
}
sub
_table_fk_info { croak
"ABSTRACT METHOD"
}
sub
_tables_list { croak
"ABSTRACT METHOD"
}
sub
_dbic_stmt {
my
$self
=
shift
;
my
$class
=
shift
;
my
$method
=
shift
;
if
(!
$self
->debug && !
$self
->dump_directory) {
$class
->
$method
(
@_
);
return
1;
}
my
$args
=
dump
(
@_
);
$args
=
'('
.
$args
.
')'
if
@_
< 2;
my
$stmt
=
$method
.
$args
.
q{;}
;
warn
qq|$class\->$stmt\n|
if
$self
->debug;
$class
->
$method
(
@_
);
push
(@{
$self
->{_dump_storage}->{
$class
}},
$stmt
)
if
$self
->dump_directory;
1;
}
1;