Search code examples
arraysperlclonemoose

Deep cloning Moose object with attributes that are ArrayRef[Object] and Set::Object


I've got a Moose object:

class My::Game {
  has 'players' => (isa => 'Set::Object', ...)
  has 'action_sequence' => (isa => 'ArrayRef[My::Game::Action]', ...)
}

Now I want to be able to clone this object with a call like $game2 = $game->clone; How do I deep clone it so that the objects in the ArrayRef are cloned? And more trickily, the Set::Object?

I've looked at MooseX::Clone, but I'm unclear how to apply it to this case. Example code would be appreciated.

Thanks!


Solution

  • Turns out that simply adding the MooseX::Clone role to the class provides a clone() method that recursively clones attributes.

    • For hashref/arrayref attributes, it copies structures.
    • For scalars (including references) it simply does a shallow copy of the reference.
    • If you add traits => ['Clone'] to the attribute, it will recursively clone the attribute by calling clone() on the attribute value.

    To support cloning Set::Object, I ended up creating a trait called CloneByCoercion by subclassing the Clone trait, parameterized with the type to coerce to/from before cloning.

    So to use it, I wrote:

    has 'blah' => (isa => 'Set::Object', is => rw,
      traits => ['CloneByCoercion' => {to=>'ArrayRef'}]
    );
    

    MooseX::Types::Set::Object provides coercions to and from ArrayRef (although I needed to patch a bug in it: the coercion to ArrayRef should return a reference, not a list)

    I also modified MooseX::Clone to keep an objects-seen hash, so that it supports cloning interlinked object structures with circular references.

    I'll eventually get around to putting this stuff up on CPAN or submitting patches to the modules.