How do I create a Catalyst::Model::DBIC::Schema from a DBIC::Schema? - perl

Total lack of documentation makes this difficult..
Essentially, I have
package MyApp::Schema;
sub new_schema {
__PACKAGE__->connect(...)
}
Then I have
package MyCatApp::Model::MyApp;
use Moose;
extends 'Catalyst::Model::DBIC::Schema';
## what here;
_PACKAGE__->make_immutable;
How do I make this work? If I have this in my config...
<Model::MyApp>
schema_class MyApp::Schema
traits Caching
user_defined_schema_accessor foo
</Model::MyApp>
I get the following error:
BEGIN failed--compilation aborted at MyCatapp.psgi line 4, line 1.
Error while loading MyCatapp.psgi: Couldn't instantiate component "Either ->config->{connect_info} must be defined for MyCatApp::Model::MyApp or MyApp::Schema must have connect info defined on it.
But adding sub connect_info {} in MyApp::Schema does not change anything. That error is generated on line 480 of this file.
That method seems to be looking at $schema_class->storage->connect_info which I believe is an method on an instantiated object, and not a function in the package. However, if I try to set
__PACKAGE__->config('schema_class', MyApp::Schema->new_schema)
I then get...
Error while loading : Couldn't instantiate component "MyCatApp::Model::MyApp", "Attribute (schema_class) does not pass the type constraint because: Validation failed for 'Catalyst::Model::DBIC::Schema::Types::SchemaClass' with value MyApp::Schema=HASH(0xb4a5ff0) at /usr/local/lib/perl/5.18.2/Moose/Exception.pm line 37
So, how do I go about doing this...

So.. I'm not sure this is right so I won't mark it as accepted but I was pretty damn close.. This line is dead on..
__PACKAGE__->config('schema_class', MyApp::Schema->new_schema)
And the code is in place for that to work, as per the link above on line 480 of Catalyst::Model::DBIC::Schema. However, the type is wrong..
The type of schema_class is SchemaClass. And, SchemaClass is defined here
subtype SchemaClass,
as LoadableClass,
where { $_->isa('DBIx::Class::Schema') };
Now, SchemaClass is a subtype of a MooseX::Types::LoadableClass which essentially just takes a package name and requires it..
The problem is LoadableClass doesn't take an instance of DBIC::Schema. Anyway.. I was just changed it too..
has schema_class => (
is => 'ro',
isa => 'Object',
required => 1
);
Now I can stuff an instantiated schema into schema_class and pass the conditional if($schema_class->storage && $schema_class->storage->connect_info).
I filed a bug on this here

You could instantiate the connection info in your MyApp::Schema by in it doing:
__PACKAGE__->connection(#your_connection_details);
This will set up a Storage on the class itself, not a schema object. This will then pass the Catalyst::Model::DBIC::Schema check and work successfully. I think this achieves what you're after without changing the underlying classes (though might depend on the details not included).

From the documentation you referenced, your MyCatApp::Model::MyApp would look like this:
__PACKAGE__->config(
schema_class => 'MyApp::Schema',
connect_info => {
dsn => 'dbi:Pg:dbname=mypgdb',
user => 'postgres',
password => '',
pg_enable_utf8 => 1,
on_connect_do => [
'some SQL statement',
'another SQL statement',
],
}
);
And in your controller, you would:
my $db = $c->model('MyApp');

Related

How to use Class:DBI with own constructors or OO-systems like Moo(se)?

When using Class::DBI in Perl, the insert() method from Class::DBI acts as a constructor returning an object. How can I use Class::DBI in combination with object attributes that are not part of any database tables?
For example: I would like to have my Music::Artist class to have a version attribute that is part of the resulting objects (so that I can use this object attribute in my overall application logic), but does not get written to the database?
Ultimately I would like to be able to combine the usage of Class::DBI with OO-systems like Moo(se).
Vanilla Class:DBI example code from metacpan:
package Music::DBI;
use base 'Class::DBI';
Music::DBI->connection('dbi:mysql:dbname', 'username', 'password');
package Music::Artist;
use base 'Music::DBI';
Music::Artist->table('artist');
Music::Artist->columns(All => qw/artistid name/);
#-- Meanwhile, in a nearby piece of code! --#
my $artist = Music::Artist->insert({ artistid => 1, name => 'U2' });
Pseude-code of what I would like to do:
package Music::Artist;
use base 'Music::DBI';
use Moo;
Music::DBI->connection('dbi:mysql:dbname', 'username', 'password');
Music::Artist->table('artist');
Music::Artist->columns(All => qw/artistid name/);
has name => ( is => 'rw' );
has version => ( is => 'rw' );
#-- Meanwhile, in a nearby piece of code! --#
my $artist = Music::Artist->new( name => 'U2', version => '0.1.0' );
$artist = Music::Artist->insert({ artistid => 1, name => $artist->name });
# ... do something with $artist->version ...
(Although this code could run, Class::DBI's insert() of cause overrides the object returned by Moo's new() in the first place.)
How to combine Class::DBI with own or third-party (Moo) constructors?
I read Class::DBIs documentation but did not find any information on how to override insert() as an approach to supply a combined constructor method. I also tried to find repositories on GitHub that make use of Class::DBI and own constructors or OO-systems in the same packages, but did not succeed either.

Perl5 - Moose - Attribute accessors' names

I'd like to ask you for an advice regarding attribute accessors' naming.
I started to develop a project that is supposed to have quite a ramified hierarchy of classes, for example, the SomeFramework class, a bunch of classes like SomeFramework::Logger and, let's say, classes similar to SomeFramework::SomeSubsystem::SomeComponent::SomeAPI classes.
My goal is to design the most efficient communication between all these classes. I'll explain how I'm doing it now, so maybe you would like to share some opinions on how to make it better.
When I initialize the SomeFramework class, I have an object reference which I use from my application.
my $someframework = SomeFramework->new(parameter => 'value');
The SomeFramework class has some attributes, such as logger, configuration, etc, here are some examples of their definitions:
has 'logger' => (
is => 'ro',
isa => 'SomeFramework::Logger',
reader => 'get_logger',
writer => '_set_logger',
builder => '_build_logger',
lazy => 1
);
sub _build_logger {
my $self = shift;
SomeFramework::Logger->new(someframework => $self);
}
I'm passing the reference to the parent object to the child object, because I need the child to have access to the parent and its methods & accessors. So in the SomeFramework::Logger I have such attribute:
has 'someframework' => (
is => 'ro',
isa => 'SomeFramework',
reader => 'get_someframework',
writer => '_set_someframework',
required => 1
);
It lets me to have access to any object from within the SomeFramework::Logger class, usually it looks something like that:
my $configuration =
$self->
get_someframework->
get_configuration->
get_blah_blah;
To extrapolate it, let's look into the SomeFramework::SomeSubsystem::SomeComponent::SomeAPI class. This class has its own "parent" attribute (let's call it somecomponent) which is supposed to have a reference to a SomeFramework::SomeSubsystem::SomeComponent object as the value. The SomeFramework::SomeSubsystem::SomeComponent class has the attribute for its own parent attribute (we can call it somesubsystem) which is supposed to contain a reference to a SomeFramework::SomeSubsystem object. And, finally, this class has the attribute for its own parent too (someframework), so it contains the reference to a SomeFramework object.
It all makes it possible to have something like that inside of the SomeFramework::SomeSubsystem::SomeComponent::SomeAPI class:
my $configuration =
$self->
get_someframework->
get_somesubsystem->
get_somecomponent->
get_configuration->
get_blah_blah;
The first thing I'd like to know: is it a good practice? I hope, it is, but maybe you would advice me to go some more smooth way?
The second question is a bit more complicated (as for me), but I hope you'll help me with it. :) I like canonical names of accessors recommended by D.Conway in his "Perl Best Practices", but I'd like to do something like that:
my $configuration = $self->sc->ss->sf->conf->blah_blah;
Surely I can name all readers in this laconical manner:
has 'some_framework' => (
is => 'ro',
isa => 'SomeFramework',
reader => 'sf',
writer => '_set_someframework',
required => 1
);
But I don't like the idea of managing without the "standard" accessors names. :(
Also I can use MooseX::Aliases, it works fine for something like that:
has 'some_framework' => (
is => 'ro',
isa => 'SomeFramework',
reader => 'get_someframework',
writer => '_set_someframework',
required => 1,
alias => 'sf'
);
It looks fine, but there's an issue with attributes which names do NOT needed to be shortened. For example:
has 'api' => (
is => 'ro',
isa => 'SomeFramework::SomeSubsystem::SomeComponent::API',
reader => '_get_api',
writer => '_set_api',
required => 1,
alias => 'api'
);
In this case Moose throws an exception: Conflicting init_args: (api, api) at constructor. :( As I understand, MooseX::Aliases tries to create an attribute with the same value of the init_args parameter, so it fails. By the way, sometimes it happens, but sometimes it works fine, I haven't discovered when exactly it doesn't work.
Maybe I should have something like that:
has 'api' => (
is => 'ro',
isa => 'SomeFramework::SomeSubsystem::SomeComponent::API',
reader => '_get_api',
writer => '_set_api',
required => 1,
handles => {
api => 'return_self' # It's supposed to have some method that only
# returns the reference to its own object
}
);
? But it doesn't seem to be the best option too, because it helps me only if the attribute contains a reference some object for which I can define the return_self method. If the attribute contains a reference to some "foreign" object or some other value (e.g., a hash), it won't be possible to call that method. :(
Ugh... Sorry for such a long rant! I hope, you have managed to read to here. :)
I'll be very happy to get to know what do you thing and what would you suggest to do. Feel free to share any your ideas on this topic, any fresh ideas will be very appreciated!
Updated on 25.10.2015
As for the bigger question, let me see if I understood. There are an Apple and a Banana. The Fridge has both of them inside. But you want the Apple to know about the Fridge, and the Worm should know about the Apple, so that it can go from Worm up to Apple up to Fridge and turn the $fridge->light off when it wants to sleep. Is that correct? Sounds like a horrible idea that breaks all kinds of design patterns
Well, to be frank, I didn't think it's horrible. As for me, it's quite good when it's possible to have access from some class to some other class within the same framework. Why not? For example, let's imagine we have some class for the jobs-queue runner (let's call it SomeFramework::JobsQueue::Executor) and some class for jobs. Is it really bad to do something like:
package SomeFramework::JobsQueue::Executor;
use Moose;
use MooseX::Params::Validate;
has queue {
isa => 'SomeFramework::JobsQueue',
required => 1,
reader => 'get_queue',
writer => '_set_queue'
}
# This attribute is being set by the framework when the framework
# creates the SomeFramework::JobsQueue::Executor-based object
sub execute {
my($self, $job, $options) = validated_hash(
\#_,
job => { isa => 'SomeFramework::JobsQueue::Job' },
options => { isa => 'HashRef' }
);
my $queue = $self->get_queue;
$queue->mark_as_running($job->get_id);
$job->execute(options => $options);
$queue->mark_as_completed($job->get_id);
}
? So, our queue-runner object is aware about the queue object it "belongs" to, so it can call some methods of this queue object.
Or let's look at much more simple example:
package SomeFramework::SomeSubsystem;
use Moose;
has 'some_framework' => {
isa => 'SomeFramework',
required => 1,
reader => 'get_some_framework',
writer => '_set_some_framework'
}
sub some_method {
my $self = shift;
$self->get_some_framework->get_logger->log_trace("Hello, world!");
}
So, our object knows how to call methods of the framework's object that has initialized that object, moreover it can call some methods of the framework's object and even some methods of other objects initialized and stored by the framework's object.
If it's really bad, would you be so kind as to help me to understand why? Thank you!

In Perl, what's the meaning of this code " has 'absolute_E' => (is => 'rw',default => sub {0} );"

The following codes make me so confused, I can't find any related knlowledge about the syntax "has ,is ,default, lazy". Can anybody make a detailed explain for me, best wishes.
has 'absolute_E' => (is => 'rw', default => sub {0} );
has 'retract_speed_mm_min' => (is => 'lazy');
has 'retract_speed_mm_min' => (is => 'lazy');
Judging by this line, this is probably a Moo class. To confirm this, have a look near the top of the file, and you should see something like use Moo.
Moo is an object-oriented framework for Perl. I'll assume you understand OO concepts.
Some historical background: Perl 5 has built-in OO capabilities, however it can get a little cumbersome at times. Then Moose came around as an improved way of OOP in Perl. But Moose was also quite heavy, with a compile-time cost, so Moo (and also Mouse just before it) came after that as something of a lighter-weight subset of Moose.
has is for defining attributes in your class.
has 'absolute_E' => ( is => 'rw', default => sub {0} );
This defines an attribute named absolute_E.
is => 'rw' means it is readable and writable, which means you can do this:
my $value = $obj->absolute_E; # gets the value
$obj->absolute_E($value); # sets the value
When you instantiate the object, you can supply a value for the attribute:
my $obj = My::Class->new( absolute_E => 5 );
But if you don't supply anything then absolute_E is set to 0 by default.
This second attribute has a few more things:
has 'retract_speed_mm_min' => (is => 'lazy');
This is short form for:
has 'retract_speed_mm_min' => (
is => 'ro',
lazy => 1,
builder => '_build_retract_speed_mm_min'
);
This attribute is readonly which means you can't change its value after construction. But you can supply a value at construction as before.
The builder is another way of providing a default value. It requires the class to have a separate method named _build_retract_speed_mm_min that should return the default value.
lazy works with builder. It means that the attribute should not be set by the builder until it the attribute is used. The delay may be used because the builder depends on other attributes in order to build this attribute's value.
There's a lot more in Moo and Moose. I would suggest reading http://modernperlbooks.com/books/modern_perl_2014/07-object-oriented-perl.html and https://metacpan.org/pod/Moose::Manual and https://metacpan.org/pod/Moo.
That code basically equals
has ('absolute_E', 'is', 'rw', 'default', sub {0} );
has ('retract_speed_mm_min', 'is', 'lazy');
And has looks like a user-defined subroutine.
=> is almost the same as ,:
The => operator is a synonym for the comma except that it causes a word on its left to be interpreted as a string if it begins with a letter or underscore and is composed only of letters, digits and underscores.

Moose: injecting parameters into construction

I have a filename attribute that has attached validation. Now when the filename isn't specified during construction I want to read it from a configuration file.
subtype 'filename'
=> as 'Str'
=> where { -r $_ }
=> message { "$_ is not a readable file" };
has 'students_file' => ( is => 'rw', isa => 'filename', default => '' );
has 'teachers_file' => ( is => 'rw', isa => 'filename', default => '' );
The issue is that BUILD seems to be run after the validation occurs. All examples of BUILDARGS seem to handle a different way of constructing the object.
So where should I read the configuration and set the attribute?
Give teachers_file and students_file builder methods (or inline default subs) that set them from the config file. The builder will only run if those attributes aren't provided as keys to the constructor (unless you use init_arg => undef to prevent them from being set in the constructor).
If your config is an attribute of its own, with a builder that reads the config file, then you have an ordering problem between config and teachers_file and students_file. You can resolve this by making teachers_file and students_file lazy attributes, which ensures that they won't try to be constructed before the config attribute. However, you may want to make sure that the "foo is not a readable file" error is thrown as early as possible during construction, instead of the first time the attribute is used. You can work around that by adding
sub BUILD {
my $self = shift;
$self->teachers_file;
$self->students_file;
}
which ensures that those attributes are read once (and constructed) before the constructor returns.

Using Moose, how do I set the value of a 'ro' Attribute Trait, in runtime?

I've got an Attribute Trait that I want to set on the basis of other class attributes. I realy want a default on an Attribute Trait that gets a copy of the class $self and not the meta for the attribute. I want to do something like this in my class:
after 'BUILD' => sub {
my $self = shift;
$self->meta->get_attribute('id')->column_name( $self->_unique_key_name );
};
But, I want to keep my attribute trait RO? Is this possible. I know the MOP allows one to set the value of a class-attribute, but I can't figure out how to set an attribute on the meta-attribute.
This sounds like a really odd design (why would a metaclass need an instance of a class it is describing?) -- but you can do this easily enough by using the metaclass of the metaclass (remember that Moose meta classes are bootstrapped using the MOP itself):
$self->meta->meta->get_attribute("foo")->default($some_value);
Also remember that defaults need to be wrapped in a coderef if they are references themselves: $some_value = sub { $instance };
Actually, this won't work - 'default' is read only. Rather than fiddling with the MOP at such a low level, I would urge you to reconsider your design - e.g. store your 'default' in another attribute, and writing a default sub that delegated to it:
package MyApp::Meta::Attribute::Trait::Foo;
# set at runtime, when we have an instance to store here
has _default_of_foo => (
is => 'rw', isa => 'Object',
);
has foo => (
is => 'ro', isa => 'Object',
lazy => 1,
default => sub { shift->_default_of_foo },
);