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.