Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Re: Enumerating all attributes of a Moo object

by tobyink (Canon)
on Jul 09, 2013 at 06:47 UTC ( [id://1043247]=note: print w/replies, xml ) Need Help??


in reply to Enumerating all attributes of a Moo object

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

Replies are listed 'Best First'.
Re^2: Enumerating all attributes of a Moo object
by McA (Priest) on Jul 09, 2013 at 07:20 UTC

    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

      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

        Hi tobyink,

        another "Thank you" and ++ for that.

        Do you think this would be "good" design?

        Refering to the warning post above, don't you have to react on the %spec in the my_has-method, so that you really grab the accessor-name for the initialisation?

        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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1043247]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (3)
As of 2024-04-19 22:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found