IO::All - IO::All of it to Graham and Damian!


NAME

IO::All - IO::All of it to Graham and Damian!


NOTE

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.


SYNOPSIS

    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');


SYNOPSIS II

    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;


SYNOPSIS III

    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);
    }


SYNOPSIS IV

    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 \/(.*) / });


SYNOPSIS V

    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('');


DESCRIPTION

``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!


USAGE

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.

Options


COOKBOOK

This section describes some various things that you can easily cook up with IO::All.

Operator Overloading

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'}};

File Locking

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.

Round Robin

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;
    }

Reading Backwards

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.

File Subclassing

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;

Inline Subclassing

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


CONSTRUCTOR

NOTE: The io function takes all the same parameters as new.


INSTANCE METHODS

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.


STABILITY

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.

Known Bugs and Deficiencies

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.


SEE ALSO

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.


AUTHOR

Brian Ingerson <INGY@cpan.org>


COPYRIGHT

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!