durusmail: qp: ConnectiveSpecOperator: how to know which spec gave out?
ConnectiveSpecOperator: how to know which spec gave out?
2006-02-26
2006-02-27
2006-02-27
2006-02-27
2006-02-27
2006-02-27
2006-02-28
2006-02-28
ConnectiveSpecOperator: how to know which spec gave out?
mario ruggier
2006-02-28
On Feb 28, 2006, at 2:55 PM, David Binger wrote:
> On Feb 27, 2006, at 3:48 PM, mario ruggier wrote:
>
>>         form.add_password('passwd1', title="Password Confirm",
>>             required=False,
>>             hint="Case sensitive. At least 4 chars long. At least 1
>> digit.",
>>             spec=spec(both(
>>                         spec(equalwidget(form, 'passwd2'), 'not
>> equal.'),
>>                         spec(pattern('^\d|^\D+\d'), 'at least 1
>> digit.'),
>>                         spec(length(4, None), 'too short.')),
>>                     'Password is not OK... ', 'Password is OK.'))
>
>
> What would you think of having the widget itself, instead of the spec
> handle the error messages for 1 level AND specifications like this?
> I don't see any easy way to produce nice error messages from
> any compound spec that is deeper.

Yes, I think to me that seems a good way to to about it. And if I get
you right I think it is already what I am doing. In my widget parsing
loop (i.e. as in an expanded version of qp's current widget._parse()
method) I have something like this (where self is the widget being
parsed) :

         if self.spec is not None:
             value, gfs = self._fs['value'], self.spec
             if not (value and match(value, gfs)):
                 self._fs['ok'] = False
                 if isinstance(gfs.spec, ConnectiveSpecOperator):
                     self._fs['message'] = gfs.mer +
gfs.spec.break_spec.mer or ''
                 else:
                     self._fs['message'] = gfs.mer or ''
             else:
                 self._fs['message'] = gfs.mok

where gfs is therefore the wrapped spec instance, that had been
initialised with an error (mer) and an ok (mok) message. The self._fs
is a "field status" dict with four keys: name, value, ok, and message.
It is a dict because this way I can pass it around on the server-side
freely, but can also pass it rather trivially to the client-side, as
response to ajax callbacks from specific fields... re-using my own code
as much as i can.

All the gfs needs to know is which sub-spec gave out... and get its
error message from its wrapped instance (the spec.break_out attr, as
per the modified connectivespec classes i sent earlier).

I would like to repeat that I do not think it is spec's responsibility
to produce nice error messages, but it should bubble up as much
information as it can, to enable applications to construct the messages
as they need.

> What if the widget argument is a list
> of (spec, error) pairs, all of which must be
> satisfied by the value?
>
> form.add_password('passwd1',
>     specs=[(pattern('\d+$'), 'must be digits')
>            (length(4), 'must have length 4')])

Yes, except that in most cases when there will be a spec, it will be
just one. So I just thought to use spec itself for the cases where a
list of specs must be satisfied.

I guess for the generalized case, the shape of what you suggest could
be:

specs = [ (spec, mer), (spec, mer), ..., mer, mok ]

i.e. any number of spec,error pairs, with the last 2 element being an
error and an ok message for the top level spec (only top level needs an
ok message I think). Each sub-spec can be further compounded, for a
hierarchy of compounded specs if necessary (although this should not
ever occur hopefully!).

As I pointed out previously, I have a wrapper class for this. Doing it
with tuples or with a class is more of a stylistic difference though.
It does not change the basic issue of whether I know which sub-spec
gave out.

I do however see your point of doing the "top level spec loop" myself,
as opposed to allowing match() to do it... i.e. the code extract above
could be turned into something like:

         if self.spec is not None:
             value, gfs = self._fs['value'], self.spec
             if isinstance(gfs.spec, ConnectiveSpecOperator):
                 # need to consider also any otherwise compound spec...
                 for subspec in gfs.spec:
                     ... call match, etc.

This can work, but isn't this duplicating some functionality that is
already provided by spec.match()?

mario



reply