snoofkin
snoofkin

Reputation: 8895

Perl Moose class design

I have a class (Foo), Moose based, that has 4 properties, lets say:

SF1...SF4

each of type HashRef[Any].

Currently all have default values. Later, we are going to get these values from a MySQL table.

My approach was, to have the Foo class consume roles depending on where the data comes from, I can store the SF1...SF4 in a role called Foo::DB which it will provide the SF1...SF3 with the default values from the database.

And also to have a role, Foo::Local, which will have the default values hard-coded, so later, when we will use the DB, I will only need to change the 'with....'.

Am I going in the right direction, or I should do it differently?

Upvotes: 3

Views: 445

Answers (2)

Jeffrey Ray
Jeffrey Ray

Reputation: 1264

Let me see if I understand you correctly.

You have four attributes that should be assigned a default value.

You have defined that default value in the MySQL database schema. What you would like to happen is that anytime you create a Foo instance, the default values will be populated from the defaults you have defined in the MySQL schema.

If I am correct in my understanding of what you are trying to do, then my advice is: Don't do it that way (unless this absolutely a requirement to your project). Define the default values of your attributes using Moose's default or builder properties.

has 'bar' => (
    default => 'fubar',
);

If you were to lookup the default values that have been set in the database schema instead of defining them in your class you will create more work for yourself, add unnecessary complexity to your program, and will be adding expensive database calls that could be avoided. You would need to parse the database schema and determine what the defaults should be for the given attribute. You would either need to do this every time you created a new object (expensive) or create a cache of the default values. Sure you could create a Moose extension that implements some magic and does this for you transparently. Seems like a lot of work for a not-so-appealing solution. I would just use Moose's 'default' attribute property unless you have a really good reason not to.

Upvotes: 0

masonk
masonk

Reputation: 9788

It's not clear why you need to populate the data from a role. I think you can just use initializer subs. Make your attributes lazy, and then define init_attribute subs. The first time the value of the attribute is needed, if it is not already set, then initializer sub will be called to provide the value. When you plug in the database you can simply teach your initializers how to query the database for the values.

package Foo;
has SF1 => ( is => 'rw', lazy => 1, isa => 'HashRef[Any]' );

sub init_SF1 {
    { hi => 'how are you' }
};

Alternatively, if you want to be able to go back and forth (e.g., for testing), then yes, you can bundle your initializers into the roles and apply the role depending on the situation. Or you can just supply the values inline in your tests. For instance

use Test::More;
use Foo;
my $foo = Foo->new(
    SF1 => { 
        row1 => 'fake test data', 
        row2 => 'also fake' 
    },
    SF2 => {},
); # now init_SF[12] will not be called

If you tell me why you're doing this, then I can give you a better answer.

Upvotes: 1

Related Questions