[cgiapp] handling data and control flow better (longish)

Ron Savage ron at savage.net.au
Mon Jun 8 20:49:56 EDT 2009


Hi David

On Mon, 2009-06-08 at 10:23 -0400, David N. Blank-Edelman wrote:
> Hi Ron-
>    Thanks so much for writing. I always appreciate seeing other  
> people's code. I have to admit that I'm a bit puzzled as to how my  
> questions and your answers mesh. I'm sure this is just my lack of  
> understanding. Let me see if I can repeat back your answer in a  
> different way so you can see where the disconnect lies.
> 
> To the question of how to deal with several sets of data context (CGI  
> query, stuff stored in a session, etc), you seemed to reply "REST". I  

No. I was just warning you, and any other readers, that the sub name I
chose, add_note, may strike you as better written as note_add, if you
thought REST was the only way to do things. Also, if I find time to
rewrite this code, I will change the name myself, which would in turn
(perhaps) need explaining if anyone cared. But it's no big deal.

> thought I had a pretty decent handle of REST interfaces, but I don't  
> see how they come into play for me in this case. In my case I'm not  
> necessarily operating on specific objects like you were (contacts in  
> an address book). I think of rest as coming into play when one wants  
> to interact with objects like contacts, usually in a more random  
> access way.  That differs from my mental model for my app in that I  
> see mine as more of moving through a sequence of cumulative steps (/ 
> pages). Even if I were to think of it as moving around in a state  
> machine, I still can't conceive of how I would shoehorn it into a REST  
> frame of mind (the S in REST nonwithstanding). But even if I could,  
> how does this model reduce the multiple sets of data problem?

It doesn't. See below.

> [Aside #1: I did look at using C::A::P::Stash, ala Catalyst, but it  
> didn't seem like it would help much]

I agree. It involves copying data but I don't see how that's a gain of
any sort. The objects holding the data in the first place are not
defective, so why copy anything?

And these objects belong to different classes for good reason, so just
sit back and let them do what they do best.

> To the question of "how do I deal with the complex control flow/data  
> handling introduced by validation", part of the answer seems to be to  
> use Data::FormValidator vs. C::A::P::ValidateRM. You seem to avoid the  

No. I'm just saying /I/ use Data::FV directly, and have no problem doing
so. That is, I do not see the value in adding another layer in there. So
I'm suggesting that you don't need it either.

> "run_mode_1 leads to run_mode_2 leads to run_mode_3, oh wait, I have  
> to go back to run_mode_2 because the input run_mode_3 got wasn't  
> valid" control flow.

What I'm saying is that I have structured my code so as to avoid this
'have to go back' scenario. So, I'm suggesting your code can be
restructured to avoid it also.

When my code begins running, e.g. via the add_note run mode/sub, it
stays in that run mode until output is sent to the user. Along the way,
various subs are called, and in some cases these subs can be called by
other run modes, but that does not matter.

This makes me think your run modes are linked together much too tightly
(when you talk about 1 leads to 2 leads to 3). Perhaps this linkage is
in your head, or perhaps it's built into the code.

When my user selects something on the screen and submits a choice, I
don't care what the last run mode was [1], and I regard their selection
as specifying precisely 1 run mode.

So, again, it doesn't worry me how many subs I have to call to handle
their choice, the flow of control is simply handling 1 run mode.

In fact, every such user-driven submission is just 1 run mode from my
point of view. I never change run modes within the app.

[1] I just had another look at the main-line app, and I don't store the
current run mode in the session, so the next entry/instantiation never
has to worry about what the previous run mode was.

> But I'm not clear how much of that avoidance comes from your  
> application not needing to backtrack with all previous state intact if  
> something goes wrong. Can I check if I understand something vis a vis  
> your recovery from error? I see you constructing an error-full  
> template to return from report_add, but I don't understand how that  
> gets the user back to add_note in such a fashion that they could  
> correct their mistake and continue on from there. Are you basically  
> resetting your notion of state at that point, such that when the user  
> comes back into add_note from the error'd template he or she does so  
> with no previous state saved? In my example, where I'm moving from  
> cumulative step to step I'm not clear I could do the same.

Actually, I admit (finally :-)) I did not address your original concern
about the variety of places where data is stashed. And yes we all have
the same sorts of issues to face.

But, as I see it, each location in which data resides, is holding a
different type of data. So, I just leave it there until I need it.

And 'need it' does not mean needing data in some generic sense, but
means needing a specific piece of data, and being specific means it'll
be residing in a specific stash, so that's where I go to to get it.

So, this explains why I don't bother with C::A::P::Stash, since putting
disparate bits of data in the same place confuses this issue IMHO, even
if others (Catalyst users perhaps) see things differently.

So, to start the explanation again:

o The user's submission is 1 set of data.

o That data will contain a session id, or cause one to be generated, so
that that id ties the current submission (data) to any previously saved
data now in the session object.

o That submission triggers calls to retrieve specific data from the
database (to handle the current run mode), and this db data is tied to
the submission data and to the session data.

o This is no more that what you've said, right? But this situation
should not be regarded as a problem, it's just how things are. I don't
see how you can chop any one of those 3 sets of data. Each contributes
(differently) to the work being done at any point in the lifetime of the
current run mode.

As I see it:

(a) The user's submitted data is the most transient, i.e. has the
shortest lifetime. It is not connected with memory in any way [2]. It
just hits the deck at the beginning of the run mode's lifetime, and the
run mode must deal with it.

The loss of this data, at the end of the current instantiation, does not
matter. If the data is important, a copy [2] of it will have been saved
in long-term memory (the db) as part of the current run mode's work.

[2] It's a copy which is stored in memory.

(b) The session data /is/ (medium-term) memory. It ties this current
instantiation with the previous one and the next one. So it will hold
just enough data to solve the problem of how to make that tie work for
the benefit of the user, i.e. so the current run mode's work is
meaningful.

It is user-specific but not permanent. It lasts for N instantiations and
is then destroyed. The loss of this data does not matter.

User-specific, /permanent/, data would be things like the user's
password, and all such data must fall into the next category.

(c) The db data is also memory, and is the longest lasting of the 3.
It is project-specific and permanent. It out-lasts the user's N
instantiations, in that it existed before their 1st run mode (except
when the user adds data) and will persist after their Nth. It's loss
would matter, except of course for natural deletes of obsolete data.

HTH.

-- 
Ron Savage
ron at savage.net.au
http://savage.net.au/index.html




More information about the cgiapp mailing list