schwern's Journal schwern'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-25T01:44:50+00:00 pudge Technology hourly 1 1970-01-01T00:00+00:00 schwern's Journal Finally, some Test::Builder2 examples! <p>For my presentation tonight on <a href="">Test::Builder2</a> I threw together some quick examples of some of its killer features, in particular demonstrating changing how Test::Builder2 behaves using method modifiers and applying object roles.</p><p>First, demonstrating end-of-assert actions, there's <a href="">die on fail</a> but even cooler is <a href="">DEBUG on fail</a>! That's right, run your test in the debugger and have it automatically set a breakpoint on a failure. How cool is that?</p><p>I'm sure somebody with better debugger foo than I can make it even cooler and stop at the top of the assert stack rather than inside DebugOnFail.</p><p>The second is reimplementing <a href="">Test::NoWarnings</a> safely. <a href="">TB2::NoWarnings</a> demonstrates hooking into the start and end of the test as well as safely altering the number of tests planned by trapping the call to set_plan.</p><p>You can safely use them all together, though its a crap shoot if DebugOnFail or DieOnFail will trigger first.</p><p>While roles and method modifiers are relatively new to the Perl community, using them in lieu of designing my own event system for TB2 has two great advantages. First, I didn't have to design and debug my own event system.<nobr> <wbr></nobr>:) Second, rather than having to learn the quirks of a one-off system, you learn the quirks of Mo[uo]se and then can apply that knowledge all over the place.</p><p>There's <a href="">a pile of stuff to be done in TB2</a>, a lot of them are fairly small and self contained. Have a look. Patches welcome.</p> schwern 2010-09-09T08:33:42+00:00 journal Test::Builder2 at 10k Feet <p>Here's a diagram of the "flow" of assert results through Test::Builder version 1.</p><blockquote><div><p> <tt>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.-------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| foo.t |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp;.-------------.&nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp;.----------------.<br>&nbsp; &nbsp; &nbsp;| Test::More&nbsp; |&lt;---------&gt;| Test::Whatever |<br>&nbsp; &nbsp; &nbsp;'-------------'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'----------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp;.---------------.&nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '----&gt;| Test::Builder |&lt;----'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '---------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.-----.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| TAP |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-----'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.---------------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Test::Harness |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '---------------'</tt></p></div> </blockquote><p>You write foo.t using Test::More and Test::Whatever. These both<br>use the same Test::Builder object. It spits out TAP which<br>Test::Harness converts into something human readable.</p><p>The big problem there is Test::Builder is monolithic. There's no<br>further breakdown of responsibilities. It only spits out TAP, and<br>only one version of TAP.</p><p>Here's what Test::Builder2 looks like:</p><blockquote><div><p> <tt>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.-------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.----------------| foo.t |-------------------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '-------'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;v<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.------------.&nbsp; &nbsp; &nbsp;.----------------.&nbsp; &nbsp; &nbsp;.------------------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Test::More |&nbsp; &nbsp; &nbsp;| Test::Whatever |&nbsp; &nbsp; &nbsp;| Test::NotUpdated |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '------------'&nbsp; &nbsp; &nbsp;'----------------'&nbsp; &nbsp; &nbsp;'------------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;v<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.----------------.&nbsp; &nbsp; &nbsp; &nbsp;.---------------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'----------&gt;| Test::Builder2 |&lt;------| Test::Builder |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'----------------'&nbsp; &nbsp; &nbsp; &nbsp;'---------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.--------------.&nbsp; &nbsp;<nobr> <wbr></nobr>.-------------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| TB2::History |&lt;---| TB2::Result |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'--------------'&nbsp; &nbsp; '-------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp;<nobr> <wbr></nobr>.--------------------------.&nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp;.---------------------.<br>&nbsp; &nbsp; | TB2::Formatter::TAP::v13 |&lt;-----'------&gt;| TB2::Formatter::GUI |<br>&nbsp; &nbsp; '--------------------------'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '---------------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp;<nobr> <wbr></nobr>.-------------------------------.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; | TB2::Formatter::Streamer::TAP |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; '-------------------------------'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.-----.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| TAP |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-----'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.---------------.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.-----------------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Test::Harness |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| Pretty Pictures |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '---------------'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-----------------'</tt></p></div> </blockquote><p>It starts out the same, foo.t uses a bunch of test modules<br>including Test::More and Test::Whatever using the same Test::Builder2<br>object, but it also uses Test::NotUpdated which is still using<br>Test::Builder. That's ok because Test::Builder has been rewritten in<br>terms of Test::Builder2 (more on that below).</p><p>Test::Builder2, rather than being a monolith, produces a<br>Test::Builder2::Result object for each assert run. This gets stored<br>in a Test::Builder2::History object for possible later use. It also<br>gets handed to a Test::Builder2::Formatter object, the default is<br>Test::Builder2::TAP::v13 which produces TAP version 13. This is fed<br>to a Streamer that prints it to STDOUT and STDERR which is read by<br>Test::Harness and made human readable.</p><p>Because Test::Builder2 is not monolithic, you can swap out parts. For<br>example, instead of outputting TAP it could instead hand results to a<br>formatter that produced a simple GUI representation, maybe a green<br>bar, or something that hooks into a larger GUI. Or maybe one that<br>produces JUnit XML.</p><p>Here's how Test::Builder and Test::Builder2 Relate.</p><blockquote><div><p> <tt>&nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.-----.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.-----.<br>&nbsp; &nbsp; &nbsp; &nbsp; | TB2 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| TB1 |<br>&nbsp; &nbsp; &nbsp; &nbsp; '-----'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-----'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;v<br>&nbsp; &nbsp;<nobr> <wbr></nobr>.-------------.&nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.--------------.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.-------------.<br>&nbsp; &nbsp; | TB2::Result |-------&gt;| TB2::History |&lt;--------| TB2::Result |<br>&nbsp; &nbsp; '-------------'&nbsp; &nbsp; &nbsp; &nbsp; '--------------'&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.----------------.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'-------------&gt;| TB2::Formatter |&lt;--------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '----------------'<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;v<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<nobr> <wbr></nobr>.--------.<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | Output |<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '--------'</tt></p></div> </blockquote><p>Test::Builder and Test::Builder2 coordinate their actions by sharing<br>the same History and Formatter objects. If you call TB1-&gt;ok() it<br>produces a Result object which it hands to the History singleton and<br>the Formatter singleton. If you call TB2-&gt;ok() it produces a Result<br>object which it hands to the same History and Formatter objects.</p><p>This allows most of the Test::Builder code to remain the same while<br>still coordinating with Test::Builder2. It also allows radically<br>different builders to be made without Test::Builder2 dictating how<br>they're to work.</p><p>The downside is that roles applied to Test::Builder2 will not effect<br>Test::Builder. Because of this, Test::Builder may become more closely<br>coupled with Test::Builder2 in the future.</p><p>Diagrams by <a href="">App::Asciio</a>.</p> schwern 2010-09-09T08:15:22+00:00 journal A month of Test::Builder2 <p>I've had <a href="">a grant open for Test::Builder2</a> for, oh god over two years now. Since I started it, Perl 6 has had a release! I think its the second oldest running dev grant.</p><p>I've cleared the decks of other responsibilities and can dedicate September to, if not finishing, then at least releasing something people can poke at. First alpha release was supposed to be "two weeks after the start" ha ha ha! oh god. The design has evolved and simplified greatly in the intervening two years, but its time to get something the hell out the door. At least a <a href="">Test::Builder2</a> Star if you will.</p><p>There's critical components missing. There's no diagnostics, YAML or otherwise. The issues with nested asserts are still congealing. Plans are not enforced. Result objects are in the middle of being remodeled... again. But Test::Builder is using what parts of Test::Builder2 are usable. Multiple output formats and streams work. Asserts can be nested in the common, simple cases without having to fiddle with $Level. And you can hook into various events.</p><p>Step one is I'm going to seal up what's there, write docs where they're missing, and release something.</p><p>A release before October or the grant dies.</p> schwern 2010-08-28T04:08:39+00:00 journal Alien::SVN - new release, new management <p>Those of you still stuck using Subversion will be happy to find <a href="">a new release of Alien::SVN</a>. It drags it forward to 1.6.12, doesn't do much else.</p><p>Also, Alien::SVN has finally found a new manager! From out of the blue comes <a href="">Matthew Lanier</a> with a patch and the will and a PAUSE ID. He'll be taking care of things from now on. Its his first CPAN module, be gentle. Godspeed, Matthew.</p> schwern 2010-08-18T22:33:29+00:00 journal Test::Builder2::Design <p>In an effort to shed some light on what Test::Builder2 is about, I took a few hours and performed a brain dump about its goals and design. You can see the result in the new <a href="">Test::Builder2::Design</a> document.</p><p>The key design goals are 1) that it has to work, 2) that it has to work everywhere and 3) that it has to test everything. This throws out a lot of 98% solutions.</p> schwern 2010-08-10T02:31:05+00:00 journal Method::Signatures returns! 5.12, func() and fast! <p>Chip submitted a minor performance patch to Method::Signatures today. That drove me to push out <a href="">a new release</a> making it friendly to 5.12 and adding func() for non methods!</p><p> &nbsp; &nbsp; &nbsp; &nbsp; func hello(:$greeting = "Hello",<nobr> <wbr></nobr>:$place = "World") {<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print "$greeting, $place!\n";<br> &nbsp; &nbsp; &nbsp; &nbsp; }</p><p> &nbsp; &nbsp; &nbsp; &nbsp; hello( place =&gt; "Earth" );</p><p>For those who don't know, one of the neato features of <a href="">Method::Signatures</a> is that it can <a href="">alias references</a> to make working with references less of a trial:</p><p> &nbsp; &nbsp; &nbsp; &nbsp; func popn(\@array, $howmany) {<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return splice @array, -$howmany;<br> &nbsp; &nbsp; &nbsp; &nbsp; }</p><p> &nbsp; &nbsp; &nbsp; &nbsp; my @stuff = (1,2,3,4,5);<br> &nbsp; &nbsp; &nbsp; &nbsp; my @last_three = popn(\@stuff, 3); # 3,4,5<br> &nbsp; &nbsp; &nbsp; &nbsp; print @last_three;</p><p>It does this with the amazing <a href="">Data::Alias</a> module. Unfortunately, 5.12 broke its black magic and its non-trivial to fix. Method::Signatures now makes Devel::Alias an optional dependency. If its available, it'll use it. Otherwise, no aliasing for you.</p><p>But that's ok, because perl5i makes working with references enjoyable. And while perl5i is adding its own simple signatures, they're forward compatible with Method::Signatures! They play together, so if you want perl5i and the full power of Method::Signatures you can have them.</p><p> &nbsp; &nbsp; &nbsp; &nbsp; use perl5i::2;<br> &nbsp; &nbsp; &nbsp; &nbsp; use Method::Signatures;</p><p> &nbsp; &nbsp; &nbsp; &nbsp; func echo($message is ro) {<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; say $message;<br> &nbsp; &nbsp; &nbsp; &nbsp; }</p><p>Just make sure you load MS after perl5i. The last one loaded wins.</p><p>Finally, I was comparing Method::Signatures with MooseX::Method::Signatures and made a disturbing discovery. I always new MooseX::Method::Signatures would have a performance penalty, it does more checks than Method::Signatures, I just didn't realize how bad it was.</p><p>Here's comparing an empty signature: <code>method foo() {}</code>.</p><blockquote><div><p> <tt>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Rate&nbsp; &nbsp; MMS&nbsp; &nbsp; &nbsp;MS&nbsp; &nbsp; Std<br>MMS&nbsp; &nbsp; 3207/s&nbsp; &nbsp; &nbsp;--&nbsp; -100%&nbsp; -100%<br>MS&nbsp; 1498875/s 46644%&nbsp; &nbsp; &nbsp;--&nbsp; &nbsp; -1%<br>Std 1508351/s 46940%&nbsp; &nbsp; &nbsp;1%&nbsp; &nbsp; &nbsp;--</tt></p></div> </blockquote><p>That's showing MooseX::Method::Signatures is 450x slower than either Method::Signatures or a normal method call creaking out a mere 3500 method calls per second as compared to the 1.5 million it should be doing. And that's for a method with an empty signature!</p><p>To be clear, that's the speed of calling a method, not compiling them.</p><p>Here's one comparing a simple signature that requires a check, so MS can't optimize it away: <code>method foo($arg!) { return $arg + 1 }</code> That's a required positional argument.</p><blockquote><div><p> <tt>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Rate&nbsp; &nbsp; MMS&nbsp; &nbsp; &nbsp;MS&nbsp; &nbsp; Std<br>MMS&nbsp; &nbsp; 2928/s&nbsp; &nbsp; &nbsp;--&nbsp; -100%&nbsp; -100%<br>MS&nbsp; &nbsp;983127/s 33481%&nbsp; &nbsp; &nbsp;--&nbsp; &nbsp; -2%<br>Std 1005357/s 34240%&nbsp; &nbsp; &nbsp;2%&nbsp; &nbsp; &nbsp;--</tt></p></div> </blockquote><p>3000 method calls instead of a million.</p><p>Now I'm the first to counter arguments bemoaning method call overhead. Usually it doesn't matter. Usually the extra cost of calling a method and checking arguments is insignificant compared to what that method actually does. And MooseX::Method::Signatures has features Method::Signatures does not, most significantly type checking. But my god! Three orders of magnitude of performance lost! And its not even using the extra MMS features. That's just too much.</p> schwern 2010-07-31T00:49:55+00:00 journal Perl 6 Is The Language Your Language Could Smell Like <p><a href="">Hello programmers</a>. Look at your code, now at Perl 6, now back at your code, now back at Perl 6! Sadly, your code is not written in Perl 6. But if you use <a href="">Rakudo Star</a> then Perl 6 is the language your code could be written in!</p><p><a href="">Now Microsoft scented!</a></p> schwern 2010-07-29T19:24:09+00:00 journal "def" or "func"? <p>perl5i 2.3.0_01 now has <a href="">basic methods and subroutine signatures</a> with code basically lifted straight from <a href="">Method::Signatures::Simple</a>. <a href="">MooseX::Declare</a> got me addicted, now I want them everywhere.</p><blockquote><div><p> <tt>use perl5i::2;<br> &nbsp; <br>def add($this, $that) {<br>&nbsp; &nbsp; return $this + $that;<br>}<br> &nbsp; <br>method new($class: %args) {<br>&nbsp; &nbsp; return bless \%args, $class;<br>}<br> &nbsp; <br>my $echo = def($arg) { return $arg };</tt></p></div> </blockquote><p>Its alpha for two reasons. First, I don't have time right now to really thoroughly test it, but I really want it.</p><p>Second, overriding "sub" is hard. <a href="">Its been done</a> but its a bit twitchy. Defining a new keyword is easy(er). So what should that keyword be? I've come up with two that have good arguments. "def" and "func". Both are short. "def" has the benefit of being used by other programming languages a Perl programmer is likely to encounter and not hate (Python, Ruby, Scala, Groovy). "func" is nice because it pretty clearly means "function" whereas "define" is a bit ambiguous.</p><p>perl5i currently does both. Only one will survive in version 3 (the other will be deprecated). Before you comment on which is your favorite, try it for a little bit. I found a difference between what I thought I like and what I actually use.</p> schwern 2010-07-13T00:06:57+00:00 journal Where The Hell Is Test::Builder2? <p>My progress and communication about the Test::Builder2 grant has been nothing short of appalling. There is a sort of herky-jerky progress where I figure out a design problem, push the code forward, then remember a use-case that throws a wrench in the whole design and the whole thing comes to a screeching halt again.</p><p>At the QA hackathon we elegantly solved the problem of things like <a href="ahref=">die-on-fail and Test::NoWarnings</a> but then ran afoul of things like Test::Warn and Test::Exception which runs tests inside of tests but those aren't actually part of the test stack.</p><p>Confused? I'll post more about it another time. Point is, TB2 continues to move forward, its just that there's long periods of rumination between sprints of development. And I get distracted by other projects. At this rate I'll be collecting Social Security before I collect the second half of the grant. I really want TB2 to happen, but something decisive has to be done. I work best with hard deadlines, so the plan is to clear a month for working mostly on TB2. A lot of the wibbling is trying to come up with the most elegant solution, but I usually have a less than elegant way to solve it and move forward. If its between an elegant TB2 that doesn't exist and a less elegant TB2 that does, well, go with the one that exists. With the way my schedule is looking, that will probably be mid-August to mid-September. If that doesn't produce an alpha, then I'll kill the grant.</p><p>That doesn't mean TPF gets nothing for your money. Chunks of TB2 can be harvested to improve TB1. Specifically, the TB2 formatting and history objects. The TB2 formatter makes the guts of TB1 cleaner, and it also allows it to produce something other than TAP. Used together, history and formatter allows non-Test::Builder based test frameworks to work together with Test::Builder providing even more flexibility. This is <a href="">already done in the TB2 branch</a>.</p><p>Looking at the grant deliverables, most of it is done:</p><blockquote><div><p> <tt>* Split up global shared Test resources into individual objects<br>&nbsp; &nbsp; * The test counter<br>&nbsp; &nbsp; * The output filehandles<br>&nbsp; &nbsp; * The plan<br> &nbsp; <br>* Allow hooks for global beginning and end of test functions.<br>&nbsp; &nbsp; * Ensure multiple hooks "stack"<br>&nbsp; &nbsp; * die on fail<br>&nbsp; &nbsp; * debug on fail<br> &nbsp; <br>* Hooks for global beginning and end of test actions<br>&nbsp; &nbsp; * Example: A safer Test::NoWarnings<br>&nbsp; &nbsp; * Example: Don't cleanup temp files on failure so they can be debugged<br> &nbsp; <br>* Allow for test output other than TAP<br> &nbsp; <br>* Allow another Test::Builder-like module to work in the same process<br>&nbsp; as Test::Builder (for example, sharing the counter).<br> &nbsp; <br>* Rewrite Test::Builder in terms of Test::Builder2.</tt></p></div> </blockquote><p>Here's what's not complete:</p><blockquote><div><p> <tt>* Split up localizable behaviors into objects<br> &nbsp; <br>* Allow individual test modules to locally override Test::Builder2 behaviors<br> &nbsp; <br>* Allow test modules to globally override Test::Builder2 behaviors<br>&nbsp; &nbsp; * How the plan works</tt></p></div> </blockquote><p>Since I'm not writing to the letter of the law, <a href="">there's more than that to be done before release</a>, but the project does move.</p> schwern 2010-06-27T19:16:09+00:00 journal The Post-YAPC Plan <p>I spent most of YAPC::NA mildly sick, sleep deprived and writing talks. Each of those things alone isn't so bad, but put all together meant I had time and energy enough to do my talks, discuss with people after, and that's about it. As a result, I was kind of dead in the head most of the time and didn't do a whole lot of interaction with people. I didn't feel like I got the most out of the one opportunity a year I get to hang out with huge gobs of Perl folk.</p><p>One of the things which I wanted to do at YAPC was get <a href="">gitpan</a> restarted. It can run right now, but the code is a mess and needs to be babied. It needs a rewrite. That rewrite was supposed to happen at YAPC but see above. I'm doing that now, using MooseX::Declare, perl5i and Path::Class just to mess around with them seriously. Also log4perl, which I'm finally learning a decade late. Its fun, far more pleasant than knocking it together without, once you learn to cope with Moose's idiosyncrasies. Better to learn the quirks of one complete system than nine incomplete ones.</p><p>That's what's absorbing my time right now. After that I want to add subroutine signatures to perl5i and the <a href="">&#252;ber file and directory objects</a>. They were supposed to be in, at least as a prototype, by YAPC but that didn't work out. Using MooseX::Declare, Path::Class and perl5i together has me drooling for them.</p><p>Will Coleda, representing the TPF, found me at YAPC and mercifully did not break my legs. We hashed out a plan to make a last stab at Test::Builder2 before calling the grant done. That's not going to happen until August, I'll post about that later.</p><p>Oh, and I have a talk to do at OSCON about how <a href="">the world is going to end in 2038</a> assuming <a href="">2012 doesn't claim the prize first</a>. And a two part <a href="">Git tutorial for Drupal programmers</a> that I'm developing into a commercial class. And two paid clients to keep happy.</p><p>One thing I *don't* have to worry about is MakeMaker. Gird your loins, MakeMaker has provisionally been handed off to Matt Trout. Maybe I need to worry about it more...</p> schwern 2010-06-27T18:34:30+00:00 journal Object::ID - A unique object identifier for any object <p>Something Perl's OO has been missing has been a reliable way to identify an object. Is $this the same as $that? Not asking if it contains the same information, but is it a referent to the same object? Have we seen it before? When I alter $this will I also be changing $that?</p><blockquote><div><p> <tt>package Foo;<br> &nbsp; <br>use Object::ID;<br> &nbsp; <br>...write the class however you want...</tt></p></div> </blockquote><p>Really, HOWEVER YOU WANT! Inside out, outside in, code refs, regexes, globs, Moose, Mouse... Call the constructor whatever you like, add in a DESTROY method. Doesn't matter, it'll work.</p><blockquote><div><p> <tt>my $id&nbsp; &nbsp;= $obj-&gt;object_id;<br>my $uuid = $obj-&gt;object_uuid;</tt></p></div> </blockquote><p> <code>object_id()</code> is a cheap, process-specific identifier. <code>object_uuid()</code> is a bit more expensive on first call (it has to generate the <a href="">UUID</a>, about 30% slower) but it should be universally unique across machines and processes.</p><p>That's great for YOUR objects, but what about everyone else? You can either inject the Object::ID role one class at a time...</p><blockquote><div><p> <tt>package DateTime;<br>use Object::ID;<br> &nbsp; <br>my $date = DateTime-&gt;now;<br>say $date-&gt;object_id;</tt></p></div> </blockquote><p>or you can load UNIVERSAL::Object::ID and every object has it. EVERY OBJECT! Even things you don't realize are objects.</p><blockquote><div><p> <tt>use UNIVERSAL::Object::ID;<br> &nbsp; <br># Regexes are objects<br>say qr/foo/-&gt;object_id;<br> &nbsp; <br># Loading IO::Handle turns all filehandles into objects<br>use IO::Handle;<br>open my $fh, "foo/bar";<br>say $fh-&gt;object_id;</tt></p></div> </blockquote><p>But OH GOD UNIVERSAL! Well, use at your own risk. Its handy to use in your own programs and private libraries. Or you can use <a href="">Method::Lexical</a> and apply it lexically.</p><p>Why not just use the object's reference address? Well, as people implementing inside-out objects discovered, they're not unique. They're not thread safe, and worse they're not even unique for the life of the process. Perl will reuse the reference of a destroyed object. Observe:</p><blockquote><div><p> <tt>{<br>&nbsp; &nbsp; package Foo;<br> &nbsp; <br>&nbsp; &nbsp; sub new {<br>&nbsp; &nbsp; &nbsp; &nbsp; my $class = shift;<br>&nbsp; &nbsp; &nbsp; &nbsp; return bless {}, $class;<br>&nbsp; &nbsp; }<br>}<br> &nbsp; <br>for(1..3) {<br>&nbsp; &nbsp; my $obj = Foo-&gt;new;<br>&nbsp; &nbsp; print "Object's reference is $obj\n";<br>}</tt></p></div> </blockquote><p>Run that and you should get the same reference, three times, for three different objects.</p><p>And then there's the problem of string overloaded objects. You have to be careful to always use Scalar::Util::refaddr or overload::StrVal.</p><p>It turns out inside-out objects have nearly the same problem, and 5.10.0 introduced field hashes to solve that. rjbs explains the pain of all this at slide 120 in his excellent <a href="">5.10 For People Who Aren't Totally Insane</a>. You can read the <a href="">gory details of field hashes</a> but it comes down to this: in 5.10 you can A) get a process unique, thread safe identifier for an object and B) you can store it in hash such that it gets destroyed when the object is destroyed. Perfect!</p><p>Because of this, if you look inside Object::ID you'll see there's not a lot to it. It makes a field hash to store the IDs in, a state variable to hold an ID counter, and then just accesses the field hash.</p><blockquote><div><p> <tt>use Hash::Util::FieldHash qw(fieldhash);<br>fieldhash(my %IDs);<br> &nbsp; <br>sub object_id {<br>&nbsp; &nbsp; my $self = shift;<br> &nbsp; <br>&nbsp; &nbsp; state $last_id = "a";<br> &nbsp; <br>&nbsp; &nbsp; return $IDs{$self}<nobr> <wbr></nobr>//= ++$last_id;<br>}</tt></p></div> </blockquote><p>No scary black magic (beyond what's inside fieldhash). Its so simple, which is why it works with everything.</p><p>Now, I didn't come up with this implementation. I just laid out the requirements and <a href="">Vincent Pit</a> filled in the blanks. I was only vaguely aware of field hashes, Vincent made the connection. Thank you VPIT!</p><p>Practical applications? Honestly, I'm not sure. I needed it as a shortcut for expensive object equality checks in perl5i. Maybe some of the OO theorists out there can fill this part in. Let me know what you might use it for.</p><p>Possible extensions? Well... with some tweaking Object::ID can be used as a universal object registry. Not only can you ask "does the object associated with this ID still exist" but field hashes provide the ability to get the object associated with an ID. It would only work on objects that have had their ID asked of them, and thus registered with the field hash, but how else would you have the ID? Is this useful? Is this a security hole? I dunno, but it would be easy.</p> schwern 2010-05-02T17:17:01+00:00 journal OMG UNICORNIFY::URL!!! &lt;3 <p><a href="">OMG! A UNICORN PONY FOR MEEEE!!!</a></p><p>You can get 1 2 wth <a href="">Unicornify::URL</a>!! Here's a program to do it at the command line.</p><blockquote><div><p> <tt>use Acme::Pony;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;bUf<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Fybuf<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FyBuFFYbU<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FfyBUFfYbUff<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; YBuffybuFfyBuF<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fYbUffYBUfFYbUff<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; YbUFfYBuffYBuFFYBu<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FFybUffYBUffYBUfFYb<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UffYbUFfyBUffYBuFfyB<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UFFybUfFYBuffYbUFF<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ybUfFyBuFfyBufFy<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;BuffYBufFyBUfFYB&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uffY<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bUffybuffyBUFfyBuf&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FYBuFfy<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BuFFybUFFyBUffyBuFFYbuffybUf&nbsp; &nbsp;fYbUfFYBu<br>&nbsp; &nbsp; fFYB&nbsp; &nbsp;uFFyBufFyBUfFYbufFYbUFFYbUFfyBufFYBufFYBu<br>&nbsp; &nbsp;FFyBufFyBUffYBufFYbUffYBUFfYBUFFyBuFfYbUFFybUffYBU<br> &nbsp; ffyBU fFYbuffYbUffybuffYbuFfYbuFFyBuFFyBUfFybufFYbUf<br>fYbUF&nbsp; &nbsp; &nbsp;fybUFfYBuffybuFfyBuFFYBuffYBUFFybuffybUffYBu<br>&nbsp; fFYBuf&nbsp; &nbsp; fyBuFFyBufFyBUffYBufFYbufFyBUFfybUFfYbuffyb<br>&nbsp; &nbsp;uFfyB&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UffYBUfFybUfFYbuFfYBUFfYbUffYBuffybuFf<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;yBuFFY&nbsp; &nbsp; &nbsp;BuffYBUFFybuffybUffYbufFYb<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ufFyb&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; UFfybufFYBuffybufFyb<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UffYb&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; UffybUFfY buffybuF<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fybUf&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fyBuffy BUFfYbuF<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FYbUF&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fyBuffYbuFFyBUFf<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ybUf&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Fybu&nbsp; ffYb UffybuF<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fYbu&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ffYB&nbsp; UFFy&nbsp; BuFFYB<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UfFy&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;BuF&nbsp; &nbsp;fybU&nbsp; &nbsp;ffYB<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Uff&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Ybu<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fFyb&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uFf<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; YBUF<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; F<br> &nbsp; <br>yBuFFYBUfFy</tt></p></div> </blockquote><p>&lt;3 k thx bye!!1!11!!!</p> schwern 2010-04-01T22:51:34+00:00 journal Some Facts About Schwern <p><a href="">Open Source Bridge</a> requested that I improve <a href="">my bio</a>, so I decided to share some facts about myself...</p><blockquote><div><p>Schwern has a copy of Perl 6, he lets Larry Wall borrow it and take notes.</p><p>Schwern once sneezed into a microphone and the text-to-speech conversion was a regex that turns crap into gold.</p><p>Damian Conway and Schwern once had an arm wrestling contest. The superposition still hasn&#8217;t collapsed.</p><p>Schwern was the keynote speaker at the first YAPC::Mars.</p><p>When Schwern runs a smoke test, the fire department is notified.</p><p>Dan Brown analyzed a JAPH Schwern wrote and discovered it contained the Bible.</p><p>Schwern writes Perl code that writes Makefiles that write shell scripts on VMS.</p><p>Schwern does not commit to master, master commits to Schwern.</p><p>SETI broadcast some of Schwern&#8217;s Perl code into space. 8 years later they got a reply thanking them for the improved hyper drive plans.</p><p>Schwern once accidentally typed &#8220;git pull &#8212;hard&#8221; and dragged Github&#8217;s server room 10 miles.</p><p>There are no free namespaces on CPAN, there are just modules Schwern has not written yet.</p><p>Perl's threads are implemented with a single strand of Schwern's hair.</p><p>"Schwern" cmp "Chuck Norris" will cause Perl to segfault rather than try to compare them.</p><p>Schwern&#8217;s tears are said to cure cancer, unfortunately his Perl code gives it right back.</p></div></blockquote> schwern 2010-03-26T03:45:05+00:00 journal The Basic Unit of Bug Report Frustration <p>I submitted a proposal to OSCON called "How To Report A Bug" about the social issues involved in reporting and accepting bug reports. Its still pending, but its caused me to do a little writing for it. I came up with this introduction which I feel sums up the problem well. I'm also tickled that one measures bug report frustration in bags of shit.</p><blockquote><div><p>Developers often treat bug reports like someone dumping a bag of shit on your doorstep, ringing the bell and telling you to clean it up. That's not what they are. A bug report is someone pointing out that there's some shit on your doorstep, they stepped in it, and maybe it should be cleaned up.</p><p>Either way, nobody likes stepping in shit. And nobody likes cleaning up shit. So the whole interaction starts off on the wrong foot, perhaps the one covered in shit. Your job, as developer or as reporter, is to deliberately steer it back to being a positive one where the developer wants to fix shit and the reporter wants to continue to report shit.</p></div></blockquote> schwern 2010-03-22T23:57:26+00:00 journal The return of perl5i! <p>After an extended period in hibernation, <a href="">perl5i returns to CPAN</a> with a rack of <a href="">new changes</a> since the last CPAN release. Thanks to Bruno Vecchi, Chas Owens, Darian Patrick, Jeff Lavallee, Michael Greb, rjbs, benh and chromatic for contributing!</p><p>Why was it deleted from CPAN? Version numbers. I'd used my usual ISO date integer style versioning but realized pretty quick that I need <a href="">full X.Y.Z versioning</a> to indicate incompatible changes. Yes, perl5i is planning to be incompatible but without breaking your code!</p><p>How? By allowing you to declare what major version of perl5i you rely on. At this point its most likely going to be "use perl5i::2" which has the nice benefit of also allowing you to declare a dependency on perl5i::2. Easy peasy.</p><p>The question remains, what should "use perl5i" do? Should it A) load the currently installed version of perl5i? Or B) die with instructions on what to do? A is convenient, but means you'll get (probably unknowingly) walloped by an incompatibility later. B is a little inconvenient, but it makes the planned incompatibilities explicit and visible. The existence of a perl5i command line program (which also works on the #! line) makes A less of a concern if you want to go that route. So likely in short order "use perl5i" won't work any more. But I'd still like your thoughts on the matter.</p> schwern 2010-01-24T02:00:43+00:00 journal Great Perl Code <p>I was in a bar the other day talking with somebody about Perl. He asked, "what is some great Perl code I could read?" He was looking for a non-trivial amount of production Perl 5 code that elegantly solves a problem, and is beautiful to read.</p><p>I'm couldn't answer that. The code I see is either beautiful to read but fairly boring in what it does OR is an elegant solution but is terrifying to read (for example, autodie). I'm a biased observer, I work mostly with the plumbing so I see mostly the scary stuff that implements the elegant solutions.</p><p>What code would you show a non-Perl programmer read that is both beautiful and interesting?</p> schwern 2010-01-11T22:59:39+00:00 journal gitPAN now updating <p>After a month's break I've fixed up the <a href="">gitPAN</a> importer so it can update distributions in place fairly efficiently. This involved a major overhaul of <a href="">Parse::BACKPAN::Packages</a> into the new SQLite and DBIx::Class backed <a href="">BackPAN::Index</a>. Faster, leaner, far more flexible.</p><p>Its grinding through an update now, should be done before tomorrow morning. Still running off my laptop, hosting is in the works and then daily updates can happen.</p><p>UPDATE: The update is complete. Hit a few snags. 1) I wasn't always sorting releases by date, so I might have gotten some out of order in this update. Eventually there will be a full sweep and rebuild. 2) Some parts of github are case sensitive, some parts are not, and this caused some issues when distribution names subtly change case (like WebService vs Webservice). BackPAN::Index treats this as two different distributions, github treats it as one repository, hilarity ensues.</p><p>So that's the first 90% complete. Given that too about a month and the last 10% takes 90% of the time... see you in 2011.</p> schwern 2010-01-07T03:05:02+00:00 journal CPAN's Greatest Hits - Path::Class <p>Once upon a time, file and directory manipulation was considered very convenient. Compared to languages like C and Java, which consider I/O as some sort of distasteful act that should best be done behind at least 9 layers of abstraction, its positively enlightened. But once you use something like Ruby's <a href="">File</a> and <a href="">Dir</a> objects Perl starts to look a touch out of date.</p><p>In Perl, reading a file or directory is a three step process. Safe path manipulation requires the brilliant but cumbersome File::Spec. Even something like deleting a file requires special code to be safe. Want to reliably delete or create a directory? That's another module, File::Path. Copying a file? File::Copy. And so on.</p><p>The root of the problem is that Perl represents paths as just strings. And while that's enough to uniquely identify them, its not nearly enough to actually do anything with them. You want an object, files and directories which know how to do it all. Path::Class provides.</p><p>Created by Ken Williams, <a href="">Path::Class</a> is it. Short, convenient constructors, string overloading and providing just about everything you'd want to do with a path. Since its just sugar on top of all the pre-existing and well-built File modules, its extremely robust.</p><p>Here is why it is awesome.</p><p>Slurp a file.</p><blockquote><div><p> <tt># Perl<br>open my $fh, "&lt;", $file;<br>my $content = do { local $/; &lt;$fh&gt; };<br>close $fh;<br> &nbsp; <br># Path::Class<br>my $contents = file($file)-&gt;slurp;</tt></p></div> </blockquote><p>Iterate over every file in a directory.</p><blockquote><div><p> <tt># Perl<br>opendir my $dh, $dir;<br>for my $thing (grep { $_ ne '.' or $_ ne '..' } readdir $dh) {<br>&nbsp; &nbsp;<nobr> <wbr></nobr>...<br>}<br>closedir $dh;<br> &nbsp; <br># Path::Class<br>for my $thing (dir($dir)-&gt;children) {<br>&nbsp; &nbsp;<nobr> <wbr></nobr>...<br>}</tt></p></div> </blockquote><p>Change the subdir and file on a path (ie. from<nobr> <wbr></nobr>/some/path/foo/bar.txt to<nobr> <wbr></nobr>/some/path/baz/biff.txt)</p><blockquote><div><p> <tt># Perl<br>my($vol, $dir, $file) = File::Spec-&gt;splitpath($path);<br>my @dirs = File::Spec-&gt;splitdir($dir);<br>pop @dirs;<br>my $newpath = File::Spec-&gt;catpath($vol, @dirs, $newdir, $newfile);<br> &nbsp; <br># Path::Class<br>my $newpath = file($file)-&gt;parent-&gt;parent-&gt;subdir($newdir)-&gt;file($newfile);</tt></p></div> </blockquote><p>That last example starts to demonstrate what happens once Path::Class objects become ubiquitous in your code. Rather than instantiating them when needed, they're just there and can be chained together for rapid manipulation. Since they're string overloaded there's no reason not to use them.</p><p>I neglected error handling in the examples above. Path::Class was written back when library functions calling die() was considered impolite. Before pjf hammered home (cleaved with a Bat'leth?) the point that <a href="">exceptions are awesome</a>. So you still have to do all the "or die<nobr> <wbr></nobr>..." junk with Path::Class, you don't even get the convenience of autodie. Fortunately I <a href=";results=895ad259e1e3f1ef62f38a900bd1cebe">hope to do something about that</a>.</p><p>The next time you find yourself writing "use File::Spec" give Path::Class a shot.</p> schwern 2010-01-03T04:10:17+00:00 journal Numbered test file abuse <p>I hate numbered test files. Not that it isn't useful to force the ordering of some tests, but because its not then necessary to force the ordering of EVERY test. At the worst case you're back to BASIC. Observe the test suite from <a href="">SQL::Statement</a>.</p><blockquote><div><p> <tt>00error.t<br>01prepare.t<br>02executeDirect.t<br>03executeDBD.t<br>04names.t<br>05create.<nobr>t<wbr></nobr> <br>06group.t<br>07case.t<br>08join.t<br>09ops.t<br>10limit.t<br>11functions.t<br>12eval.t<br>1<nobr>3<wbr></nobr> call.t<br>14allcols.t<br>15naturaljoins.t<br>16morejoins.t<br>17quoting.t<br>18bigjoin.t <br> &nbsp; &nbsp; 19idents.t<br>20pod.t<br>21pod_coverage.t</tt></p></div> </blockquote><p>Now, how much of that is really trying to order the tests and how much of it is just the order it happened to be written? Does the limit test really have to go after the functions test? Why is there a quoting test in the middle of three join tests? I see three that make any kind of sense, 00error.t (since most of the tests use RaiseError), 20pod.t and 21pod_coverage.t, which maybe go last, though its honestly not important that they do.</p><p>What's the harm? It cripples command line completion. And you've got the old BASIC problem of renumbering. I want to add a new test, where does it go? Do I have to puzzle out the implied dependencies? Do I stick it at the end? But then the POD tests aren't last any more. Do I renumber everything? Do I use a duplicate number? Do I say the hell with it and cram it into an existing test file?</p><p>Not worth it.</p><p>Realistically most test dependencies really want to express two things: Run this first and run this last. For that you have 00foo.t and zz-bar.t. 00compile.t, 00setup.t, zz-teardown.t, zz-pod.t, etc... Anything else, just write it without the number. Or if you really do have a fixed order use <a href="">Test::Manifest</a>.</p><p>If you do have tests that would do better running in order, then instead of smashing them together into one arbitrary numbering system, group them. That is, stick them into a common subdirectory and then apply the first/last numbering again. A clear candidate in SQL-Statement would be t/join to contain naturaljoins.t, morejoins.t and bigjoin.t. This has the advantage of easily letting you run all one group's tests in one shot. "prove -lr t/join".</p><p>I don't mean to pick on SQL::Statement, its just what I'm patching right now. Lots of distributions obsessively number their test files to no real advantage.</p> schwern 2009-12-29T01:43:23+00:00 journal gitPAN is complete! <p>The gitPAN import is complete.</p><p>From BackPAN<br>------------<br>118,752 files<br>10,440,348,937 bytes (measured by adding individual file size)<br>21987 distributions (I skipped perl, parrot and parrot-cfg)</p><p>To git<br>------<br>21,766 repositories<br>4,495,204 bytes (measured by total disk usage<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; after git gc with no checkout)<br>150 gigs on github (they have to index it)<br>12 days (lots of starts and stops)<br>1 laptop (1st gen Macbook)</p><p>I had to do it on a disk image because OS X's case-insensitive filesystem</p><p>I've written up a small <a href="">FAQ</a>. gitpan is reasonably stable, but you may have to rebase in the future.</p><p>Next, I take a break.</p><p>Then begins the second pass, mostly improving and adding tags. Here's the list of planned <a href="">features</a>. The second pass will be a rolling reimport of each distribution to bring everything up to the same standard, there was a lot of incremental improvements during the first pass. I expect this to be changes to commit logs and tags with very little content change.</p><p>The issue of PAUSE ownership I'm going to punt on. Its ugly and can be done entirely in parallel. If someone else makes available a historical distribution ownership database, gitPAN will use it.</p> schwern 2009-12-14T22:27:48+00:00 journal MSCHWERN has a PAUSEID <p>ANDRE has a <a href="">posse</a>.</p><p>MSCHWERN has a <a href="">PAUSEID</a>.</p><p><a href="">Do you?</a></p> schwern 2009-12-13T07:36:42+00:00 journal gitPAN and the PAUSE index <p>As you may or may not know, people on CPAN own modules (technically they own the namespace). Each Foo::Bar is owned by one or more CPAN accounts. Usually you gain ownership on a "first-come" basis, but it can also be transferred. Only the "official" tarball for a given namespace is indexed. So if the owner of Foo::Bar uploads Foo-Bar-1.23.tar.gz Foo::Bar will point at Foo-Bar-1.23.tar.gz. If I (presumably unauthorized) upload Foo-Bar-1.24.tar.gz the index will still point at Foo-Bar-1.23.tar.gz.</p><p>Here's the rub. Not owning a module doesn't stop you from uploading. It also says nothing about who owns the distribution. gitpan is by distribution. Now it gets a little more difficult to figure out who owns what. For example, look at <a href="">MQSeries-1.30</a>. All but two modules are unauthorized. BUT notice that is authorized. The CPAN index does point MQSeries at M/MQ/MQSERIES/MQSeries-1.30.tar.gz (everything else is at 1.29). Likely what we have here is a botched ownership transfer.</p><p>How do you mark that? seems to take the strict approach, if anything's unauthorized its out. The CPAN uploads database I have available is the opposite, if anything is authorized its in. What to do?</p><p>Then there's stuff like <a href="">lcwa</a>. Looks like junk, but here's the thing. CPAN has a global module index to worry about, gitpan doesn't. Each distribution is its own distinct unit. So lcwa does no harm on gitpan, it can be recorded.</p><p>What does matter? The continuity of a distribution's releases, and this is precisely what CPAN does not track. It doesn't even have a concept of a distribution, just modules inside tarballs. CPAN authors playing nice with tarball naming conventions gives the illusion of a continuous distribution.</p><p>So... for a given release of a distribution (ie. a tarball), how does gitpan determine if the release should be included in the distribution's history? If we go strict, like, we're going to lose legit releases and even entire distributions (like lcwa). If we let anything in gitpan is not showing an accurate history.</p><p>Add the complication that authorization changes. For example, the MQSeries module ownership will eventually be fixed. What then?</p><p>First pass through, gitpan is ignoring this problem. Its just chucking everything from BackPAN in. Second pass will rebuild individual repos with collected improvements. This is the first thing I'm not sure what to do about.</p><p>Suggestions?</p> schwern 2009-12-12T22:02:03+00:00 journal gitPAN's first success story <p>Sitting next to David Wheeler at a bar, he co-maintains Pod::Simple. The repo is on github. Previously it was in SVN. Before that, Sean Burke's hard drive. The SVN repo was imported into git, but as Sean had no repo they're left with a history gap. He wants that history back.</p><p>I imported Pod-Simple into gitPAN for him, then went about pasting his repository on top of gitPAN's. This means a rebase. First, we fetched the gitpan repo into his repository.</p><p> &nbsp; &nbsp; &nbsp; &nbsp; git remote add gitpan git://<br> &nbsp; &nbsp; &nbsp; &nbsp; git fetch gitpan</p><p>Then we find the first commit to David's repo and note the date: Nov 18th, 2005. We find the commit just before that in gitpan/master, 3.02, and its tagged 3.02. Then rebase all of David's repo on top of that tag.</p><p> &nbsp; &nbsp; &nbsp; &nbsp; git rebase --onto 3.02 --root master</p><p>That replays all of master on top of the tag 3.02 from gitpan. Ta da! Done. You can remove the gitpan remote.</p><p> &nbsp; &nbsp; &nbsp; &nbsp; git remote rm gitpan</p><p>As a final bit of cleanup, we made sure all the release tags after 3.02 are pointing to David's commits and not gitPAN's. I'll leave retagging as an exercise for the reader.</p><p>Push that (has to be forced, since its not a fast-forward) and it done.</p><p> &nbsp; &nbsp; &nbsp; &nbsp; git push origin master -f</p><p>gitPAN is currently using lightweight tags, so they have to be pushed too.</p><p> &nbsp; &nbsp; &nbsp; &nbsp; git push --tags</p><p><a href="">Pod-Simple's history is complete</a>.</p> schwern 2009-12-04T07:33:04+00:00 journal gitPAN <p>If you're like me, and I know I am, you've often wondered things about other people's CPAN modules like: what changed in this release; when did this bug/feature get introduced; where's that old version that got deleted off CPAN?</p><p> provides <a href="">some web tools</a>, which is very cool, but pointy-clickies only go so far. What you really want is a repository of releases.</p><p>Sometimes you can find the project's repository, usually involving digging through the documentation. Now projects are starting to use the <a href="">repository resource in their metadata</a> and links to it so things have gotten a little better. And maybe its complete, or maybe the history cuts off where the last maintainer took over. And maybe they've tagged their releases in some sane way.</p><p>Wouldn't it be nice if every CPAN distribution had a repository of all their releases, all tagged the same way? The idea has been kicking around for a while. Eric Wilhelm took a stab at it with Subversion, but its less than trivial to get a useful history out of a pile of tarballs with SVN. And then where do you host it?<br>Turns out git makes this process trivial. You delete all the files, unpack the new release, and commit it all. Git figures out what moved, what got deleted, what got added, etc. So that's one part solved.</p><p>Then brian d foy has been working on indexing BackPAN. Leon made a module to access this index, <a href="">Parse::BACKPAN::Packages</a>. That's another part solved.</p><p>Yanick developed a <a href="">pile of tools to make turning a CPAN distribution into a git repository easy</a> including one to import all the releases from BackPAN. Put a loop around that and call it done.</p><p>Finally, hosting. I'm never one to DIY system administration, so plop it on github. Their <a href="">APIs</a> make creating repositories trivial and their web site provides far more functionality than I'd ever want to maintain. And, perversely, once I get tagging working you can download tarballs! Unfortunately BackPAN is about 20 gigs and while the size of the resulting git repos is looking to be far smaller (projects with a lot of releases come out much smaller, projects with few releases come out a little larger) it still bleads well over their 300M free account limit. Hopefully they'll be receptive to a little begging.</p><p>I give you <a href="">gitPAN</a>, a (soon to be) complete set of repositories for all of BackPAN. The process is fully automated, but I'm still tweaking things and the available repositories are sporadic. There's a lot of optimization and small corrections which needs done, my tweaked versions of <a href="">Parse::BACKPAN::Packages</a> and <a href="">Git::CPAN::Import</a> are available.</p><p>There are two open problems. First, I haven't even looked into how to keep the repositories up to date. There's some <a href="">new indexes on BackPAN</a> as part of the <a href="">File::Rsync::Mirror::Recent</a> mirroring optimization Andreas has been working on which will probably prove useful. If code suddenly appeared to handle that that would be great.</p><p>Second, I know of no historical index of authorized releases. This means gitPAN will just pull in everything on BackPAN causing a slightly skewed history. If a solution to that appeared, that too would be great.</p><p>I don't have any clear idea of what this might be used for, nothing to justify its scale. But I figure make the data available and someone will do something awesome with it. "If you build it they will come." If nothing else it'll make patching easier, I've already started generating gitPAN repos for modules I'm about to patch and cloning that to work on, but hopefully this will be more than an extended yak shaving exercise.</p> schwern 2009-12-03T23:55:03+00:00 journal I don't understand why people think Unix is hard. <blockquote><div><p> <tt>$ pax --help<br>pax: illegal option -- -<br>usage: pax [-cdnvzO] [-E limit] [-f archive] [-s replstr]<nobr> <wbr></nobr>... [-U user]<nobr> <wbr></nobr>...<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[-G group]<nobr> <wbr></nobr>... [-T [from_date][,to_date]]<nobr> <wbr></nobr>... [pattern<nobr> <wbr></nobr>...]<br>&nbsp; &nbsp; &nbsp; &nbsp;pax -r [-cdiknuvzDOYZ] [-E limit] [-f archive] [-o options]<nobr> <wbr></nobr>...<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[-p string]<nobr> <wbr></nobr>... [-s replstr]<nobr> <wbr></nobr>... [-U user]<nobr> <wbr></nobr>... [-G group]<nobr> <wbr></nobr>...<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [-T [from_date][,to_date]]<nobr> <wbr></nobr>...&nbsp; [pattern<nobr> <wbr></nobr>...]<br>&nbsp; &nbsp; &nbsp; &nbsp;pax -w [-dituvzHLOPX] [-b blocksize] [ [-a] [-f archive] ] [-x format]<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[-B bytes] [-s replstr]<nobr> <wbr></nobr>... [-o options]<nobr> <wbr></nobr>... [-U user]<nobr> <wbr></nobr>...<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[-G group]<nobr> <wbr></nobr>... [-T [from_date][,to_date][/[c][m]]]<nobr> <wbr></nobr>... [file<nobr> <wbr></nobr>...]<br>&nbsp; &nbsp; &nbsp; &nbsp;pax -r -w [-diklntuvDHLOPXYZ] [-p string]<nobr> <wbr></nobr>... [-s replstr]<nobr> <wbr></nobr>...<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[-U user]<nobr> <wbr></nobr>... [-G group]<nobr> <wbr></nobr>... [-T [from_date][,to_date][/[c][m]]]<nobr> <wbr></nobr>...<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[file<nobr> <wbr></nobr>...] directory</tt></p></div> </blockquote><p>(just for fun, one of those lines is hard tab indented)</p> schwern 2009-07-15T21:56:27+00:00 journal No, its not about make. <p>In another edition of "I cut and paste from my email", I was responding to a user who doesn't understand what the problem is with using Makefiles to generate Perl modules.</p><p>Well, he asked...</p><p>-----------------------------</p><p><cite><br>&gt; I could never follow the arguments against Makefiles or Makefile generators.<br></cite></p><p>Fortunately I wrote a talk alllll about it called<br><a href="">MakeMaker Is DOOMED!</a></p><p>The make dependency is only part of the problem. There's a small mountain of compatibility issues (which make dialect? which shell? what tools are available? are they GNU or BSD or some broken 3rd party thing? did you know that according to POSIX "cp -r" is deprecated?! or that it considers tar a legacy tool and we should all use something called pax?) in which you wind up rewriting a lot of things in Perl anyway... painfully squeezed through a shell one-liner like dough through a pasta maker.</p><p>A shell that often can only safely handle 1024 characters (some versions of Windows, old Unixen, VMS only gives us 255)... oh, and you don't really know how long that command might wind up being because it contains make variables which can change at runtime so you just sort of have to guess and hope nobody uses too many modules or something.</p><p>And god forbid you have a filename with a space or special character in it. Now you need to escape everything... but you don't know how a variable is going to be used. Maybe it'll be used by the shell...</p><p><code><br> &nbsp; &nbsp; &nbsp; &nbsp; $(CMD) $(VARIABLE)<br></code></p><p>Maybe it'll already be quoted.</p><p><code><br> &nbsp; &nbsp; &nbsp; &nbsp; $(PERL) -e 'print $(VARIABLE)'<br></code></p><p>I'm sorry, I got the wrong quoting. Single quotes don't work on most Windows makes so I have to double quote them.</p><p><code><br> &nbsp; &nbsp; &nbsp; &nbsp; $(PERL) -e "print $(VARIABLE)"<br></code></p><p>But double quotes expand variables on Unix, and I don't know what's inside $(VARIABLE). What a conundrum! Now I need to write a portable method to write portable one-liners so I can put portable Perl code in my very unportable Makefile.</p><p>And what's the odds a module author is going to consider any of that when they extend MakeMaker?</p><p>Even if you wrote your own make-like build tool that solved all the compatibility problems and everyone magically had installed you'd still be up shit creek. You're taking a dynamic situation with lots of state and turning it into a bunch of static one liners with no state. Its like writing down the instructions about how to build a custom tailored car for an idiot child to do. You wind up saying "fuck it, we're not even going to try doing that".</p><p>See <a href="">this recent perfectly sensible ticket I had to reject</a> for a concrete example of something which should be easy but is almost impossible in MakeMaker.</p><p>Finally, to customize how their modules are built you're asking Perl programmers to write make (through the funhouse mirror of MakeMaker), something they're totally unfamiliar with. The days of Perl programmers being old C programmers is long gone. Any alternative build system has the same problem except NOBODY will know it. Perl programmers know how to write Perl.</p><p>Aren't you glad you asked?</p><p>There is one thing MakeMaker has over Module::Build. You can skim the Makefile to figure out what its going to do (except for all the logic that went into generating the Makefile in the first place). This makes it more visible to those who know make, for everyone else its just more magical gibberish. Module::Build is more visible to those who know Perl and know to look in Module::Build::Base. It would be nice of MB had an equivalent to "make -d" to report what's going on, what actions are being called and what dependencies are being resolved.</p> schwern 2009-07-15T21:45:53+00:00 journal perl/vendor/site explained <p>As part of answering <a href="">a MakeMaker ticket</a> I wrote out a fairly solid explanation of what the three different install locations mean for CPAN modules.</p><blockquote><div><p>&gt; (Personally I've always found the perl/site/vendor distinction and the<br>&gt; triplicated set of directories to be fairly impenetrable<nobr> <wbr></nobr>:-), beyond<br>&gt; that only 1 of the 3 at various times did something like I thought I<br>&gt; wanted!)</p></div></blockquote><p>Part of the problem is the whole philosophy is never fully explained. Part of it is that until 5.12 the default look up order is wrong.</p><p>Its pretty simple. Think of it like MacPorts or fink which doesn't control the entire operating system. You effectively have three managers working at the same time. You have the user installing things by hand. You have the package manager. And then there's the operating system.</p><p>In a flat layout they'll stomp all over each other. You might install an upgraded program from MacPorts and then have the next OS upgrade downgrade it again. The user might install the latest version from a tarball and then MacPorts installs an older version on top of it next upgrade.</p><p>"site" is what gets installed by the user, like<nobr> <wbr></nobr>/usr/local.</p><p>"vendor" is what gets installed by the package manager.<nobr> <wbr></nobr>/opt/local for MacPorts,<nobr> <wbr></nobr>/sw for fink.</p><p>"core" is what originally shipped with the operating system (or in Perl's case with Perl itself), like<nobr> <wbr></nobr>/usr.</p><p>You look in<nobr> <wbr></nobr>/usr/local first, then<nobr> <wbr></nobr>/opt/local, then<nobr> <wbr></nobr>/usr. In fact, that's how my PATH is set up. Unfortunately Perl itself incorrectly puts "core" first. Fortunately Debian (and I think Redhat now) fixes that. And 5.12 will finally fix it for all.</p><p>Packagers should be setting INSTALLDIRS=vendor. The CPAN shell should be using INSTALLDIRS=site. Nothing should be using perl/core. The broken lookup order complicates this because upgrades to dual-life CPAN modules have to go into core else they're not found, but Debian users don't have to worry about that.</p> schwern 2009-07-07T23:29:39+00:00 journal The best diagram about git evar <p>Leto and I got chromatic to <a href="">actually use github</a> today at <a href="">OS Bridge</a>. In the process of explaining it to him I drew up <a href="">the most useful diagram of git you will ever see</a>. It illustrates the five layers (stash, working copy, staging area, local repo, remote repo), how you move changes between them and what layers different invocations of diff act on.</p><p>I wish someone had shown me this months ago.</p><p>UPDATE: Of course I'm not the first person to think of this. Here's <a href="">a much cleaner version</a> of what I did from <a href="">an article about the git workflow</a>.</p> schwern 2009-06-20T08:55:47+00:00 journal Trapped In A Room With Schwern <p>I signed on at <a href="">YAPC</a> to do a talk simply entitled <a href="">Trapped In A Room With Schwern</a>. Robert Blackwell said this of what he wants:</p><blockquote><div><p>"I think you have a nice bent/slant/angle etc on a lot of stuff. I want you to get people talking. And no I would not expect every talk to be about perl. It would make me sad if they were all perl. But if you do it I would hope you could get everyone in the room to get excited about something. And I hope you would bore the crap out of others. Why do I think that b/c your audience is everyone from Larry to noob. You are not going to shock both of them or bore both of them with the same stuff."</p></div></blockquote><p>I have a goldfish's memory for what's interesting to me. As I'm writing up a list of things to talk about, I'm thinking that I'm missing something really obvious that I've simply forgotten about or that seems old and obvious to me.</p><p>So, suggestions? What would people like to hear about? Perl or otherwise. What do I tend to babble animatedly about between sessions?</p> schwern 2009-06-15T02:03:03+00:00 journal Unit test your Javascript in 8 seconds! <p>I posted about my <a href="">WWW::Selenium + Test.Simple</a> hack yesterday to enable automated Javascript unit testing. One of the problems was it was very slow. It had to start and kill a Firefox instance between each test which takes 8 seconds per test on my machine. Running 7 tests is a full minute.</p><p>Solution? Cache the selenium object! This will reuse the same Firefox session between tests so you only get slammed by the startup cost once. Now my 7 tests run in 8 seconds, the time to start up Firefox. That's awesome!</p><p>Will reusing the same Firefox process cause a problem? Unlikely. When I test web sites, with or without Selenium, I sure don't restart Firefox between checks. And neither will your users, so this is far more realistic. Web browsers are designed to isolate page requests from one another.</p><p>The prototype works. Future directions...</p><p>* Roll selenium-server into the distribution.<br>* Automate starting the selenium server.<br>* Add a config file...<br> &nbsp; &nbsp; * Which browser(s) to use?<br> &nbsp; &nbsp; * Which selenium server to use, or start its own?<br> &nbsp; &nbsp; * What file extensions to test with selenium?<br>* Rerun tests across multiple browsers<br>* Turn the HTML wrapper into a configurable template<br>* Make it play nice with prove.<br> &nbsp; &nbsp; * Turn it into something which can be used with --exec<br> &nbsp; &nbsp; * Turn it into something which can be put into<nobr> <wbr></nobr>.proverc<br>* Modularize it<br>* Figure out how to keep the Firefox process from appearing<br> &nbsp; &nbsp; * Or at least run backgrounded</p> schwern 2009-06-08T00:20:35+00:00 journal