Acme-oop-ism Part Three: techniques
Acme-oop-ism is about writing code that works in Moose, Mouse and Moo. We've already looked at how Type::Tiny has achieved this. Now I'm going to introduce you to some Acme-oop-ist techniques.
use Moo
Want to write a reusable class or role to stick on CPAN? If you were planning on using Moose, how about considering Moo instead? Moo doesn't have all of Moose's features, but if your project doesn't need advanced metaprogramming, it may still have enough.
Moo is designed to interact nicely with Moose. Moose and Moo classes can extend each other. Moose classes can consume Moo roles, and vice versa. It won't much help you interact with Mouse, but two out of three ain't bad.
Role::Tiny is sometimes also an option.
Delegate
Rather than creating roles intended for consumption, or base classes intended for inheriting from, each of which would potentially lock people using your module into a particular OO framework, consider creating small classes that can be delegated to.
As an example, rather than creating a HorseDrawn
role that can be applied to cart objects, create a Horse
class that cart objects can delegate conveyance to.
Moo, Mouse and Moose each have handy shortcuts (handles
) for delegated methods.
Use Moo or Class::Tiny, or even hand-written classes to keep dependencies down.
Use the surface syntax
The internals of Moo, Mouse and Moose are rather different to each other (particularly Moo). Their syntactic sugar is less so. Rather than calling $class->meta->add_attribute()
, can you get away with calling has()
?
MooX::HandlesVia and MooX::late are two CPAN modules that extend Moo by providing wrappers for has()
. Each of these is Moo-specific, but it's easy to imagine applications of this idea that are not.
MooseX::MungeHas provides generic logic for munging has
across OO frameworks.
MooX::PrivateAttributes and MooX::ProtectedAttributes manage to work for Moose by using a similar technique - calling the has
and around
sugar instead of metaclass tinkering.
Multiple code paths
As a last resort, you can add different code paths for handling Moose, Mouse and Moo. You can check to see what framework a class is using via $class->meta->isa(...)
and handle it appropriately.
Tools of the trade include run-time loading via require
or Module::Runtime, and judicious use of stringy eval.
Kavorka::MethodModifier's install_sub
method is an example of this, providing different code paths for installing method modifiers into Moo, Mouse, Moose, and plain Perl classes.
Subclass::Of is another example.
That's all your Acme-oop for now. I'll write some more on the topic soon.
By the way, just to clear up a bit of confusion on my part: does Class::Tiny support multiple inheritance? Because it isn't stated anywhere in the documentation. I managed to subclass two other packages, but the code also works when I replace Class::Tiny with Object::Tiny which specifically states in the docs that MI won't work. Maybe I'm doing something wrong?
https://gist.github.com/ggl/7219790
It looks like it ought to work. The only places that matter are the constructor and destructor, and they both use
mro::get_linear_isa
to climb the class hierarchy, which supports multiple inheritance just fine.There are no test cases for MI in the Class::Tiny test suite though. If Class::Tiny supporting MI is important for your project, then I'd recommend submitting some test cases to be included in the test suite.
PS: your example doesn't use multiple inheritance; just two generations of single inheritance.
I'm was just toying around with Perl OO builders trying to grasp some more advanced concepts and wondered if Class::Tiny supports MI. I know it can be extended to do roles and other Moose-isms via Class::Tiny::Antlers but saw nothing about MI in the documentation, the source, github. Your article turned out to be a proper opportunity to ask.
I've modified the gist after reading again through perlobj.