diff options
-rw-r--r-- | Changes | 26 | ||||
-rw-r--r-- | MANIFEST | 12 | ||||
-rw-r--r-- | META.json | 57 | ||||
-rw-r--r-- | META.yml | 27 | ||||
-rw-r--r-- | Makefile.PL | 80 | ||||
-rw-r--r-- | README | 46 | ||||
-rw-r--r-- | lib/Module/Reader.pm | 174 | ||||
-rw-r--r-- | maint/Makefile.PL.include | 7 | ||||
-rw-r--r-- | t/00-load.t | 6 | ||||
-rw-r--r-- | t/lib/TestLib.pm | 3 | ||||
-rw-r--r-- | t/main.t | 19 | ||||
-rw-r--r-- | t/memory.t | 45 |
12 files changed, 502 insertions, 0 deletions
@@ -0,0 +1,26 @@ +Revision history for Module-Reader + +0.002003 - 2014-08-21 + - fix dist name in meta files + +0.002002 - 2014-08-16 + - include README in dist + - include additional metadata + +0.002001 - Nov 23, 2013 + - fix module NAME in Makefile.PL + - minor doc corrections + - fix in-memory file test on perl 5.6 + +0.002000 - Feb 11, 2013 + - Improve documentation. + - Add 'found' option for always loading from specific files. + +0.001002 - Feb 2, 2013 + - fix copyright attribution + +0.001001 - Feb 1, 2013 + - fix version number format + +0.001000 - Feb 1, 2013 + - initial version diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..ad92939 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,12 @@ +Changes +lib/Module/Reader.pm +maint/Makefile.PL.include +Makefile.PL +MANIFEST This list of files +t/00-load.t +t/lib/TestLib.pm +t/main.t +t/memory.t +META.yml Module YAML meta-data (added by MakeMaker) +META.json Module JSON meta-data (added by MakeMaker) +README README file (added by Distar) diff --git a/META.json b/META.json new file mode 100644 index 0000000..a080149 --- /dev/null +++ b/META.json @@ -0,0 +1,57 @@ +{ + "abstract" : "Read the source of a module like perl does", + "author" : [ + "haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>" + ], + "dynamic_config" : 1, + "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "Module-Reader", + "no_index" : { + "directory" : [ + "t", + "inc" + ] + }, + "prereqs" : { + "build" : {}, + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "0" + } + }, + "runtime" : { + "requires" : { + "Scalar::Util" : "0", + "perl" : "5.006" + } + }, + "test" : { + "requires" : { + "Test::More" : "0.88" + } + } + }, + "release_status" : "stable", + "resources" : { + "bugtracker" : { + "mailto" : "bug-Module-Reader@rt.cpan.org", + "web" : "https://rt.cpan.org/Public/Dist/Display.html?Name=Module-Reader" + }, + "license" : [ + "http://dev.perl.org/licenses/" + ], + "repository" : { + "type" : "git", + "url" : "git://github.com/haarg/Module-Reader", + "web" : "https://github.com/haarg/Module-Reader" + } + }, + "version" : "0.002003" +} diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..d64ec63 --- /dev/null +++ b/META.yml @@ -0,0 +1,27 @@ +--- +abstract: 'Read the source of a module like perl does' +author: + - 'haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>' +build_requires: + Test::More: '0.88' +configure_requires: + ExtUtils::MakeMaker: '0' +dynamic_config: 1 +generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520' +license: perl +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: '1.4' +name: Module-Reader +no_index: + directory: + - t + - inc +requires: + Scalar::Util: '0' + perl: '5.006' +resources: + bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Module-Reader + license: http://dev.perl.org/licenses/ + repository: git://github.com/haarg/Module-Reader +version: '0.002003' diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..b184d36 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,80 @@ +use strict; +use warnings FATAL => 'all'; +use 5.006; + +my %META = ( + name => 'Module-Reader', + prereqs => { + configure => { requires => { + 'ExtUtils::MakeMaker' => 0, + } }, + test => { requires => { + 'Test::More' => 0.88, + } }, + runtime => { requires => { + 'perl' => '5.006', + 'Scalar::Util' => 0, + } }, + }, + resources => { + repository => { + url => 'git://github.com/haarg/Module-Reader', + web => 'https://github.com/haarg/Module-Reader', + type => 'git', + }, + bugtracker => { + web => 'https://rt.cpan.org/Public/Dist/Display.html?Name=Module-Reader', + mailto => 'bug-Module-Reader@rt.cpan.org', + }, + license => [ 'http://dev.perl.org/licenses/' ], + }, +); + +my %MM_ARGS = ( + PREREQ_PM => { + ($] < 5.008 ? ('IO::String' => 0) : ()), + }, +); + +## BOILERPLATE ############################################################### +require ExtUtils::MakeMaker; +(do 'maint/Makefile.PL.include' or die $@) unless -f 'META.yml'; + +# have to do this since old EUMM dev releases miss the eval $VERSION line +my $eumm_version = eval $ExtUtils::MakeMaker::VERSION; +my $mymeta = $eumm_version >= 6.57_02; +my $mymeta_broken = $mymeta && $eumm_version < 6.57_07; + +($MM_ARGS{NAME} = $META{name}) =~ s/-/::/g; +($MM_ARGS{VERSION_FROM} = "lib/$MM_ARGS{NAME}.pm") =~ s{::}{/}g; +$META{license} = [ $META{license} ] + if $META{license} && !ref $META{license}; +$MM_ARGS{LICENSE} = $META{license}[0] + if $META{license} && $eumm_version >= 6.30; +$MM_ARGS{NO_MYMETA} = 1 + if $mymeta_broken; +$MM_ARGS{META_ADD} = { 'meta-spec' => { version => 2 }, %META } + unless -f 'META.yml'; + +for (qw(configure build test runtime)) { + my $key = $_ eq 'runtime' ? 'PREREQ_PM' : uc $_.'_REQUIRES'; + my $r = $MM_ARGS{$key} = { + %{$META{prereqs}{$_}{requires} || {}}, + %{delete $MM_ARGS{$key} || {}}, + }; + defined $r->{$_} or delete $r->{$_} for keys %$r; +} + +$MM_ARGS{MIN_PERL_VERSION} = delete $MM_ARGS{PREREQ_PM}{perl} || 0; + +delete $MM_ARGS{MIN_PERL_VERSION} + if $eumm_version < 6.47_01; +$MM_ARGS{BUILD_REQUIRES} = {%{$MM_ARGS{BUILD_REQUIRES}}, %{delete $MM_ARGS{TEST_REQUIRES}}} + if $eumm_version < 6.63_03; +$MM_ARGS{PREREQ_PM} = {%{$MM_ARGS{PREREQ_PM}}, %{delete $MM_ARGS{BUILD_REQUIRES}}} + if $eumm_version < 6.55_01; +delete $MM_ARGS{CONFIGURE_REQUIRES} + if $eumm_version < 6.51_03; + +ExtUtils::MakeMaker::WriteMakefile(%MM_ARGS); +## END BOILERPLATE ########################################################### @@ -0,0 +1,46 @@ +NAME + Module::Reader - Read the source of a module like perl does + +SYNOPSIS + use Module::Reader qw(:all); + my $io = module_handle('My::Module'); + my $content = module_content('My::Module'); + + my $io = module_handle('My::Module', @search_dirs); + + my $io = module_handle('My::Module', @search_dirs, { found => \%INC }); + +DESCRIPTION + Reads the content of perl modules the same way perl does. This includes + reading modules available only by @INC hooks, or filtered through them. + +EXPORTS + module_handle( $module_name, @search_dirs, \%options ) + Returns an IO handle to the given module. Searches the directories + specified, or @INC if none are. + + Options + found + A reference to a hash like %INC with module file names (in the style + 'My/Module.pm') as keys and full file paths as values. Modules + listed in this will be used in preference to searching through + directories. + + module_content( $module_name, @search_dirs, \%options ) + Returns the content of the given module. Accepts the same options as + "module_handle". + +AUTHOR + haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org> + + CONTRIBUTORS + None yet. + +COPYRIGHT + Copyright (c) 2013 the Module::Reader "AUTHOR" and "CONTRIBUTORS" as + listed above. + +LICENSE + This library is free software and may be distributed under the same + terms as perl itself. + diff --git a/lib/Module/Reader.pm b/lib/Module/Reader.pm new file mode 100644 index 0000000..f263915 --- /dev/null +++ b/lib/Module/Reader.pm @@ -0,0 +1,174 @@ +package Module::Reader; +BEGIN { require 5.006 } +use strict; +use warnings; + +our $VERSION = '0.002003'; +$VERSION = eval $VERSION; + +use base 'Exporter'; +our @EXPORT_OK = qw(module_content module_handle); +our %EXPORT_TAGS = (all => [@EXPORT_OK]); + +use File::Spec; +use Scalar::Util qw(blessed reftype openhandle); +use Carp; +use constant _OPEN_STRING => $] >= 5.008; +BEGIN { + require IO::String + if !_OPEN_STRING; +} + +sub module_content { + my $module = _get_module(@_); + if (ref $module) { + local $/; + return scalar <$module>; + } + else { + return $module; + } +} + +sub module_handle { + my $module = _get_module(@_); + if (ref $module) { + return $module; + } + elsif (_OPEN_STRING) { + open my $fh, '<', \$module; + return $fh; + } + else { + return IO::String->new($module); + } +} + +sub _get_module { + my ($package, @inc) = @_; + (my $module = "$package.pm") =~ s{::}{/}g; + my $opts = ref $_[-1] && ref $_[-1] eq 'HASH' && pop @inc || {}; + if (!@inc) { + @inc = @INC; + } + if (my $found = $opts->{found}) { + if (my $full_module = $found->{$module}) { + if (ref $full_module) { + @inc = $full_module; + } + elsif (-f $full_module) { + open my $fh, '<', $full_module + or die "Couldn't open ${full_module} for ${module}: $!"; + return $fh; + } + } + } + for my $inc (@inc) { + if (!ref $inc) { + my $full_module = File::Spec->catfile($inc, $module); + next unless -f $full_module; + open my $fh, '<', $full_module + or die "Couldn't open ${full_module} for ${module}: $!"; + return $fh; + } + + my @cb = ref $inc eq 'ARRAY' ? $inc->[0]->($inc, $module) + : blessed $inc ? $inc->INC($module) + : $inc->($inc, $module); + + next + unless ref $cb[0]; + my $fh; + if (reftype $cb[0] eq 'GLOB' && openhandle $cb[0]) { + $fh = shift @cb; + } + + if (ref $cb[0] eq 'CODE') { + my $cb = shift @cb; + # require docs are wrong, perl sends 0 as the first param + my @params = (0, @cb ? $cb[0] : ()); + + my $module = ''; + while (1) { + local $_ = $fh ? <$fh> : ''; + $_ = '' + if !defined; + last if !$cb->(@params); + $module .= $_; + } + return $module; + } + elsif ($fh) { + return $fh; + } + } + croak "Can't find module $module"; +} + +1; + +__END__ + +=head1 NAME + +Module::Reader - Read the source of a module like perl does + +=head1 SYNOPSIS + + use Module::Reader qw(:all); + my $io = module_handle('My::Module'); + my $content = module_content('My::Module'); + + my $io = module_handle('My::Module', @search_dirs); + + my $io = module_handle('My::Module', @search_dirs, { found => \%INC }); + +=head1 DESCRIPTION + +Reads the content of perl modules the same way perl does. This +includes reading modules available only by L<@INC hooks|perlfunc/require>, or filtered +through them. + +=head1 EXPORTS + +=head2 module_handle( $module_name, @search_dirs, \%options ) + +Returns an IO handle to the given module. Searches the directories +specified, or L<@INC|perlvar/@INC> if none are. + +=head3 Options + +=over 4 + +=item found + +A reference to a hash like L<%INC|perlvar/%INC> with module file names (in the +style 'F<My/Module.pm>') as keys and full file paths as values. +Modules listed in this will be used in preference to searching +through directories. + +=back + +=head2 module_content( $module_name, @search_dirs, \%options ) + +Returns the content of the given module. Accepts the same options as C<module_handle>. + +=head1 AUTHOR + +haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org> + +=head2 CONTRIBUTORS + +None yet. + +=head1 COPYRIGHT + +Copyright (c) 2013 the Module::Reader L</AUTHOR> and L</CONTRIBUTORS> +as listed above. + +=head1 LICENSE + +This library is free software and may be distributed under the same terms +as perl itself. + +=cut diff --git a/maint/Makefile.PL.include b/maint/Makefile.PL.include new file mode 100644 index 0000000..e3a9588 --- /dev/null +++ b/maint/Makefile.PL.include @@ -0,0 +1,7 @@ +BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") } +use lib 'Distar/lib'; +use Distar; + +use ExtUtils::MakeMaker 6.58; + +author 'haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>'; diff --git a/t/00-load.t b/t/00-load.t new file mode 100644 index 0000000..06de4f7 --- /dev/null +++ b/t/00-load.t @@ -0,0 +1,6 @@ +use strict; + +use Test::More tests => 1; + +require_ok('Module::Reader') + || BAIL_OUT("Stopping due to compile failure!"); diff --git a/t/lib/TestLib.pm b/t/lib/TestLib.pm new file mode 100644 index 0000000..3e1b6ff --- /dev/null +++ b/t/lib/TestLib.pm @@ -0,0 +1,3 @@ +package TestLib; + +1; diff --git a/t/main.t b/t/main.t new file mode 100644 index 0000000..67c86ae --- /dev/null +++ b/t/main.t @@ -0,0 +1,19 @@ +use strict; +use warnings; + +use Test::More 0.88; +use Module::Reader qw(:all); + +my $mod_content = do { + open my $fh, '<', 't/lib/TestLib.pm'; + local $/; + <$fh>; +}; + +{ + local @INC = @INC; + unshift @INC, 't/lib'; + is module_content('TestLib'), $mod_content, 'correctly load module from disk'; +} + +done_testing; diff --git a/t/memory.t b/t/memory.t new file mode 100644 index 0000000..d33974e --- /dev/null +++ b/t/memory.t @@ -0,0 +1,45 @@ +use strict; +use warnings; + +use Test::More 0.88; +use Module::Reader qw(:all); + +my $mod_content = do { + open my $fh, '<', 't/lib/TestLib.pm'; + local $/; + <$fh>; +}; + +{ + local @INC = @INC; + unshift @INC, sub { + return unless $_[1] eq 'TestLib.pm'; + if ($] < 5.008) { + my $mod = $mod_content; + return sub { + return 0 unless length $mod; + $mod =~ s/^([^\n]*\n?)//; + $_ = $1; + return 1; + }; + } + open my $fh, '<', \$mod_content; + return $fh; + }; + is module_content('TestLib'), $mod_content, 'correctly load module from sub @INC hook'; + SKIP: { + skip 'found option doesn\'t work with @INC hooks in perl < 5.8', 2 + if $] < 5.008; + require TestLib; + unshift @INC, sub { + return unless $_[1] eq 'TestLib.pm'; + my $content = '1;'; + open my $fh, '<', \$content; + return $fh; + }; + is module_content('TestLib'), '1;', 'loads overridden module from sub @INC hook'; + is module_content('TestLib', { found => \%INC } ), $mod_content, 'found => \%INC loads mod as it was required'; + } +} + +done_testing; |