Moose Triggers Not Firing When Using DBIX::Class - perl

I am new to Moose and am trying to use it with DBIx::Class. Basic DBIC querying and updating work find, but any trigger I attempt to write does not get executed when I modify an attribute.
use Modern::Perl;
use Data::Dumper;
my $schema = My::Schema->connect(<connect str>, <usr>, <psw>) or die $!;
my $rs = $schema->resultset('Isin')->search( sid => 3929 );
my $security_obj = $rs->first;
print $security_obj->isin, "\n";
$security_obj->isin('Test1Foo'); # <- expect to see FOO printed by trigger
print $security_obj->isin, "\n";
I expect to see the trigger for 'isin' print 'FOO', but nothing happens. If I strip out DBIx::Class from the package the trigger is executed as expected.
I suspect that DBIx::Class is setting the value in a way that prevents the trigger from firing.
Unfortunately, I haven't had much luck finding resources about using DBIx::Class with Moose. What I have written is mostly based on what I found at DBIx::Class and Moose.
Am I using DBIx::Class and/or Moose wrong? Is there a different ORM that I should be using with Moose?
The package with the trigger that won't fire:
package My::Schema::Result::Isin;
use DBIx::Class;
use Moose;
use Carp;
extends 'DBIx::Class';
has 'isin' => ( is => "rw", isa => "Str", trigger => \&_mod_isin);
has 'sid' => ( is => "ro", isa => "Int");
sub _mod_isin {
print "FOO\n";
return;
};
no Moose;
__PACKAGE__->load_components('Core');
__PACKAGE__->table('isin');
__PACKAGE__->add_columns(
isin => { data_type => 'varchar2', size => 12 },
sid => { data_type => 'integer', size => 6 },
);
__PACKAGE__->set_primary_key('isin');

First, you have the problem of extending a non-Moose class from within Moose. This is a problem because DBIx::Class doesn't inherit from Moose::Object, so you won't get the standard Moose methods like does. See Moose::Cookbook::Basics::Recipe11 for solving this problem.
Second, you have the bigger problem that you have two different sets of magic which are trying to create subroutines for you. You have Moose, whose magic creates isin and sid subroutines, and you have DBIx::Class, whose magic also creates isin and sid subroutines which replace the ones that Moose created.
You might want to compose in a Moose Role with an around modifier, as jrockway suggested.

Have you tried using writer => \&_mod_isin instead?

Related

Writing to read-only attributes inside a Perl Moose class

Using Perl and Moose, object data can be accessed in 2 ways.
$self->{attribute} or $self->attribute()
Here is a simple example demonstrating both:
# Person.pm
package Person;
use strict;
use warnings;
use Moose;
has 'name' => (is => 'rw', isa => 'Str');
has 'age' => (is => 'ro', isa => 'Int');
sub HAPPY_BIRTHDAY {
my $self = shift;
$self->{age}++; # Age is accessed through method 1
}
sub HAPPY_BIRTHDAY2 {
my $self = shift;
my $age = $self->age();
$self->age($age + 1); # Age is accessed through method 2 (this will fail)
}
1;
# test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Person;
my $person = Person->new(
name => 'Joe',
age => 23,
);
print $person->age()."\n";
$person->HAPPY_BIRTHDAY();
print $person->age()."\n";
$person->HAPPY_BIRTHDAY2();
print $person->age()."\n";
I know that when you are outside of the Person.pm file it is better to use the $person->age() version since it prevents you from making dumb mistakes and will stop you from overwriting a read only value, but my question is...
Inside of Person.pm is it best to use $self->{age} or $self->age()? Is it considered bad practice to overwrite a read-only attribute within the module itself?
Should this attribute be changed to a read/write attribute if its value is ever expected to change, or is it considered acceptable to override the read-only aspect of the attribute by using $self->{age} within the HAPPY_BIRTHDAY function?
When using Moose, the best practice is to always use the generated accessor methods, even when inside the object's own class. Here are a few reasons:
The accessor methods may be over-ridden by a child class that does something special. Calling $self->age() assures that the correct method will be called.
There may be method modifiers, such as before or after, attached to the attribute. Accessing the hash value directly will skip these.
There may be a predicate or clearer method attached to the attribute (e.g. has_age). Messing with the hash value directly will confuse them.
Hash keys are subject to typos. If you accidentally say $self->{aeg} the bug will not be caught right away. But $self->aeg will die since the method does not exist.
Consistency is good. There's no reason to use one style in one place and another style elsewhere. It makes the code easier to understand for newbs as well.
In the specific case of a read-only attribute, here are some strategies:
Make your objects truly immutable. If you need to change a value, construct a new object which is a clone of the old one with the new value.
Use a read-only attribute to store the real age, and specify a private writer method
For example:
package Person;
use Moose;
has age => ( is => 'ro', isa => 'Int', writer => '_set_age' );
sub HAPPY_BIRTHDAY {
my $self = shift;
$self->_set_age( $self->age + 1 );
}
Update
Here's an example of how you might use a lazy builder to set one attribute based on another.
package Person;
use Moose;
has age => ( is => 'rw', isa => 'Int', lazy => 1, builder => '_build_age' );
has is_baby => ( is => 'rw', isa => 'Bool', required => 1 );
sub _build_age {
my $self = shift;
return $self->is_baby ? 1 : 52
}
The lazy builder is not called until age is accessed, so you can be sure that is_baby will be there.
Setting the hash element directly will of course skip the builder method.
I don't think $self->{age} is a documented interface, so it's not even guaranteed to work.
In this case I'd use a private writer as described in https://metacpan.org/pod/Moose::Manual::Attributes#Accessor-methods:
has 'weight' => (
is => 'ro',
writer => '_set_weight',
);
You could even automate this using 'rwp' from https://metacpan.org/pod/MooseX::AttributeShortcuts#is-rwp:
use MooseX::AttributeShortcuts;
has 'weight' => (
is => 'rwp',
);
Out-of-the-box perl isn't type safe and doesn't have much in the way of encapsulation, so it's easy to do reckless things. Moose imposes some civilization on your perl object, exchanging security and stability for some liberty. If Moose gets too stifling, the underlying Perl is still there so there are ways to work around any laws the iron fist of Moose tries to lay down.
Once you have wrapped your head around the fact that you have declared an attribute read-only, but you want to change it, even though you also said you wanted it to be read-only, and in most universes you declare something read only because you don't want to change it, then by all means go ahead and update $person->{age}. After all, you know what you are doing.

Perl share variables with subclasses

I know this may be a very simple topic but I am trying to get the best logic since I am still new to Perl.
If I do not use OO and just split the code into files, all global variables are accessed among all files.
I am trying to do the same but using OO style. Example is I want a base class say called "BaseSub" that has a hash containing the configuration for the application say called %Config. Now I have a sub class called "DB" for the database connection and I want to access the settings from %Config which lives in "BaseSub" package. How do I do that.
If you're writing OO perl in this day and age, you really should be using Moose. It makes OO code much easier, cleaner and smaller.
The proper way to inherit variables is to make object attributes. Here's a quick example:
package MyBaseClass;
use Moose;
has config => (
is => 'ro',
default => sub { {
who => 'World',
} }
);
package MyClass;
use Moose;
extends qw(MyBaseClass);
sub greet
{
my $self = shift;
printf("Hello %s!\n", $self->config->{who});
}
package main;
my $object = MyClass->new();
$object->greet();
A great starting point for learning about Moose is the Moose::Manual.
Edit:
If you want be able to modify the config, you can either just poke the hashref returned from the config accessor directly:
$object->config->{who} = 'Friends';
But a better approach might be to make a config class and make the config attribute hold an instance of that:
package Myconfig;
use Moose;
has who => (is => 'rw', default => 'World');
package MyBaseClass;
use Moose;
has config => (
is => 'ro',
isa => 'MyConfig',
default => sub { MyConfig->new },
);
# inherit, instantiate, etc as before...
$object->config->who('Friends');
Another approach could be Moose::Meta::Attribute::Native::Trait::Hash which makes it easy to setup helper methods to work with native Perl datatypes.
Use its full name.
for (keys(%BaseSub::Config)) {
print("$_: $BaseSub::Config{$_}\n");
}
You could also import it.
our %Config; *Config = \%BaseSub::Config;
for (keys(%Config)) {
print("$_: $Config{$_}\n");
}

Using blessed CodeRefs with Moose type constraints

We use Moose classes that serialize iterators into various output formats. We describe the iterator as an attribute:
has iterator => (
is => 'ro',
isa => 'CodeRef',
required => 1,
);
This has worked fine so far, but we have lately been using Iterator::Simple to prepare iterators for later consumption. This means that we can go about writing this:
has iterator => (
is => 'ro',
isa => 'CodeRef|Iterator::Simple::Iterator',
required => 1,
);
And allow our serializers to accept the iterator class correctly. However, that seems to be a incomplete solution.
Is there a way in Moose to specify the constraint that the attribute must be callable? I suspect it may be possible with Moose::Util::TypeConstraints and using overload::Overloaded on &{} to check, but I'd like to know if anyone has created a module to do this already or if there is a Moose-standard way to test for this.
CodeRef only allows unblessed code references. Fortunately, it's easy to make your own types.
Define Callable as shown below, then use it instead of CodeRef. It allows the following:
Unblessed code references.
Blessed code references.
Objects that pretend to be code references (i.e. those that overload &{}).
use Moose::Util::TypeConstraints;
use overload qw( );
use Scalar::Util qw( );
subtype 'Callable'
=> as 'Ref'
=> where {
Scalar::Util::reftype($_) eq 'CODE'
||
Scalar::Util::blessed($_) && overload::Method($_, "&{}")
}
# Written such that parent's inline_as needs not be prepended.
=> inline_as {'(
(Scalar::Util::reftype('.$_[1].') // "") eq 'CODE'
||
Scalar::Util::blessed('.$_[1].') && overload::Method('.$_[1].', "&{}")
)'};
no Moose::Util::TypeConstraints;
Do you know enough to get from Scalar::Util::reftype?

Setting Up Perl Module Structure

I'm having trouble figuring out how to structure Perl modules in an object oriented way so I can have one parent module with a number of submodules and only the specific submodules that are needed would be loaded by a calling script. For example I want to be able to make method calls like so:
use Example::API;
my $api = Example::API->new();
my $user = {};
$user->{'id'} = '12345';
$api->Authenticate();
$user->{'info'} = $api->Users->Get($user->{'id'});
$user->{'friends'} = $api->Friends->Get($user->{'id'});
In terms of file structure I'd like to have the modules setup as follows or in whatever structure is required to make everything work correctly:
api.pm
users.pm
friends.pm
...
The reason I want to do this in the first place is so that if someone just wants to authenticate against the API they don't have to load all the other modules. Similarly, if someone just wants to get a user's information, they wouldn't have to load the friends.pm module, just the users.pm. I'd appreciate it if you could provide the necessary example Perl code for setting up each module as well as explain how the file structure should be setup. If I'm going about this all wrong to accomplish what I'm try to accomplish I'd appreciate an explanation of the best way to do this and some example code on how it should be setup.
From your example, in your main module I assume you will be providing accessor methods to get at the subclasses. So all you have to do is include require Sub::Module; at the top of that method. Nothing will happen at compile time, but the first time that code is run, perl will load the module. After the first load, the line require Sub::Module; will become a no-op.
If all of your code is object oriented, you won't need to worry about importing functions. But if you do, the statement use Module qw(a b c); is interpreted as:
BEGIN {
require Module;
Module->import(qw(a b c));
}
BEGIN makes it happen at compile time, but there is nothing stopping you from using the internals at run time. Any subroutines you import at runtime must be called with parenthesis, and prototypes will not work, so unless you know what you are doing, runtime imports are probably a bad idea. Runtime requires and access via package methods are completely safe though.
So your $api->Users method might work something like this:
# in package 'Example::API' in the file 'Example/API.pm'
sub Users {
require Example::API::Users; # loads the file 'Example/API/Users.pm'
return Example::API::Users->new( #_ ); # or any other arguments
}
In my examples above, I showed two translations between package names and the files they were in. In general, all :: are changed to / and .pm is added to the end. Then perl will search for that file in all of the directories in the global variable #INC. You can look at the documentation for require for all of the details.
Update:
One way to cache this method would be to replace it at runtime with a function that simply returns the value:
sub Users {
require Example::API::Users;
my $users = Example::API::Users->new;
no warnings 'redefine';
*Users = sub {$users};
$users
}
Here's a big ugly Moose example that selectively applies roles to an API driver instance.
script.pl
use Example::User;
# User object creates and authenticates a default API object.
my $user = Example::User->new( id => '12345' );
# When user metadata is accessed, we automatically
# * Load the API driver code.
# * Get the data and make it available.
print "User phone number is: ", $user->phone_number, "\n";
# Same thing with Friends.
print "User has ", $user->count_friends, " friends\n";
print "User never logged in\n" unless $user->has_logged_in;
Example/API.pm - the basic protocol driver class:
package Example::API;
use Moose;
has 'host' => (
is => 'ro',
default => '127.0.0.1',
);
sub Authenticate {
return 1;
}
# Load the user metadata API driver if needed.
# Load user metadata
sub GetUserInfo {
my $self = shift;
require Example::API::Role::UserInfo;
Example::API::Role::UserInfo->meta->apply($self)
unless $self->does('Example::API::Role::UserInfo');
$self->_Get_UserInfo(#_);
}
# Load the friends API driver if needed.
# Load friends data and return an array ref of Friend objects
sub GetFriends {
my $self = shift;
#require Example::API::Role::Friends;
Example::API::Role::Friends->meta->apply($self)
unless $self->does('Example::API::Role::Friends');
$self->_Get_Friends(#_);
}
The user metadata and friends data drivers are built as 'roles' which are dynamically applied to an API driver instance as needed.
Example/API/Role/UserInfo.pm:
package Example::API::Role::UserInfo;
use Moose::Role;
sub _Get_UserInfo {
my $self = shift;
my $id = shift;
my $ui = Example::API::User::MetaData->new(
name => 'Joe-' . int rand 100,
phone_number => int rand 999999,
);
return $ui;
}
Example/API/Role/Friends.pm:
use Moose::Role;
sub _Get_Friends {
my $self = shift;
my $id = shift;
my #friends = map {
Example::API::Friend->new(
friend_id => "$id-$_",
name => 'John Smith'
);
} 1 .. (1 + int rand(5));
return \#friends;
}
A friend object:
Example/API/Friend.pm
package Example::API::Friend;
use Moose;
has 'friend_id' => (
is => 'ro',
isa => 'Str',
required => 1,
);
has 'name' => ( isa => 'Str', is => 'ro', required => 1 );
And a user metadata object.
Example/API/User/MetaData.pm
package Example::API::User::MetaData;
use Moose;
has 'name' => (
is => 'ro',
isa => 'Str',
);
has 'phone_number' => (
is => 'ro',
isa => 'Str',
);
has 'last_login' => (
is => 'ro',
isa => 'DateTime',
predicate => 'has_logged_in',
);
And finally a user object. I've used many Moose features to make this a very capable object with only a small amount of imperative code.
package Example::User;
use Moose;
has 'id' => (
is => 'ro',
isa => 'Int',
required => 1,
);
has 'server_connection' => (
is => 'ro',
isa => 'Example::API',
builder => '_build_server_connection',
);
# Work with a collection of friend objects.
has 'friends' => (
is => 'ro',
isa => 'ArrayRef[Example::API::Friend]',
traits => ['Array'],
handles => {
all_friends => 'elements',
map_friends => 'map',
filter_friends => 'grep',
find_option => 'first',
get_option => 'get',
join_friends => 'join',
count_friends => 'count',
has_no_friends => 'is_empty',
sorted_friends => 'sort',
},
lazy_build => 1,
);
has 'user_info' => (
is => 'ro',
isa => 'Example::API::User::MetaData',
handles => {
name => 'name',
last_login => 'last_login',
phone_number => 'phone_number',
has_logged_in => 'has_logged_in',
},
lazy_build => 1,
);
sub _build_server_connection {
my $api = Example::API->new();
$api->Authenticate();
return $api;
}
sub _build_friends {
my $self = shift;
$self->server_connection->GetFriends( $self->id );
}
sub _build_user_info {
my $self = shift;
$self->server_connection->GetUserInfo( $self->id );
}
This example makes use of a lot of Moose magic, but you wind up with a very simple interface for those using the objects. While this is close to 200 lines of formatted code, we get a huge amount done.
Adding type coercion would give an even easier interface. Raw string dates can be automatically parsed into DateTime objects. Raw IP addresses and server names can be converted into API servers.
I hope this inspires you to take a look at Moose. The documentation is excellect, check out the Manual and the Cookbooks, in particular.
Managing the exports is tricky, but you could use an AUTOLOAD solution to this problem. If perl doesn't recognize the subroutine name you are trying to call, it can pass it to a sub called AUTOLOAD. Suppose we did this:
use Example::API;
sub AUTOLOAD {
my $api = shift;
eval "require $AUTOLOAD"; # $api->Foo->... sets $AUTOLOAD to "Example::API::Foo"
die $# if $#; # fail if no Example::API::Foo package
$api;
}
Then this code:
$api = new Example::API;
$api->Foo->bar(#args);
will (assuming we haven't imported Example::API::Foo first) call our AUTOLOAD method, attempt to load the Example::API::Foo module, and then try to call the method Example::API::Foo::bar with the $api object and the other arguments you provide.
Or in the worst case,
$api->Foo->bar(#args)
causes this code to be invoked
eval "require Example::API::Foo";
die $# if $#;
&Example::API::Foo::bar($api,#args);
Depending on how you use this feature, it might be a lot more overhead than just importing everything you need.
There are a number of tools that can be used to quickly build an skeletal structure for your new module development.
h2xs comes with the standard Perl distribution. Its primary focus is on building XS code for interfacing with C libraries. However, it does provide basic support for laying out pure Perl projects: h2xs -AX --skip-exporter -n Example::API
I use Module::Starter to build a beginning layout for my module development. It does a lot that h2xs doesn't do. module-starter --module=Example::API,Example::Friends,Example::Users --author="Russel C" --email=russel#example.com
Dist::Zilla is a new tool that handles many tasks related to maintaining a Perl module distribution. It is amazingly powerful and flexible. But it is new and the docs are a bit rough. The unavoidable complexity that comes with all that power and flexibility means that learning to use it is a project. It looks very interesting, but I haven't taken the time to dive in, yet.
If you need to limit the number of methods loaded, you can use AutoLoader or SelfLoader to load subroutines as they are called. This will lead to a slight overhead when a method is called for the first time. In my experience, this approach is rarely needed.
The best thing is to keep your objects small and strictly defined so that they embody a simple concept. Do not allow ambiguity or half-way concepts into your objects, instead consider using composition and delegation to handle areas of potential confusion. For example, instead of adding date formatting methods to handle a user's last login, assign DateTime objects to the last_login attribute.
In the interest of making composition and delegation easy, consider using Moose to build your objects. It removes much of the drudgery involved in Perl OOP and object composition and delegation in specific.

Do MooseX::AttributeHelpers and MooseX::FollowPBP interact correctly?

The following code defines two classes (DeckA and DeckB) that differ only in whether they use the features that come with MooseX::AttributeHelpers. The getters generated by Moose for DeckB are not what I expected. Is this a bug or am I misunderstanding how MooseX::AttributeHelpers and MooseX::FollowPBP ought to interact?
My workaround for now has been to avoid using the is argument in such situations and instead declare a reader and writer as needed.
use strict;
use warnings;
my %moose_args = (
isa => 'ArrayRef[Str]',
is => 'ro',
default => sub {[]},
);
my %moose_attr_helper_args = (
metaclass => 'Collection::Array',
provides => {
elements => 'get_all_cards',
},
);
package DeckA;
use Moose;
use MooseX::FollowPBP;
use MooseX::AttributeHelpers;
has 'cards' => (%moose_args);
package DeckB;
use Moose;
use MooseX::FollowPBP;
use MooseX::AttributeHelpers;
has 'cards' => (%moose_args, %moose_attr_helper_args);
package main;
for my $class (qw(DeckA DeckB)){
my $deck = $class->new;
print "\n$class\n";
for my $method ( qw(cards get_cards get_all_cards) ){
print "$method: ", $deck->can($method) ? 'yes' : 'no', "\n";
}
}
Output:
DeckA
cards: no
get_cards: yes
get_all_cards: no
DeckB
cards: yes # Not what I expected.
get_cards: no # Not what I expected.
get_all_cards: yes
They don't work when you use the metaclass option for MX::AH.
However, the latest Moose has integrated support for native helpers, with a slightly tweaked API. This version uses traits (a role applied to the attribute), and it should work just fine with MX::FollowPBP.
I had the same problem, so I really appreciate FM's question and Dave Rolsky's answer.
Rephrasing part of his answer so that my simple self would have understood it the first time I read it:
Instead of using MooseX::AttributeHelpers, you can simply use "traits" in the latest version of Moose. This eliminates the conflict with MooseX::FollowPBP, while still giving you the same functionality.
For using traits, see Moose::Meta::Attribute::Native.