Devel-hdb/lib/Devel/hdb/App/PackageInfo.pm
package Devel::hdb::App::PackageInfo;
use strict;
use warnings;
use URI::Escape qw(uri_escape);
use base 'Devel::hdb::App::Base';
our $VERSION = '0.25';
__PACKAGE__->add_route('get', qr(/packageinfo/((\w+)(::\w+)*)), \&pkginfo);
__PACKAGE__->add_route('get', qr(/subinfo/((\w+)(::\w+)*)), \&subinfo);
# Get data about the packages and subs within the mentioned package
sub pkginfo {
my($class, $app, $env, $package) = @_;
my $stash_exists = do {
my $stash = "${package}::";
no strict 'refs';
scalar(%$stash);
};
unless ($stash_exists) {
return [404,
[ 'Content-Type' => 'text/html' ],
[ "Package $package not found" ] ];
}
my @sub_packages = map { { name => $_, href => '/packageinfo/' . uri_escape($_) } }
_namespaces_in_package($package);
my @subs = map { { name => $_, href => '/subinfo/' . uri_escape(join('::', $package, $_)) } }
grep { $app->subroutine_location("${package}::$_") }
@{ _subs_in_package($package) };
return [ 200,
[ 'Content-Type' => 'application/json' ],
[ $app->encode_json({ name => $package, packages => \@sub_packages, subroutines => \@subs }) ]
];
}
# Get information about a subroutine
sub subinfo {
my($class, $app, $env, $subname) = @_;
my $loc = $app->subroutine_location($subname);
if ($loc) {
my @keys = qw( filename line end source source_line package subroutine );
my %data;
@data{@keys} = map { $loc->$_ } @keys;
return [ 200,
[ 'Content-Type' => 'application/json' ],
[ $app->encode_json(\%data) ],
];
} else {
return [ 404,
[ 'Content-Type' => 'text/html' ],
[ "$subname not found" ],
];
}
}
sub _namespaces_in_package {
my $pkg = shift;
no strict 'refs';
return () unless %{"${pkg}::"};
my @packages = sort
map { substr($_, 0, -2) } # remove '::' at the end
grep { m/::$/ }
keys %{"${pkg}::"};
return @packages;
}
sub _subs_in_package {
my $pkg = shift;
no strict 'refs';
my @subs = sort
grep { defined &{"${pkg}::$_"} }
keys %{"${pkg}::"};
return \@subs;
}
1;
=pod
=head1 NAME
Devel::hdb::App::PackageInfo - Get information about packages and subroutines
=head2 Routes
=over 4
=item GET /packageinfo/<package>
Get information about the named package, or 'main::' if no package is given.
Returns 200 and a JSON in the body:
{
name: String - package name
packages: [ // list of packages under this package
{
name: String - package name
href: URL (/packageinfo/<That::package::name>)
},
...
],
subroutines: [ // List of subroutine names in this package
{
name: String - subroutine name including package
href: URL (/subinfo/<That::package::subname>)
},
...
],
}
Returns 400 if the named package is not a valid package name
Returns 404 if the named package is not present
=item GET /subinfo/<subname>
Get information about the named subroutine. If the subname has no package
included, package main:: is assummed.
Returns 200 and a JSON-encoded hash in the body with these keys:
subroutine: String - subroutine name, not including package
package => Package the subroutine is in
filename => File the sub was defined
line => What line the sub is defined
end => Last line where the sub is defined
source => If the sub was created in an eval, this is the file the
eval happened in
source_line => Line the eval happened in
Returns 404 if the given subroutine was not found.
source and source_line can differ from file and start in the case of
subroutines defined inside of a string eval. In this case, "file" will
be a string like
(eval 23)[/some/file/path/module.pm:123]
representing the text that was eval-ed, and "start" will be the line within
that text where the subroutine was defined. "source" would be
/some/file/path/module.pm
showing where in the original source code the text came from, and
"source_line" would be 123, the line in the original source file.
=back
=head1 SEE ALSO
L<Devel::hdb>
=head1 AUTHOR
Anthony Brummett <brummett@cpan.org>
=head1 COPYRIGHT
Copyright 2018, Anthony Brummett. This module is free software. It may
be used, redistributed and/or modified under the same terms as Perl itself.