File Coverage

blib/lib/Namespace/Subroutines.pm
Criterion Covered Total %
statement 60 60 100.0
branch 14 20 70.0
condition 0 4 0.0
subroutine 9 9 100.0
pod 0 1 0.0
total 83 94 88.3


line stmt bran cond sub pod time code
1             package Namespace::Subroutines;
2 2     2   232615 use 5.010;
  2         16  
3 2     2   12 use strict;
  2         3  
  2         38  
4 2     2   10 use warnings;
  2         4  
  2         44  
5 2     2   603 use attributes;
  2         1243  
  2         21  
6 2     2   112 use Carp qw( carp );
  2         4  
  2         94  
7 2     2   11 use File::Find ();
  2         5  
  2         34  
8 2     2   9 use feature 'say';
  2         4  
  2         1803  
9              
10             our $VERSION = '0.01';
11              
12             my %skip = (
13             AUTOLOAD => 1,
14             BEGIN => 1,
15             MODIFY_CODE_ATTRIBUTES => 1,
16             FETCH_CODE_ATTRIBUTES => 1,
17             );
18              
19             sub find {
20 1     1 0 91 my ( $ns, $cb ) = @_;
21              
22             # 'My::App::Controller' -> 'My/App/Controller'
23 1         5 my $ns2 = $ns =~ s{::}{/}gr;
24              
25 1         3 my @modules;
26 1         4 foreach my $path (@INC) {
27 9 50       149 next unless -d $path;
28             File::Find::find(
29             sub {
30 4244 100   4244   119032 return unless /\.pm$/;
31 1483         6353 my $name = $File::Find::name =~ s{$path/}{}r;
32 1483 100       27092 return unless $name =~ /^$ns2/;
33 1         37 push @modules, [ $name, $File::Find::name ];
34             },
35 9         629 $path
36             );
37             }
38              
39 1         22 foreach my $m (@modules) {
40 1         5 my ( $modname, $path ) = @$m;
41              
42             # 'Data/Dumper.pm' -> qw(Data Dumper.pm)
43 1         7 my @a = split( m{/}, $modname );
44 1         10 pop @a; # qw(Data)
45 1         5 my $namespace = join( '/', @a ); # 'Data'
46              
47             # 'My/App/Controller/Users.pm', 'My/App/Controller/Inventory.pm', etc.
48 1 50       30 next unless $namespace =~ /^$ns2/;
49 1 50       527 require $modname unless defined $INC{$modname};
50              
51 1         5932 my $module = $modname; # 'My/App/Controller/Users.pm'
52 1         8 $module =~ s{\.pm$}{}; # 'My/App/Controller/Users'
53 1         4 $module =~ s{/}{::}g; # 'My::App::Controller::Users'
54 1         4 $module .= '::'; # 'My::App::Controller::Users::'
55 1         3 my $table = '%' . $module; # '%My::App::Controller::Users::'
56              
57             ## no critic (BuiltinFunctions::ProhibitStringyEval)
58 1         75 my @symbols = split( m{\|}, eval "join('|', keys $table)" );
59 1         7 my @subroutines = grep { defined &{ $module . $_ } } @symbols;
  5         8  
  5         17  
60 1         2 my %subroutines;
61              
62 1 50 0     53 open my $fh, '<', $path or ( carp "unable to open $!" and next );
63 1         29 while ( my $line = <$fh> ) {
64 10 100       42 next unless $line =~ /^sub\s+(\w+)[\:\(\s]/;
65 2         10 $subroutines{$1} = 1;
66             }
67 1 50 0     15 close $fh or ( carp "error closing $!" and next );
68              
69             # 'My::App::Controller::Users::' -> 'Users'
70 1         33 $module =~ s{^$ns\::(.+)::$}{$1};
71              
72 1         4 foreach my $sub (@subroutines) {
73 4 50       20 next if $skip{$sub};
74 4 100       10 next unless $subroutines{$sub};
75 2         6 my $name = join( '::', $ns, $module, $sub );
76 2         6 my $ref = \&$name;
77 2         10 my @attrs = attributes::get( \&$name );
78 2         70 $cb->( [ split( /::/, $module ) ], $sub, $ref, \@attrs );
79             }
80             }
81             }
82              
83             1;
84             __END__