The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package TAEB::Role::Reblessing;
use Moose::Role;

requires 'base_class';

sub rebless {
    my $self    = shift;
    my $new_pkg = shift;

    my $base    = $self->base_class;
    my $old_pkg = blessed $self;

    # if the new_pkg doesn't exist, then just rebless into the base class
    return $self->downgrade unless $new_pkg->can('meta');

    # no work to be done, yay
    return if $old_pkg eq $new_pkg;

    # are we a superclass of the new package? if not, we need to revert
    # to the base class so we can be reblessed into a subclass of the base
    # class. in other words, Moose doesn't let you rebless into a sibling class
    unless ($new_pkg->isa($old_pkg)) {
        $self->downgrade("Reblessing a $old_pkg into $base (temporarily) because Moose doesn't let us rebless into sibling classes.");
    }

    TAEB->log->moose("Reblessing a $old_pkg into $new_pkg.");

    # and do the rebless, which does all the typechecking and whatnot
    $new_pkg->meta->rebless_instance($self);

    $self->reblessed($old_pkg, @_) if $self->can('reblessed');
}

sub downgrade {
    my $self = shift;
    my $base = $self->base_class;
    my $old  = blessed($self);

    return $self if $old eq $base;

    TAEB->log->moose(shift || "Reblessing $old into $base.");

    # rebless_instance only lets you get more specific, not less specific
    bless $self => $base;
}

no Moose::Role;

1;