File Coverage

blib/lib/Lexical/Hints.pm
Criterion Covered Total %
statement 63 66 95.4
branch 15 22 68.1
condition 13 18 72.2
subroutine 12 13 92.3
pod 1 2 50.0
total 104 121 85.9


line stmt bran cond sub pod time code
1             package Lexical::Hints;
2              
3 7     7   32046 use 5.010; use warnings;
  7     7   23  
  7         479  
  7         41  
  7         14  
  7         379  
4              
5             our $VERSION = '0.000005';
6              
7             # Track phase...
8             my $compiling;
9 7     7   738 BEGIN { $compiling = 1 }
10 7     7   18613 CHECK { $compiling = 0 }
11              
12              
13             # Track lexical hints for each namespace...
14             my %LEXICAL_HINTS_FOR;
15              
16              
17             sub import {
18 8     8   29 my ($package, $opt_ref) = @_;
19 8   100     69 my $set_hint = $opt_ref->{set_hint} // 'set_hint';
20 8   100     43 my $get_hint = $opt_ref->{get_hint} // 'get_hint';
21              
22             # Install API...
23 8         25 my $caller = caller;
24 7     7   47 no strict 'refs';
  7         15  
  7         7579  
25 8         24 *{$caller.'::'.$set_hint} = _gen_set_hint($set_hint);
  8         55  
26 8         20 *{$caller.'::'.$get_hint} = \&get_hint;
  8         216  
27             }
28              
29             sub _gen_set_hint {
30 8     8   70 my ($set_hint) = @_;
31              
32             return sub {
33 20     20   42 my $key = shift;
34 20         29 my $value = shift;
35 20         55 my $opts_ref = _unpack_opts( shift );
36              
37             # Who is setting this hint???
38 20         45 my $hint_owner = caller;
39              
40             # Lexical hints can only be autovivified at compile-time...
41 20 100 100     89 if ($compiling) {
    100          
42             # Allocate a unique number for the scope currently being compiled...
43 17   100     22 my $scope_ID = scalar @{ $LEXICAL_HINTS_FOR{$hint_owner} //= [] };
  17         75  
44              
45             # Save that info in the lexical scope curently being compiled...
46 17         135 $^H{$hint_owner.'->'.$key} = $scope_ID;
47              
48             # Save the corresponding value internally...
49 17         23 push @{$LEXICAL_HINTS_FOR{$hint_owner}}, $value;
  17         43  
50             }
51              
52             # Pre-existing hints can still be updated at run-time...
53             elsif (defined(my $scope_ID = ((caller $opts_ref->{up}+1)[10]//{})->{$hint_owner.'->'.$key})) {
54             # Update the corresponding value internally...
55 2         7 $LEXICAL_HINTS_FOR{$hint_owner}[$scope_ID] = $value;
56             }
57              
58             # But non-existing hints can't be created at run-time...
59             else {
60 1   33     7 $value = ref($value) || qq{"$value"};
61 1         8 _croak(
62             "Cannot autovivify hint '$key' at runtime for $hint_owner\n",
63             "in call to $set_hint()",
64             );
65             }
66              
67 19         8787 return;
68             }
69 8         51 }
70              
71             sub get_hint {
72 38     38 1 62 my $key = shift;
73 38         78 my $opts_ref = _unpack_opts( shift );
74              
75             # Who is retrieving this hint???
76 38         81 my $hint_owner = caller;
77              
78             # Query the caller's scope...
79 38 100       234 my $hints_hash = $compiling ? \%^H : (caller $opts_ref->{up}+1)[10];
80              
81             # Recover the appropriate scope ID...
82 38         303 my $scope_ID = $hints_hash->{$hint_owner.'->'.$key};
83              
84             # No such hint --> undef...
85 38 100       149 return undef if !defined $scope_ID;
86              
87             # Otherwise, recover the appropriate value...
88 31         425 return $LEXICAL_HINTS_FOR{$hint_owner}[$scope_ID];
89             }
90              
91             sub dump {
92 5     5 0 4726 my $opts_ref = _unpack_opts( shift );
93              
94             # Obtain dump of data...
95 5         1094 require Data::Dumper;
96 5 100 50     12199 my $dump = Data::Dumper::Dumper($compiling ? \%^H : (caller $opts_ref->{up})[10] // {});
97              
98 5         814 $dump = substr($dump,8,-2);
99 5         33 $dump =~ s{[ ]{8}}{}gxms;
100              
101             # Return dump in non-void contexts...
102 5 100       27 return $dump if defined wantarray;
103              
104             # Report to STDERR in void contexts...
105 2         4 print {*STDERR} $dump, "\n";
  2         16  
106             }
107              
108 1     1   11 sub _croak { require Carp; Carp::croak(@_); }
  1         614  
109 0     0   0 sub _carp { require Carp; Carp::carp(@_); }
  0         0  
110              
111             sub _unpack_opts {
112 63   50 63   345 my $opts_ref = shift // {};
113              
114             # Is it a valid hash ref???
115 63         132 my $type = ref($opts_ref);
116 63 0       171 _croak('Invalid option: expected hash ref, but was passed '
    50          
117             . ( $type ? lc($type) . ' ref instead' : 'a scalar instead')
118             ) if $type ne 'HASH';
119              
120             # Copy arg and insert default value, if necessary...
121 63         88 $opts_ref = { up => 0, %{$opts_ref} };
  63         225  
122              
123             # Watch for misunderstandings...
124 63 50 66     314 if ($compiling && $opts_ref->{'up'}) {
125 0         0 _carp("Useless compile-time 'up' option ignored");
126             }
127              
128             # Are there invalid options???
129 63         83 my @unknown_opts = grep {$_ ne 'up'} keys %{$opts_ref};
  63         203  
  63         175  
130 63 0       212 _croak('Unknown option'.(@unknown_opts==1?q{}:q{s}).": @unknown_opts")
    50          
131             if @unknown_opts;
132              
133             # By this point, the options hash is cleansed...
134 63         125 return $opts_ref;
135             }
136              
137              
138             1; # Magic true value required at end of module
139             __END__