Commits

Toby Inkster committed f325258

initial version

  • Participants
  • Tags 0.001

Comments (0)

Files changed (18)

+This isn't really a FAQ.
+
+There are some examples bundled with the installation, but they are
+not installed by default. If you grab a copy of the "word-examples"
+and "word-lib" directories though, you ought to be able to run them
+like this (assuming Acme::RunDoc and icrosoft::Word are installed on
+your system)...
+
+# Example .doc
+perl -Microsoft::Word 'word-examples/helloworld.doc'
+
+# Example .docm
+perl -Iword-lib -MAcme::RunDoc=Hello::World -E'greet;'
+
+use inc::Module::Package 'RDF:standard';
+

lib/Acme/RunDoc.pm

+package Acme::RunDoc;
+
+use strict;
+use autodie;
+
+BEGIN {
+	$Acme::RunDoc::AUTHORITY = 'cpan:TOBYINK';
+	$Acme::RunDoc::VERSION   = '0.001';
+}
+
+# 'undef' is Text::Extract::Word's default filter, and probably the only
+# one that makes sense.
+use constant FILTER => undef;
+
+use Carp qw//;
+use Data::Dumper qw//;
+use File::Spec;
+use IO::File;
+use Text::Extract::Word;
+use Module::Runtime qw//;
+
+sub do
+{
+	my ($class, $file) = _args(@_);
+	my $text = Text::Extract::Word->new($file)->get_body(FILTER)
+		or CORE::do { $! = 'cannot read file'; return undef };
+	return CORE::eval($text);
+}
+
+sub require_file
+{
+	my ($class, $file) = _args(@_);
+	my $fh = IO::File->new($file, 'r')
+		or Carp::croak("Could not require file $file: $!");
+	$class->do($fh) or Carp::croak("Could not require file $file: $@");
+}
+
+sub require
+{
+	my ($class, $module) = _args(@_);
+	(my $filename = Module::Runtime::module_notional_filename($module))
+		=~ s{ \.pm $ }{ '.docm' }ex;
+	my ($file) = 
+		grep { -e $_ }
+		map { File::Spec->catfile($_, $filename) }
+		@INC;
+	Carp::croak("Could not find $filename in \@INC: ".join q{ }, @INC)
+		unless defined $file;
+	$class->require_file($file);
+}
+
+sub use
+{
+	my ($class, $module, @args) = _args(@_);
+	$class->require($module);
+	
+	{
+		my $import = $module->can('import');
+		@_ = ($module, @args);
+		goto $import if $import;
+	}
+}
+
+sub import
+{
+	my ($class, @args) = _args(@_);
+	my $caller = scalar caller;
+	local $Data::Dumper::Indent = 0;
+	while (@args)
+	{
+		my $module = shift @args;
+		my $args   = ref $args[0] ? shift @args : undef;
+		my $eval = sprintf(
+			"{ package %s; my \@args = %s; Acme::RunDoc->use('%s', \@args); }",
+			$caller,
+			ref $args eq 'HASH'
+				? sprintf('do { my %s; %%$VAR1 }', Data::Dumper::Dumper($args))
+				: ref $args
+				? sprintf('do { my %s; @$VAR1 }',  Data::Dumper::Dumper($args))
+				: '()',
+			$module,
+			);
+		eval "$eval; 1" or Carp::croak($@);
+	}
+}
+
+sub _args
+{
+	my (@args) = @_;
+	return @args if $args[0] eq __PACKAGE__;
+	return @args if UNIVERSAL::can($args[0] => 'isa')
+	             && $args[0]->isa(__PACKAGE__);
+	unshift @args, __PACKAGE__;
+	return @args;
+}
+
+__FILE__
+__END__
+
+=head1 NAME
+
+Acme::RunDoc - executes a Microsoft Word document as if it were Perl code
+
+=head1 SYNOPSIS
+
+ Acme::RunDoc->do("helloworld.doc"); 
+
+=head1 DESCRIPTION
+
+It is recieved wisdom that word processors are better than text editors.
+After all, you can style your documents with different fonts and colours;
+you can take advantage of the built-in spell check; and your ugly single
+and double quote characters get auto-replaced with "smart" curly versions.
+
+This module allows you to run Perl documents edited in Microsoft Word
+(and other word processors capable of saving in the ".doc" format) as
+normal Perl code. You can write scripts and run them like this:
+
+  perl -Microsoft::Word helloworld.doc
+
+or call them from other files using:
+
+  Acme::RunDoc->do("helloworld.doc");
+
+You can write Perl modules using Microsoft Word too. (Just take care to
+rename ".doc" to ".docm".) To "require" them:
+
+  Acme::RunDoc->require_file("Hello/World.docm");
+  Acme::RunDoc->require("Hello::World");
+
+Acme::RunDoc searches C<< @INC >> just like you'd expect.
+
+You can even "use" modules written in Microsoft Word:
+
+  BEGIN {
+    require Acme::RunDoc;
+    Acme::RunDoc->use("Hello::World", "greet");
+  }
+
+There's a handy shortcut for that too:
+
+  use Acme::RunDoc "Hello::World" => ["greet"];
+
+=head2 C<< do($file) >>
+
+This module provides a class method C<do> which works in an analagous method
+to Perl's built-in C<< do $file >> function. In other words, it reads the
+contents of the file, and executes it (via C<eval>).
+
+Unlike Perl's built-in, it expects the Perl code to be in Microsoft Word's
+"doc" format. Headers, footers, footnotes and annotations are ignored.
+"Smart quotes" should be treated as their normal ASCII equivalents.
+
+It may take a file name or an open file handle. (The filehandle needs to be
+seekable - see L<IO::Seekable> and L<IO::File>.)
+
+=head2 C<< require_file($file) >>
+
+This class method is analagous to Perl's built-in C<< require $file >>
+function. Performs a C<do> on the given filename, but croaks if the file
+returns false at the end.
+
+=head2 C<< require($module) >>
+
+This class method is analagous to Perl's built-in C<< require Module >>
+function.
+
+Unlike Perl's built-in, it expects Module::Foo to correspond to the file
+"Module/Foo.docm".
+
+=head2 C<< use($module) >>
+
+This class method is analagous to Perl's built-in C<< use Module >> function.
+
+Unlike Perl's built-in, this is not automatically executed at compile time.
+You'll need to wrap it in a C<< BEGIN { ... } >> block for that to happen.
+
+Unlike Perl's built-in, there is no method for skipping the module's C<import>
+method. If you don't want to run C<import>, then just C<require> the module.
+
+=head2 C<< import($module1, \@args1, ...) >>
+
+A handy shortcut for:
+
+ BEGIN {
+   require Acme::RunDoc;
+   Acme::RunDoc->use($module1, @args1);
+   Acme::RunDoc->use($module2, %args2);
+   Acme::RunDoc->use($module3);
+ }
+
+is:
+
+ use Acme::RunDoc
+     $module1  => \@args1,
+     $module2  => \%args2,
+     $module3  => undef;
+
+(See the sections on C<use>, C<import> and C<require> in L<perlfunc> if any
+of that confuses you.)
+
+=head1 SEE ALSO
+
+L<icrosoft::Word>, L<Text::Extract::Word>.
+
+=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.
+

lib/icrosoft/Word.pm

+package icrosoft::Word;
+BEGIN {
+	$icrosoft::Word::AUTHORITY = 'cpan:TOBYINK';
+	$icrosoft::Word::VERSION   = '0.001';
+}
+package main;
+use strict;
+use Acme::RunDoc;
+if ([caller(0)]->[3] > 0)
+{
+	Carp::croak("Usage:  perl -Microsoft::Word somefile.doc");
+}
+exit( Acme::RunDoc->do($0) );
+__FILE__
+__END__
+
+=head1 NAME
+
+icrosoft::Word - (sic) syntactic sugar for Acme::RunDoc.
+
+=head2 SYNOPSIS
+
+ perl -Microsoft::Word helloworld.doc
+
+=head1 SEE ALSO
+
+L<Acme::RunDoc>, L<Text::Extract::Word>.
+
+=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.
+
+# This file acts as the project's changelog.
+
+@prefix :        <http://usefulinc.com/ns/doap#> .
+@prefix dcs:     <http://ontologi.es/doap-changeset#> .
+@prefix dc:      <http://purl.org/dc/terms/> .
+@prefix dist:    <http://purl.org/NET/cpan-uri/dist/Acme-RunDoc/> .
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+
+dist:project :release dist:v_0-001 .
+dist:v_0-001
+	a               :Version ;
+	dc:issued       "2012-02-14"^^xsd:date ;
+	:revision       "0.001"^^xsd:string ;
+	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/Acme-RunDoc-0.001.tar.gz> ;
+	rdfs:label      "Initial release" .
+
+# This file contains general metadata about the project.
+
+@prefix :        <http://usefulinc.com/ns/doap#> .
+@prefix dc:      <http://purl.org/dc/terms/> .
+@prefix foaf:    <http://xmlns.com/foaf/0.1/> .
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+
+<http://purl.org/NET/cpan-uri/dist/Acme-RunDoc/project>
+	a               :Project ;
+	:programming-language "Perl" ;
+	:name           "Acme-RunDoc" ;
+	:shortdesc      "executes a Microsoft Word document as if it were Perl code" ;
+	:homepage       <https://metacpan.org/release/Acme-RunDoc> ;
+	:download-page  <https://metacpan.org/release/Acme-RunDoc> ;
+	:repository     [ a :HgRespository ; :browse <https://bitbucket.org/tobyink/p5-acme-rundoc> ];
+	:bug-database   <http://rt.cpan.org/Dist/Display.html?Queue=Acme-RunDoc> ;
+	:created        "2012-02-14"^^xsd:date ;
+	:license        <http://dev.perl.org/licenses/> ;
+	:developer      [ a foaf:Person ; foaf:name "Toby Inkster" ; foaf:mbox <mailto:tobyink@cpan.org> ] .
+
+<http://dev.perl.org/licenses/>
+	dc:title        "the same terms as the perl 5 programming language system itself" .
+

meta/makefile.ttl

+# This file provides instructions for packaging.
+
+@prefix : <http://purl.org/NET/cpan-uri/terms#> .
+
+<http://purl.org/NET/cpan-uri/dist/Acme-RunDoc/project>
+	:perl_version_from _:main ;
+	:version_from _:main ;
+	:readme_from _:main ;
+	:test_requires "Test::More 0.61", "lib"  ;
+	:requires "strict" , "autodie" , "constant", "Data::Dumper","Module::Runtime","Carp","File::Spec","IO::File","Text::Extract::Word" .
+
+_:main <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#fileName> "lib/Acme/RunDoc.pm" .
+
+use Test::More tests => 1;
+BEGIN { use_ok('Acme::RunDoc') };
+
+use Test::More tests => 1;
+use Acme::RunDoc;
+
+our $test_result = 0;
+
+(my $file = __FILE__)
+	=~ s{ 02do.t }{ 'test-file.doc' }ex;
+
+Acme::RunDoc->do($file);
+
+ok $test_result or diag $file;
+use Test::More tests => 1;
+use Acme::RunDoc;
+
+use lib "t";
+use lib ".";
+
+Acme::RunDoc->use('Local::TestModule');
+my $pass = Local::TestModule->can('PASS');
+ok($pass && $pass->());

t/Local/TestModule.docm

Binary file added.

t/test-file.doc

Binary file added.

word-examples/helloworld.doc

Binary file added.

word-lib/Hello/World.docm

Binary file added.
+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 Test::More;
+use Test::Pod::Coverage;
+
+my @modules = qw(Acme::RunDoc);
+pod_coverage_ok($_, "$_ is covered")
+	foreach @modules;
+done_testing(scalar @modules);
+

xt/03meta_uptodate.t

+use Test::More tests => 1;
+use Test::RDF::DOAP::Version;
+doap_version_ok('Acme-RunDoc', 'Acme::RunDoc');
+
+use Test::EOL;
+all_perl_files_ok();