Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

[Moose] Critique request on my Moose design (anti?)pattern

by three18ti (Scribe)
on Jan 27, 2014 at 15:19 UTC ( #1072236=perlquestion: print w/ replies, xml ) Need Help??
three18ti has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I find myself using this design pattern with moose. Obviously, this is an abbreviated example, but I find myself needing "sub" objects regularly. How do you deal with passing required attributes through?

package My::App; use 5.010; use Moose; use strict; use warnings; use MooseX::HasDefaults::RO; has [qw( foo bar )] => ( isa => 'Int', required => 1, ); has [qw( biz baz )] => ( isa => 'Int', default => '1', ); package My; use 5.010; use Moose; use strict; use warnings; use MooseX::HasDefaults::RO; has [qw(app_foo app_bar)] => ( isa => 'Int', required => 1, ); has [qw(app_biz app_baz)] => ( isa => 'Int', required => 0, ); has 'MyApp' => ( lazy => 1, isa => 'My::App', default => sub { my $attrs = [qw( biz baz )]; my %opt_params; foreach my $attribute (@$attrs) { my $method_name = 'app_' . $attribute; $opt_params{$attribute} = $_[0]->$method_name if $_[0]->$m +ethod_name; } My::App->new( foo => $_[0]->app_foo, bar => $_[0]->app_bar, %opt_params, ); }, handles => qr/^(.*)/, ); package main; use 5.010; use strict; use warnings; use Test::More; #BEGIN { use_ok 'My' } my $app = new_ok 'My' => [ app_foo => 1, app_bar => 2, app_biz => 3, ]; foreach my $test (qw( foo bar biz baz app_foo app_bar app_biz app_baz +)){ is $app->$test, $app->$test, '$app' . "->" . $test; } is $app->foo, 1, "foo ok"; is $app->app_foo, 1, "app_foo ok"; # Not sure what I was going for here... it started out as the followin +g but that didn't work # is $app->$test, $count++ %4 ? $app->$test : undef , '$app' . "->" . +$test my $count = 0; foreach my $test (qw( foo bar biz baz app_foo app_bar app_biz app_baz +)){ is $app->$test, $count++ %4 ? $app->$test : $app->$test , '$app' . + "->" . $test; }

Obviously, in the real world I wouldn't be testing the output with Test::More and would likely be making decisions based on those values obtained from $obj->$value. I tried

there's a gist here of the same thing...ish... (I modified it for some reason... but it's minimal... actually I think the example here may be more modified than the gist...)

Comment on [Moose] Critique request on my Moose design (anti?)pattern
Download Code
Re: [Moose] Critique request on my Moose design (anti?)pattern
by greengaroo (Hermit) on Jan 27, 2014 at 19:09 UTC

    I use Moose a lot and can probably help you, but I'm not sure I understand your question.

    You said: ...needing "sub" objects regularly. What is a "sub" object to you? What I see in your code is that MyApp (My::App object) is an attribute of the "My" class. Is that what you call a sub object?

    Also: How do you deal with passing required attributes through? ... You mean through the "sub" object? If you want to pass your required attributes from the "My" object to the "MyApp" attribute, I recommend using a "lazy" attribute with a "builder". Try this (you can get fancy later):

    has 'MyApp' => ( lazy => 1, isa => 'My::App', builder => '_build_myapp', ); sub _build_myapp { my $self = shift; return My::App->new( foo => $self->app_foo, bar => $self->app_bar, ); };

    It's much cleaner.

    A for will get you from A to Z; a while will get you everywhere.

      Hey thanks for the reply.

      For instance, if I'm building a Car object, I'll probably want to declare an "Engine" object and then handle the delegation of that objects methods in the Car object.

      E.g.:

      package Car::Engine; use 5.010; use Moose; use strict; use warnings; has [qw(cylinder rpm fuel)] { is => 'rw', isa => 'Int', } sub get_rpm { my $self = shift; my $curr_rpm = $self->rpm / $self->cylinder; } package Car; use 5.010; use Moose; use strict; use warnings; has [qw(engine_cylinder engine_rpm engine_fuel)] => ( is => 'rw', isa => 'Int', ); has engine => ( is => 'rw', isa => 'Car::Engine', default => { Car::Engine->new( cylinder => $_[0]->engine_cylinder, rpm => $_[0]->engine_rpm, fuel => $_[0]->engine_rpm, ); }, handles => { get_rpm => 'get_rpm', } ); package main; use 5.010; use strict; use warnings; my $car = Car->new( engine_cylinder => '8', engine_rpm => '8000', engine_fuel => '1000', ); say $car->get_rpm;

      (actually, the equation for rpms doesn't make any sense, but it was just to show that ->get_rpm is different than ->rpm)

      In the real world I'm working on a library to manage VMs and I've created several objects to represent things like the Hypervisor Cluster and the VM (which represents all the attributes for the vm, the actual running vm is then represented by a different object which I'm trying to figure out how to handle).

      I don't know what the correct term for "Car::Engine" would be in this case which is why I called it a "sub-object" in my previous comment.

      I just seem to be using the pattern a lot which results is a lot of duplicate typing (or cut and paste) which got me to thinking maybe there's a better way to go about using objects in my primary (in this case "Car") object.

Re: [Moose] Critique request on my Moose design (anti?)pattern
by tobyink (Abbot) on Jan 27, 2014 at 20:12 UTC

    Unless you're wedded to making the attribute lazy, you could build it in the BUILD method:

    use v5.14; package Part { use Moose; has foo => (is => 'ro'); has bar => (is => 'ro'); } package Whole { use Moose; has baz => (is => 'ro'); has part => ( is => 'ro', writer => '_set_part', init_arg => 0, isa => 'Part', handles => qr/.*/, ); sub BUILD { my $self = shift; $self->_set_part(Part->new(@_)); } } my $obj = Whole->new(foo => 1, bar => 2, baz => 3); print $obj->dump; say for $obj->foo, $obj->bar, $obj->baz;
    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (13)
As of 2014-07-24 11:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (160 votes), past polls