diff --git a/Changes b/Changes new file mode 100644 index 0000000..fd91573 --- /dev/null +++ b/Changes @@ -0,0 +1,48 @@ +Revision history for Perl extension Devel::Hide. + +0.0009 Mon Jan 28 2013 + - avoid "defined @HIDDEN" which generates + a warning since Perl 5.15.7 + CPAN RT #74225 + +0.0008 Thu Nov 15 2007 + - no real code changes + - make t/050child-processes.t less clever for + Windows' sake: which choked with too long + argument and exec() + +0.0007 Mon Nov 5 2007 + - promoted: no longer a development release + +0.0006_01 + - some POD fixes + - new option -from:children makes the selected + modules hidden from process children as well + (thanks to David Cantrell) + +0.0005 Wed May 30 2007 + - code reformatting with perltidy + - now warns about already loaded modules + - META.yml is auto-generated by Makefile.PL + - a bit of refactoring to affect code readability + - side effect: warnings are emitted at compile time + and at every import call - a hopefully better timing + - the package variable @HIDDEN does not change anymore + +0.0004 Tue May 29 2007 + - slight implementation change preparing for the big time + +0.0003 Wed Oct 4 2006 + - not a development version anymore + - get rid of warning at "t/003user.t" + - new test "t/098pod-coverage.t" + +0.00_02 Sun Sep 25 2005 + - lifted the requirement on perl 5.8: now 5.6.1 will do + (because it may use File::Temp to fake bad modules) + +0.00_01 Tue Sep 20 2005 + - first release to CPAN + +0.00_00 Tue Sep 13 2005 + - absolute beginning diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..dc8c08e --- /dev/null +++ b/MANIFEST @@ -0,0 +1,23 @@ +Changes +Makefile.PL +MANIFEST +README + +t/001use.t +t/002basic.t +t/003user.t +t/004env.t +t/005lib.t +t/006before.t +t/050child-processes.t Tests -from:children +t/child.pl +t/090pod.t Tests POD for errors +t/098pod-coverage.t Tests for POD coverage + +t/P.pm Dummy module used in tests (002-006, 050) +t/Q.pm " +t/R.pm " + +lib/Devel/Hide.pm +META.yml Module meta-data (added by MakeMaker) +META.json Module JSON meta-data (added by MakeMaker) diff --git a/META.json b/META.json new file mode 100644 index 0000000..6a71ff4 --- /dev/null +++ b/META.json @@ -0,0 +1,50 @@ +{ + "abstract" : "Forces the unavailability of specified Perl modules (for testing)", + "author" : [ + "A. R. Ferreira " + ], + "dynamic_config" : 1, + "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.113640", + "license" : [ + "perl_5" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "Devel-Hide", + "no_index" : { + "directory" : [ + "t", + "inc" + ] + }, + "prereqs" : { + "build" : { + "requires" : { + "ExtUtils::MakeMaker" : 0 + } + }, + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : 0 + } + }, + "runtime" : { + "recommends" : { + "Test::Pod" : "1.18", + "Test::Pod::Coverage" : "1.04" + }, + "requires" : { + "Test::More" : 0 + } + } + }, + "release_status" : "stable", + "resources" : { + "repository" : { + "url" : "https://github.com/aferreira/cpan-Devel-Hide" + } + }, + "version" : "0.0009" +} diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..14507bb --- /dev/null +++ b/META.yml @@ -0,0 +1,27 @@ +--- +abstract: 'Forces the unavailability of specified Perl modules (for testing)' +author: + - 'A. R. Ferreira ' +build_requires: + ExtUtils::MakeMaker: 0 +configure_requires: + ExtUtils::MakeMaker: 0 +dynamic_config: 1 +generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.113640' +license: perl +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: 1.4 +name: Devel-Hide +no_index: + directory: + - t + - inc +recommends: + Test::Pod: 1.18 + Test::Pod::Coverage: 1.04 +requires: + Test::More: 0 +resources: + repository: https://github.com/aferreira/cpan-Devel-Hide +version: 0.0009 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..268488f --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,36 @@ + +use 5.006001; +use ExtUtils::MakeMaker; + +my $EUMM_VERSION = eval $ExtUtils::MakeMaker::VERSION; + +WriteMakefile( + NAME => 'Devel::Hide', + VERSION_FROM => 'lib/Devel/Hide.pm', + PREREQ_PM => { + Test::More => 0, + ($] <= 5.008 ? ( + File::Temp => 0 + ) : ()) + }, + ($] >= 5.005 ? ( + ABSTRACT_FROM => 'lib/Devel/Hide.pm', + AUTHOR => 'A. R. Ferreira ', + ) : ()), + ($EUMM_VERSION >= 6.31 ? ( + LICENSE => 'perl', + ) : ()), + ($EUMM_VERSION >= 6.4501 ? ( + META_MERGE => { + recommends => { + # optional tests + 'Test::Pod' => 1.18, + 'Test::Pod::Coverage' => 1.04, + }, + resources => { + repository => 'https://github.com/aferreira/cpan-Devel-Hide', + }, + }, + ) : ()), +); + diff --git a/README b/README new file mode 100644 index 0000000..863ba92 --- /dev/null +++ b/README @@ -0,0 +1,43 @@ +Devel-Hide version 0.0008 +========================= + +Simple tool for developers which allows to hide +installed Perl modules. Used like this: + + perl -MDevel::Hide=Module/ToHide.pm script.pl + + +INSTALLATION + +To install this module type the following: + + perl Makefile.PL + make + make test + make install + +DEPENDENCIES + +This module requires + + File::Temp (for perls older than 5.8.0) + +and + + Test::More + +for the testing part and recommends + + Test::Pod 1.18 + Test::Pod::Coverage 1.04 + +also for testing - the POD part. + +COPYRIGHT AND LICENCE + +Copyright (C) 2005-2007 by Adriano R. Ferreira + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + + diff --git a/lib/Devel/Hide.pm b/lib/Devel/Hide.pm new file mode 100644 index 0000000..97ffa0f --- /dev/null +++ b/lib/Devel/Hide.pm @@ -0,0 +1,403 @@ + +package Devel::Hide; + +use 5.006001; +use strict; +use warnings; + +our $VERSION = '0.0009'; + +# blech! package variables +use vars qw( @HIDDEN $VERBOSE ); + +# a map ( $hidden_file => 1 ) to speed determining if a module/file is hidden +my %IS_HIDDEN; + +# whether to hide modules from ... +my %HIDE_FROM = ( + children => 0, # child processes or not +); + +=begin private + +=item B<_to_filename> + + $fn = _to_filename($pm); + +Turns a Perl module name (like 'A' or 'P::Q') into +a filename ("A.pm", "P/Q.pm"). + +=end private + +=cut + +sub _to_filename { + my $pm = shift; + $pm =~ s|::|/|g; + $pm .= '.pm'; + return $pm; +} + +=begin private + +=item B<_as_filenames> + + @fn = _as_filenames(@args); + @fn = _as_filenames(qw(A.pm X B/C.pm File::Spec)); # returns qw(A.pm X.pm B/C.pm File/Spec.pm) + +Copies the argument list, turning what looks like +a Perl module name to filenames and leaving everything +else as it is. To look like a Perl module name is +to match C< /^(\w+::)*\w+$/ >. + +=end private + +=cut + +sub _as_filenames { + return map { /^(\w+::)*\w+$/ ? _to_filename($_) : $_ } @_; +} + +BEGIN { + + unless ( defined $VERBOSE ) { # unless user-defined elsewhere, set default + $VERBOSE + = defined $ENV{DEVEL_HIDE_VERBOSE} ? $ENV{DEVEL_HIDE_VERBOSE} : 1; + } + +} + +# Pushes a list to the set of hidden modules/filenames +# warns about the modules which could not be hidden +# and about the ones that were successfully hidden (if $VERBOSE) +# +# It works as a batch producing warning messages +# at each invocation (when appropriate). +# +sub _push_hidden { + + return unless @_; + + my @too_late; + for ( _as_filenames(@_) ) { + if ( $INC{$_} ) { + push @too_late, $_; + } + else { + $IS_HIDDEN{$_}++; + } + } + if ( $VERBOSE && @too_late ) { + warn __PACKAGE__, ': Too late to hide ', join( ', ', @too_late ), "\n"; + } + if ( $VERBOSE && keys %IS_HIDDEN ) { + warn __PACKAGE__, ' hides ', join( ', ', sort keys %IS_HIDDEN ), "\n"; + } +} + +# $ENV{DEVEL_HIDE_PM} is split in ' ' +# as well as @HIDDEN it accepts Module::Module as well as File/Names.pm + +BEGIN { + + # unless @HIDDEN was user-defined elsewhere, set default + if ( !@HIDDEN && $ENV{DEVEL_HIDE_PM} ) { + _push_hidden( split q{ }, $ENV{DEVEL_HIDE_PM} ); + + # NOTE. "split ' ', $s" is special. Read "perldoc -f split". + } + else { + _push_hidden(@HIDDEN); + } + + # NOTE. @HIDDEN is not changed anymore + +} + +# works for perl 5.8.0, uses in-core files +sub _scalar_as_io8 { + open my $io, '<', \$_[0] + or die $!; # this should not happen (perl 5.8 should support this) + return $io; +} + +# works for perl >= 5.6.1, uses File::Temp +sub _scalar_as_io6 { + my $scalar = shift; + require File::Temp; + my $io = File::Temp::tempfile(); + print {$io} $scalar; + seek $io, 0, 0; # rewind the handle + return $io; +} + +BEGIN { + + *_scalar_as_io = ( $] >= 5.008 ) ? \&_scalar_as_io8 : \&_scalar_as_io6; + + # _scalar_as_io is one of the two sub's above + +} + +sub _dont_load { + my $filename = shift; + my $oops; + my $hidden_by = $VERBOSE ? 'hidden' : 'hidden by ' . __PACKAGE__; + $oops = qq{die "Can't locate $filename ($hidden_by)\n"}; + return _scalar_as_io($oops); +} + +sub _is_hidden { + my $filename = shift; + return $IS_HIDDEN{$filename}; +} + +sub _inc_hook { + my ( $coderef, $filename ) = @_; + if ( _is_hidden($filename) ) { + return _dont_load($filename); # stop right here, with error + } + else { + return undef; # go on with the search + } +} + +use lib ( \&_inc_hook ); + +=begin private + +=item B<_core_modules> + + @core = _core_modules($perl_version); + +Returns the list of core modules according to +Module::CoreList. + +!!! UNUSED BY NOW + +It is aimed to expand the tag ':core' into all core +modules in the current version of Perl ($]). +Requires Module::CoreList. + +=end private + +=cut + +sub _core_modules { + require Module::CoreList; # XXX require 2.05 or newer + return Module::CoreList->find_modules( qr/.*/, shift ); +} + +# _append_to_perl5opt(@to_be_hidden) +sub _append_to_perl5opt { + + $ENV{PERL5OPT} = join( ' ', + defined($ENV{PERL5OPT}) ? $ENV{PERL5OPT} : (), + 'MDevel::Hide=' . join(',', @_) + ); + +} + +sub import { + shift; + if( @_ && $_[0] eq '-from:children' ) { + $HIDE_FROM{children} = 1; + shift; + } + if (@_) { + _push_hidden(@_); + if ($HIDE_FROM{children}) { + _append_to_perl5opt(@_); + } + } + +} + +# TO DO: +# * write unimport() sub +# * write decent docs +# * refactor private function names +# * RT #25528 + +=begin private + +perl -MDevel::Hide=!:core -e script.pl # hide all non-core modules +perl -MDevel::Hide=M,!N -e script.pl # hide all modules but N plus M + +how to implement + +%IS_HIDDEN +%IS_EXCEPTION if there is an exception, all but the set of exceptions are to be hidden + plus the set of hidden modules + + :core(5.8) + :core synonym to :core($]) + + +=end private + +=cut + +1; + +__END__ + +=head1 NAME + +Devel::Hide - Forces the unavailability of specified Perl modules (for testing) + + +=head1 SYNOPSIS + + use Devel::Hide qw(Module/ToHide.pm); + require Module::ToHide; # fails + + use Devel::Hide qw(Test::Pod Test::Pod::Coverage); + require Test::More; # ok + use Test::Pod 1.18; # fails + +Other common usage patterns: + + $ perl -MDevel::Hide=Module::ToHide Makefile.PL + + bash$ PERL5OPT=MDevel::Hide + bash$ DEVEL_HIDE_PM='Module::Which Test::Pod' + bash$ export PERL5OPT DEVEL_HIDE_PM + bash$ perl Makefile.PL + +outputs (like blib) + + Devel::Hide hides Module::Which, Test::Pod, etc. + + +=head1 DESCRIPTION + +Given a list of Perl modules/filenames, this module makes +C and C statements fail (no matter the +specified files/modules are installed or not). + +They I with a message like: + + Can't locate Module/ToHide.pm (hidden) + +The original intent of this module is to allow Perl developers +to test for alternative behavior when some modules are not +available. In a Perl installation, where many modules are +already installed, there is a chance to screw things up +because you take for granted things that may not be there +in other machines. + +For example, to test if your distribution does the right thing +when a module is missing, you can do + + perl -MDevel::Hide=Test::Pod Makefile.PL + +forcing C to not be found (whether it is installed +or not). + +Another use case is to force a module which can choose between +two requisites to use the one which is not the default. +For example, C needs a parser module and may use +C or C (preferring the latter). +If you have both of them installed, it will always try C. +But you can say: + + perl -MDevel::Hide=XML::SAX script_which_uses_xml_simple.pl + +NOTE. This module does not use L. As said before, +denial I. + +This module is pretty trivial. It uses a code reference +in @INC to get rid of specific modules during require - +denying they can be successfully loaded and stopping +the search before they have a chance to be found. + +There are three alternative ways to include modules in +the hidden list: + +=over 4 + +=item * + +setting @Devel::Hide::HIDDEN + +=item * + +environment variable DEVEL_HIDE_PM + +=item * + +import() + +=back + +Optionally, you can propagate the list of hidden modules to your +process' child processes, by passing '-from:children' as the +first option when you use() this module. This works by populating +C, and is incompatible with Taint mode, as +explained in L. + + +=head2 CAVEATS + +There is some interaction between C and this module + + use Devel::Hide qw(Module/ToHide.pm); + use lib qw(my_lib); + +In this case, 'my_lib' enters the include path before +the Devel::Hide hook and if F is found +in 'my_lib', it succeeds. + +Also for modules that were loaded before Devel::Hide, +C and C succeeds. + +Since 0.0005, Devel::Hide warns about modules already loaded. + + $ perl -MDevel::Hide=Devel::Hide -e '' + Devel::Hide: Too late to hide Devel/Hide.pm + + +=head2 EXPORTS + +Nothing is exported. + + +=head1 ENVIRONMENT VARIABLES + +DEVEL_HIDE_PM - if defined, the list of modules is added + to the list of hidden modules + +DEVEL_HIDE_VERBOSE - on by default. If off, supresses + the initial message which shows the list of hidden modules + in effect + +PERL5OPT - used if you specify '-from:children' + + +=head1 SEE ALSO + +L + +L + + +=head1 BUGS + +Please report bugs via CPAN RT L. + + +=head1 AUTHORS + +Adriano R. Ferreira, Eferreira@cpan.orgE + +with contributions from David Cantrell Edcantrell@cpan.orgE + + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2005-2007 by Adriano R. Ferreira + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + diff --git a/t/001use.t b/t/001use.t new file mode 100644 index 0000000..d861663 --- /dev/null +++ b/t/001use.t @@ -0,0 +1,7 @@ + +use strict; +use Test::More tests => 1; + +use_ok('Devel::Hide'); + +diag( "Testing Devel::Hide $Devel::Hide::VERSION, Perl $], $^X" ); \ No newline at end of file diff --git a/t/002basic.t b/t/002basic.t new file mode 100644 index 0000000..b1e53f8 --- /dev/null +++ b/t/002basic.t @@ -0,0 +1,18 @@ + +use strict; +use Test::More tests => 5; + +use_ok('lib', 't'); + +# this script tests the standard use statement + +use_ok('Devel::Hide', 'Q.pm', 'R'); + +eval { require P }; +ok(!$@, "P was loaded (as it should)"); + +eval { require Q }; +like($@, qr/^Can't locate Q\.pm/, "Q not found (as it should)"); + +eval { require R }; +like($@, qr/^Can't locate R\.pm/, "R not found (as it should)"); diff --git a/t/003user.t b/t/003user.t new file mode 100644 index 0000000..241188f --- /dev/null +++ b/t/003user.t @@ -0,0 +1,24 @@ + +use strict; +use warnings; +use Test::More tests => 5; + +use_ok('lib', 't'); + +# this script tests setting @HIDDEN before using Devel::Hide + +{ +no warnings 'once'; # @HIDDEN is used by Devel::Hide +@Devel::Hide::HIDDEN = qw(Q.pm R); +} + +use_ok('Devel::Hide'); + +eval { require P }; +ok(!$@, "P was loaded (as it should)"); + +eval { require Q }; +like($@, qr/^Can't locate Q\.pm/, "Q not found (as it should)"); + +eval { require R }; +like($@, qr/^Can't locate R\.pm/, "R not found (as it should)"); diff --git a/t/004env.t b/t/004env.t new file mode 100644 index 0000000..68b79c5 --- /dev/null +++ b/t/004env.t @@ -0,0 +1,19 @@ + +use strict; +use Test::More tests => 5; + +use_ok('lib', 't'); + +# this script tests Devel::Hide respects environment variable DEVEL_HIDE_PM + +$ENV{DEVEL_HIDE_PM} = 'Q.pm R'; +use_ok('Devel::Hide'); + +eval { require P }; +ok(!$@, "P was loaded (as it should)"); + +eval { require Q }; +like($@, qr/^Can't locate Q\.pm/, "Q not found (as it should)"); + +eval { require R }; +like($@, qr/^Can't locate R\.pm/, "R not found (as it should)"); diff --git a/t/005lib.t b/t/005lib.t new file mode 100644 index 0000000..93683d3 --- /dev/null +++ b/t/005lib.t @@ -0,0 +1,13 @@ + +use strict; +use Test::More tests => 4; + +use_ok('lib', 't'); + +# this script tests "use lib" after "use Devel::Hide" + +use_ok('Devel::Hide'); +use_ok('lib', 't'); # put 't' before the Devel::Hide hook in @INC + +eval { require P }; +ok(!$@, "P was loaded (as it should)"); diff --git a/t/006before.t b/t/006before.t new file mode 100644 index 0000000..5f7a130 --- /dev/null +++ b/t/006before.t @@ -0,0 +1,13 @@ + +use strict; +use Test::More tests => 4; + +use_ok('lib', 't'); + +# this script tests that already loaded modules can't be hidden + +use_ok('P'); # loads P +use_ok('Devel::Hide', 'P'); # too late to hide + +eval { require P }; +ok(!$@, "P was loaded (as it should)"); diff --git a/t/050child-processes.t b/t/050child-processes.t new file mode 100644 index 0000000..77f23da --- /dev/null +++ b/t/050child-processes.t @@ -0,0 +1,11 @@ + +use strict; +use Devel::Hide qw(-from:children Q.pm R); + +# Mlib=t is to get around 'use lib' etc being annoying + +$ENV{PERL5OPT} = 'Mlib=t '.$ENV{PERL5OPT}; + +my $ans = system( $^X, 't/child.pl' ); + +exit( $ans >> 8 ); diff --git a/t/090pod.t b/t/090pod.t new file mode 100644 index 0000000..10319d0 --- /dev/null +++ b/t/090pod.t @@ -0,0 +1,7 @@ + +use strict; +use Test::More; +eval "use Test::Pod 1.18"; +plan skip_all => "Test::Pod 1.18 required for testing POD" if $@; + +all_pod_files_ok(all_pod_files(".")); diff --git a/t/098pod-coverage.t b/t/098pod-coverage.t new file mode 100644 index 0000000..30a71c2 --- /dev/null +++ b/t/098pod-coverage.t @@ -0,0 +1,7 @@ + +use Test::More; + +eval "use Test::Pod::Coverage 1.04"; +plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@; + +all_pod_coverage_ok(); \ No newline at end of file diff --git a/t/P.pm b/t/P.pm new file mode 100644 index 0000000..f184c8c --- /dev/null +++ b/t/P.pm @@ -0,0 +1,5 @@ + +package P; + +1; + diff --git a/t/Q.pm b/t/Q.pm new file mode 100644 index 0000000..d0d2297 --- /dev/null +++ b/t/Q.pm @@ -0,0 +1,5 @@ + +package Q; + +1; + diff --git a/t/R.pm b/t/R.pm new file mode 100644 index 0000000..0ba346d --- /dev/null +++ b/t/R.pm @@ -0,0 +1,5 @@ + +package R; + +1; + diff --git a/t/child.pl b/t/child.pl new file mode 100644 index 0000000..1bc9b23 --- /dev/null +++ b/t/child.pl @@ -0,0 +1,15 @@ + +# invoked by "t/050child-processes.t" + +use strict; +use Test::More tests => 4; + +ok($ENV{PERL5OPT} =~ /\bMlib=t\b/, "PERL5OPT is added to, not overwritten: $ENV{PERL5OPT}"); +eval { require P }; +ok(!$@, "P was loaded (as it should)"); + +eval { require Q }; +like($@, qr/^Can't locate Q\.pm/, "Q not found (as it should)"); + +eval { require R }; +like($@, qr/^Can't locate R\.pm/, "R not found (as it should)");