Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Re: RFC: Class::CGI

by rjray (Chaplain)
on Apr 07, 2006 at 22:53 UTC ( #541984=note: print w/replies, xml ) Need Help??

in reply to RFC: Class::CGI

A few questions:

  1. Does Class::CGI::new consume query data and/or request body directly? Does it use an internal instance of a CGI object, and if so can it use an existing one? (In which case I would expect it to copy over all the param data triggering any relevant handlers as it encounters them. Of course, that might lead to ordering problems if something later in the form is sensitive to something earlier in the form.)
  2. My first thought when reading this was that you were referring to class-ifying structured data within forms, but from the longer examples I see that you're mostly just abstracting away the untaint and ->new() steps. So, aside from the sugaring, I'm not sure what benefits this provides. Is there a way to express interrelations between elements? If my instantiation of My::Customer needs the contents of other fields as well, do I have to go back to vanilla CGI?
  3. How hard would it be to have handlers that took on multiple input parameters? For example, to have a single handler that gathers address1, address2, city, state and zip_code and returns a single My::StreetAddress? (This is similar to, but not the same as the previous.)

Still, interesting concept. ++


Replies are listed 'Best First'.
Re^2: RFC: Class::CGI
by Ovid (Cardinal) on Apr 07, 2006 at 23:46 UTC

    This module does not consume the query data. It's merely a subclass of CGI-Simple. As such, it behaves exactly as that class does with the exception noted in the documentation.

    As for your second and third questions, they're closely related. The current implemenation handles existing objects and could easily create new objects for a single field and you'd have to populate the other fields manually. For existing objects that's cumbersome and I don't like it. The only thing which stopped me from implementing that is figuring out the best syntax. One way would be this:

    use Class::CGI handlers => { customer => { handler => 'Class::CGI::Customer', fields => [qw/ first last address1 address2 email city state zip /], } }; my $cgi = Class::CGI->new; my $cust = $cgi->param('customer'); my $zip = $cgi->param('zip');

    With that syntax, the handler would look for a "customer" param and, failing to find one, would attempt to build a new customer object from the fields list. Still, it's a bit clunky.

    One way around that would be to implement another idea I had: "profiles". You would do this:

    use Class::CGI profiles => $profile, use => 'customer';

    With that syntax, $profile would point to a file or class (user's choice) which describes various form profiles and the "use" key would point to a scalar or array ref detailing the profiles to be used for this code. That would put the object profiles in a centralized spot.

    Alternately, one could do this:

    use Class::CGI profiles => $profile, from_param => '__PROFILE__';

    With syntax like that, you specify the location of the profile file or class and the "from_param" lists the name of the param(s) in the form specifying which profile(s) to use. The default would be "__PROFILE__", so even naming it in your code would be optional. Thus, the forms would identify which handlers are needed instead of the code being responsible for it.

    These are all very basic ideas and are not fleshed out, so I just put together a simple proof of concept to see how things would work. More suggestions are welcome.

    Update: D'oh! I'm an idiot! With a very minor change to my existing code, I can make this work very easily. Instead of passing the param value to the handler, I can pass the Class::CGI object to the handler. The handler would have the resposibility of knowing which field(s) it needed. That's the proper place to put this!


    New address of my CGI Course.

Re^2: RFC: Class::CGI
by Ovid (Cardinal) on Apr 08, 2006 at 00:45 UTC

    I've just finished updating the code to handle all of this. Here's an example from the docs:

    package My::Date::Handler; use My::Date; sub new { my ($class, $cgi) = @_; my $month = $cgi->raw_param('month'); my $day = $cgi->raw_param('day'); my $year = $cgi->raw_param('year'); return My::Date->new( month => $month, day => $day, year => $year, ); } 1;

    And in the user's code:

    use Class::CGI handlers => { date => 'My::Date::Handler', }; my $cgi = Class::CGI->new; my $date = $cgi->param('date'); my $day = $date->day;

    It's not uploaded anywhere yet, though. I'll wait for more suggestions. I must say, it's really nice to see how clean design of classes makes extending them so easy :)


    New address of my CGI Course.

      The one thing that aproach does is mean that each class is set in its parameters.. So you couldn't have two of any one type because they wouldn't know which fields to use unless you passed in the actual param that the user put. /me thinks maybe code will help here

      use Class::CGI handlers => { invoice_date => 'My::Date::Handler', sales_date => 'My::Date::Handler', }; my $cgi = Class::CGI->new; my $date = $cgi->param('invoice_date'); my $date = $cgi->param('sales_date'); my $day = $date->day;

      Sense the handler is picking the fields on its own it doesn't know which three fields belong where. Instead of having the handler pick fields maybe allow the user to specify fields to send as arguments.

      use Class::CGI handlers => { invoice_date => ['My::Date::Handler', qw(invoice_month invoice_d +ay invoice_year)], sales_date => ['My::Date::Handler', qw(sales_month sales_day sal +es_year)], }; my $cgi = Class::CGI->new; my $date = $cgi->param('invoice_date'); my $date = $cgi->param('sales_date'); my $day = $date->day;

      The main class could then grab those params and send the values to the handler so your handler would look like the following

      package My::Date::Handler; use My::Date; sub new { my ($class, $cgi, $param) = (shift,shift,shift); #standard my ($month, $day, $year) = (shift,shift,shift); #extra return My::Date->new( month => $month, day => $day, year => $year, ); } 1;

      The new method would then be sent the class, the cgi object, and the name of the handler (i.e. invoice_date, sales_date) followed by the values of any parameters listed in its definition.

      use Class::CGI handlers => { customer_id => 'My::Customer', referral_id => 'My::Customer', sales_date => ['My::Date::Handler', qw(sales_month sales_day sales_year)], }; my $cgi = Class::CGI->new; my $sales_date = $cgi->param('sales_date'); my $customer = $cgi->param('customer_id'); my $referrer = $cgi->param('referrar_id');

      I think you get the best of all worlds this way. You can have generic classes that validate the field and return it (without hardcoding the fields into the handler). You get handlers that can consume multiple fields in cases where they are known before hand (hardcoded in, like customer_id might expect some other fields in order to build the customer object), and handlers that consume multiple fields but can be passed the params they need so they are flexible. Of course maybe i'm seeing a problem where there isn't one, but date validation definitly needs to be able to be used multiple times on the same page with different fields (or sets of fields).

      The module sounds great if you can tie it to HTML::Template, TT and CGI::Application you will have a winner! ;)

      Eric Hodges

        Being able to specify an arrayref so that you can pass user-defined additional parameters is a good idea anyway, but the need for it could be obviated in many cases. The following should be enough:

        use Class::CGI handlers => { invoice_date => 'My::Date::Handler', sales_date => 'My::Date::Handler', };

        This can work if you just pass the parameter name to the handler constructor. Users can then create a convention based on the name and avoid the need for configuration:

        package My::Date::Handler; use Carp; use My::Date; sub new { my $class = shift; my ( $cgi, $param_name ) = @_; $param_name =~ s/_date\z// or croak "Profile name expected to end in '_date': $param_name +"; my %date = map { $_ => scalar $cgi->raw_param( $param_name . '_' . $_ ) } qw( month day year ); return My::Date->new( %date ); } 1;

        Makeshifts last the longest.

        Tying it into CGI::Application wouldn't be a problem. In your CGI::Application base class override cgiapp_get_query():

        package My::CGI::Application; use base 'CGI::Application'; use Class::CGI handlers => { customer_id => 'My::Customer', referral_id => 'My::Customer', sales_date => ['My::Date::Handler', qw(sales_month sales_day sales_year)], }; sub cgiapp_get_query { my $self = shift; return Class::CGI->new; } 1;

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://541984]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (6)
As of 2017-01-22 17:24 GMT
Find Nodes?
    Voting Booth?
    Do you watch meteor showers?

    Results (189 votes). Check out past polls.