Toby Inkster avatar Toby Inkster committed c44723d Draft

initial version

Comments (0)

Files changed (16)

+use inc::Module::Package 'RDF:tobyink 0.009';
+

lib/Module/Hash.pm

+package Module::Hash;
+
+use 5.006;
+use strict;
+
+BEGIN {
+	$Module::Hash::AUTHORITY = 'cpan:TOBYINK';
+	$Module::Hash::VERSION   = '0.001';
+}
+
+use base qw( Tie::Hash );
+use Carp qw( croak );
+use Module::Runtime qw( use_package_optimistically use_module );
+
+# Tied interface
+#
+
+sub TIEHASH
+{
+	my $class = shift;
+	return $class->new(@_);
+}
+
+sub STORE
+{
+	croak "Attempt to modify read-only hash, caught";
+}
+
+sub FETCH
+{
+	my ($self, $key) = @_;
+	return $self->use($key);
+}
+
+sub FIRSTKEY
+{
+	return;
+}
+
+sub NEXTKEY
+{
+	return;
+}
+
+sub EXISTS
+{
+	my ($self, $key) = @_;
+	defined $self->use($key);
+}
+
+sub DELETE
+{
+	croak "Attempt to modify read-only hash, caught";
+}
+
+sub CLEAR
+{
+	croak "Attempt to modify read-only hash, caught";
+}
+
+sub SCALAR
+{
+	return !!1;
+}
+
+# Object-oriented interface
+#
+
+sub new
+{
+	my $class = shift;
+	my %args  = ref $_[0] eq 'HASH' ? %{$_[0]} : @_;
+	
+	# Defaults
+	$args{optimistic} = 1 unless exists $args{optimistic};
+	
+	bless \%args, $class;
+}
+
+sub optimistic { $_[0]{optimistic} };
+sub prefix     { $_[0]{prefix} };
+sub has_prefix { exists $_[0]{prefix} };
+
+sub use
+{
+	my ($self, $module) = @_;
+	
+	my @mv = grep /\w/, split /\s/, $module;
+	croak "No module name given" unless @mv;
+	
+	$mv[0] = join(q[::], $self->prefix, $mv[0]) if $self->has_prefix;
+	
+	$self->optimistic ? &use_package_optimistically(@mv) : &use_module(@mv);
+	return $mv[0];
+}
+
+# Import-oriented interface
+#
+
+sub import
+{
+	my $class = shift;
+	tie %$_, $class for @_;
+}
+
+1
+__END__
+
+=head1 NAME
+
+Module::Hash - a tied hash that requires modules for you
+
+=head1 SYNOPSIS
+
+	use strict;
+	use Test::More tests => 1;
+	use Module::Hash;
+	
+	tie my %MOD, "Module::Hash";
+	
+	my $number = $MOD{"Math::BigInt"}->new(42);
+	
+	ok( $number->isa("Math::BigInt") );
+
+=head1 DESCRIPTION
+
+Module::Hash provides a tied hash that can be used to load and quote
+module names.
+
+=head2 Tied Interface
+
+	tie my %MOD, "Module::Hash", %options;
+
+The hash is tied to Module::Hash. Every time you fetch a hash key, such
+as C<< $MOD{"Math::BigInt"} >> that module is loaded, and the module
+name is returned as a string. Thus the following works without you
+needing to load L<Math::BigInt> in advance.
+
+	$MOD{"Math::BigInt"}->new(...)
+
+You may wonder what the advantage is of this hash, rather that using good
+old:
+
+	require Math::BigInt;
+	Math::BigInt->new(...)
+
+Well, the latter is actually ambiguous. Try defining a sub called
+C<BigInt> in the C<Math> package!
+
+You can provide an optional minimum version number for the module. The
+module will be checked against the required version number, but the
+version number will not be included in the returned string. Thus the
+following works:
+
+	$MOD{"Math::BigInt 1.00"}->new(...)
+
+The following options are supported:
+
+=over
+
+=item *
+
+B<prefix> - an optional prefix for modules
+
+	tie my $MATH, "Module::Hash", prefix => "Math";
+	my $number = $MATH{BigInt}->new(42);
+
+=item *
+
+B<optimistic> - a boolean. If the hash is optimistic, then it doesn't
+croak when modules are missing; it silently returns the module name
+anyway. Hashes are optimistic by default; you need to explicitly
+pessimize them:
+
+	tie my $MOD, "Module::Hash", optimistic => 0;
+
+=back
+
+Attempting to modify the hash will croak.
+
+=head2 Import-Oriented Interface
+
+If you just want to use the default options, you can supply a reference
+to the hash in the import statement:
+
+	my %MOD;
+	use Module::Hash \%MOD;
+	$MOD{"Math::BigInt"}->new(...);
+
+Or:
+
+	my $MOD;
+	use Module::Hash $MOD;
+	$MOD->{"Math::BigInt"}->new(...);
+
+Little known fact: Perl has a built-in global hash called C<< %\ >>.
+Unlike C<< %+ >> and C<< %- >> and some other built-in global hashes,
+the Perl core doesn't use it for anything. And I don't think anybody
+else uses it either. The following makes for some cute code...
+
+	use Module::Hash \%\;
+	$\{"Math::BigInt"}->new(...);
+
+... or an unmaintainable nightmare depending on your perspective.
+
+=head2 Object-Oriented Interface
+
+This module also provides an object-oriented interface, intended
+for subclassing, etc, etc.
+
+Methods:
+
+=over
+
+=item C<< new(%options) >>
+
+=item C<< optimistic >>
+
+=item C<< has_prefix >>
+
+=item C<< prefix >>
+
+=item C<< use($hash_key) >>
+
+=back
+
+=head1 BUGS
+
+Please report any bugs to
+L<http://rt.cpan.org/Dist/Display.html?Queue=Module-Hash>.
+
+=head1 SEE ALSO
+
+Most of the tricky stuff is handled by L<Module::Runtime>.
+
+L<Module::Quote> is similar to this, but more insane. If this module isn't
+insane enough for you, try that.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+=head1 COPYRIGHT AND LICENCE
+
+This software is copyright (c) 2012 by Toby Inkster.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=head1 DISCLAIMER OF WARRANTIES
+
+THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+

meta/changes.pret

+# This file acts as the project's changelog.
+
+`Module-Hash 0.001 cpan:TOBYINK`
+	issued  2012-11-23;
+	label   "Initial release".
+
+# This file contains general metadata about the project.
+
+@prefix : <http://usefulinc.com/ns/doap#>.
+
+`Module-Hash`
+	:programming-language "Perl" ;
+	:shortdesc            "a tied hash that requires modules for you";
+	:homepage             <https://metacpan.org/release/Module-Hash>;
+	:download-page        <https://metacpan.org/release/Module-Hash>;
+	:bug-database         <http://rt.cpan.org/Dist/Display.html?Queue=Module-Hash>;
+	:repository           [ a :HgRepository; :browse <https://bitbucket.org/tobyink/p5-module-hash> ];
+	:created              2012-11-23;
+	:license              <http://dev.perl.org/licenses/>;
+	:maintainer           cpan:TOBYINK;
+	:developer            cpan:TOBYINK.
+
+<http://dev.perl.org/licenses/>
+	dc:title  "the same terms as the perl 5 programming language system itself".
+

meta/makefile.pret

+# This file provides instructions for packaging.
+
+`Module-Hash`
+	perl_version_from m`Module::Hash`;
+	version_from      m`Module::Hash`;
+	readme_from       m`Module::Hash`;
+	test_requires     p`Test::More 0.61`;
+	requires          p`Module::Runtime 0.002`;
+.
+
+# This file contains data about the project developers.
+
+@prefix : <http://xmlns.com/foaf/0.1/>.
+
+cpan:TOBYINK
+	:name  "Toby Inkster";
+	:mbox  <mailto:tobyink@cpan.org>.
+
+use Test::More tests => 1;
+BEGIN { use_ok('Module::Hash') };
+
+use strict;
+use Test::More tests => 1;
+use Module::Hash;
+
+tie my %MOD, "Module::Hash";
+
+my $number = $MOD{"Math::BigInt"}->new(42);
+
+ok( $number->isa("Math::BigInt") );
+use strict;
+use Test::More tests => 3;
+
+{
+	my $MOD;
+	use Module::Hash $MOD;
+
+	my $number = $MOD->{"Math::BigInt"}->new(42);
+	ok( $number->isa("Math::BigInt") );
+}
+
+{
+	my %MOD;
+	use Module::Hash \%MOD;
+
+	my $number = $MOD{"Math::BigInt"}->new(42);
+	ok( $number->isa("Math::BigInt") );
+}
+
+{
+	use Module::Hash \%\;
+
+	my $number = $\{"Math::BigInt"}->new(42);
+	ok( $number->isa("Math::BigInt") );
+}
+use Test::More;
+eval "use Test::Pod 1.00";
+plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
+all_pod_files_ok();
+

xt/02pod_coverage.t

+use XT::Util;
+use Test::More;
+use Test::Pod::Coverage;
+
+plan skip_all => __CONFIG__->{skip_all}
+	if __CONFIG__->{skip_all};
+
+if ( __CONFIG__->{modules} )
+{
+	my @modules = @{ __CONFIG__->{modules} };
+	pod_coverage_ok($_, "$_ is covered") for @modules;
+	done_testing(scalar @modules);
+}
+else
+{
+	all_pod_coverage_ok();
+}
+

xt/03meta_uptodate.config

+{"package":"Module-Hash"}
+

xt/03meta_uptodate.t

+use XT::Util;
+use Test::More tests => 1;
+use Test::RDF::DOAP::Version;
+doap_version_ok(__CONFIG__->{package}, __CONFIG__->{version_from});
+
+use Test::EOL;
+all_perl_files_ok();
+use Test::Tabs;
+all_perl_files_ok();
+use XT::Util;
+use Test::More;
+use Test::HasVersion;
+
+plan skip_all => __CONFIG__->{skip_all}
+	if __CONFIG__->{skip_all};
+
+if ( __CONFIG__->{modules} )
+{
+	my @modules = @{ __CONFIG__->{modules} };
+	pm_version_ok($_, "$_ is covered") for @modules;
+	done_testing(scalar @modules);
+}
+else
+{
+	all_pm_version_ok();
+}
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.