File Coverage

blib/lib/autobox/Bless.pm
Criterion Covered Total %
statement 69 70 98.5
branch 19 22 86.3
condition 5 6 83.3
subroutine 15 15 100.0
pod n/a
total 108 113 95.5


line stmt bran cond sub pod time code
1             package autobox::Bless;
2              
3 2     2   46444 use 5.010000;
  2         8  
  2         78  
4 2     2   13 use strict;
  2         6  
  2         71  
5 2     2   24 use warnings;
  2         10  
  2         98  
6              
7             our $VERSION = '0.02';
8              
9 2     2   1596 use autobox;
  2         20841  
  2         12  
10 2     2   1041 use base 'autobox';
  2         5  
  2         230  
11 2     2   3660 use Devel::Gladiator;
  2         2446  
  2         159  
12 2     2   2802 use Data::Dumper;
  2         27081  
  2         154  
13 2     2   18 use Scalar::Util;
  2         5  
  2         96  
14 2     2   8744 use Devel::Peek;
  2         1510  
  2         17  
15 2     2   296 use Carp;
  2         5  
  2         147  
16 2     2   1776 use Devel::Caller 'caller_cv'; # cx_type is 40 not CXt_SUB unless it's the current version
  2         11979  
  2         831  
17              
18             # use PadWalker;
19             # use B;
20              
21             # 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
22             # it's a memory vs accuracy tradeoff
23             # could also take a hybrid approach and if we don't find an exact match, look for a best match
24              
25             sub HASH::AUTOLOAD {
26 2     2   71 my $unblessed_hash = shift;
27 2 50       12 return if $HASH::AUTOLOAD =~ m/::DESTROY$/;
28 2         17 (my $method) = $HASH::AUTOLOAD =~ m/.*::(.*)/;
29             # warn "``$method'' called";
30             # my @contenders; # ( [ package, score ], ... )
31 2         4 my $keeper_type;
32 2         5 for my $sv ( @{ Devel::Gladiator::walk_arena() } ) {
  2         12460  
33 97681 100       197309 next unless UNIVERSAL::isa($sv, 'HASH');
34 1196 100       2603 next unless Scalar::Util::blessed $sv;
35 15 100       245 next unless $sv->can($method);
36             # warn "considering type " . Scalar::Util::blessed $sv;
37 1         3 for my $field ( %{ $unblessed_hash } ) {
  1         5  
38 4 100       24 exists $sv->{$field} or next;
39             }
40             # use Devel::ArgNames; my @argnames = Devel::ArgNames::arg_names(@_ XXX before the shift); my $type = ref $sv; bless peek_my(0)->{'%'.$argnames[0]}, $type;
41 1         5 $keeper_type = Scalar::Util::blessed $sv;
42             }
43 2   66     4376 $keeper_type ||= autobox::Bless::_package_with_method($method); # backup plan
44 2 50       41 if( $keeper_type ) {
45             # warn "won with type " . $keeper_type;
46             # $keeper_type->can($method)->($unblessed_hash, @_); # or even better:
47 2         9 bless $unblessed_hash, $keeper_type; $unblessed_hash->$method(@_);
  2         22  
48             } else {
49 0         0 Carp::confess qq{Can't call method "$method" without a package or object reference, and believe me, I tried};
50             }
51             }
52              
53             sub _package_with_method {
54             # look through the package hierarchy looking for something with the given method (er, function)
55 1     1   6 my $given_method = shift;
56             sub {
57 4     4   36 my $package = shift;
58             # warn "considering package ``$package''";
59 2     2   19 no strict 'refs';
  2         4  
  2         645  
60 4         139 for my $k (keys %$package) {
61 296 100 100     314 if(*{$package.$k}{CODE} and $k eq $given_method) {
  296         1119  
62             # warn "found it!";
63 1         6 $package =~ s{::$}{};
64 1         5 return $package; # success!
65             }
66             }
67 3         59 for my $k (keys %$package) {
68 34 50       62 next if $k =~ m/main::$/;
69 34 100       81 next if $k =~ m/[^\w:]/;
70 31 100       72 next unless $k =~ m/::$/;
71             # recurse into that namespace unless it corresponds to a .pm module that got used at some point
72 3         7 my $modulepath = $package.$k;
73             # for($modulepath) { s{^main::}{}; s{::$}{}; s{::}{/}g; $_ .= '.pm'; }
74             # next if exists $INC{$modulepath};
75 3         16 my $maybe_result = caller_cv(0)->($package.$k); # press on forward into darker depths
76 3 100       14 return $maybe_result if $maybe_result;
77             }
78 2         9 return; # backtrack/failure
79 1         17 }->('main::');
80             }
81              
82              
83              
84             1;
85             __END__