IO::All - IO::All of it to Graham and Damian! |
IO::All - IO::All of it to Graham and Damian!
If you've just read the perl.com article at http://www.perl.com/pub/a/2004/03/12/ioall.html, there have already been major additions thanks to the great feedback I've gotten from the Perl community. Be sure and read the latest doc. Things are changing fast.
Many of the changes have to do with operator overloading for IO::All objects, which results in some fabulous new idioms.
use IO::All;
my $my_stuff = io('./mystuff')->slurp; # Read a file my $more_stuff < io('./morestuff'); # Read another file
io('./allstuff')->print($my_stuff, $more_stuff); # Write to new file
or like this:
io('./mystuff') > io('./allstuff'); io('./morestuff') >> io('./allstuff');
or:
my $stuff < io('./mystuff'); io('./morestuff') >> $stuff; io(./allstuff') << $stuff;
or:
${io('./stuff')} . ${io('./morestuff')} > io('./allstuff');
use IO::All;
# Print name and first line of all files in a directory my $dir = io('./mydir'); while (my $io = $dir->next) { print $io->name, ' - ', $io->getline if $io->is_file; }
# Print name of all files recursively print "$_\n" for io('./mydir')->All_Files;
use IO::All;
# Various ways to copy STDIN to STDOUT io('-') > io('-');
io('-') < io('-');
io('-')->print(io('-')->slurp);
my $stdin = io('-'); my $stdout = io('-'); $stdout->buffer($stdin->buffer); $stdout->write while $stdin->read;
# Copy STDIN to a String File one line at a time my $stdin = io('-'); my $string_out = io('$'); while (my $line = $stdin->getline) { $string_out->print($line); }
use IO::All;
# A forking socket server that writes to a log my $server = io('server.com:9999'); my $socket = $server->accept('-fork'); while (my $msg = $socket->getline) { io('./mylog')->appendln(localtime() . ' - $msg'); } $socket->close;
# A single statement web server for static files and cgis too io(":8080")->accept("-fork")-> (sub { $_[0] < io(-x $1 ? "./$1 |" : $1) if /^GET \/(.*) / });
use IO::All;
# Write some data to a temporary file and retrieve all the paragraphs. my $temp = io; $temp->print($data); $temp->seek(0, 0); my @paragraphs = $temp->getlines('');
``Graham Barr for doing it all. Damian Conway for doing it all different.''
IO::All combines all of the best Perl IO modules into a single Spiffy
object oriented interface to greatly simplify your everyday Perl IO
idioms. It exports a single function called io
, which returns a new
IO::All object. And that object can do it all!
The IO::All object is a proxy for IO::File, IO::Dir, IO::Socket, IO::String, Tie::File and File::ReadBackwards. You can use most of the methods found in these classes and in IO::Handle (which they all inherit from). IO::All is easily subclassable. You can override any methods and also add new methods of your own.
Optionally, every IO::All object can be tied to itself. This means that you can use most perl IO builtins on it: readline, <>, getc, print, printf, syswrite, sysread, close. (Due to an unfortunate bug in Perl 5.8.0 only, this option is turned off by default. See below.)
The distinguishing magic of IO::All is that it will automatically open (and close) files, directories, sockets and io-strings for you. You never need to specify the mode ('<', '>>', etc), since it is determined by the usage context. That means you can replace this:
open STUFF, '<', './mystuff' or die "Can't open './mystuff' for input:\n$!"; local $/; my $stuff = <STUFF>; close STUFF;
with this:
my $stuff < io('./mystuff');
And that is a good thing!
The use statement for IO::All can be passed several options:
use IO::All; use IO::All '-base'; use IO::All '-tie'; use IO::All '-lock';
With the exception of '-base', these options are simply defaults that
are passed on to every io
function within the program.
package IO::Different; use IO::All '-base';
io
function
should be tied to themselves.
use IO::All qw(-tie); my $io = io('file1'); my @lines = <$io>; $io->close;
As you can see, you can use both method calls and builtin functions on the same object.
NOTE: If you use the -tie
option with Perl 5.8.0, you need may need
to call the close function explicitly. Due to a bug, these objects will
not go out of scope properly, thus the files opened for output will not
be closed. This is not a problem in Perl 5.6.1 or 5.8.1 and greater.
This section describes some various things that you can easily cook up with IO::All.
IO::All objects stringify to their file or directory name. Here we print the contents of a directory:
perl -MIO::All -le 'print for io(".")->all'
'>' and '<' move data between strings and files:
$content1 < io('file1'); $content1 > io('file2'); io('file2') > $content3; io('file3') < $content3; io('file3') > io('file4'); io('file5') < io('file4');
'>>' and '<<' do the same thing except the recipent string or file is appended to.
An IO::All file used as an array reference becomes tied using Tie::File:
$file = io('file'); # Print last line of file print $file->[-1]; # Insert new line in middle of file $file->[$#$file / 2] = 'New line';
IO::All directories used as hashes have file names as keys, and IO::All objects as values:
print io('dir')->{'foo.txt'}->slurp;
Files used as scalar references get slurped:
print ${io('dir')->{'foo.txt'}};
IO::All makes it very easy to lock files. Just use the -lock
flag. Here's a
standalone program that demonstrates locking for both write and read:
use IO::All; my $io1 = io(-lock => 'myfile'); $io1->println('line 1');
fork or do { my $io2 = io(-lock => 'myfile'); print $io2->slurp; exit; };
sleep 1; $io1->println('line 2'); $io1->println('line 3'); $io1->unlock;
There are a lot of subtle things going on here. An exclusive lock is
issued for $io1
on the first println
. That's because the file
isn't actually opened until the first IO operation.
When the child process tries to read the file using $io2
, there is
a shared lock put on it. Since $io1
has the exclusive lock, the
slurp blocks.
The parent process sleeps just to make sure the child process gets a
chance. The parent needs to call unlock
or close
to release the
lock. If all goes well the child will print 3 lines.
This simple example will read lines from a file forever. When the last line is read, it will reopen the file and read the first one again.
my $io = io('file1.txt'); $io->autoclose(1); while (my $line = $io->getline || $io->getline) { print $line; }
If you call the backwards()
method on an IO::All object, the
getline()
and getlines()
will work in reverse. They will read the
lines in the file from the end to the beginning.
my @reversed; my $io = io('file1.txt'); $io->backwards; while (my $line = $io->getline) { push @reversed, $line; }
or more simply:
my @reversed = io('file1.txt')->backwards->getlines;
The backwards()
method returns the IO::All object so that you can
chain the calls.
NOTE: This operation requires that you have the File::ReadBackwards module installed.
=head2 Client/Server Sockets
IO::All makes it really easy to write a forking socket server and a client to talk to it.
In this example, a server will return 3 lines of text, to every client that calls it. Here is the server code:
use IO::All;
my $socket = io(':12345')->accept('-fork'); $socket->print($_) while <DATA>; $socket->close;
__DATA__ On your mark, Get set, Go!
Here is the client code:
use IO::All;
my $io = io('localhost:12345'); print while $_ = $io->getline;
You can run the server once, and then run the client repeatedly (in another terminal window). It should print the 3 data lines each time.
Note that it is important to close the socket if the server is forking, or else the socket won't go out of scope and close.
Subclassing is easy with IO::All. Just create a new module and use IO::All as the base class. Since IO::All is a Spiffy module, you do it like this:
package NewModule; use IO::All '-base';
You need to do it this way so that IO::All will export the io
function.
Here is a simple recipe for subclassing:
IO::Dumper inherits everything from IO::All and adds an extra method
called dump()
, which will dump a data structure to the file we
specify in the io
function. Since it needs Data::Dumper to do the
dumping, we override the open
method to require Data::Dumper
and
then pass control to the real open
.
First the code using the module:
use IO::Dumper;
io('./mydump')->dump($hash);
And next the IO::Dumper module itself:
package IO::Dumper; use IO::All '-base'; use Data::Dumper;
sub dump { my $self = shift; $self->print(Data::Dumper::Dumper(@_)); return $self; }
1;
This recipe does the same thing as the previous one, but without needing
to write a separate module. The only real difference is the first line.
Since you don't ``use'' IO::Dumper, you need to still call its import
method manually.
IO::Dumper->import; io('./mydump')->dump($hash);
package IO::Dumper; use IO::All '-base'; use Data::Dumper;
sub dump { my $self = shift; $self->print(Data::Dumper::Dumper(@_)); return $self; }
=head1 OPERATION NOTES
To keep input objects from closing at EOF, do this:
$io->autoclose(0);
open
and close
explicitly, if you need that
level of control.
NOTE: The io
function takes all the same parameters as new
.
new()
new(file_descriptor, '-', '=', '$', -file_name => $file_name, -file_handle => $file_handle, -dir_name => $directory_name, -dir_handle => $directory_handle, '-tie', );
File descriptor is a file/directory name or file/directory handle or anything else that can be used in IO operations.
IO::All will use STDIN or STDOUT (depending on context) if file descriptor is '-'. It will use an IO::String object if file descriptor is '$'.
If file_descriptor is missing and neither -file_handle
nor
-dir_handle
is specified, IO::All will create a temporary file
which will be opened for both input and output.
-tie
uses the tie interface for a single object.
IO::All provides lots of methods for making your daily programming tasks simpler. If you can't find what you need, just subclass IO::All and add your own.
accept()
If the '-fork' option is specified, the process will automatically be forked for every connection.
all()
'.' and '..' are excluded.
The -r
flag can be used to get all files and subdirectories recursively.
The items returned are sorted by name unless the -no_sort
flag is used.
All()
all('-r')
.
all_dirs()
all()
, but only return directories.
All_Dirs()
all_dirs('-r')
.
all_files()
all()
, but only return files.
All_Files()
all_files('-r')
.
all_links()
all()
, but only return links.
All_Links()
all_links('-r')
.
append()
appendf()
appendln()
autoclose()
If you don't want this behaviour, say so like this:
$io->autoclose(0);
The object will then be closed when $io
goes out of scope, or you
manually call <$io-
close>>.
autoflush()
backwards()
getline
operations will read backwards from the end of the file.
Requires Uri Guttman's File::ReadBackwards CPAN module.
block_size()
read()
and sysread()
calls.
Defaults to 1024.
buffer()
This is the buffer that read()
and write()
will use by default.
You can easily have IO::All objects use the same buffer:
my $input = io('abc'); my $output = io('xyz'); my $buffer; $output->buffer($input->buffer($buffer)); $output->write while $input->read;
clear()
write()
after it writes
the buffer.
close()
domain()
domain_default()
eof()
fileno()
getc()
getline()
getlines()
hash()
See IO::Dir for more information on Tied Directories.
io_handle()
is_dir()
is_file()
is_link()
is_open()
is_socket()
is_string()
length()
mode()
$io->mode('>>'); $io->mode(O_RDONLY);
name()
next()
open()
mode
and
perms
, which can also be set ahead of time using the mode()
and
perms()
methods.
NOTE: Normally you won't need to call open (or mode/perms), since this happens automatically for most operations.
perms()
port()
print()
printf()
println()
read()
For a file, this will proxy IO::File::read(). This means you must pass it a buffer, a length to read, and optionally a buffer offset for where to put the data that is read. The function returns the length actually read (which is zero at EOF).
If you don't pass any arguments for a file, IO::All will use its own
internal buffer, a default length, and the offset will always point at
the end of the buffer. The buffer can be accessed with the buffer()
method. The length can be set with the block_size
method. The default
length is 1024 bytes. The clear()
method can be called to clear
the buffer.
For a directory, this will proxy IO::Dir::read().
readline()
getline()
.
recv()
rewind()
rmdir()
seek()
send()
shutdown()
slurp()
stat()
string_ref()
Returns a reference to the internal string that is acting like a file.
sysread()
syswrite()
tell()
throw()
truncate()
type()
file dir link socket string pipe
Returns undef if type is not determinable.
NOTE: You can unlink a file after it is open, and continue using it until it is closed.
-lock
flag.
read()
for file operations only.
NOTE: When used with the automatic internal buffer, write()
will
clear the buffer after writing it.
The goal of the IO::All project is to continually refine the module to be as simple and consistent to use as possible. Therefore, in the early stages of the project, I will not hesitate to break backwards compatibility with other versions of IO::All if I can find an easier and clearer way to do a particular thing.
IO is tricky stuff. There is definitely more work to be done. On the other hand, this module relies heavily on very stable existing IO modules; so it may work fairly well.
I am sure you will find many unexpected ``features''. Please send all problems, ideas and suggestions to INGY@cpan.org.
Not all possible combinations of objects and methods have been tested. There are many many combinations. All of the examples have been tested. If you find a bug with a particular combination of calls, let me know.
If you call a method that does not make sense for a particular object, the result probably won't make sense. No attempt is made to check for improper usage.
Support for format_write and other format stuff is not supported yet.
IO::Handle, IO::File, IO::Dir, IO::Socket, IO::String, IO::ReadBackwards, Tie::File
Also check out the Spiffy module if you are interested in extending this module.
Brian Ingerson <INGY@cpan.org>
Copyright (c) 2004. Brian Ingerson. All rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See http://www.perl.com/perl/misc/Artistic.html
IO::All - IO::All of it to Graham and Damian! |