File Coverage

blib/lib/Scope/With.pm
Criterion Covered Total %
statement 33 34 97.0
branch 2 2 100.0
condition 2 2 100.0
subroutine 8 9 88.8
pod 1 2 50.0
total 46 49 93.8


line stmt bran cond sub pod time code
1             package Scope::With;
2              
3 3     3   13459 use 5.008001;
  3         10  
  3         123  
4              
5 3     3   17 use strict;
  3         4  
  3         87  
6 3     3   14 use warnings;
  3         8  
  3         66  
7              
8 3     3   3260 use Devel::Declare::Context::Simple ();
  3         107388  
  3         452  
9              
10             our $VERSION = '0.01';
11              
12             =begin comment
13              
14             Under the hood, C turns a statement like the following:
15              
16             with (MyClass $foo) {
17             bar();
18             baz ...;
19             quux (42);
20             }
21              
22             into (formatted for clarity):
23              
24             with {
25             use Scope::With::Inject qw(MyClass);
26             set_invocant($foo);
27             no Scope::With::Inject;
28             bar();
29             baz ...;
30             quux = 42;
31             }
32              
33             sub with(&) {
34             my $block = shift;
35             $block->();
36             }
37              
38             C installs a lexical sub for each of C's methods (determined at compile-time)
39             which calls the corresponding method on the specified invocant (set at runtime). C unimports
40             the C method, which provides it conflicting with any prior or subsequent subroutines
41             of that name, but it leaves the lexical delegating subs intact.
42              
43             If no class is specified in the C statement, then only C and C
44             are installed in the scope, and C is called with no argument.
45              
46             =end comment
47              
48             =cut
49              
50 0     0 1 0 sub with(&) { $_[0]->() }
51              
52             sub import {
53 3     3   26 my $class = shift;
54 3   100     22 my $keyword = shift || 'with';
55 3         14 my $caller = Devel::Declare::get_curstash_name;
56              
57             Devel::Declare->setup_for(
58             $caller,
59             {
60             $keyword => {
61 3     3   90689 const => sub { $class->parser(Devel::Declare::Context::Simple->new->init(@_)) }
62             }
63             }
64 3         52 );
65              
66 3     3   29 no strict 'refs';
  3         6  
  3         700  
67 3         91 *{"$caller\::$keyword"} = \&with;
  3         102  
68             }
69              
70             sub parser {
71 3     3 0 138 my ($class, $context) = @_;
72              
73 3         19 $context->skip_declarator;
74 3         118 $context->skipspace;
75              
76 3         76 my $proto = $context->strip_proto;
77 3         170 $context->skipspace;
78              
79 3         25 my $inject;
80              
81 3 100       28 if ($proto =~ /^\s*(\S+)\s+(.+?)\s*$/) {
82 2         14 $inject = "use Scope::With::Inject qw($1); set_invocant($2); no Scope::With::Inject;";
83             } else {
84 1         4 $inject = "use Scope::With::Inject; set_invocant($proto); no Scope::With::Inject;";
85             }
86              
87             # prefix our injected code with code that appends a semicolon to the end of the block
88 3         18 $inject = $context->scope_injector_call(';') . $inject;
89 3         39 $context->inject_if_block($inject);
90             }
91              
92             1;
93              
94             __END__