Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Journal of markjugg (792)

Tuesday April 17, 2007
11:04 AM

CGI.pm uploading bugs fixed

Some years ago, I noticed an oddity when using CGI.pm file uploads. File uploading only worked on first object I created. If I called CGI->new() again, the file upload would be gone.

At the time, I considered file uploading to a difficult and mysterious area, and assumed it "had to be this way", so I didn't file a bug report, and learned to work around the issue.

"CGI.pm is very popular" I reasoned. "Therefore, obvious bugs in it would be spotted and repaired."

For years, I avoided the problem by using CGI::Application and related plugins. It loads CGI.pm just once and provides access to it through a "query" method. So, as long as all the plugins use this method and don't access CGI.pm directly, the bug can continue avoided.

Finally, however, it bit me again recently in a way that was very hard to track down. Suddenly, we were getting weird empty files. This seemed impossible, as the files clearly existed and were being validated by Data::FormValidator::Constraints::Upload.

The files were "disappearing" because they weren't available on a second call to CGI->new.

This time, I had more experience and a different attitude. I decided this might be a solvable bug, and I dove into the CGI.pm guts to investigate.

This time around, I also had an understanding how to write an automated test for a file upload, which had also been a mystery in the past.

The result was that I was able to verify the issue was real, *and* solvable, and those fixes are what's new in CGI.pm 3.29:

http://search.cpan.org/src/LDS/CGI.pm-3.29/Changes

The moral, though, is to not settle for working around weird, mysterious behavior. At least report it to a user's mailing list for the software, or be adventuresome and dive into the code and poke around.

The fix for the issue was not particular hard and the symptoms were certainly obvious to those who ran into it. I can only conclude that the bug existed for years unfixed because other people like me had an attitude that the issue was "just mysterious" or couldn't be fixed.

Saturday March 17, 2007
02:10 PM

suggestions for solving the 'pip' bootstrapping problem?

pip represents a really neat idea. Make the requirements of any Perl project easy to install, without needed to understand how CPAN works.

I was considering trying to use it make the dependencies of this Ical2remind script easy to install. You should be able to get a Perl script working without needing to understand Perl or CPAN culture.

'pip' could help with that, but it has a bootstrapping problem. How to get pip installed without understanding pip or CPAN culture?

The solution it seems to me would be something like 'par': Distribute 'pip' as a single Perl script with all the dependencies for it glommed into the same file. ("Glommed" may not be a word. Globbed? Stuffed?).

Suggestions?

Saturday March 03, 2007
09:58 AM

Adam Kennedy's method for checking perl module memory usage

I wanted to know: How much memory is my perl module consuming? Adam Kennedy had several times referenced the memory size of Perl modules, so I asked him how he figured it out. Here's the recipe he was kind enough to share, which I've reworked and embellished a bit for clarity:

Use ps.

> perl -de 1
> use Dependency::Of::YourMod::One;
> use Dependency::Of::YourMod::Two;

Then ps the process:

ps -O rss,vsz | grep 'perl -de'

The second and third numbers you'll see are the resident and virtual memory size.

> use YourMod;

Then ps the process again:

ps -O rss,vsz | grep 'perl -de'

That difference covers the needs of the module itself, and ignores the dependencies.

Now, that assumes that all your initialization runs at compile-time. If you have a singleton implementation you might need to create one to get the full module use.

Tuesday November 28, 2006
08:52 PM

CGI::App: A Generic Base Controller

This is in response to LTJake's examples of setting up a generic base controller with Catalyst

Here's what the same thing would look like using CGI::Application.

# Normally one CGI::App dispatch table is used for
# an entire application, not just one module.
use CGI::Application::Dispatch;
CGI::Application::Dispatch->dispatch(
prefix  => 'MyApp::Controller::Admin::Account',
    table => [
        'admin/account/:id/:rm'  => {},
        'admin/account/:rm'      => {},
        ''                       => {},
    ]

);

package MyApp::Controller::Admin::Account;
use base 'CGI::Application';
use CGI::Application::Plugin::AutoRunmode;

use strict;
use warnings;

# default  for /admin/account/
sub list     : StartRunmode { die "index of accounts" }

# methods on /admin/account/$rm
sub create   : Runmode { die "create an account" }

# methods on /admin/account/$id/[$rm]
sub instance : Runmode { # do something with $self->param('id') }
sub view     : Runmode { die "view account" }
sub update   : Runmode { die "update account" }
1;

The amount of code needed in both cases is comparable, but there are two important philosophical differences here:

  1. Small components CGI::App encourages small apps that play well together. Here you see that the dispatcher and 'AutoRunmode' extensions get pulled in. Don't need 'em? They don't get loaded. Catalyst has a more monolithic approach, which I believe consumes more memory in a default setup.
  2. Global dispatching allows you to see and adjust the URI to runmode mappings at a bird's eye level, making adjustments across the application in one place. With Catalyst, dispatching logic is stored with each run mode, tying each run mode to one kind of dispatching, and making it difficult to see the big picture of all the mappings defined.
Thursday October 19, 2006
08:55 PM

New CGI::App article published on Perl.com

A new article I wrote about CGI::Application has now been published on Perl.com.

It provides a number of examples how tasks can be simplified with CGI::Application and the plugin system.

CGI::Application holds the distinction of being over five years old now!

Thursday September 14, 2006
09:59 PM

Reduction operators

Today I was in a different sort of mood. I decided to play with some Haskell, and ended up with the following. I started with a recursive function in Haskell which computes products:

product [] = 1
product (x:xs) = x * product xs

I then decided to write the same functionality in Perl 5 and Perl 6 to see how they compared. Along the way I ran into reduction operators, which turn out to appear in some form in all three languages, although I have never used them in any. Here's the function looks lik all three languages. I provide the print statement in only one case, since it is basically the same in all three:

-- Haskell
prod = foldr (*) 1

# Perl 5
use List::Util qw(reduce);
sub prod { reduce { $a * $b } 1, @_ }

# Perl 6
sub prod (*@xs) { [*] @xs }
print prod(2,3,4);

Not only does the Haskell solution look the cleanest to me, it was the only language I didn't find a related bug in. I added tests for [*]() and [+](), which are not yet returning 1 and 0 as expected in Pugs.

It turns out perl5 List::Util::sum() was returning "undef" for a empty lists instead of 0. I submitted a patch for that.

Thursday August 31, 2006
06:25 PM

CGI::App to Perl6: Another improvement for references

Earlier I wrote about how references are gone in Perl6. However, subroutine signature Perl6 expose just opposite-- that really everything is a reference.

We use modify-by-reference in CGI::Application to prevent copying large HTML documents into the "postrun" routine.

This is a technique that should be used sparingly, as returning explicit values creates for clearer code flow.

Perl6 helps promotes good programming practices in two ways in this regard. 1. You have explicitly turn on modify-by-reference for a subroutine argument, and 2. This explicitness makes it clear that this trick is being employeed. Let's a take a look at the syntax:

  Was: sub foo {...};        foo(\$bar)
  Now: sub foo ($bar is rw); foo($bar)

Perl6 is /always/ passing references to subroutines in the background, they are just read-only by default. While in Perl5 you might have a dig around to see if modification by reference was happening in a subroutine, in Perl6 you can look no further than the signature.

If you want a copy of the value passed, Perl6 covers that too:

  sub foo ($bar is copy);

Aspects of this may seem formal compared to the looseness of Perl5. So far I've generally liked these changes, as compactness and clarity often come with them.

Tuesday August 29, 2006
09:03 PM

CGI::App to Perl6: param() is now 90% shorter

Lot's of OO modules have a param() method: CGI::App, HTML::Template, CGI::Session... and they are mostly all about the same. That is, they are a glorifed hash wrapper.

Yet, in Perl5 it's easy for the code for them to be verbose, especiall if you want to accept both a hash /and/ a hashref, and provide some error checking on the input.

The param() method of CGI::App in Perl5 was 33 lines (including some whitespace). The Perl6 translation was 3 lines! This is possible with subroutine signatures and multi-method dispatch. Further, Perl6 tries somewhat successfully to hid e the difference between a hash and hashref, which eliminates another case to check for.

For constrast, here's the two pieces of code:

In Perl5

sub param {
    my $self = shift;
    my (@data) = (@_);

    # First use?  Create new __PARAMS!
    $self->{__PARAMS} = {} unless (exists($self->{__PARAMS}));

    my $rp = $self->{__PARAMS};

    # If data is provided, set it!
    if (scalar(@data)) {
        # Is it a hash, or hash-ref?
        if (ref($data[0]) eq 'HASH') {
            # Make a copy, which augments the existing contents (if any)
            %$rp = (%$rp, %{$data[0]});
        } elsif ((scalar(@data) % 2) == 0) {
            # It appears to be a possible hash (even # of elements)
            %$rp = (%$rp, @data);
        } elsif (scalar(@data) > 1) {
            croak("Odd number of elements passed to param().  Not a valid hash");
        }
    } else {
        # Return the list of param keys if no param is specified.
        return (keys(%$rp));
    }

    # If exactly one parameter was sent to param(), return the value
    if (scalar(@data) <= 2) {
        my $param = $data[0];
        return $rp->{$param};
    }

In Perl6

multi method param ()         returns Array { %!params.keys }
multi method param (Str $key) returns Array { @( %!params{$key} ) }
multi method param (%h,*%h)   { %!params = %(%!params, %h); undef }

I think the difference stands for itself...

Saturday August 26, 2006
02:59 PM

CGI::App to Perl6: Reference handling not needed here

I'm still wrapping my head around what means that references have been replaced with Capture objects in Perl6.

However, I was pleased to see it means this common idiom is no longer necessary. Often I have a variable that may contain a single scalar value or an arrayref of values.

Perl5:

my @new = (ref $old eq 'ARRAY' ) ? @$old : ($old);

But Perl6 Does The Right Thing, Easily

my @new = @$old;

Perl5 would complain if you tried to deference $old as an array when it wasn't an arrayref.

Now I have look through the CGI::Application code and see all the places I can shave off a few bytes with this refactor...

Here's another case in Perl6 where reference handling is not needed, while Perl5 requires it:

# Perl6 will properly assign @a to the 'k' key,
# without using reference notation.
my @a = ( 1,2 );
my %h = (
    k => @a,
    j => 'm',
);

07:28 AM

CGI::App to Perl6: A detour, improving CGI.pm for p6

As I looked in my failing tests for CGI::Application for Perl6, I realized that CGI.pm for Perl6 wasn't as developed as I thought.

It wasn't an OO interface, apparently because it was developed a while ago before that part of pugs was finished. Further, it didn't yet support creating a new object by passing some params to new.

A little bit of hacking later, I fixed and commited both those parts of CGI.pm for Perl6. You can now do this in Perl6:

  use Test;
  plan 1;

  use CGI;
  my $q = CGI.new( rm => 'cgiapp_rocks' );
  is($q.param('rm'), 'cgiapp_rocks');

In fact, that looks a lot like one of tests I wrote. ;-).

From there I moved on to providing feedback about the documentation spec for Perl6. Damian Conway responded there, and I look forward to reading the updated spec for that should be available soon.

Until then, back to hacking CGI::Application and Perl6...