/usr/local/CPAN/DBICx-Modeler/DBICx/Modeler/Model/Meta.pm
package DBICx::Modeler::Model::Meta;
use strict;
use warnings;
use Moose;
use DBICx::Modeler::Carp;
use constant TRACE => DBICx::Modeler::Carp::TRACE;
has parent => qw/is ro isa Maybe[DBICx::Modeler::Model::Meta] lazy_build 1/;
sub _build_parent {
my $self = shift;
if (my $method = $self->model_class->meta->find_next_method_by_name( '_model__meta' )) {
return $method->();
}
return undef;
}
has model_class => qw/is ro required 1/;
has _specialization => qw/is ro isa HashRef/, default => sub { {} };
has _initialized => qw/is rw/;
sub _specialize_relationship {
my $self = shift;
my ($relationship_kind, $relationship_name, $model_class) = @_;
$self->_specialization->{relationship}->{$relationship_name} = {
kind => $relationship_kind,
name => $relationship_name,
model_class => $model_class,
};
}
sub belongs_to {
my $self = shift;
$self->_specialize_relationship( belongs_to => @_ );
}
sub has_one {
my $self = shift;
$self->_specialize_relationship( has_one => @_ );
}
sub has_many {
my $self = shift;
$self->_specialize_relationship( has_many => @_ );
}
sub might_have {
my $self = shift;
$self->_specialize_relationship( might_have => @_ );
}
sub specialize_model_source {
my $self = shift;
my $model_source = shift;
if ( local $_ = $self->_specialization->{relationship} ) {
for my $specialized_relationship (values %$_) {
my ($name, $kind, $model_class) = @$specialized_relationship{qw/ name kind model_class /};
my $relationship = $model_source->relationship( $name );
$model_class = '+' . $relationship->default_model_class unless defined $model_class;
$model_class = $model_source->modeler->find_model_class( $model_class );
$relationship->model_class( $model_class );
}
}
return $model_source;
}
sub initialize_base_model_class {
my $self = shift;
my $model_source = shift;
my $model_class = $self->model_class;
if ($self->_initialized) {
TRACE->( "[$self] Already initialized $model_class" );
return;
}
$self->_initialized( 1 );
my $meta = $model_class->meta;
if ($self->parent) {
$self->parent->initialize_base_model_class( $model_source );
}
else {
my $result_source = $model_source->result_source;
TRACE->( "[$self] Initializing base model class $model_class" );
# $model_source should have been specialized already
for my $relationship ( $model_source->relationships ) {
my $name = $relationship->name;
my $method = "_model__relation_$name";
my $is_many = $relationship->is_many;
my $alias = ! $meta->has_method( $name );
if ($is_many) {
$meta->add_method( $method => sub {
my $self = shift;
return $self->_model__source->search_related( $self, $name, @_ );
} );
if ($alias) {
$meta->add_method( $name => sub {
my $self = shift;
return $self->$method( @_ );
} );
}
}
else {
$meta->add_attribute( $method => qw/is ro lazy 1/, default => sub {
my $self = shift;
return $self->_model__source->inflate_related( $self, $name );
} );
if ($alias) {
$meta->add_method( $name => sub {
my $self = shift;
return $self->$method( @_ );
} );
}
}
}
unless ($meta->has_method( 'create_related' )) {
$meta->add_method( 'create_related' => sub {
my $self = shift;
return $self->_model__source->create_related( $self, @_ );
});
}
unless ($meta->has_method( 'search_related' )) {
$meta->add_method( 'search_related' => sub {
my $self = shift;
return $self->_model__source->search_related( $self, @_ );
});
}
my $attribute;
if ($attribute = $meta->get_attribute( '_model__storage' )) {
if ($attribute->has_handles) {
TRACE->("[$self] Not setting up model storage handles for $model_class since it already has them");
# Assume the user know's what they're doing
}
else {
my @columns = $result_source->columns;
my %handles = map { $_ => $_ } qw/insert update/;
$handles{$_} = $_ for grep { ! $meta->has_method( $_ ) } @columns;
@handles{ map { "_model__column_$_" } @columns } = @columns;
my $new_attribute = $meta->_process_inherited_attribute( $attribute->name, handles => \%handles );
$meta->add_attribute( $new_attribute );
}
}
else {
croak "Couldn't set up model storage handles for $model_class since it doesn't have a '_model__storage' attribute"
}
}
for my $method_modifier (@{ $self->_specialization->{method_modifier} }) {
my ($kind, @arguments) = @$method_modifier;
Moose::Util::add_method_modifier( $model_class, $kind, \@arguments );
}
}
1;