My current Perl Ironman Challenge status is: My Ironman Badge

Saturday, May 16, 2009

MooseX::Declare and MooseX::Method::Signatures

In my on going efforts to have code written before YAPC::NA for my Voltron talk, I came across an odd problem. Well, first, let's back up and talk a little about Voltron. Basically, I am combiningPOE, IKC, PubSub and Moose to create an application server that can introspect into connected pieces and automagically setup applications and participants to each other. The introspection is the key part.

Anyhow, MooseX::Declare supports a method syntax that is pretty cool. Basically it gives you a Perl 6 feel in Perl 5 land. That said, it was missing an important feature which is return type constraints which I had found out experimentally. I had made a naive assumption that the method syntax had feature parity with MooseX::Method::Signatures. It didn't.

So I make mention of this in #devel-declare to Florian Ragwitz, Robert Sedlacek, and the others. Come to find out MXD /reimplemented/ bits from MXMS, instead of making use of the module directly. So I peek inside the code for both modules. Ultimately, both modules make use of Devel::Declare to work their magic, so that helps. But they way they go about using that module is slightly different.

MXD actually does most of it's processing via a Devel::Declare::Context::Simple object. The context is contained, and lots of stuff is delegated to it. If I was going to make MXMS parse methods inside of MXD, I was going to need to pass the current context to MXMS and have it operate on that.

MXMS doesn't instantiate a Context::Simple object, it is the object. Or rather a subclass once removed. So instead of delegating operations to a separate object, it is making calls to $self. Obviously that isn't going to fly.

First, MooseX::Method::Signatures needed to have it's context split out from the rest of the code. MXMS was actually subclassing Devel::Declare::MethodInstaller::Simple which is a subclass of Devel::Declare::Context::Simple and it was doing that to get a method for parsing attributes (not the Moose kind, the Perl 5 kind). Handy, but if I was to split out the context and absorb a context from MXD, it needed to be Context::Simple and nothing more. So I copy/pasted the attribute parsing code in to MXMS and tweaked it to operate on a separate context. The rest of the module needed all of the instances of $self->foo turned into $ctx = $self->context; $ctx->foo. But once that was done, it was easy street from then on out.

Then for MXD to make use of MXMS, all I had to do was instantiate it, pass it a context, and tell it to do it's thing. Rockin'.

Except now, my previous work to defer method modifiers until after roles consumption broke. MXMS had no clue that it was supposed to defer method modifier application. Nor should it. So how is that reconciled? Well, what if MXMS had a coderef to call upon method application, so that you could override the default $meta->add_method or $meta->add_method_modifier? So that is what I implemented. Now in Keyword::MethodModifier, I simply pass in a closure around what should happen (pushing method modifer application coderefs onto the modifiers_store for later execution).

Awesome. Then Ash noted that applying the around method modifier to something that provided an invocant would break things because you would end up with /two/ invocants (the $orig coderef and the original invocant) and that would break Parse::Method::Signature. And because the old MXD code was playing with the method signature and creating a Meta::Method object itself I had cargo culted that behavior since I didn't want to fundamentally change anything. So my shiny was broken. Hrm.

So we discuss options on how to fix. I suggest that perhaps we make modifications to Parse::Method::Signature::Sig to allow for creating and manipulating them, because that is what I was doing anyhow, but with raw strings, so I could change the invocant and futz with the arguments. Then Ash provided a Good Enough solution which was to dig into Meta::Method inside MXMS, and inject the $orig parameter. And so I did. Anytime the declarator was 'around', I injected $orig both into the lexicals and into the positional args. Clean fix.

So after all that what is the next result? Full support of MXMS style methods inside of MXD including return value type constraints. So now in Voltron, I have full introspection ability which will be need for autowiring participants to applications.

1 comment:

  1. Excellent work and shows the power of our communities ability to coordinate features and implement them.