cubabit
cubabit

Reputation: 2700

How can I access attributes of my object in a Moose coercion?

I want to coerce a Str into a DBIx::Class::Row object for an attribute in my Moose class. To do this I need to perform a lookup on the DBIC schema to find the row. I want to push an error onto an ArrayRef attribute if the lookup failed.

I currently pass the schema in as an attribute to my class.

With coercion it appears I cannot access the object so I cannot push onto the error arrayref attribute or use the schema object to perform my lookup.

An alternative I tried was to use 'around' to lookup and set the attribute when set, but this of course does not get called when the attribute value is passed via the constructor.

Is this possible, or does someone have an alternative implementation to do what I want to achieve?

Upvotes: 4

Views: 607

Answers (1)

Ether
Ether

Reputation: 53976

You can catch and mutate a value being stored when passed into a constructor with an attribute initializer. (However, it is only run when the attribute is set in the constructor, not at any other time.) Documentation for initializers can be found in Class::MOP::Attribute.

Since this only catches cases where the attribute is set via the constructor, you still need to catch the other cases where the attribute is set. This can be done with a method modifier as you said, but you can combine the two into one method, wrapped around the autogenerated accessor:

has my_attr => (
    is => 'rw',
    isa => 'DBIx::Class::Row',
    initializer => 'my_attr',
);

# my_attr is the autogenerated accessor - we method-modify it to mutate the
# value being set, and catch cases where it is called as an initializer.

# called for reads, writes, and on initialization at construction time
around 'my_attr' => sub {
    my $orig = shift;
    my $self = shift;
    # value is not defined if being called as a reader
    # setter and attr are only defined if being called as an initializer
    my ($value, $setter, $attr) = @_;

    # the reader behaves normally
    return $self->$orig if not @_;

    # convert the string to the row object
    my $row = $self->convert_str_to_row_obj($value);

    # if called as an initializer, set the value and we're done
    return $setter->($row) if $setter;

    # otherwise, call the real writer with the new value
    $self->$orig($row);
};

Upvotes: 4

Related Questions