I very much like the way IO::All allows you to do all kinds of IO operations under a very simple and general interface without having to worry about the details. I was wondering if it could be extended for handling various kinds of URIs; this is what I came up with as a first hack:
Note: I sent this to Brian Ingerson in case he wants to include something like it in a future version of IO::All.
Examples:
# It is trivial to fetch a file by http and save it to an ftp # server, or send it as an email: io("http://a.com") > io("ftp://b.com/incoming/myfile"); io("http://a.com") > io("ftp://me:secret@b.com/myfile"); io("ftp://a.com/pub/xyz") > io("mailto:me@c.com"); # You can also use local files, of course: io("file.txt") > io("mailto:me@c.com?subject=file.txt"); # Or to use the IO object as a filehandle: my $io = io("ftp://b.com/incoming/xyz")->tie; print $io "hello world\n";
Update: Moved the examples before the readmore tag. Brian is very interested in including all sorts of protocols in future versions of IO::All
#!/usr/bin/perl # This script is an example of how IO::All can be extended for handlin +g # various kinds of URIs. Currently it works for http:, ftp:, file: (GE +T only), # and mailto: ("PUT" only) use strict; use warnings; IO::All::URI->import; die "please give two URIs, filenames, or somethings\n" unless (@ARGV > += 2); io(shift) > io(shift); ################################# package IO::All::URI; use IO::All '-Base'; my $ua; # The LWP user agent. It is created the first time it's used sub new { my $new = super; my $name = shift; return $new->lwp($name) if $name =~ /^(https?|ftp|file):/; return $new->mailer($name) if $name =~ /^mailto:/; $new; } sub lwp { my $name = shift; $self->name($name); $self; } # I use Mail::Mailer instead of POSTing to a mailto: URI with LWP beca +use # this seemed simpler and I already had it. However, the other option +still # exists and it would introduce fewer dependencies. sub mailer { require Mail::Mailer; require URI; my $name = shift; $self->name($name); $self->is_open(1); # Uppercase first letter in header names my $i = 1; my %headers = map { $_ = ucfirst if $i++ % 2; $_ } URI->new($name)->headers; # create mailer my $mailer = Mail::Mailer->new->open(\%headers); $self->io_handle($mailer); $self; } sub open { # this regex check wouldn't be duplicated if I added an lwp IO typ +e # instead of using 'file'! Bad form of laziness... if ($self->name =~ /^(https?|ftp|file):/) { $self->open_lwp(@_); } else { super; } } # note that this method only handles the < and > modes. sub open_lwp { # create user agent if it hasn't been created already require LWP::UserAgent; $ua ||= LWP::UserAgent->new; $self->is_open(1); # find out the file mode my ($mode) = @_; $self->mode($mode) if defined $mode; $self->mode('<') unless defined $self->mode; $mode = $self->mode; my $name = $self->name; my $fh; my $content; if ($mode eq '<') { # GET my $content = $ua->get($name)->content or die "couldn't get $n +ame\n"; # create filehandle CORE::open($fh, "<", \$content); $self->io_handle($fh); } elsif ($mode eq '>') { # PUT CORE::open($fh, "+>", \$content); $self->io_handle($fh); } else { die "invalid mode '$mode' for lwp open\n"; } $self; } sub close { if ($self->name =~ /^(https?|ftp|file):/) { $self->close_lwp(@_); } else { super; } } # This is the method that actually sends PUT requests, because we wait + until # we have the whole content before sending it. sub close_lwp { if ($self->mode eq '>') { # PUT $self->is_open(0); my $name = $self->name; # get the content back. This is probably a roundabout way of d +oing it, # but I wasn't sure I wanted to add another field to the IO::A +LL # object... my $fh = $self->io_handle; seek $fh, 0, 0; my $content = do { local $/; <$fh> }; # LWP request my $req = HTTP::Request->new(PUT => $name, undef, $content); my $res = $ua->request($req); unless ($res->is_success) { die "PUT error: " . $res->status_line . "\n"; } } $self; } # This is not a very efficient way of copying large files, but it's a +quick # hack while I add 'uri' as a type in the overload_table. This should +work # for files and uris (including mailer). sub overload_file_to_file() { my $temp; $_[1] > $temp; $temp > $_[2]; } # same here sub overload_file_from_file() { my $temp; $_[2] > $temp; $temp > $_[1]; } # This is to ensure that PUT requests are done automatically. Another +option # would be to create an intermediate filehandle object for the URI, wh +ich would # be destroyed when the IO::All object falls out of scope. This seemed + simpler. sub DESTROY { $self->close; super; }
In reply to IO::All::URI by itub
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |