Extending Mojo::Promise First time poster, be gentle. 

I wanted something similar to Mojo::Promise->all, except with an option to discard certain rejected (or, I guess, resolved) promises dependent on rejection value, as determined by caller. 

Only using this internally, so nevermind my polluting the Mojo::Promise:: namespace. 

package Mojo::Promise::Filter;
use Moose;

extends 'Mojo::Promise';

has 'promises' =&gt; (
    isa =&gt; 'ArrayRef[Mojo::Promise]',
    is =&gt; 'ro',
    required =&gt; 1,
);

has 'resolve_filter' =&gt; ( # truthy return means *DO* filter out
    isa =&gt; 'CodeRef',
    is =&gt; 'ro',
    required =&gt; 1,
    lazy =&gt; 1,
    default =&gt; sub { return sub {}; },
);

has 'reject_filter' =&gt; ( # truthy return means *DO* filter out
    isa =&gt; 'CodeRef',
    is =&gt; 'ro',
    required =&gt; 1,
    lazy =&gt; 1,
    default =&gt; sub { return sub {}; },
);

has '_promises_state_tracker' =&gt; (
    # numbered hashref whose primary keys correspond to passed promise-array's indices
    # secondary keys are flags for whether each original promise filtered
    # and whether promise resolved/rejected,
    # plus the original promise itself, e.g. : 
    # { 0 =&gt; { filtered =&gt; (undef|1), ror =&gt; (undef|1), original_promise =&gt; (Mojo::Promise) } }
    isa =&gt; 'HashRef[HashRef]',
    is =&gt; 'ro',
    lazy =&gt; 1,
    builder =&gt; '_build_promises_state_tracker',
);


sub _build_promises_state_tracker {
    my $self = shift;
    my $i = 0;
    return { map { $i++ =&gt; { original_promise =&gt; $_ } } @{$self-&gt;promises} };
}

sub _resolve_or_reject_self_if_last_child {
    my $self = shift;

    my @ror = grep { $_-&gt;{ror} } values %{$self-&gt;_promises_state_tracker};
        # gather the thus-far resolved-or-rejected promises
    return unless scalar keys %{$self-&gt;_promises_state_tracker} == scalar @ror;
        # return unless we have as many ror promises as were passed in

    my @unfiltered = map { $_-&gt;{original_promise} } grep { !$_-&gt;{filtered} } values %{$self-&gt;_promises_state_tracker};
    return Mojo::Promise-&gt;all(@unfiltered)-&gt;then(sub {
        $self-&gt;resolve(@_)
    })-&gt;catch(sub {
        $self-&gt;reject(@_)
    });
}

sub filter {
    my $self = shift;

    for my $i (0 .. $#{$self-&gt;promises}) {
        $self-&gt;promises-&gt;[$i]-&gt;then(sub {
            $self-&gt;_promises_state_tracker-&gt;{$i}-&gt;{filtered} = 1
                if $self-&gt;resolve_filter-&gt;(@_);
        })-&gt;catch(sub {
            $self-&gt;_promises_state_tracker-&gt;{$i}-&gt;{filtered} = 1
                if $self-&gt;reject_filter-&gt;(@_);
        })-&gt;finally(sub {
            $self-&gt;_promises_state_tracker-&gt;{$i}-&gt;{ror} = 1;
                # ror = Resolved Or Rejected
            $self-&gt;_resolve_or_reject_self_if_last_child();
        });
    }

    return $self;
}

no Moose;

1;


Use as : 

my $re_filter = qr/some special text/;
my $reject_filter = sub {
        my $v = shift;
        return 1 if ref \$v eq 'SCALAR' &amp;&amp; $v =~ $re_filter;
    };

my $p = Mojo::Promise::Filter-&gt;new(
        reject_filter =&gt; $reject_filter,
        promises =&gt; \@promises
    );

return $p-&gt;filter;
I'm not entirely sure I understand what this does, but here goes.


Mojolicious doesn't use Moose. I would think long and hard whether I'd extend a Mojo::Base class with Moose. Does this work properly?
There are several instances of required together with default and lazy in your new attributes. Those do not make sense together. If it's required it needs to be passed in at construction. There is no need to make it lazy then as it will always be there from the start.
Your default for both reject_filter as well as resolve_filter are subs that return undef implicitly. That means if you don't pass those in, everything is discarded. Is that the intended behavior?
Your class is a subclass of Mojo::Promise, so there is no need to call Mojo::Promise-&gt;all. It's got an inherited -&gt;all method that you can call on $self. (Mojo::Promise-&gt;all) returns a new object, so this breaks.