Ricardo SIGNES > MooseX-ComposedBehavior-0.004 > MooseX::ComposedBehavior



Annotate this POD


View/Report Bugs
Module Version: 0.004   Source  


MooseX::ComposedBehavior - implement custom strategies for composing units of code


version 0.004


First, a warning: MooseX::ComposedBehavior is a weird and powerful tool meant to be used only well after traditional means of composition have failed. Almost everything most programs will need can be represented with Moose's normal mechanisms for roles, classes, and method modifiers. MooseX::ComposedBehavior addresses edge cases.

Second, another warning: the API for MooseX::ComposedBehavior is not quite stable, and may yet change. More likely, though, the underlying implementation may change. The current implementation is something of a hack, and should be replaced by a more robust one. When that happens, if your code is not sticking strictly to the MooseX::ComposedBehavior API, you will probably have all kinds of weird problems.


First, you describe your composed behavior, say in the package "TagProvider":

  package TagProvider;
  use strict;

  use MooseX::ComposedBehavior -compose => {
    method_name  => 'tags',
    sugar_name   => 'add_tags',
    context      => 'list',
    compositor   => sub {
      my ($self, $results) = @_;
      return map { @$_ } @$results if wantarray;

Now, any class or role can use TagProvider to declare that it's going to contribute to a collection of tags. Any class that has used TagProvider will have a tags method, named by the method_name argument. When it's called, code registered the class's constituent parts will be called. For example, consider this example:

    package Foo;
    use Moose::Role;
    use TagProvider;
    add_tags { qw(foo baz) };

    package Bar;
    use Moose::Role;
    use t::TagProvider;
    add_tags { qw(bar quux) };

    package Thing;
    use Moose;
    use t::TagProvider;
    with qw(Foo Bar);
    add_tags { qw(bingo) };

Now, when you say:

  my $thing = Thing->new;
  my @tags  = $thing->tags;

...each of the add_tags code blocks above is called. The result of each block is gathered and an arrayref of all the results is passed to the compositor routine. The one we defined above is very simple, and just concatenates all the results together.

@tags will contain, in no particular order: foo, bar, baz, quux, and bingo

Result composition can be much more complex, and the context in which the registered blocks are called can be controlled. The options for composed behavior are described below.


  1. make a helper module, like the "TagProvider" one above
  2. use the helper in every relevant role or class
  3. write blocks using the "sugar" function
  4. call the method on instances as needed
  5. you're done!

There isn't much to using it beyond knowing how to write the actual behavior compositor (or "helper module") that you want. Helper modules will probably always be very short: package declaration, use strict, MooseX::ComposedBehavior invocation, and nothing more. Everything important goes in the arguments to MooseX::ComposedBehavior's import routine:

  package MyHelper;
  use strict;

  use MooseX::ComposedBehavior -compose => {
    ... important stuff goes here ...


Options to MooseX::ComposedBehavior


This is the name of the method that you'll call to get composed results. When this method is called, all the registered behavior is run, the results gathered, and those results passed to the compositor (described below).


This is the of the sugar to export into packages using the helper module. It should be called like this (assuming the sugar_name is add_behavior):

  add_behavior { ...the behavior... ; return $value };

When this block is invoked, it will be passed the invocant (the class or instance) followed by all the arguments passed to the main method -- that is, the method named by method_name.


This parameter forces a specific calling context on the registered blocks of behavior. It can be either "scalar" or "list" or may be omitted. The blocks registered by the sugar function will always be called in the given context. If no context is given, they will be called in the same context that the main method was called.

The context option does not affect the context in which the compositor is called. It is always called in the same context as the main method.

Void context is propagated as scalar context. This may change in the future to support void context per se.


The compositor is a coderef that gets all the results of registered behavior (and also_compose, below) and combines them into a final result, which will be returned from the main method.

It is passed the invocant, followed by an arrayref of block results. The block results are in an undefined order. If the blocks were called in scalar context, each block's result is the returned scalar. If the blocks were called in list context, each block's result is an arrayref containing the returned list.

The compositor is always called in the same context as the main method, even if the behavior blocks were forced into a different context.


This parameter is a coderef or method name, or an arrayref of coderefs and/or method names. These will be called along with the rest of the registered behavior, in the same context, and their results will be composed like any other results. It would be possible to simply write this:

  add_behavior {
    my $self = shift;

...but if this was somehow composed more than once (by repeating a role application, for example) you would get the results of some_method more than once. By putting the method into the also_compose option, you are guaranteed that it will run only once.


By default, registered behaviors are called on the most derived class and its roles, first. That is: the class closest to the class of the method invocant, then upward toward superclasses. This is how the DEMOLISH methods in Moose::Object work.

If method_order is provided, and is "reverse" then the methods are called in reverse order: base class first, followed by derived classes. This is how the BUILD methods in Moose::Object work.


Ricardo Signes <rjbs@cpan.org>


This software is copyright (c) 2013 by Ricardo Signes.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

syntax highlighting: