+4  A: 

As documented in Class::MOP::Attribute:

my $attr = $this->meta->find_attribute_by_name($attr_name);
my $is_read_only = ! $attr->get_write_method();

$attr->get_write_method() will get the writer method (either one you created or one that was generated), or undef if there isn't one.

Ether
Thanks! Knowing that it returns `undef` allows for a one line test (I tried posting it here, but it did not look very pretty).
molecules
Well, actually... That tests if it has a write method. That doesn't test if it has a read method though. It doesn't have to have either, technically. It's not a very useful attribute if it doesn't, but you can have it!
Robert P
@Robert: Yes, strictly speaking it checks that the attribute is "not writeable" (not isa => 'rw'), which is not quite the same as "readonly" (isa => 'ro').
Ether
Attributes without readers are plenty useful. Consider the case of `has foo => ( isa => 'ArrayRef', traits => ['Array'], handles => { add_foo => 'push', get_foo => 'pop' })`. No need for a reader!
jrockway
@jrockway that's an excellent example of an attribute without readers or writers. Thanks!
Robert P
+3  A: 

You should be able to get this from the object's metaclass:

unless ( $snp_obj->meta->get_attribute( 'sample_for' )->get_write_method ) { 
    # no write method, so it's read-only
}

See Class::MOP::Attribute for more.

friedo
Thanks! That is what I needed.
molecules
+2  A: 

Technically, an attribute does not need to have a read or a write method. Most of the time it will, but not always. An example (graciously stolen from jrockway's comment is below:

has foo => ( 
    isa => 'ArrayRef', 
    traits => ['Array'], 
    handles => { add_foo => 'push', get_foo => 'pop' }
)

This attribute will exist, but not have standard readers and writers.

So to test in every situation that an attribute has been defined as is => 'RO', you need to check both the write and the read method. You could do it with this subroutine:

# returns the read method if it exists, or undef otherwise.
sub attribute_is_read_only {
    my ($obj, $attribute_name) = @_;
    my $attribute = $obj->meta->get_attribute($attribute);

    return unless defined $attribute;
    return (! $attribute->get_write_method() && $attribute->get_read_method());
}

Alternatively, you could add a double negation before the last return to boolify the return value:

return !! (! $attribute->get_write_method() && $attribute->get_read_method());
Robert P
I love the first example ;)
jrockway