rcaputo's Journal http://use.perl.org/~rcaputo/journal/ rcaputo's use Perl Journal en-us use Perl; is Copyright 1998-2006, Chris Nandor. Stories, comments, journals, and other submissions posted on use Perl; are Copyright their respective owners. 2012-01-25T02:48:28+00:00 pudge pudge@perl.org Technology hourly 1 1970-01-01T00:00+00:00 rcaputo's Journal http://use.perl.org/images/topics/useperl.gif http://use.perl.org/~rcaputo/journal/ Reflex Progress http://use.perl.org/~rcaputo/journal/40313?from=rss <p> I've been making good progress on Reflex lately. I have to: I've applied to talk about it at YAPC::NA, so I need something to talk about! The code has been moving too fast for CPAN right now, but you can follow along at <a href="http://github.com/rcaputo/reflex/">Reflex's repository on github</a>. Here's a summary of the latest changes: </p><p> I've added support for as many callback mechanisms as I could think of, including one I'm not sure existed before. Callback consumers (the things being called back into) decide the kinds of callbacks they prefer. Objects doing the calling use a single, consistent method: emit(). Reflex handles the rest. The callback types supported so far: </p><ul> <li> <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-20-rcb-coderef.pl">Plain coderefs.</a> Great for the closure addicts among us.</li> <li> <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-21-rcb-method.pl">Class and Object methods.</a> The Moose troop may approve.</li> <li> <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-24-rcb-role.pl">Role-based methods</a>, which I'll have to explain later.</li> <li>Condvar- or promise-like <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-32-promise-tiny.pl">wait() polling</a>. This one isn't really a callback. It's here for people who would rather not use them.</li> </ul><p> There's even an example that runs with <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-33-all-callbacks.pl">every supported callback type at once</a>. Just because it can! </p><p> I've also implemented a few classes that support TCP (and maybe UNIX) clients and servers. </p><p> <a href="http://github.com/rcaputo/reflex/blob/master/lib/Reflex/Stream.pm">Reflex::Stream</a> is a base class for stream I/O. It extends Reflex::Handle, adding sysread(), syswrite() and buffering. <a href="http://github.com/rcaputo/reflex/blob/master/eg/EchoStream.pm">Example usage.</a> </p><p> <a href="http://github.com/rcaputo/reflex/blob/master/lib/Reflex/Listener.pm">Reflex::Listener</a> is a base class for connection-based servers. Give it an IO::Socket, and it will listen() for and accept() client connections. It also extends Reflex::Handle. <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-34-tcp-server-echo.pl">See TcpEchoServer in this example.</a> </p><p> <a href="http://github.com/rcaputo/reflex/blob/master/lib/Reflex/Connector.pm">Reflex::Connector</a> is the other side of the connection-based client/server dialog. It will perform a non-blocking connect() on the IO::Socket you provide. Eventually it'll return that socket in its connected state, or notify you of failure. Reflex::Connector also extends Reflex::Handle. Reflex::Client (below) uses it. </p><p> <a href="http://github.com/rcaputo/reflex/blob/master/lib/Reflex/Client.pm">Reflex::Client</a> extends Reflex::Connector and integrates Reflex::Stream for a basic but usable non-blocking client class. Here's <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-35-tcp-client.pl">a small subclass of Reflex::Client.</a> </p><p> And <a href="http://github.com/rcaputo/reflex/blob/master/lib/Reflex/Collection.pm">Reflex::Collection</a> is a small object manager. <a href="http://github.com/rcaputo/reflex/blob/master/eg/eg-34-tcp-server-echo.pl">The TcpEchoServer</a> uses it to automatically clean up client objects when they disconnect. This is another common and tedious task that Reflex automates. </p><p> Still to do: Document and release all this new goodness, then (presuming it's accepted) write my talk! I hope to see you at YAPC::NA either way. </p> rcaputo 2010-04-16T06:04:36+00:00 journal POE 1.280 is Faster http://use.perl.org/~rcaputo/journal/39749?from=rss <p> POE 1.280 has just been released, but it may take up to a day before your favorite CPAN mirror catches wind of it. You'll also need the latest POE::Test::Loops, version 1.030. </p><p> Those following along may notice that the version numbers jumped a little. That's because POE has undergone a couple optimizations that alter its behavior a little. </p><p> <b>I/O Dispatch is Faster</b> </p><p> I/O events jump the queue in POE 1.280. Skipping an enqueue and dequeue saves significant time. The drawback is that I/O is dispatched in a different order relative to other kinds of events. Most people won't care, but it has affected one of POE::Component::Client::HTTP's tests. See below for contact information if you think it's affected you. </p><p> <b>Session Cleanup is Faster</b> </p><p> In POE 1.280, session cleanup is done just before the event loop would wait for new events. Programs that aren't event bound should show decreased latency because the cleanup will be scheduled during idle times. This optimization changes the timing of session cleanup and their resulting _stop events. While I don't think it affects many people, it has already bitten Adam Kennedy. Please let us know if you suspect it's gnawing on you, too. </p><p> <b>Staying in Touch</b> </p><p> People who need realtime notice of POE changes may watch them at <a href="http://sourceforge.net/projects/poe/">SourceForge</a>, <a href="http://www.ohloh.net/p/poe">Ohlo</a> and <a href="http://cia.vc/stats/project/POE">CIA</a>. </p><p> POE's community may be found everywhere. Several members tend to accumulate in <a href="mailto:poe-subscribe@perl.org">POE's mailing list</a> and <a href="irc:ircperlorgpoe">IRC channel</a> at irc.perl.org #poe. You're welcome to join us, even if you don't have problems to report. </p><p>Thank you.</p> rcaputo 2009-10-14T05:19:58+00:00 journal Upcoming POE Changes http://use.perl.org/~rcaputo/journal/39736?from=rss <p> Those of you who use POE for serious things should be aware of a couple changes introduced after 1.269. </p><p> First, here's a public service annoucement from POE: Developers who want to keep up with POE's latest changes are encouraged to follow its <a href="https://poe.svn.sourceforge.net/svnroot/poe/trunk/poe/">repository</a>. You'll get instant notification of changes as often as you'd like, and you'll have maximum lead time to <a href="http://rt.cpan.org/Public/Dist/Display.html?Name=POE">report new bugs</a> or port production code to new features. I've also released <a href="http://search.cpan.org/dist/POE/">Version 1.269_002</a> to the CPAN for developers who are more comfortable with that. Remember: CPAN shells don't install developer releases by default. And now back to the changes! </p><p> <b>Synchronous I/O Dispatch</b> </p><p> POE::Kernel dispatches I/O events synchronously after 1.269. Previously these events would be enqueued along with everything else, only to be dequeued and dispatched as soon as possible. Now they're dispatched as they happen, saving the overhead of a round trip through the event queue. Developers should notice an improvement in I/O bound programs. </p><p> This naturally changes the timing of I/O events. Where in the past they may have been enqueued behind other events, they now come first. This could cause problems for users who depend on the relative timing of I/O events compared to other kinds of events. I expect most developers won't notice the difference. </p><p> This optimization is most effective for code that uses POE::Kernel's select_read() and select_write() watchers directly. Additional layers of abstraction, such as POE::Component::Server::TCP or AnyEvent add overhead that has not been reduced by this optimization. </p><p> <b>Mark and Sweep Session Garbage Collection</b> </p><p> Session termination and memory reclamation have been combined and delayed. Combining them reduces the overhead of POE::Kernel's dispatcher by eliminating a few method calls per event. Delaying garbage collection allows POE to do this work when it's likely to be idle anyway. While the latter optimization doesn't reduce CPU consumption, it should reduce callback latency in the common case. </p><p> People who require _stop to be called immediately when a session releases its last resource may be disappointed. _stop events are dispatched as a side effect of garbage collection, which as I've mentioned has been delayed. Adam Kennedy's Test::POE::Stopping has been affected. I hope his is a special case. </p><p> <b>More to Come</b> </p><p> There will be more improvements in the future, but that's all for now. Stay tuned, and please report any problems you encounter early and often. Thanks! </p> rcaputo 2009-10-09T17:40:13+00:00 journal POE Supports Other Event Loops, Really http://use.perl.org/~rcaputo/journal/39612?from=rss <p>Lately I've been hearing, <a href="http://www.catalyzed.org/2009/06/poe-anyevent321-fight.html">repeatedly</a> and <a href="http://www.slideshare.net/miyagawa/asynchronous-programming-with-anyevent">loudly</a>, that POE doesn't work with other event loops. I wrote the first bridge between POE and another event loop in May of 2000, so this is rather shocking news to me. I've had to rerun "make test" just to be sure I wasn't dreaming!<nobr> <wbr></nobr><tt>:)</tt> </p><p>Here is a slightly edited repost of my reply to <a href="http://bulknews.typepad.com/blog/2009/09/asynchronous-programming-with-anyevent.html">Tatsuhiko Miyagawa</a>, who writes in regards to his YAPC::Asia presentation:</p><blockquote><div><p>Rocco, the author of POE pointed out that slides 19-21 are misleading since POE also has the POE::Loop concept so that you can run your event loop under different backends like EV or Gtk. While that is true (and that is great), POE still can't run with the other main loop, like IO::Async::Loop-&gt;loop_forever. You still need to call POE::Kernel-&gt;run and to do so you should rewrite your main program to use POE everywhere, since existent IO::Async based program can't run any of POE::Component, which is totally possible with AnyEvent and AnyEvent::* modules.</p></div> </blockquote><p>I respectfully disagree, and I have code to back me up.</p><p>POE runs with other main loops. In fact, POE::Kernel-&gt;run() is often implemented in terms of native event dispatchers. POE::Loop classes implement event watchers using native watchers, and native callbacks are used to generate POE events. Your call to $kernel-&gt;select_read() becomes Event-&gt;io(), Gtk::Gdk-&gt;input_add(), etc.</p><p>Depending on the POE::Loop implementation, POE::Kernel-&gt;run() need not be called upon to dispatch events. This is CPAN, so your mileage may vary. If you're interested in improving CPAN, submit bug reports to the appropriate queues.</p><p>I mentioned I had code. Here's a working example of POE code and native Glib code running together in the same program, using Glib's MainLoop:</p><blockquote><div><p> <tt>use Glib; # Install POE::Loop::Glib.<br>use POE;<br> <br>POE::Session-&gt;create(<br>&nbsp; inline_states =&gt; {<br>&nbsp; &nbsp; _start =&gt; sub { $_[KERNEL]-&gt;delay(tick =&gt; 1) },<br>&nbsp; &nbsp; tick =&gt; sub {<br>&nbsp; &nbsp; &nbsp; print "poe tick at ", time(), "...\n";<br>&nbsp; &nbsp; &nbsp; $_[KERNEL]-&gt;delay(tick =&gt; 1);<br>&nbsp; &nbsp; },<br>&nbsp; },<br>);<br> <br>my $glib_timer = Glib::Timeout-&gt;add(<br>&nbsp; 500, sub {<br>&nbsp; &nbsp; print "glib tick at ", time(), "...\n";<br>&nbsp; &nbsp; return 1;<br>&nbsp; }<br>);<br> <br># Dispatches Glib and POE events.<br>Glib::MainLoop-&gt;new-&gt;run();</tt></p></div> </blockquote><p>Of course you can call POE::Kernel-&gt;run() instead of Glib's main loop. This is a good thing. A single, portable run() method covers every supported event loop.</p><p> <b>Addendum:</b> </p><p>Things get really interesting when you realize that POE can be loaded into event loops that are already running. For example, Martijn van Beers and Chris Williams have been <a href="http://use.perl.org/~BinGOs/journal/31221">running POE inside irssi</a> for years. Chris has even embedded <a href="http://use.perl.org/~BinGOs/journal/32846">an entire POE-based IRC server in irssi</a>, which got a little mind-bendy when he connected to irssi from itself. Both cases work without rewriting irssi's main program.</p> rcaputo 2009-09-11T05:57:26+00:00 journal Help me rename Lexical::Persistence http://use.perl.org/~rcaputo/journal/39588?from=rss <p> "Persistence" implies that data will be saved and loaded from disk, to a database, or some other persistent storage. That's not what the module does, so I think it needs a new name. </p><p> Lexical::Persistence objects are closures that make the values of lexical variables stick (or persist) in everything they call. In the following example, the Lexical::Persistence object makes sure that getter()'s version of $x contains the value stored by setter().</p><blockquote><div><p> <tt>use Lexical::Persistence;<br> <br>my $lp = Lexical::Persistence-&gt;new();<br>$lp-&gt;call(\&amp;setter);<br>$lp-&gt;call(\&amp;getter);<br> <br>sub setter { my $x = "some value" }<br>sub getter { print my $x, "\n" }</tt></p></div> </blockquote><p> Lexical::Persistence's closures are objects. Programs may examine and manipulate their contents between calls. The following code displays something like this:</p><blockquote><div><p> <tt>My mind is going. I can feel it.<br>My is going. I can feel it.<br>My is going. I feel it.<br>My going. I feel it.<br>My going. I feel<br>My I feel<br>My I<br>My</tt></p></div> </blockquote><p>Here's the example:</p><blockquote><div><p> <tt>use Lexical::Persistence;<br> <br>my $lp = Lexical::Persistence-&gt;new();<br>$lp-&gt;set_context(<br>&nbsp; &nbsp; _ =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; '@mind' =&gt; [qw(My mind is going. I can feel it.)]<br>&nbsp; &nbsp; }<br>);<br> <br>while (1) {<br>&nbsp; &nbsp; $lp-&gt;call(\&amp;display);<br>&nbsp; &nbsp; my $mind = $lp-&gt;get_context("_")-&gt;{'@mind'};<br>&nbsp; &nbsp; splice @$mind, rand(@$mind), 1;<br>&nbsp; &nbsp; last unless @$mind;<br>}<br> <br>sub display {<br>&nbsp; &nbsp; my @mind;<br>&nbsp; &nbsp; print "@mind\n";<br>}</tt></p></div> </blockquote><p> So, what would be a better name for this module? </p> rcaputo 2009-09-06T06:51:42+00:00 journal Trying Out this Git Thing http://use.perl.org/~rcaputo/journal/39423?from=rss <b>Distributing Development</b> <p> Distributed version control is great. It'll bring on the patches, establish world peace, and get you laid. It adds extra steps to your workflow, but they make everything easier. Don't ask me how. Even if I understood that trick, the Magician's Code wouldn't let me explain it to you. </p><p> There's more than one way to distribute development, and many of them are free. <a href="http://bazaar-vcs.org/">Bazaar</a> stood out as having the best mix of flexibility, extensibility and usability. It also has <a href="https://launchpad.net/">Launchpad</a>, which provides frameworks for distributed project management and feature design. </p><p> While Bazaar looks nicer, doing DVCS right means going where the people are. For Perl, that's <a href="http://git-scm.com/">Git</a>. Sorry, Bazaar, it's not you, it's me. </p><p> <b>Getting my Feet Wet</b> </p><p> <a href="http://search.cpan.org/dist/POE/">POE</a> is a perfect project to migrate to a distributed development model. It's large, it's popular, and it can always benefit from more developers. I mean, what <em>can't</em>, right? </p><p> POE's size and visibility also make it a bad choice for a first foray into distributed development. New software solutions, like new cleaning solutions, are best tried somewhere that won't show if they stain. </p><p> POE's <a href="http://edp.org/monkey.htm">scratch monkey</a> is <a href="http://search.cpan.org/dist/POE-Stage">POE::Stage</a>, a much smaller, much less visible project that, needless to say, can benefit from more developers. I recently migrated it from Subversion using <a href="http://blog.woobling.org/2009/06/git-svn-abandon.html">Yuval Kogman's tools and advice</a>. The popular svn2git alternative didn't work well for me. </p><p> <b>Decisions, Decisions</b> </p><p> There's more than one way to host a Git project. GitHub and Gitorious have been recommended for different reasons, but neither is a clear winner. Fortunately, a distributed repository can be hosted in more than one place. POE::Stage is available at both <a href="http://github.com/rcaputo/poe-stage/tree">GitHub</a> and <a href="http://gitorious.org/poe-stage">Gitorious</a> until I decide. </p><p> <b>What Next?!</b> </p><p> So I'm all set up. I've successfully migrated a project to Git, hosted it publicly (twice) and even awkwardly pushed a few changes. </p><p> The journey's only beginning, though. I have plenty of small projects to migrate while I commit the DVCS dance to muscle memory. I also have some <a href="http://poe.perl.org/?How_to_Help">specific Achievements</a> to acquire before tackling the Boss Level, migrating POE itself. </p><p> It should be fun, or at least a good challenge. I'll keep you posted. </p> rcaputo 2009-08-08T21:38:29+00:00 journal POE Changes Coming Soon http://use.perl.org/~rcaputo/journal/39340?from=rss <p> POE::Test::Loops 1.020 will be released tonight. This is a suite of reusable tests for developers who want to add external event loop support to POE. Version 1.020 acquires more signal handler tests from POE. Existing POE::Loop authors should try the new tests at their earliest convenience. Those who need the code right away can track the repository:</p><blockquote><div><p> <tt>&nbsp; &nbsp; svn co \<br>&nbsp; &nbsp; https://poe.svn.sourceforge.net/svnroot/poe/trunk/poe-test-loops ptl</tt></p></div> </blockquote><p> A developer version of POE will be released shortly after the new tests work their way around CPAN. The upcoming POE release incorporates Philip Gwyn's fixes for race conditions in POE's signal handlers. Users who have noticed strange behavior during prolonged periods of signal activity should test against this release as soon as possible. Those who want to get a jump on the changes can track POE's latest development through its repository:</p><blockquote><div><p> <tt>&nbsp; &nbsp; svn co \<br>&nbsp; &nbsp; https://poe.svn.sourceforge.net/svnroot/poe/trunk/poe poe</tt></p></div> </blockquote><p> POE changes are also tracked at <a href="http://sourceforge.net/projects/poe/">SourceForge</a>, <a href="http://www.ohloh.net/p/poe">Ohlo</a> and <a href="http://cia.vc/stats/project/POE">CIA</a>. </p><p> POE 1.007 will officially go out after a successful round of testing. You're welcome to join us in irc.perl.org #poe to watch the CPAN tester fallout. </p><p> POE 1.008 will be released shortly afterward. It will spin off some optional dependencies, mainly POE's "core" support for optional, external event loops. POE::Loop::Event, POE::Loop::Gtk and POE::Loop::Tk will join <a href="http://search.cpan.org/search?query=POE%3A%3ALoop&amp;mode=dist">their siblings on the CPAN</a>. Their test coverage should improve, and POE's distribution size and installation footprint will shrink. </p><p>Thank you.</p> rcaputo 2009-07-24T04:30:21+00:00 journal Test post, please ignore. http://use.perl.org/~rcaputo/journal/39282?from=rss <p> BinGOs pointed out WWW::UsePerl::Journal and kindly pasted the code he uses to drive it. I've hacked it a little to embed the article title into a header block on the article. Since I'm writing these offline, I figure the title should be associated with the article for posterity. </p><p> Here's my hacked version of BinGOs' posting script:</p><blockquote><div><p> <tt>#!/usr/bin/env perl<br> <br>use warnings;<br>use strict;<br> <br>use WWW::UsePerl::Journal;<br> <br>my $user = 'rcaputo';<br>my $pass = '********';<br> <br># Headers.<br> <br>my $title;<br>my $comments = 1;<br> <br>while (&lt;&gt;) {<br>&nbsp; &nbsp; chomp;<br>&nbsp; &nbsp; last if<nobr> <wbr></nobr>/^\s*$/;<br> <br>&nbsp; &nbsp; unless (/^\s*([^:]+?)\s*:\s*(\S.*?)\s*$/) {<br>&nbsp; &nbsp; &nbsp; &nbsp; die "Unknown header line ($_)\n";<br>&nbsp; &nbsp; }<br> <br>&nbsp; &nbsp; my ($header, $value) = (lc($1), $2);<br> <br>&nbsp; &nbsp; # Avoid the lulz.<br>&nbsp; &nbsp; $value =~ s/\Q$pass/********/g;<br> <br>&nbsp; &nbsp; if ($header eq "title") {<br>&nbsp; &nbsp; &nbsp; &nbsp; $title = $value;<br>&nbsp; &nbsp; &nbsp; &nbsp; next;<br>&nbsp; &nbsp; }<br> <br>&nbsp; &nbsp; if ($header eq "comments") {<br>&nbsp; &nbsp; &nbsp; &nbsp; if (lc($value) eq "yes") {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $comments = 1;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; elsif (lc($value) eq "no") {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $comments = 0;<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; else {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; die "Comments header may only be yes or no.\n";<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; }<br>}<br> <br># Body HTML.<br> <br>my $text = '';<br>while (&lt;&gt;) {<br>&nbsp; &nbsp; # Only one per line.<br>&nbsp; &nbsp; if (/^\s*&lt;ecode\s*file="([^"]+)"\s*\/*\s*&gt;\s*$/) {<br>&nbsp; &nbsp; &nbsp; &nbsp; my $code = `/bin/cat $1`;<br>&nbsp; &nbsp; &nbsp; &nbsp; $code =~ s/\n+$//;<br> <br>&nbsp; &nbsp; &nbsp; &nbsp; # Break up the ecode tags so this script can<br>&nbsp; &nbsp; &nbsp; &nbsp; # present itself.<br>&nbsp; &nbsp; &nbsp; &nbsp; $_ = "&lt;" . "ecode&gt;\n$code\n&lt;" . "/ecode&gt;";<br>&nbsp; &nbsp; }<br> <br>&nbsp; &nbsp; $text<nobr> <wbr></nobr>.= $_;<br>}<br> <br># Avoid the lulz.<br>$text =~ s/\Q$pass/********/g;<br> <br>die "Article needs a Title header.\n" unless $title;<br>die "Article needs body HTML.\n" unless $text;<br> <br># Post.<br> <br>my $journal = WWW::UsePerl::Journal-&gt;new($user);<br>my $post = $journal-&gt;login($pass);<br> <br>unless (<br>&nbsp; &nbsp; $post-&gt;postentry(<br>&nbsp; &nbsp; &nbsp; &nbsp; title&nbsp; &nbsp; =&gt; $title,<br>&nbsp; &nbsp; &nbsp; &nbsp; text&nbsp; &nbsp; &nbsp;=&gt; $text,<br>&nbsp; &nbsp; &nbsp; &nbsp; topic&nbsp; &nbsp; =&gt; 34,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# user journal<br>&nbsp; &nbsp; &nbsp; &nbsp; comments =&gt; $comments,&nbsp; # allow comments?<br>&nbsp; &nbsp; &nbsp; &nbsp; type&nbsp; &nbsp; &nbsp;=&gt; 2,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # ???<br>&nbsp; &nbsp; &nbsp; &nbsp; promote&nbsp; =&gt; 'publish',&nbsp; # ???<br>&nbsp; &nbsp; )<br>) {<br>&nbsp; &nbsp; &nbsp;die "Failure.\n";<br>}<br> <br>print "Success.\n";<br>exit;</tt></p></div> </blockquote><p> Here's the source of this article:</p><blockquote><div><p> <tt>Title: Test post, please ignore.<br> <br>&lt;p&gt;<br>&nbsp; &nbsp; BinGOs pointed out WWW::UsePerl::Journal and kindly pasted the code<br>&nbsp; &nbsp; he uses to drive it.&nbsp; I've hacked it a little to embed the article<br>&nbsp; &nbsp; title into a header block on the article.&nbsp; Since I'm writing these<br>&nbsp; &nbsp; offline, I figure the title should be associated with the article<br>&nbsp; &nbsp; for posterity.<br>&lt;/p&gt;<br> <br>&lt;p&gt;<br>&nbsp; &nbsp; Here's my hacked version of BinGOs' posting script:<br>&nbsp; &nbsp; &lt;ecode file="~/bin/useperlpost.pl"/&gt;<br>&lt;/p&gt;<br> <br>&lt;p&gt;<br>&nbsp; &nbsp; Here's the source of this article:<br>&nbsp; &nbsp; &lt;ecode file="~/Documents/useperl/testing-www-useperl-journal.html"/&gt;<br>&lt;/p&gt;<br> <br>&lt;p&gt;<br>&nbsp; &nbsp; Thanks for reading anyway.<br>&lt;/p&gt;</tt></p></div> </blockquote><p> Thanks for reading anyway. </p> rcaputo 2009-07-13T06:17:14+00:00 journal