user1235777 user1235777 - 1 month ago 6
Perl Question

Moose accessors in Catalyst ("Can't use string as HASH ref" error)

Very simple question:

package MyApp::Model::Foo;
use Moose;
use namespace::autoclean;
extends 'Catalyst::Model';
has 'firstname' => ( is => 'ro', isa => 'Str' ); # to be populated in config file

# ...

sub check_name {
my $self = shift;
my $firstname = $self->firstname;
# ...
}


When I call
check_name()
from a test script, at the "$self->firstname" line I get the error
Can't use string ("MyApp::Model::Foo") as a HASH ref while "strict refs" in use at reader MyApp::Model::Foo::firstname
. How am I supposed to use this?

I can't reproduce the test stuff as it's too extensive, but by the time I run the test script, I've called a setup script that loads the Catalyst application (and thus reads the Catalyst config file), deploys and populates database tables, etc.

The test script worked fine in the original version (which did not take a value from the config file; that's what I'm trying to do now; originally I passed in a value), and the relevant bit is simply

my $name_check = MyApp::Model::Foo->check_name();
ok(defined $name_check, "Name is OK");

Answer

It appears you want to do a kind-of unit-test, or maybe an integration test, and verify if your application is getting the correct data from the configuration file.

Catalyst components (Models, Views and Controller) are Moose objects, you've got that right. In order for them to have Moose magic (which isn't really magic), you need to instantiate them. You can't just call an accessor as a class method.

use MyApp::Model::Foo;
my $name_check = MyApp::Model::Foo->new->check_name();

But that wouldn't work, because now you've got a new instance of the model object, and it doesn't have the name set through the config.

Catalyst internally takes care of creating the objects for you, including their configuration. You said you have a Catalyst running. You can use Catalyst::Test to go in there, get a context object $c and then use the model accessor to get you the right kind of model object that has been given the configuration.

The ctx_request function lets Catalyst handle a request and returns the actual HTTP::Response object as well as the context object. You can then work with that context.

use Catalyst::Test 'MyApp';
use Test::More;

my ( $res, $c ) = ctx_request('/');
ok defined $c->model('Foo')->name, 'Name is defined';

You probably already have Catalyst::Test in your test stack somewhere. If not, you're doing something weird.

Note that this doesn't work if you want the session that's attached to a certain user, so if you have a Test::WWW::Mechanize::Catalyst or another user agent that has a session cookie, you will need to extract the cookie and build your own HTTP::Request object, than use the user agents cookie jar to put the cookie into that request before you pass it to ctx_request.

Also note that the test you're doing is not very useful, unless you're building the code that does the configuration reading. And even then, you can build unit tests that don't require a full running Catalyst.