| DBICx-Modeler documentation | Contained in the DBICx-Modeler distribution. |
DBICx::Modeler - A Moose-based model layer over DBIx::Class
Version 0.005
# Given the following schema:
My::Schema::Artist
My::Schema::Cd
My::Schema::Track
# ... and the following model:
My::Model::Artist
use DBICx::Modeler::Model
My::Model::Cd ...
My::Model::Track ...
...
my $modeler = DBICx::Modeler->new( schema => ..., namespace => My::Model );
my $artist = $modeler->create( Artist => { ... } ) # $artist is My::Model::Artist
my $cd = $artist->create_related( cds => { ... } ) # $cd is My::Model::Cd
...
my $search = $artist->cds( { title => ... } ) # Start a search ...
$search->search( { ... } ) # Refine the search ...
my @cds = $search->slice( 0, 9 ) # Get the first 10
# Each is of type My::Model::Cd
DBICx::Modeler is tool for making a thin, Moose-based model layer over a DBIx::Class schema
Many-to-many relationships are ignored, for now. You'll have to access _model__storage (which is the DBIx::Class::Row) if you want
to play with them
# With the following example:
$artist->cds( ... )->slice( 0 )->artist # $artist and ->artist are different objects
This shouldn't be too difficult to fix.
This shouldn't be too difficult to fix, either.
DBIC_MODELER to trace modeler setupSet DBIC_MODELER to 1 if you want to trace what is going on in the modeler internally
$ENV{DBIC_MODELER} = 1
DBICx::Modeler->new( ... )
schema The connected DBIx::Class schema to use/inspect
namespace The package containing the Moose classes that will mimic the class structure of <schema>
$modeler->model( <moniker> )
Return the model source for <moniker>
$modeler->create( <moniker> => ... )
Create a new row for <moniker> and return the modeled object
$modeler->search( <moniker> => ... )
Make a search of <moniker> that will inflate into modeled objects
Robert Krimen, <rkrimen at cpan.org>
Please report any bugs or feature requests to bug-dbicx-modeler at rt.cpan.org, or through
the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=DBICx-Modeler. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
You can find documentation for this module with the perldoc command.
perldoc DBICx::Modeler
You can also look for information at:
Copyright 2009 Robert Krimen, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| DBICx-Modeler documentation | Contained in the DBICx-Modeler distribution. |
package DBICx::Modeler; use warnings; use strict;
our $VERSION = '0.005';
use Moose; use DBICx::Modeler::Carp; use constant TRACE => DBICx::Modeler::Carp::TRACE; use Class::Inspector(); use Scalar::Util qw/weaken/; use DBICx::Modeler::Model::Source; ######### # Class # ######### sub ensure_class_loaded { shift; my $class = shift; return $class if Class::Inspector->loaded( $class ); eval "require $class;"; die "Couldn't load class $class: $@" if $@; return $class; } sub _expand_relative_name { my ($self, $name) = @_; my $class = ref $self || $self; return unless $name; my $parent_class = $class; if ($name =~ s/^\+//) { # Hammatime: Don't touch this! } else { if ($name =~ s/^\-//) { # User wants the parent (wants to be a sibling) my @class = split m/::/, $parent_class; pop @class; $parent_class = join '::', @class; } $name = $parent_class . '::' . $name; } return $name; } ########### # Object ## ########### has schema => qw/is ro required 1/; has schema_class => qw/is ro lazy_build 1/; has [qw/ namespace skip_moniker /] => qw/is rw/; has [qw/ create_refresh sibling_namespace /] => qw/is rw default 1/; has skip_schema_modeler_accessor => qw/is rw default 0/; has [qw/ _model_source_list /] => qw/is ro required 1 lazy 1 isa ArrayRef/, default => sub { [] }; has [qw/ _namespace_list /] => qw/is ro lazy_build 1 isa ArrayRef/; sub _build__namespace_list { my $self = shift; my $class = ref $self || $self; my $default_namespace = do { my @default = split m/::/, $class; if ( my $name = $self->sibling_namespace ) { $name = "Model" if $name eq 1; pop @default; # Use Example::${name} instead of Example::Modeler::${name} (e.g. Example::Model) push @default, $name; } "+" . join "::", @default; }; my $namespace = $self->namespace; $namespace = [] unless defined $namespace; $namespace = [ $namespace ] unless ref $namespace eq "ARRAY"; unless (@$namespace) { croak "You didn't specify a namespace" if $class eq __PACKAGE__; @$namespace = ("?"); # Use the default namespace if none specified } @$namespace = map { $_ eq "?" ? $default_namespace : $_ } @$namespace; $_ = $self->_expand_relative_name( $_ ) for @$namespace; return [ @$namespace ]; } has [qw/ _model_source_lookup_map _model_class_by_moniker_map _moniker_by_model_class_map /] => qw/is ro required 1 lazy 1 isa HashRef/, default => sub { {} }; sub _build_schema_class { my $self = shift; return ref $self->schema; } sub BUILD { my $self = shift; my $given = shift; $self->skip_moniker( $given->{skip} ) if ! exists $given->{skip_moniker} && $given->{skip}; my $schema = $self->schema; my $schema_class = $self->schema_class; $self->_setup_schema_modeler_accessor unless $self->skip_schema_modeler_accessor; $self->_setup_base_model_sources; { $self->schema->modeler( $self ); weaken $self->schema->{modeler}; } return 1; } sub _setup_schema_modeler_accessor { my $self = shift; return if $self->schema_class->can( qw/modeler/ ); $self->schema_class->mk_group_accessors( simple => qw/modeler/ ); } sub _setup_base_model_sources { my $self = shift; my %option = @_; for my $moniker ($self->schema->sources) { my $model_class = $self->model_class_by_moniker( $moniker ); # Initialize base model classes & moniker_by_model_class/model_class_by_moniker my $model_source = DBICx::Modeler::Model::Source->new( moniker => $moniker, modeler => $self, schema => $self->schema, model_class => $model_class, ); $model_class->_model__meta->initialize_base_model_class( $model_source ); $self->_register_model_source( $model_source ); } } sub namespaces { my $self = shift; return @{ $self->_namespace_list } } sub moniker_by_model_class { my $self = shift; my $model_class = shift; return $self->model_source_by_model_class( $model_class )->moniker; # croak "Couldn't find moniker for (model class) $model_class" unless $moniker; } sub find_model_class { my $self = shift; my $query = shift; if ($query =~ s/^\+//) { return $self->ensure_class_loaded( $query ); } # A relative class... 'moniker' return $self->model_class_by_moniker( $query ); } sub model_class_by_moniker { my $self = shift; my $moniker = shift; # Has to be done this way, because the model source might not be loaded yet my $model_class = $self->_model_class_by_moniker_map->{$moniker}; return $model_class if $model_class; for my $namespace ( $self->namespaces ) { my $potential_model_class = "${namespace}::${moniker}"; if (Class::Inspector->loaded( $potential_model_class )) { } else { eval "require $potential_model_class;"; if ($@) { my $file = join '/', split '::', $potential_model_class; if ($@ =~ m/^Can't locate $file/) { TRACE->( "[$self] Unable to load file ($file) for $potential_model_class" ); next; } else { die "Couldn't load class $potential_model_class for $moniker: $@" if $@; } } } $model_class = $potential_model_class; last; # We found something! } croak "Couldn't find model class for (moniker) $moniker" unless $model_class; $self->_moniker_by_model_class_map->{$model_class} = $moniker; return $self->_model_class_by_moniker_map->{$moniker} = $model_class; } sub model_class_by_result_class { my $self = shift; my $result_class = shift; my $moniker = $self->schema_class->source( $result_class )->source_name; return $self->model_class_by_moniker( $moniker ); } sub model_sources { my $self = shift; return @{ $self->_model_source_list }; } sub _model_source { my $self = shift; my $model_source = shift; $model_source = $self->_model_source_lookup_map->{$model_source} while defined $model_source && ! ref $model_source; return $model_source; } sub model_source { my $self = shift; my $model_source = shift; return $self->_model_source( $model_source ) or croak "Couldn't find model source with key $model_source"; } sub model { my $self = shift; return $self->model_source( @_ ); } sub model_source_by_moniker { my $self = shift; my $moniker = shift; my $model_source = $self->_model_source( "::${moniker}" ) or croak "Couldn't find model source for (moniker) $moniker"; return $model_source; } sub model_source_by_model_class { my $self = shift; my $model_class = shift; my $model_source = $self->_model_source( "+${model_class}" ); return $model_source if $model_source; TRACE->( "[$self] Building model source for $model_class" ); # The model class might not have been loaded yet $self->ensure_class_loaded( $model_class ); die "Can't get model source for $model_class since it doesn't have a model meta" unless $model_class->can( '_model__meta' ); my $parent_model_meta = $model_class->_model__meta->parent; die "Strange, model source for $model_class doesn't exist, but it doesn't have a parent" unless $parent_model_meta; my $parent_model_class = $parent_model_meta->model_class; my $parent_model_source = $self->model_source_by_model_class( $parent_model_class ); $model_source = $parent_model_source->clone( model_class => $model_class ); $self->_register_model_source( $model_source ); return $model_source; } sub _register_model_source { my $self = shift; my $model_source = shift; push @{ $self->_model_source_list }, $model_source; my $moniker = $model_source->moniker; my $moniker_key = "::${moniker}"; my $model_class = $model_source->model_class; my $model_class_key = "+${model_class}"; $self->_model_source_lookup_map->{$model_class_key} = $model_source; $self->_model_source_lookup_map->{$model_class} = $model_class_key; $self->_model_source_lookup_map->{$moniker} = $model_class_key; $self->_model_source_lookup_map->{$moniker_key} = $model_class_key; # TODO Add more aliasing } sub create { my $self = shift; my $key = shift; return $self->model_source( $key )->create( @_ ); } sub inflate { my $self = shift; my $key = shift; return $self->model_source( $key )->inflate( @_ ); } sub search { my $self = shift; my $key = shift; return $self->model_source( $key )->search( @_ ); }
1; # End of DBICx::Modeler