in reply to AJAX popup windows - an example

If you do make it up into a tutorial, perhaps you might focus a bit more on "separation of concerns".

At the moment it's a little difficult to read the Javascript code. Maybe I'm just biased, but since CGI::Ajax can't do all the Javascript you need, I just don't see the point in using it at all. Perl handles the server side nicely, why not use one of the many excellent Javascript tooklits to handle the client side?

There are large comment blocks over each Javascript function, perhaps you could move all the Javascript into a separate file? If you use a modern JS/Ajax toolkit such as jQuery (or Dojo or Ext) you're Javascript code will be much shorter and more concise.

Perhaps your tutorial could deal with issues such as:


Replies are listed 'Best First'.
Re^2: AJAX popup windows - an example
by snowhare (Friar) on Sep 16, 2007 at 13:09 UTC


    Don't take what I'm about to write as disagreeing with you. It is more about the thought process that went into my choice to use CGI::Ajax for the example. Your points are strong, although possibly not complete: You've validly pointed out that clarity and 'non-intrusiveness' suffers in my example. What you missed is that it traded it for performance.

    I deliberately used CGI::Ajax to reduce the amount of explict Javascript I used for the example. You see, I normally just add the actual Javascript generated by CGI::Ajax as another block of output text - without actually using the CGI::Ajax module. I used the actual CGI::Ajax module here to make a clearer example without the burden of a full fledged toolkit like JQuery.

    Sound backwards?

    Not really. You see, performance and size tend to be my primary goals because I frequently need these kinds of scripts to be used on web pages that both receive many hits per day and that are already overly heavy byte-wise.

    So my goals of "runs fast, loads fast" compromised with the goals of "code clarity, full featured" to settle on CGI::Ajax for the example to get "Ajax clarity, acceptable performance".

    Code clarity would have argued for using one of the major JS toolkits. Performance would have argued for eliminating CGI::Ajax entirely and not using a toolkit library.

    As a data point to illustrate my point on performance, I modified my script to follow my normal practice of inlining the Ajax support Javascript as part of my Perl and benchmarked it for 1000 requests at a concurrency of 20 using the 'ab' Apache benchmark script (with a dry run in each case to allow the server to 'get up to speed'). I also made the minimal modifications required to make run under FastCGI as another comparision point. The tests were run using Apache2 on a hyperthreaded 3Ghz P4 machine running Fedora Core 6 Linux (with a second machine making the HTTP requests).

    CGI ModPerl2 FastCGI Original Example 24/sec 397/sec 328/sec Inlined Ajax JS 69/sec 505/sec 446/sec

    Why such a dramatic slowdown when using CGI::Ajax? Because it pulls in a lot of extra Perl code to generate that chunk of Javascript. CGI::Ajax is about 41K, and it pulls in Exporter (14.4K), Data::Dumper (38K), base (5.4K), overload (46K), vars (2.3K), warnings::register (1K), and Carp (8.8K). For a startling 156K of Perl to generate just under 7K of final Javascript using CGI::Ajax. While the ModPerl2 and FastCGI environments are not nearly as sensitive to the byte count, there is still a noticable runtime overhead to the dynamic code generation of CGI::Ajax.

    Similiar issues arise using the off-the-shelf Ajax libraries. They add dozens to hundreds of Kbytes of stuff needing to be loaded by the web browser (with dramatic performance consequences resulting just from that fact). Additionally, they tend to run slowly above and beyond that (Digg is a classic example - they use an off-the-shelf JS Ajax library that causes my machine to bog completely down on their longer pages. It is quite annoying.)

    So, in addition to your (excellent) suggestion of covering the use of off-the-shelf Ajax JS libraries, I need to cover when-and-why NOT to use them.

      More actually. On my machine:
      qwurx [shmem] ~ > perl -le 'use CGI::Ajax; print "$_ => $INC{$_}" for +sort keys %INC' CGI/ => /usr/lib/perl5/site_perl/5.8.8/CGI/ => /usr/lib/perl5/5.8.8/ Class/ => /usr/lib/perl5/site_perl/5.8.8/Class/ Data/ => /usr/lib/perl5/5.8.8/i386-linux-thread-multi/Data/Du => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/i386-linux-thread-multi/XSLoader.p +m => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ warnings/ => /usr/lib/perl5/5.8.8/warnings/

      Class::Accessor is included out of laziness - not having to code setters/getters for objects.

      As we all know, laziness is one of the virtues of a perl programmer, so that seems OK. But it isn't, because it conflicts with hubris, which beats laziness for Module authors. You don't want anybody to ever say something bad about your module, do you?

      Data::Dumper is a leftover from development. It isn't used anywhere in that module. And that one pulls in:

      qwurx [shmem] ~ > perl -le 'use Data::Dumper; print "$_ => $INC{$_}" f +or sort keys %INC' => /usr/lib/perl5/5.8.8/ Data/ => /usr/lib/perl5/5.8.8/i386-linux-thread-multi/Data/Du => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/i386-linux-thread-multi/XSLoader.p +m => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ warnings/ => /usr/lib/perl5/5.8.8/warnings/

      Then there's the ubiquitous 'vars' module, which does

      qwurx [shmem] ~ > perl -le 'use vars qw($foo); print "$_ => $INC{$_}" +for sort keys %INC' => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ warnings/ => /usr/lib/perl5/5.8.8/warnings/

      A little bit of (crude *) cleanup to the header of CGI::Ajax

      --- /usr/lib/perl5/site_perl/5.8.8/CGI/ 2007-02-01 00:35:44.00 +0000000 +0100 +++ CGI/ 2007-09-16 16:18:47.000000000 +0200 @@ -1,17 +1,32 @@ package CGI::Ajax; -use strict; -use Data::Dumper; -use base qw(Class::Accessor); -use overload '""' => 'show_javascript'; # for building web pages, +so - # you can just say: print +$pjx BEGIN { - use vars qw ($VERSION @ISA @METHODS); + our ($VERSION, @ISA, @METHODS); @METHODS = qw(url_list coderef_list DEBUG JSDEBUG html js_encode_function cgi_header_extra); - CGI::Ajax->mk_accessors(@METHODS); - + for my $field (@METHODS) { + *{'CGI::Ajax::'.$field} = sub { + my $self = shift @_; + if (@_) { + return $self->set($field, @_); + } else { + return $self->get($field); + } + }; + }; + for (qw(set get)) { + *{'CGI::Ajax::'.$_} = sub { + my $self = shift @_; + if (@_ == 1) { + return $$self{$_[0]}; + } elsif (@_ > 1) { + return $self->{$_[0]} = $_[1]; + } else { + $self->_croak('Wrong number of arguments received'); + } + }; + } $VERSION = .701; }
      and all dependencies are eliminated
      qwurx [shmem] ~ > perl -I. -le 'use CGI::Ajax(); print "$_ => $INC{$_} +" for sort keys %INC' CGI/ => CGI/

      at the expense of not being able to just print $pjx but having to say print $pjx->show_javascript.

      qwurx [shmem] ~ > perl -le 'use overload "+" => \&add; print "$_ => $ +INC{$_}" for sort keys %INC' => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ => /usr/lib/perl5/5.8.8/ warnings/ => /usr/lib/perl5/5.8.8/warnings/

      Of course the code using CGI::Ajax might pull in Exporter, scrict, warnings, vars et al anyways - but it is not CGI::Ajax's business to do so if it can do without.


      OTOH, if the code that uses CGI::Ajax uses all those modules anyways, it is better to have them imported once instead of compiling duplicated code. So the cleanest thing would be adding a conditional in a BEGIN block, and pulling in Class::Accessor et cetera only if those namespaces are already present, because the cost of linking in already loaded modules is close to nil.

      But then, this would depend on module loading order... not doing so for this particular module would just add 15 lines of code (which doesn't say much about perl's internal waste thereof... :-)

      *)I just dumped the methods mk_accessors() generates and stuck them into the BEGIN block. Works.

      update: corrected bug in set/get constructor


      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Those are interesting points. Your example was a good, simple one; I hope you only took my concerns as input in the case that you do create a tutorial on the matter. I don't wish to rebut what you have said and don't want to prescribe solutions for anyone else, but perhaps some of the following can provide some interesting counterpoints to some issues raised in this thread.


      The reason that I don't have any speed issues with my HTML user interfaces is that, from perl's perspective, my user interfaces consist entirely of static HTML (which is nicely cached by the web server). perl code doesn't serve that HTML at all (with the exception of some mod_perl-based authentication handlers).

      Also, client-side caching of Javascript, HTML and images (through various mechanisms) has finally come of age in modern browsers; you can count on it not to be a problem. The user waits for say 20-60KB of javascript to come down once and that code is cached thereafter. They see a very attractive progress indicator in the mean-time, and let's face it 60K (jQuery, 4 plugins, and all my Javascript) is small these days.


      It may be heretical to some folks here, but I think templating is fine for web content, not so much for exposing web application functionality. The more complicated or multi-faceted a user interface the more convoluted templates become, often with the outcome that the templates don't serve their initial goals - to uncouple viewables from code (for clarity), and to make visual assets separately maintainable.

      I still like the idea of using templates to frame the overall look and feel of a site.


      Nowadays, it's possible to have fast, semantically concise, intuitive, attractive user interfaces provided in a cross-platform manner in a variety of technologies.

      Amazing cross-platform Javascript toolkits and other technologies (such as Adobe Flex and Microsoft Silverlight) exists which can provide consistent, performant, lightweight user interfaces for web applications.

      None of those technologies requires the authors of client-side user interfaces to move to completely homogeneous implementations; the technologies can be separately embedded in HTML and combined to interesting effect.


      Updated: added a bit more detail in to clarify some points.

Re^2: AJAX popup windows - an example
by shmem (Chancellor) on Sep 16, 2007 at 07:07 UTC
    Maybe I'm just biased, but since CGI::Ajax can't do all the Javascript you need, I just don't see the point in using it at all.

    No module ever claimed to Do It All For You, except perhaps some module from the Acme namespace.

    CGI::Ajax does a specific task, and it does it pretty damn well and in a KISS way. Of course it is not good-for-all and constrains you in e.g. always passing a parameter fname for the perl function callback in the XHTTP query, but then fully-fledged Ajax Toolkits aren't any different: they put you on rails.

    CGI::Ajax is a very good example of a 'module at its best': it solves a particular problem without getting too over-featured and provides a nice wheel that hasn't evolved into a monster truck. <update> Although it has some issues. See my follow-up below. </update>


    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}