Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Enumerating all attributes of a Moo object

by McA (Curate)
on Jul 08, 2013 at 21:56 UTC ( #1043195=perlquestion: print w/ replies, xml ) Need Help??
McA has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

is there a possibility to enumerate all declared attributes of a Moo object from outside? Like keys of a hash?

Best regards
McA

Comment on Enumerating all attributes of a Moo object
Re: Enumerating all attributes of a Moo object
by Preceptor (Chaplain) on Jul 08, 2013 at 22:01 UTC

    Data::Dumper may be the tool for the job. But poking at the innards of an object is dirty, and you shouldn't do it.

      Hi,

      declared attributes and their accessors are part of the API of an object, therefor IMHO not part of the innards, isn't it? Probably I asked not precise enough. Thank you for your hint.

      Best regards
      McA

Re: Enumerating all attributes of a Moo object
by frozenwithjoy (Curate) on Jul 08, 2013 at 22:37 UTC

    Moo(se) objects are essentially hashrefs, so you can just do this:

    print "$_\n" for keys %{$moo_object};
      Note, that this will not show attributes that are not set:
      use 5.010; use strict; use warnings; { package Foo; use Moose; has bar => ( is => 'rw' ); has baz => ( is => 'rw' ); } my $foo = Foo->new( bar => 1 ); say join "\n", keys %$foo; __END__ bar
Re: Enumerating all attributes of a Moo object
by tobyink (Abbot) on Jul 09, 2013 at 06:47 UTC

    Do you want all the attributes of the object that are set; or all the attributes of the class that have been declared?

    Example:

    package Foo { use Moo; has a => (is => "ro"); has b => (is => "ro"); } my $bar = Foo->new(a => 42);

    Do you want just "a", or do you want "a" and "b"?

    If you want just "a" - that is, the list of attributes that have been set for a particular object - then unless you've been playing around subclassing Method::Generate::Accessor, you ought to be able to use keys(%$bar).

    If you want "a" and "b" - that is, the list of all attributes declared for the class - then you could try:

    keys(%{ 'Moo'->_constructor_maker_for('Foo')->all_attribute_specs })

    ... which IIRC respects inheritance and role composition.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

      Hi tobyink,

      thank you for your answer. The comments above and what you showed me let me think again about what I wanted to do:

      I want to implement a factory class generating Moo-objects. IMHO a factory class IS tightly coupled with the objects it creates. I wanted to avoid to explicitly enumerate every attribute in the code for initialisation of the created object.

      Do you think that the usage of the mentioned "introspection"-method is a good way to achieve that? Or should I use a different design?

      By the way: With 'attributes' I thought of all setters related to the attributes declared via 'has', that means all names used for the initialisation hash for method 'new'. Is there a common wording for that?

      Best regards
      McA

        What you need to bear in mind, is that an attribute could have several different names associated with it. Consider:

        use v5.14; package Foo { use Moo; has foo1 => ( is => "rw", init_arg => "foo2", reader => "foo3", writer => "foo4", ); } # Create an object, setting the attribute... my $obj = Foo->new(foo2 => 40); # Increment the value... $obj->foo4( $obj->foo3 + 1 ); # Or cheat and access the hashref directly... $obj->{foo1}++; # Print the value of the attribute... say $obj->foo3;

        Moo does have all this data buried away, but you may be better off using Moose or Mouse which provide documented, public APIs for all this attribute introspection stuff.

        package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

        I'll also point out a technique you could use. Instead of FooFactory introspecting class Foo, you could have class Foo pass FooFactory all the information it needs. One simple way to do that is write a wrapper for has...

        #!/usr/bin/env perl use v5.14; use Data::Dumper; package FooFactory { use Moo; has name => (is => "ro", required => 1); our @known_attributes; sub build_foo { my $self = shift; my %params = @_; my %attrs; for (@known_attributes) { $attrs{$_} = $params{$_} if exists $params{$_}; } 'Foo'->new(%attrs, maker => $self); } } package Foo { use Moo; sub my_has { my ($attr, %spec) = @_; # Inform FooFactory of the new attribute # Note we're ignoring init_arg for this simple example. push @FooFactory::known_attributes, $attr; has($attr, %spec); } my_has foo => (is => "ro"); my_has bar => (is => "ro"); my_has baz => (is => "ro"); my_has maker => (is => "ro"); } my $factory = 'FooFactory'->new(name => "Acme Inc"); my $obj = $factory->build_foo(bar => 42); print Dumper($obj);
        package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1043195]
Approved by hdb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (9)
As of 2014-11-23 13:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My preferred Perl binaries come from:














    Results (132 votes), past polls