Catalyst add methods to a DBI model - perl

How would I add methods to my DBI model if I have 'Catalyst::Model::DBI' based model and I'd like a method to have something like $c->model('DBI')->my_method(); but $c->model('DBI') doesn't return a ref to my that object, rather I get back a DBI::db. I can get back the dbh and operate on that, but I have a bunch of utility methods that I'd prefer to add here.

I haven’t seen you code so I can’t know for sure what you’re doing but if you’re using Catalyst::Model::DBI you’re doing something wrong. The raw model does return the object, e.g.: MyApp::Model::DBI=HASH(0xdf7ba0)
It sounds like you might be trying to load DBI with the Adaptor stuff. Subclassing DBI is harder than you might think so I’d definitely shy away from that.
Minimal reproduction–
# Create a new test model with SQLite.
script/*create.pl model DBI DBI "dbi:SQLite::memory:"
# A test controller to go with it.
script/*create.pl controller DBI
# Change the index method to show your raw model–
sub index :Path Args(0) {
my ( $self, $c ) = #_;
$c->response->body( $c->model("DBI") );
}
Now you could try adding something to your model–
# lib/MyApp/Model/DBI.pm
sub add {
my $self = shift;
my #add = #_;
#add == 2 or die "2 is a terrible error message: 2";
return $self->dbh->selectrow_array("SELECT ? + ?", {}, #add);
}
And this to your controller–
# lib/MyApp/Controller/DBI.pm
sub add : Local Args(0) {
my ( $self, $c ) = #_;
$c->response->body( $c->model("DBI")->add( 2,2 ) );
}
Then visit localhost:3000/dbi/add. Continue to extend your model however you like.
Now, that the question is answered. You really, really, really should take the learning hit right now and get familiar with DBIx::Class or one of the other first class ORMs in Perl. Bare bones DBI is fine but you’re going to find 100 problems over time that DBIC has solved and it comes with a deep test suite, a long history, dozens of extensions, and a helpful community.

I am not using a direct DBI model myself, so I am not sure this works for you. I use the DBIC::Schema model
script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
create=static dbi:mysql:mydb dbusername dbpass
This creates a DB model in the Model dir that is merely a wrapper to the underlying DBIx::Class::Schema schema that is saved in lib/MyApp/Schema.pm and lib/MyApp/Schema/Result/
If I add a foo() subroutine to lib/MyApp/Model/DB.pm I can simply reference it like
$c->model('DB')->foo()
I thought the DBI model also created a wrapper model, $c->model('DBI')->dbh should return the raw DBI handle, $c->model('DBI') the catalyst model wrapper

The code below is an example of how I build my Models, I've written a tutorial on this at:
http://brainbuz.org/techinfo. I use DBIx::Simple as a convenience, you can easily skip it for raw dbi, and directly reference $self->dbh in your Model.
# Parent MODEL
package BoPeep::Model::BoPeep;
use strict;
use warnings;
use DBIx::Simple ;
use parent 'Catalyst::Model::DBI';
__PACKAGE__->config(
dsn => BoPeep->config->{dsn} ,
user => BoPeep->config->{user} ,
password => BoPeep->config->{password} ,
);
use Moose ; #use Moose immediately before calling
#on Moose to extend the object
has db=>(
is =>'ro',
isa=>'DBIx::Simple',
lazy_build=> 1,
# If we don't want to handle all dbis methods,
# specify those that we want.
# handles=> [qw/query flat /],
);
sub _build_db {
my $self = shift ;
return DBIx::Simple->connect($self->dbh);
} ;
# Child Model
package BoPeep::Model::BoPeep::Flock;
use Moose;
use BoPeep;
use namespace::autoclean;
extends 'BoPeep::Model::BoPeep';
sub List {
my $self = shift ;
my $db = $self->db ;
my #sheep = $db->query('SELECT * FROM flock')->flat ;
return #sheep ;
}
__PACKAGE__->meta->make_immutable( inline_constructor => 0 );
1;

Related

why can't I run this perl code?

While following this tutorial
https://www.codeproject.com/Articles/3152/Perl-Object-Oriented-Programming
I am failing to see where module Address.pm is.. did I miss something or article has an error or do I have a misunderstanding when one of the module says ' use Address ';
mac1:moduleTEST1 user1$ ./Employee.pl
Can't locate object method "new" via package "Address" (perhaps you forgot to load "Address"?) at ./Employee.pl line 16.
mac1:moduleTEST1 user1$
The tutorial is outdated and rather useless. Specifically, it is much worse than the documentation which comes with Perl. Use perldoc perltoc to get a table of contents, and read everything at least once.
See perldoc perlootut and perldoc perlobj.
package Address;
use strict;
use warnings;
sub new {
my $class = shift;
my $args = shift;
my %self = map +($_ => $args->{$_}), qw( street city state zip );
bless \%self => $class;
}
sub street {
my $self = shift;
if ( #_ ) {
$self->{street} = $_[0];
return;
}
return $self->{street};
}
# ditto for the rest of the accessors # there are
# ways to cut down the boilerplate once you learn
# the basics
#
# ...
__PACKAGE__
__END__
You use this module like this:
my $address = Address->new({
street => '123 E. Any St',
city => 'Any Town',
state => 'AY',
zip => '98765',
});
Of course, there a lot of things missing from this little demo. For example, the accessor, as written, allows you to change the state of the object. Immutable objects are easier to reason about, so you might want to disallow that by changing it to:
sub street { $_[0]->{street} }
It also allows you to assign any value you want to fields like state and zip. So, you may want to validate those values in the constructor, ensure that only values for the fields of the class are passed, all the values passed are defined etc.
At the end of that process, you may decide it doesn't make sense to keep writing boilerplate and use Moo or Moose to avail yourself to a richer set of features.
Even then, it helps to know what's happening under the hood.

Moose attributes: separating data and behaviour

I have a class built with Moose that's essentially a data container for an article list. All the attributes - like name, number, price, quantity - are data. "Well, what else?", I can hear you say. So what else?
An evil conspiration of unfortunate circumstances now forces external functionality into that package: Tax calculation of the data in this class has to be performed by an external component. This external component is tightly coupled to an entire application including database and dependencies that ruin the component's testability, dragging it into the everything-coupled-together stew. (Even thinking about refactoring the tax component out of the stew is completely out of the question.)
So my idea is to have the class accept a coderef wrapping the tax calculation component. The class would then remain independent of the tax calculation implementation (and its possible nightmare of dependencies), and at the same time it would allow integration with the application environment.
has 'tax_calculator', is => 'ro', isa => 'CodeRef';
But then, I'd have added a non-data component to my class. Why is that a problem? Because I'm (ab)using $self->meta->get_attribute_list to assemble a data export for my class:
my %data; # need a plain hash, no objects
my #attrs = $self->meta->get_attribute_list;
$data{ $_ } = $self->$_ for #attrs;
return %data;
Now the coderef is part of the attribute list. I could filter it out, of course. But I'm unsure any of what I'm doing here is a sound way to proceed. So how would you handle this problem, perceived as the need to separate data attributes and behaviour attributes?
A possible half thought out solution: use inheritance. Create your class as you do today but with a calculate_tax method that dies if called (i.e. a virtual function). Then create subclass that overrides that method to call into the external system. You can test the base class and use the child class.
Alternate solution: use a role to add the calculate_tax method. You can create two roles: Calculate::Simple::Tax and Calculate::Real::Tax. When testing you add the simple role, in production you add the real role.
I whipped up this example, but I don't use Moose, so I may be crazy with respect to how to apply the role to the class. There may be some more Moosey way of doing this:
#!/usr/bin/perl
use warnings;
{
package Simple::Tax;
use Moose::Role;
requires 'price';
sub calculate_tax {
my $self = shift;
return int($self->price * 0.05);
}
}
{
package A;
use Moose;
use Moose::Util qw( apply_all_roles );
has price => ( is => "rw", isa => 'Int' ); #price in pennies
sub new_with_simple_tax {
my $class = shift;
my $obj = $class->new(#_);
apply_all_roles( $obj, "Simple::Tax" );
}
}
my $o = A->new_with_simple_tax(price => 100);
print $o->calculate_tax, " cents\n";
It appears as if the right way to do it in Moose is to use two roles. The first is applied to the class and contains the production code. The second is applied to an object you want to use in testing. It subverts the first method using an around method and never calls the original method:
#!/usr/bin/perl
use warnings;
{
package Complex::Tax;
use Moose::Role;
requires 'price';
sub calculate_tax {
my $self = shift;
print "complex was called\n";
#pretend this is more complex
return int($self->price * 0.15);
}
}
{
package Simple::Tax;
use Moose::Role;
requires 'price';
around calculate_tax => sub {
my ($orig_method, $self) = #_;
return int($self->price * 0.05);
}
}
{
package A;
use Moose;
has price => ( is => "rw", isa => 'Int' ); #price in pennies
with "Complex::Tax";
}
my $prod = A->new(price => 100);
print $prod->calculate_tax, " cents\n";
use Moose::Util qw/ apply_all_roles /;
my $test = A->new(price => 100);
apply_all_roles($test, 'Simple::Tax');
print $test->calculate_tax, " cents\n";
A couple of things come to mind:
Implement the tax calculation logic in a separate TaxCalculation class that has the article list and the tax calculator as attributes.
Use a mock object as the tax calculator when you test. The tax calculator could be stored in an attribute that by default creates the real tax calculator. The test passes in a mock object that has the same interface but doesn't do anything.
Actually that's not really an abuse of get_attribute_list since that's rather exactly how MooseX::Storage works[^1]. IF you are going to continue to use get_attribute_list to build your straight data you'll want to do what MooseX::Storage does and set up an attribute trait for "DoNotSerialize"[^2]:
package MyApp::Meta::Attribute::Trait::DoNotSerialize;
use Moose::Role;
# register this alias ...
package Moose::Meta::Attribute::Custom::Trait::DoNotSerialize;
sub register_implementation { 'MyApp::Meta::Attribute::Trait::DoNotSerialize' }
1;
__END__
You then can use this in your class like so:
has 'tax_calculator' => ( is => 'ro', isa => 'CodeRef', traits => ['DoNotSerialize'] );
and in your serialization code like so:
my %data; # need a plain hash, no objects
my #attrs = grep { !$_->does('MyApp::Meta::Attribute::Trait::DoNotSerialize') } $self->meta->get_all_attributes; # note the change from get_attribute_list
$data{ $_ } = $_->get_value($self) for #attrs; # note the inversion here too
return %data;
Ultimately though you will end up in a solution similar to the Role one that Chas proposes, and I just answered his follow up question regarding it here: How to handle mocking roles in Moose?.
Hope this helps.
[^1]: And since the most basic use-case for MooseX::Storage is doing exactly what you describe, I highly suggest looking at it to do what you're doing by hand here.
[^2]: Or simply re-use the one from MooseX::Storage creates.

How to avoid globals in Perl Tk (Tkx) GUI programming using an MVC model

I have an old and very large Perl Tk GUI application that I'm refactoring to Tkx. I want to split the interface into several packages so I can build up the application UI in a modular manner. Also, I want to keep the View separate from the Model, using a Controller to provide an interface between the two.
It seems to me that the only way to design this is with two huge global variables, one holding the model ($MODEL), and the other holding references to the widgets ($UI) that are spread across many packages. Then, the Controller interfaces the two using a series of commands like the following:
$UI->{'entry_widget'}->configure(-variable=>\$MODEL->{'entry_value'});
$UI->{'button_widget'}->configure(-command=>sub {$MODEL->{'entry_value'} = "New Value"} );
My question is: Is there a better way to design the application which avoids having to use these two big globals ($UI and $MODEL)? Any suggestions would be very welcome.
I think package methods are a way to make something globally available, but not a global variable. So something like this, would work:
package MVC;
use strict;
use warnings;
use Scalar::Util qw<refaddr>;
my %MVCs;
sub _domain {
my ( $domain_name, $ref, $value ) = #_;
my $r = \$MVCs{ $key }{ $domain_name };
return unless $$r or ref( $value );
if ( ref $value ) {
$$r = $value;
}
return $$r;
}
sub model { shift; return _domain( 'model', #_ ); }
sub controller { shift; return _domain( 'controller', #_ ); }
sub view { shift; return _domain( 'view', #_ ); }
So outside the package, you would simply need to call this:
my $controller = MVC->controller( $self );
To get the controller associated to an object.
You could even put some export logic into the accessors, like:
unless ( $ref->can( $domain_name )) {
not strict 'refs';
*{ ref( $ref ) . "::$domain_name" }
= sub { _domain( $domain_name, $ref ) }
;
}
So you could just simply do this:
$self->view->view_method( #args );
You're not looking to avoid globals, you're looking to use methods, that is replace $hashref->{data} with $model->data or $self->model->data, where $model or $self, is an argument (or a "singleton" as Axeman demonstrates) passed to a signal-handler/callback/command, not a hash you access directly
You use methods to modify the $model, because methods can refuse to update the model with nonsense/incorrect data, they make sure you're not trying to pay with monopoly-money
Your app will always create a model variable, and a view variable, and connect them (maybe via an intermediary, a controller) through argument passing
They don't have to be actual global variables in the perl sense ( Coping with Scoping ), they can be my $variables and still work fine just the way you're using them now (via closures), and you avoid the problems of http://perl.plover.com/varvarname.html, but you don't get the benefits of smart models that know what kind of fuel they need (diesel or unleaded); and connecting your views to your model is more typing
See also the answers and links from What is Model View Presenter?

Perl WWW::Mechanize as child class; can't stay logged in to scraped site

I have a simple login script using Perl WWW::Mechanize. I am scripting logins to Moodle. When I just do the login steps as procedural steps, it works. For example (assume "$site_url", USERNAME, and PASSWORD been set appropriately):
#THIS WORKS
$updater->get("http://".$site_url."/login/index.php");
$updater->form_id("login");
$updater->field('username', USERNAME);
$updater->field('password', PASSWORD);
$updater->click();
$updater->get("http://".$site_url."/");
print $updater->content();
When I try to encapsulate these steps inside a child class of WWW:Mechanize, the get() and content() and other methods seem to work, but logging in to the site does not. I have a feeling it has to do with variable scope, but I don't know how to resolve it.
Example (fails):
my $updater = new AutoUpdater( $site_url, USERNAME, PASSWORD );
$updater->do_login();
{
package AutoUpdater;
use base qw( WWW::Mechanize );
sub new {
my $class = shift;
my $self = {
site_url => shift,
USERNAME => shift,
PASSWORD => shift,
};
bless $self, $class;
return $self;
}
sub do_login {
my $self = shift;
$self->get("http://".$site_url."/");
$self->get("http://".$site_url."/login/index.php");
$self->form_id("login");
$self->field("username", $self->{USERNAME});
$self->field("password", $self->{PASSWORD});
$self->click();
$self->get("http://".$site_url."/");
print $self->content();
}
}
This fails. "Fail" means it does not log in. It does grab the web page, though, and I can manipulate the HTML data. It just doesn't log in. Yargh! (Yes, the "yargh" was necessary)
Thanks!
Here's a revised version:
use strict;
use warnings;
my $updater = AutoUpdater->new( $site_url, USERNAME, PASSWORD );
$updater->do_login();
{
package AutoUpdater;
use parent qw( WWW::Mechanize );
sub new {
my $class = shift;
my $self = $class->SUPER::new();
$self->{AutoUpdater} = {
site_url => shift,
USERNAME => shift,
PASSWORD => shift,
};
return $self;
}
sub do_login {
my $self = shift;
my $data = $self->{AutoUpdater};
$self->get("http://$data->{site_url}/login/index.php");
$self->form_id("login");
$self->field("username", $data->{USERNAME});
$self->field("password", $data->{PASSWORD});
$self->click();
$self->get("http://$data->{site_url}/");
print $self->content();
}
} # end package AutoUpdater
Some notes:
You should always use strict and warnings to help catch your mistakes.
Indirect object syntax is discouraged. Use Class->new instead of new Class.
The base pragma has some undesirable effects that can't be fixed for backwards compatibility reasons. The parent pragma was developed to replace it.
Your big problem was that Perl doesn't automatically initialize base classes. You have to explicitly call $class->SUPER::new if necessary.
Your other big problem was understanding how object instance data is handled. Most Perl objects are hashrefs, and you access instance data using hashref syntax. When subclassing a class I didn't write, I like to use a second hashref to avoid conflicts with the parent class. Remember that you're sharing the object with the base classes. If your subclass uses the site_url field, and then a later release of the base class starts using site_url for something else, your code will suddenly break for no obvious reason. By using only one key in the base object hashref (and one that the base class is unlikely to start using), you minimize the chance of future breakage.
While Moose provides some nice features for OO Perl programming, if you're just writing a fairly simple subclass of a non-Moose class, it's probably best to avoid it.

How do I loop over all the methods of a class in Perl?

How do you loop over all the methods of a class in Perl? Are there any good online references to Perl introspection or reflection?
The recommendation Todd Gardner gave to use Moose is a good one, but the example code he chose isn't very helpful.
If you're inspecting a non-Moose using class, you'd do something like this:
use Some::Class;
use Class::MOP;
my $meta = Class::MOP::Class->initialize('Some::Class');
for my $meth ( $meta->get_all_methods ) {
print $meth->fully_qualified_name, "\n";
}
See the Class::MOP::Class docs for more details on how to do introspection.
You'll also note that I used Class::MOP instead of Moose. Class::MOP (MOP = Meta-Object Protocol) is the base on which Moose builds. If you're working with non-Moose classes, using Moose to introspect doesn't gain you anything.
If you wanted, you could use Moose () and Moose::Meta::Class->initialize instead of CMOP.
You can easily get a list of the defined methods of a class using the answers already provided. However, Perl is a dynamic language, which means more methods may be defined later. There really isn't a way to get a list of all of the methods to which any particular class will handle. For a lot more detail on this sort of stuff, I have a few chapters in Mastering Perl.
People are giving you (and upvoting) answers without telling you about the limitations.
Adam mentions his Class::Inspector, but it doesn't really work because it's trying to do something a dynamic language doesn't do (and that's be static :) For instance, here's a snippet where Class::Inspector returns no methods, but I can still call the VERSION method (as well as isa and can):
BEGIN {
package Foo;
our $VERSION = '1.23'
}
use Class::Inspector;
my $methods = Class::Inspector->methods( 'Foo' );
print "Methods are [#$methods]\n"; # reports nothing
print Foo->VERSION, "\n";
Here's another case where I can call any method I like, but Class::Inspector only returns AUTOLOAD (and still missing VERSION, isa, and can):
BEGIN {
package Foo;
our $VERSION = '1.23';
my $object = bless {}, __PACKAGE__;
sub AUTOLOAD { $object }
}
use Class::Inspector;
my $methods = Class::Inspector->methods( 'Foo' );
print "Methods are [#$methods]\n"; # reports only "AUTOLOAD"
print Foo->dog->cat->bird, "\n";
Curiously, everyone seems to ignore UNIVERSAL, probably because they don't explicitly handle it since it's only virtually in #ISA. I can add a debug method to every class, and Class::Inspector still misses it even though it's a defined method:
BEGIN {
sub UNIVERSAL::debug { "Hello debugger!\n" }
package Foo;
}
use Class::Inspector;
my $methods = Class::Inspector->methods( 'Foo' );
print "Methods are [#$methods]\n"; # still reports nothing
print Foo->debug, "\n";
Class::MOP has the same limitations.
Not every module is going to use AUTOLOAD, but it's not an obscure or rare feature either. If you don't mind that you are going to miss some of the methods then Class::Inspector or Class::MOP might be okay. It's just not going to give you a list of every method you can call on a class or an object in every case.
If you have a class or an object and you want to know if you can call a particular method, use can(). Wrap it in an eval block so can can call can() on things that aren't even objects to still get back false, instead of death, in those cases:
if( eval { $object->can( 'method_name' ) } )
{
$object->( #args );
}
In the general case, you'll have to inspect the symbol table (unless you use Moose). For example, to list the methods defined in the IO::File package:
use IO::File;
no strict 'refs';
print join ', ', grep { defined &{"IO::File::$_"} } keys %{IO::File::};
The hash %{IO::File::} is the symbol table of the IO::File package, and the grep filters out non-subroutine entries (e.g. package variables).
To extend this to include inherited methods, you have to recursively search the symbol tables of the parent classes (#IO::File::ISA).
Here is a complete example:
sub list_methods_for_class {
my $class = shift;
eval "require $class";
no strict 'refs';
my #methods = grep { defined &{$class . "::$_"} } keys %{$class . "::"};
push #methods, list_methods_for_class($_) foreach #{$class . "::ISA"};
return #methods;
}
For more info on packages and symbol tables, see the perlmod man page.
Depends if you mean, any class, or if you were implementing your own. For the latter, I use Moose, which offers a very clean syntax for these features. From the cookbook:
my %attributes = %{ $self->meta->get_attribute_map };
for my $name ( sort keys %attributes ) {
my $attribute = $attributes{$name};
if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
# ... keeps on
You probably want Class::Inspector->methods('Your::Class').
Nuff said.
I'll just leave this here for when I forget it. This is extremely powerful; too bad it is so out of the way that most Perl programmers never get to experience it.
package Foo;
use strict;
sub foo1 {};
sub foo2 {};
our $foo3 = sub{};
my $foo4 = "hello, world!";
package Bar;
use strict;
# woo, we're javascript!
(sub {
*Bar::foo1 = sub { print "hi!"; };
*Bar::foo2 = sub { print "hello!"; };
$Bar::foo1 = 200;
})->();
package main;
use strict;
use Data::Dumper;
$Data::Dumper::Deparse = 1;
print Dumper \%Data::Dumper::;
print Dumper \%Foo::;
print Dumper \%Bar::;