autobox-Bless
view release on metacpan or search on metacpan
lib/autobox/Bless.pm view on Meta::CPAN
package autobox::Bless;
use 5.010000;
use strict;
use warnings;
our $VERSION = '0.02';
use autobox;
use base 'autobox';
use Devel::Gladiator;
use Data::Dumper;
use Scalar::Util;
use Devel::Peek;
use Carp;
use Devel::Caller 'caller_cv'; # cx_type is 40 not CXt_SUB unless it's the current version
# use PadWalker;
# use B;
# could take one of three approaches; remember every field seen in every class; remember the top n closest matches as we go; take the first good match
# it's a memory vs accuracy tradeoff
# could also take a hybrid approach and if we don't find an exact match, look for a best match
sub HASH::AUTOLOAD {
my $unblessed_hash = shift;
return if $HASH::AUTOLOAD =~ m/::DESTROY$/;
(my $method) = $HASH::AUTOLOAD =~ m/.*::(.*)/;
# warn "``$method'' called";
# my @contenders; # ( [ package, score ], ... )
my $keeper_type;
for my $sv ( @{ Devel::Gladiator::walk_arena() } ) {
next unless UNIVERSAL::isa($sv, 'HASH');
next unless Scalar::Util::blessed $sv;
next unless $sv->can($method);
# warn "considering type " . Scalar::Util::blessed $sv;
for my $field ( %{ $unblessed_hash } ) {
exists $sv->{$field} or next;
}
# use Devel::ArgNames; my @argnames = Devel::ArgNames::arg_names(@_ XXX before the shift); my $type = ref $sv; bless peek_my(0)->{'%'.$argnames[0]}, $type;
$keeper_type = Scalar::Util::blessed $sv;
}
$keeper_type ||= autobox::Bless::_package_with_method($method); # backup plan
if( $keeper_type ) {
# warn "won with type " . $keeper_type;
# $keeper_type->can($method)->($unblessed_hash, @_); # or even better:
bless $unblessed_hash, $keeper_type; $unblessed_hash->$method(@_);
} else {
Carp::confess qq{Can't call method "$method" without a package or object reference, and believe me, I tried};
}
}
sub _package_with_method {
# look through the package hierarchy looking for something with the given method (er, function)
my $given_method = shift;
sub {
my $package = shift;
# warn "considering package ``$package''";
no strict 'refs';
for my $k (keys %$package) {
if(*{$package.$k}{CODE} and $k eq $given_method) {
# warn "found it!";
$package =~ s{::$}{};
return $package; # success!
}
}
for my $k (keys %$package) {
next if $k =~ m/main::$/;
next if $k =~ m/[^\w:]/;
next unless $k =~ m/::$/;
# recurse into that namespace unless it corresponds to a .pm module that got used at some point
my $modulepath = $package.$k;
# for($modulepath) { s{^main::}{}; s{::$}{}; s{::}{/}g; $_ .= '.pm'; }
# next if exists $INC{$modulepath};
my $maybe_result = caller_cv(0)->($package.$k); # press on forward into darker depths
return $maybe_result if $maybe_result;
}
return; # backtrack/failure
( run in 1.313 second using v1.01-cache-2.11-cpan-d7f47b0818f )