Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re: Re: RFC - Parameter Objects

by Ovid (Cardinal)
on May 14, 2003 at 18:58 UTC ( [id://258207]=note: print w/replies, xml ) Need Help??


in reply to Re: RFC - Parameter Objects
in thread RFC - Parameter Objects

I have to admit that I've never cared for overloaded methods that function as both accessors and mutators, but in this case, it might make sense.

The combination of parameters can be dealt with if I pass $self to the subroutines used for validation. Then, each item can check the other values, but this could make things order dependant (untested).

my $param = Sub::ParamObject->new({ foo => qr/\d+/, bar => sub { $_[0]->{foo} > 3 && $_[1] =~ /^this|that$/ } }); $param ->foo(7) ->bar('this'); # succeeds $param ->foo(2) ->bar('this'); # fails

And thanks for the bug catch!

Cheers,
Ovid

New address of my CGI Course.
Silence is Evil (feel free to copy and distribute widely - note copyright text)

Replies are listed 'Best First'.
Re: Re: Re: RFC - Parameter Objects
by Corion (Patriarch) on May 14, 2003 at 19:10 UTC

    While this is nice and convenient for those cases where you have independently valid parameters, it's impossible (or dangerous, you choose) to have pairs (or more) of parameters that are valid together :

    my $ratio = Sub::ParamObject->new({ nominator => qr/^[-+]?\d+$/, denominator => sub { $_[1] =~ qr/^\d+$/ and $_[1] != 0 }, }); # works and is convenient $ratio->nominator(-1); $ratio->denominator(1); my $source = Sub::ParamObject->new({ selector => qr/^filename|url$/, filename => sub {$_->[0]->{selector} eq 'filename' and $_[1] =~ +qr/^\w+$/ }, url => $_->[0]->{selector} eq 'filename' and $_[1] =~ qr!^h +ttps?://!i }, }); # and now ??? # I want to have selector set to one of the values # and (only) the corresponding field set. $source->selector('filename')->filename('foo.txt'); # works $source->filename('foo.txt')->selector('filename'); # cannot in my i +mplementation

    Either you make the approach dependent on the order of parameters (plausible but ugly IMO), or you find another approach to validation (which I would rather welcome, since I did stuff like this years ago and didn't find a good solution to this).

    perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web

      Making the approach dependent on the order in which the mutators is called would be ugly. It would also be bug prone. Forget it just once and you might be wondering why the heck your code is misbehaving.

      Perhaps the contructor could be modified. The current hashref would fall under a key named "params" and a different key named "groups" would list group validations. Then, as soon as an accessor is called, the "group" validations would be checked. This would eliminate the order dependence. Workable?

      Cheers,
      Ovid

      New address of my CGI Course.
      Silence is Evil (feel free to copy and distribute widely - note copyright text)

        Perhaps the contructor could be modified. The current hashref would fall under a key named "params" and a different key named "groups" would list group validations. Then, as soon as an accessor is called, the "group" validations would be checked. This would eliminate the order dependence. Workable?

        This still leaves it up to the person constructing the Params object ot specify what the overall validation function is ... maybe the %params hash is some global that gets re-used by all of the clients -- because if they don't then the method they are passing the Params too will fail when it tries to access things with a certian name -- but they can allways just choose to not pass a "groups" test and circumvent the tests.

        I would recommend approaching this problem form a different direction -- seperate the "definition" of what a valid Param object is for a given context, from the "instantiation" of a particular instance.

        Consider something like this...

        #### in the library code ### Sub::Param::make_new_param_type($param_type_name \%param_definitions, \&validity_function); ... sub some_method { my $param = shift; die "die" unless $param->isValid($param_type_name); ... } sub some_other_method { my $param = shift; die "die" unless $param->isValid($param_type_name); ... } ... ### in some client ... my $p = new Sub::Param($param_type_name); $p->foo(1); $p->bar('Toledo'); SomeLibrary::some_method($p); ...
      The problem here is that your validation code assumes a certain order.
      my $source = Sub::ParamObject->new({ selector => sub { my $self = shift; return unless $_[0] =~ /^(filename|url)$/; return if @$self{qw(filename url)} and !exists $self->{$1}; }, filename => sub { my $self = shift; return (!exists $self->{selector} or $self->{selector} eq 'filename +') and $_[1] =~ qr/^\w+$/; }, url => sub { my $self = shift; return (!exists $self->{selector} or $self->{selector} eq 'url') and $_[1] =~ qr!^https?://!i }, });
      If you squint a bit you'll notice two levels of validation: one is of the value as such, and one is of the dependencies. This can be condensed and clarified by allowing multiple tests:
      my $source = Sub::ParamObject->new({ selector => [ qr/^(filename|url)$/, sub { @{$_[0]}{qw(filename url +)} and !exists $self->{$_[1]} }, ], filename => [ qr/^\w+$/, sub { !exists $_[0]->{filename} or $_[0]- +>{selector} eq 'url' }, ], url => [ qr!^https?://!i, sub { !exists $_[0]->{filename} or $_[0] +->{selector} eq 'url' }, ], });
      Getting the order right however is bound to get tricky for large parameter sets. That's a problem which can only be expressed well with the semantics of a logical programming language such as Prolog or make. The fundamental obstacle with order-dependent validation is that you need to change the validity of things based on that of others. You might try something like this:
      my $source = Sub::ParamObject->new(test => { selector => { value => qr/^(filename|url)$/, }, filename => { value => qr/^\w+$/, }, url => { value => qr!^https?://!i, }, }, if_valid => { selector => sub { $_[0]->add_check($_[1] eq 'url' ? 'filename' : 'url', forbidde +n => sub { return }); }, filename => sub { $_[0]->add_check(selector => need_filename => sub { $_[1] ne ' +url' }); }, url => sub { $_[0]->add_check(selector => need_url => sub { $_[1] ne 'filen +ame' }); }, });
      I used a hash of hashes here so checks can also easily be removed by a ->remove_check(). Even this is likely to become a maze of little tests, all alike, for large parameter sets, though.

      Makeshifts last the longest.

Re: Re: Re: RFC - Parameter Objects
by hossman (Prior) on May 14, 2003 at 21:13 UTC
    That approach wouldn't solve the main case I pointed out where it becomes important to have validation...
    what if none of the set methods are called?
    ...ie: what if someone constructs a Params object, but never sets any values in it? Then there is no validation of any kind.

      You are right, but I think it might be solved. In a response to Corion, I wrote:

      Perhaps the contructor could be modified. The current hashref would fall under a key named "params" and a different key named "groups" would list group validations. Then, as soon as an accessor is called, the "group" validations would be checked. This would eliminate the order dependence. Workable?

      When an accessor is called, the parameters are validated. If a value is never set, we can still validate it when the accessor is called. This is still not foolproof, but I think it's a reasonable scheme. Let me know if you think there are problems with this.

      Cheers,
      Ovid

      New address of my CGI Course.
      Silence is Evil (feel free to copy and distribute widely - note copyright text)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://258207]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (8)
As of 2024-06-14 11:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.