Catalyst-Model-DBIC-Schema
view release on metacpan or search on metacpan
lib/Catalyst/Model/DBIC/Schema.pm view on Meta::CPAN
Traits are applied at L<Catalyst::Component/COMPONENT> time using
L<CatalystX::Component::Traits>.
C<ref $self> will be an anon class if any traits are applied, C<<
$self->_original_class_name >> will be the original class.
When writing a Trait, interesting points to modify are C<BUILD>, L</setup> and
L</ACCEPT_CONTEXT>.
Traits that come with the distribution:
=over 4
=item L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>
=item L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>
=item L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>
=item L<Catalyst::TraitFor::Model::DBIC::Schema::PerRequestSchema>
=back
=head2 compose_namespaces
This model calls L<DBIx::Class::Schema/compose_namespace> by default to
install classes into the model namespaces. You can turn that off by
setting this attribute to false. Default is true.
=head2 install_model_shortcuts
If you don't want shortcut models so you can do e.g. C<< $c->model('DB::Book')
>> set this attribute to false, Default is true.
=head2 storage_type
Allows the use of a different C<storage_type> than what is set in your
C<schema_class> (which in turn defaults to C<::DBI> if not set in current
L<DBIx::Class>). Completely optional, and probably unnecessary for most
people until other storage backends become available for L<DBIx::Class>.
=head1 ATTRIBUTES
The keys you pass in the model configuration are available as attributes.
Other attributes available:
=head2 connect_info
Your connect_info args normalized to hashref form (with dsn/user/password.) See
L<DBIx::Class::Storage::DBI/connect_info> for more info on the hashref form of
L</connect_info>.
=head2 model_name
The model name L<Catalyst> uses to resolve this model, the part after
C<::Model::> or C<::M::> in your class name. E.g. if your class name is
C<MyApp::Model::DB> the L</model_name> will be C<DB>.
=head2 _default_cursor_class
What to reset your L<DBIx::Class::Storage::DBI/cursor_class> to if a custom one
doesn't work out. Defaults to L<DBIx::Class::Storage::DBI::Cursor>.
=head1 ATTRIBUTES FROM L<MooseX::Traits::Pluggable>
=head2 _original_class_name
The class name of your model before any L</traits> are applied. E.g.
C<MyApp::Model::DB>.
=head2 _traits
Unresolved arrayref of traits passed in the config.
=head2 _resolved_traits
Traits you used resolved to full class names.
=head1 CONFIGURING YOUR SCHEMA AND RESULTSETS
See the documentation for
L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy> for instructions on how
to pass config values from your L<Catalyst> config to your
L<DBIx::Class::Schema> and/or L<DBIx::Class::ResultSet> classes.
=head1 METHODS
=head2 new
Instantiates the Model based on the above-documented ->config parameters.
The only required parameter is C<schema_class>. C<connect_info> is
required in the case that C<schema_class> does not already have connection
information defined for it.
=head2 schema
Accessor which returns the connected schema being used by the this model.
There are direct shortcuts on the model class itself for
schema->resultset, schema->source, and schema->class.
=head2 composed_schema
Accessor which returns the composed schema, which has no connection info,
which was used in constructing the L</schema>. Useful for creating
new connections based on the same schema/model. There are direct shortcuts
from the model object for composed_schema->clone and composed_schema->connect
If L</compose_namespaces> is not true, L</composed_schema> is equivalent to
C<< $model->schema_class->clone >>.
=head2 clone
Shortcut for ->composed_schema->clone
=head2 connect
Shortcut for ->composed_schema->connect
=head2 source
Shortcut for ->schema->source
=head2 class
Shortcut for ->schema->class
=head2 resultset
Shortcut for ->schema->resultset
=head2 txn_do
Shortcut for ->schema->txn_do
=head2 txn_scope_guard
Shortcut for ->schema->txn_scope_guard
=head2 storage
Provides an accessor for the connected schema's storage object.
See L<DBIx::Class::Storage> and L<DBIx::Class::Storage::DBI>.
=cut
has schema_class => (
is => 'ro',
isa => SchemaClass,
required => 1
);
has compose_namespaces => (is => 'ro', isa => Bool, default => 1 );
has install_model_shortcuts => (is => 'ro', isa => Bool, default => 1 );
has storage_type => (is => 'rw', isa => Str);
has connect_info => (is => 'rw', isa => ConnectInfo, coerce => 1);
has model_name => (
is => 'ro',
isa => Str,
required => 1,
lazy_build => 1,
);
has _default_cursor_class => (
is => 'ro',
isa => LoadableClass,
default => 'DBIx::Class::Storage::DBI::Cursor',
);
has schema => (is => 'rw', isa => Schema);
my $app_class;
before COMPONENT => sub {
$app_class = ref $_[1] || $_[1];
};
sub app_class { $app_class }
sub BUILD {
my ($self, $args) = @_;
my $class = $self->_original_class_name;
my $schema_class = $self->schema_class;
if( !$self->connect_info ) {
if($schema_class->storage && $schema_class->storage->connect_info) {
$self->connect_info($schema_class->storage->connect_info);
}
else {
die "Either ->config->{connect_info} must be defined for $class"
. " or $schema_class must have connect info defined on it."
. " Here's what we got:\n"
. Dumper($args);
}
}
if (exists $self->connect_info->{cursor_class}) {
eval { use_module($self->connect_info->{cursor_class}) }
or croak "invalid connect_info: Cannot load your cursor_class"
. " ".$self->connect_info->{cursor_class}.": $@";
}
$self->setup($args);
my $is_installed = defined $self->composed_schema;
if (not $is_installed) {
$self->composed_schema($self->compose_namespaces ?
$schema_class->compose_namespace($class)
:
$schema_class->clone
);
}
$self->schema($self->composed_schema->clone)
unless $self->schema;
$self->schema->storage_type($self->storage_type)
if $self->storage_type;
$self->schema->connection($self->connect_info);
if ((not $is_installed) && $self->install_model_shortcuts) {
$self->_install_rs_models;
}
}
sub clone { shift->composed_schema->clone(@_); }
sub connect { shift->composed_schema->connect(@_); }
# some proxy methods, see also SchemaProxy
sub resultset { shift->schema->resultset(@_); }
sub txn_do { shift->schema->txn_do(@_); }
sub txn_scope_guard { shift->schema->txn_scope_guard(@_); }
sub storage { shift->schema->storage(@_); }
=head2 setup
Called at C<BUILD> time before configuration, but after L</connect_info> is
set. To do something after configuuration use C<< after BUILD => >>.
Receives a hashref of args passed to C<BUILD>.
=cut
sub setup { 1 }
=head2 ACCEPT_CONTEXT
Point of extension for doing things at C<< $c->model >> time with context,
returns the model instance, see L<Catalyst::Manual::Intro/ACCEPT_CONTEXT> for
more information.
=cut
sub ACCEPT_CONTEXT { shift }
sub _install_rs_models {
my $self = shift;
my $class = $self->_original_class_name;
no strict 'refs';
my @sources = $self->schema->sources;
unless (@sources) {
warn <<'EOF' unless $ENV{CMDS_NO_SOURCES};
******************************* WARNING ***************************************
* No sources found (did you forget to define your tables?) *
* *
* To turn off this warning, set the CMDS_NO_SOURCES environment variable. *
*******************************************************************************
EOF
}
foreach my $moniker (@sources) {
my $classname = "${class}::$moniker";
*{"${classname}::ACCEPT_CONTEXT"} = sub {
shift;
shift->model($self->model_name)->resultset($moniker);
}
}
}
sub _reset_cursor_class {
my $self = shift;
if ($self->storage->can('cursor_class')) {
$self->storage->cursor_class($self->_default_cursor_class)
if $self->storage->cursor_class ne $self->_default_cursor_class;
}
}
{
my %COMPOSED_CACHE;
sub composed_schema {
my $self = shift;
my $class = $self->_original_class_name;
my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};
$$store = shift if @_;
return $$store
}
}
sub _build_model_name {
my $self = shift;
my $class = $self->_original_class_name;
(my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;
return $model_name;
}
__PACKAGE__->meta->make_immutable;
=head1 ENVIRONMENT
=over 4
=item CMDS_NO_SOURCES
Set this variable if you will be using schemas with no sources (Result classes)
to disable the warning. The warning is there because having no Result classes
is usually a mistake.
=back
=head1 Setting up DBIC authentication
You can set this up with
L<Catalyst::Authentication::Store::DBIx::Class> in MyApp.pm:
package MyApp;
use Catalyst qw/... Authentication .../;
...
__PACKAGE__->config('Plugin::Authentication' =>
{
default_realm => 'members',
members => {
credential => {
class => 'Password',
password_field => 'password',
password_type => 'hashed'
password_hash_type => 'SHA-256'
},
( run in 0.571 second using v1.01-cache-2.11-cpan-39bf76dae61 )