From: Lars Rune Nøstdal
Subject: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <1149857240.786812.320460@j55g2000cwa.googlegroups.com>
Hi Lispers,
Ok, starting to get the hang of this, but I'm having a small issue
maybe you guys could help me with.

I'm playing with some AJAX-stuff and .. hm .. not sure how to explain
this proper, but ..

I have an INPUT-element (HTML) that can be used for both input of data
(client-initiated) and output of data (server-initiated), and I'd like
to block the observer so it does not react on input from the client and
tries to re-update the element ..

..or maybe "blocking" the observer isn't right at all, maybe I'm going
about this the wrong way..? Here is some code:


(defobserver value ((value-attribute ValueAttribute))
  (cells::trc "ValueAttribute changing from" old-value :to new-value)
(terpri)
  ;; Update the value-attribute of the HTML-element on the client-side.
  (setAttribute (parent-of value-attribute)
                (name-of value-attribute)
                "value"
                (princ-to-string (value-of value-attribute))))

(defmethod onkeyup ((value-attribute ValueAttribute) (parent Container)
&rest event-args)
  (format t "onkeyup: ~A~%" (fifth event-args))
  (setf (value-of value-attribute)
        (first event-args)))


`onkeyup' is called from the client-side and since (setf value-of ..)
triggers the observer for the slot `value', it tries to "redraw" what's
already on the client-side.

-- 
Lars Rune Nøstdal
http://lars.nostdal.org/

From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <44899E83.8080107@gmail.com>
Lars Rune N�stdal wrote:
 > Hi Lispers,
 > Ok, starting to get the hang of this, but I'm having a small issue
 > maybe you guys could help me with.
 >
 > I'm playing with some AJAX-stuff and .. hm .. not sure how to explain
 > this proper, but ..

Fair warning: I have never programmed a Web app, barely done anything 
with HTML, and understood almost nothing of what you wrote. :) But this 
(check me: input form field initialized from a database record field, 
then overwritten by user so data must flow the other way back to the 
DB?) happens all the time in standalone apps, so...

 >
 > I have an INPUT-element (HTML) that can be used for both input of data
 > (client-initiated) and output of data (server-initiated), and I'd like
 > to block the observer so it does not react on input from the client and
 > tries to re-update the element ..
 >
 > ..or maybe "blocking" the observer isn't right at all, maybe I'm going
 > about this the wrong way..?

As I said, this comes up all the time, and what you tried is the 
tempting automatic flow:

   DB(existing value -> Input(as initial value) -> DB(new value)

Your code is what I think about writing even after ten years of using 
Cells. So how do we break the cycle? There are several ways I have 
handled this.

1. In the slot definition, specify:

        (value ...etc... :unchanged-if 'string=)

You will still make the return trip to set the input field 
unnecessarily, but that will be an NOP because the new value will be 
seen as unchanged, stopping propagation (including calling observers). 
The default test is EQL and it is easy in even simple code for 'string= 
but un-EQL strings to arise and start cycling.

To avoid even the beginnings of a roundtrip, hey, have the observer take 
a peek at where it is about to write and decide if it needs to. Not as 
much fun as letting Cells worry about it, but this one observer (I 
gather) is handling all the HTML input fields, so...?

Now I revert to my earlier disclaimer about being Web-ignorant: I think 
The Right Answer overall will be decided by the exact nature of the Web 
stuff, so I will just continue dumping alternatives

2. Does the DB flow to the form only when the form is opened? If so, the 
HTML input fields can have a something another Cells user just 
requested: a rule that runs once at initialization time and then becomes 
a /Cells/ input. ie, the slot starts as c-formula but then becomes 
c-input. (No dependencies are recorded during the initial rule 
invocation.) Only the HTML input field has an observer writing back to 
the corresponding DB field. Now if only cells had some frickin decent 
doc I could tell you the syntax. Maybe:

      (c-formula (:inputp t)
         <code to lookup value for this field>)

If that is what you settle on and it does not work I will research further.

3. if you are in a multi-user environment or for any other reason need 
to update the form programmatically at the same time the user is typing, 
well, things will be pretty exciting for the user <g>, and it may be 
time to consider the failsafe: do not try to use Cells dataflow where 
the application really needs to decide the dataflow. An application flow 
will have to be concocted, hopefully facilitated by Cells, but always 
keep in mind that the functionality might dictate more savvy than the 
Cells changed/not-changed propagation smarts.

hth, ken

 > Here is some code:
 >
 >
 > (defobserver value ((value-attribute ValueAttribute))
 >   (cells::trc "ValueAttribute changing from" old-value :to new-value)
 > (terpri)
 >   ;; Update the value-attribute of the HTML-element on the client-side.
 >   (setAttribute (parent-of value-attribute)
 >                 (name-of value-attribute)
 >                 "value"
 >                 (princ-to-string (value-of value-attribute))))
 >
 > (defmethod onkeyup ((value-attribute ValueAttribute) (parent Container)
 > &rest event-args)
 >   (format t "onkeyup: ~A~%" (fifth event-args))
 >   (setf (value-of value-attribute)
 >         (first event-args)))
 >
 >
 > `onkeyup' is called from the client-side and since (setf value-of ..)
 > triggers the observer for the slot `value', it tries to "redraw" what's
 > already on the client-side.
 >

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Beaming husband to scowling life, New Yorker cartoon
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <4489AFBE.2020509@gmail.com>
Ken Tilton wrote:
> Lars Rune N�stdal wrote:
>  > Hi Lispers,
>  > Ok, starting to get the hang of this, but I'm having a small issue
>  > maybe you guys could help me with.
>  >
>  > I'm playing with some AJAX-stuff and .. hm .. not sure how to explain
>  > this proper, but ..
> 
> Fair warning: I have never programmed a Web app, barely done anything 
> with HTML, and understood almost nothing of what you wrote. :) But this 
> (check me: input form field initialized from a database record field, 
> then overwritten by user so data must flow the other way back to the 
> DB?) happens all the time in standalone apps, so...
> 
>  >
>  > I have an INPUT-element (HTML) that can be used for both input of data
>  > (client-initiated) and output of data (server-initiated), and I'd like
>  > to block the observer so it does not react on input from the client and
>  > tries to re-update the element ..
>  >
>  > ..or maybe "blocking" the observer isn't right at all, maybe I'm going
>  > about this the wrong way..?
> 
> As I said, this comes up all the time, and what you tried is the 
> tempting automatic flow:
> 
>   DB(existing value -> Input(as initial value) -> DB(new value)
> 
> Your code is what I think about writing even after ten years of using 
> Cells. So how do we break the cycle? There are several ways I have 
> handled this.
> 
> 1. In the slot definition, specify:
> 
>        (value ...etc... :unchanged-if 'string=)
> 
> You will still make the return trip to set the input field 
> unnecessarily, but that will be an NOP because the new value will be 
> seen as unchanged, stopping propagation (including calling observers). 
> The default test is EQL and it is easy in even simple code for 'string= 
> but un-EQL strings to arise and start cycling.
> 
> To avoid even the beginnings of a roundtrip, hey, have the observer take 
> a peek at where it is about to write and decide if it needs to. Not as 
> much fun as letting Cells worry about it, but this one observer (I 
> gather) is handling all the HTML input fields, so...?
> 
> Now I revert to my earlier disclaimer about being Web-ignorant: I think 
> The Right Answer overall will be decided by the exact nature of the Web 
> stuff, so I will just continue dumping alternatives
> 
> 2. Does the DB flow to the form only when the form is opened? If so, the 
> HTML input fields can have a something another Cells user just 
> requested: a rule that runs once at initialization time and then becomes 
> a /Cells/ input. ie, the slot starts as c-formula but then becomes 
> c-input. (No dependencies are recorded during the initial rule 
> invocation.) Only the HTML input field has an observer writing back to 
> the corresponding DB field. Now if only cells had some frickin decent 
> doc I could tell you the syntax. Maybe:
> 
>      (c-formula (:inputp t)
>         <code to lookup value for this field>)

Hmm, Cells needed a patch, in constructors.lisp:

(defmacro c-formula ((&rest keys &key lazy &allow-other-keys) &body forms)
   (assert (member lazy '(nil t :once-asked :until-asked :always)))
   `(make-c-dependent
     :code ',forms
     :value-state :unevaluated
     :rule (c-lambda ,@forms)
     ,@keys))

The &allow-other-keys is new. Then this should work (after fixing the 
line breaks -- or just get from CVS in two minutes):

(defpackage #:tu-rule-once-then-input (:use :cl :utils-kt :cells :tu-cells))
(in-package #:tu-rule-once-then-input)


#|

Often in interactive applications one needs to do interesting things to 
come up
with an initial value for a field which then is to be edited by a user, or
for some other reason regularly fed as a C-INPUT.

|#

(defvar *db-entry*)

(defun get-age (id)
   (bwhen (props (cdr (assoc id *db-entry* :test 'string=)))
     (getf props :age)))

(defmodel kenny-view ()
   ((age :accessor age :initform (c-formula (:inputp t)
                                   (- (get-age "555-55-5555")
                                     (^grecian-formula-amt))))
    (grecian-formula-amt :accessor grecian-formula-amt
       :initform (c-in 5))))

(defobserver age ((self kenny-view))
   (setf (getf (cdr (assoc "555-55-5555" *db-entry* :test 'string=)) 
:age) new-value))

#+test
(let ((*db-entry* (copy-list '(("555-55-5555" . (:name "ken" :age 54))
                ("666-66-6666" . (:name "satan" :age 
most-positive-fixnum))))))
   (cells-reset)
   (let ((kv (make-instance 'kenny-view)))
     (print `(:age-init ,(age kv)))
     (assert (= 49 (age kv)))

     (incf (grecian-formula-amt kv) 10) ;; try looking younger
     (assert (= 15 (grecian-formula-amt kv)))

     (assert (= 49 (age kv))) ;; unchanged -- the age rule is gone

     (print `(:happy-birthday ,(incf (age kv))))
     (assert (= 50 (age kv)(get-age "555-55-5555")))
     ;
     ; just showin' off...
     (assert (= 51 (1+ (age kv))(incf (age kv))(get-age "555-55-5555")))))
From: Lars Rune Nøstdal
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <1149902882.741366.325760@u72g2000cwu.googlegroups.com>
i'll have to take a look at this sometime tomorrow (or maybe even
sunday .. *shrug*) - because weekend, sunny weather and beer has
knocked me out for now

oh, what the heck  - just a quick note (i haven't read all of your
replies proper yet), but i'll just add that maybe mentioning this as
"web" or "HTML" is irrelevant, and one should just consider this as a
regular entry-widget like GtkEntry ..  one can both read data from this
widget and send data to it, and that is what i'm doing .. so the text
in the entry-widget can change in two ways:

* by the user pressing a key causing the text to change
* by something on the server changing the text in it

now, when something on the server changes the slot containing the text
of the widget, i'd like something observe when this happens so that i
can update the text on the client-side also

but, the other way around, when something on the client changes the
text (the user presses a key) i'd still like the slot containing the
text of the widget to be updated and also any cells depending on this
ruled cell/slot, of course - but the observer should not update the
text on the client-side; because it already contains the current data
and might even contain newer data as a _new_ event (data) has been sent
inbetween the time of the original event, and reception of the response
to that event

ie .. using `:unchanged-if #'string=' will not work here, because the
data _is_ actually changed, but the change should not propagate back to
the widget which caused the change ..

ok, i might be ramblin' here; i should get back to this tomorrow or
sunday

-- 
Lars Rune Nøstdal
http://lars.nostdal.org/
From: Lars Rune Nøstdal
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <1149903995.365097.7690@f6g2000cwb.googlegroups.com>
btw. .. this migth not work, but here it is:
http://nostdal.org:6123/hw/

html-template:

http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/templates/hello-world.txt

server-application-code (large parts of it is still very, very messy):

http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/hello-world.lisp

you should be able see what i'm trying to describe actually happening
here (check "AJAX debug output"):

 updateElements, response_text:
'[["setAttribute","entry-a",["value","1"]],["setElement","sum","44"]]'
 sndReq: '[["handleWidgetEvent", "entry-a", "onkeyup", "1", "false",
"false", "false", "1"]]'

"sndReq .." means user has pressed a key (the key "1")
"updateElements .." both updates the elements (or widgets) `entry-a'
and `sum', even though `entry-a' should not be updated

-- 
Lars Rune Nøstdal
http://lars.nostdal.org/
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <%1rig.51$qx6.24@fe09.lga>
Lars Rune N�stdal wrote:
> btw. .. this migth not work, but here it is:
> http://nostdal.org:6123/hw/
> 
> html-template:
> 
> http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/templates/hello-world.txt
> 
> server-application-code (large parts of it is still very, very messy):
> 
> http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/hello-world.lisp
> 
> you should be able see what i'm trying to describe actually happening
> here (check "AJAX debug output"):
> 
>  updateElements, response_text:
> '[["setAttribute","entry-a",["value","1"]],["setElement","sum","44"]]'
>  sndReq: '[["handleWidgetEvent", "entry-a", "onkeyup", "1", "false",
> "false", "false", "1"]]'
> 
> "sndReq .." means user has pressed a key (the key "1")
> "updateElements .." both updates the elements (or widgets) `entry-a'
> and `sum', even though `entry-a' should not be updated
> 

<g> You did not tell me what event triggered what you described (I guess 
you selected all and then typed "1" in entry-a?); you did not tell me 
that the log shows newer entries at the top; and this is not using 
Cells, so I will guess: you want to know if Cells can do better? You 
also did not demonstrate input fields being set other than to initialize 
them -- after then all data flows from user entry. So...

... now I am completely lost. What I do understand I think is handled by 
what I have offered in earlier articles (the unchanged-if bit as well as 
the formula-input hybrid Cell) so I do not think I do understand.

But it was fun seeing a Lisp web app. I really have not played with this 
stuff. Thx! :) So this AJAX stuff simulates a classic event-driven GUI 
framework? Something like Cells-Gtk or early Celtk would be a good model 
for what realistically would be a serious (2-3 week?) Cells project to 
wrap the CL Web app framework of choice (open source ACL Web Objects?) 
with a thin Cell-savvy framework. The Cell-savvy framework pretends it 
is the browser. It is not, of course, but it sends things to the real 
browser (only as needed!) and other glue pipes AJAX events (if I have 
that right) into the "faux browser". Then it all Just Works.

Keep talking, I am tempted to do it myself. High time I got into the Web 
app game.

kenny

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <e2qig.273$uk5.225@fe12.lga>
Lars Rune N�stdal wrote:
> i'll have to take a look at this sometime tomorrow (or maybe even
> sunday .. *shrug*) - because weekend, sunny weather and beer has
> knocked me out for now
> 
> oh, what the heck  - just a quick note (i haven't read all of your
> replies proper yet), but i'll just add that maybe mentioning this as
> "web" or "HTML" is irrelevant, and one should just consider this as a
> regular entry-widget like GtkEntry ..  one can both read data from this
> widget and send data to it, and that is what i'm doing .. so the text
> in the entry-widget can change in two ways:
> 
> * by the user pressing a key causing the text to change
> * by something on the server changing the text in it

I understand. And we do not need client server speak, right? I can have 
a subprocess or separate process on my system writing to the DB at the 
same time I am editing a DB entry using a GUI app, yes?

> 
> now, when something on the server changes the slot containing the text
> of the widget, i'd like something observe when this happens so that i
> can update the text on the client-side also
> 
> but, the other way around, when something on the client changes the
> text (the user presses a key) i'd still like the slot containing the
> text of the widget to be updated and also any cells depending on this
> ruled cell/slot, of course -

yep.


> but the observer should not update the
> text on the client-side; because it already contains the current data

So far "unchanged-if" works, but...

> and might even contain newer data as a _new_ event (data) has been sent
> inbetween the time of the original event, and reception of the response
> to that event

well, you lost me there. Let's slow down a little.

I start editing my personnel file. The DB shows "Kenneth" as my first 
name when I open the file. The record becomes a person-record instance, 
person-record being a cells-driven class.

Your first thought is an observer on the person-record first-name 
attribute to setf the corresponding field of some person-view instance. 
person record may not have a named attribute for first-name, it may have 
a "fieldname-value-pairs" attribute. No matter, I think.

Now, using my GUI app, I move the keyboard focus to the first-name field 
and start typing. Like any good GUI, it preselects all the text. When I 
hit "K" to simply type in "Kenny", my new name, the GUI subsitutes K for 
all of Kenneth. Do you want the text "K" now propagated to the 
person-record? How about written to the persistent store so others will 
see the change? or (for each of those) is there a discrete action to 
move the data out of the form and then commit to persistent store?

Moving on...

> 
> ie .. using `:unchanged-if #'string=' will not work here, because the
> data _is_ actually changed, 

Where? In the view? How? because I am still typing? Changed in the DB 
because some other process wrote to the DB?

> but the change should not propagate back to
> the widget which caused the change ..
> 
> ok, i might be ramblin' here; i should get back to this tomorrow or
> sunday
> 

yeah, i am lost. I now see updates coming in a stream to the DB, a user 
flailing madly away at a keyboard warding off incoming data like Yoda 
with a light saber, and poor little Cells trying to negotiate an 
acceptable outcome.

btw, I /have/ done this sort of thing. Great fun, but one does have to 
have clear precisely what goes where and when. The worst case scenario 
seems to be a field update arriving while the user is typing an update 
to that field. But I am not sure, and on this end the John Powers is not 
helping. :)

ke...

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon
From: Lars Rune Nøstdal
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <1149949117.509537.241970@u72g2000cwu.googlegroups.com>
Ken Tilton wrote:
> To avoid even the beginnings of a roundtrip, hey, have the observer take
> a peek at where it is about to write and decide if it needs to. Not as
> much fun as letting Cells worry about it, but this one observer (I
> gather) is handling all the HTML input fields, so...?

yes, this is almost what i'm looking for .. but instead of looking
where it is about to _write_, i'm interested in where the data _came
from_

maybe a quick solution would be to "tag" a cell .. when setf'ing a
cell, can i somehow tag it with something that will stick on in while
it's sent through the cell-network (i'm not sure how this stuff is
implemented)?   i could then tag it with `:from-client' so the
observer-code will later know that it should not redraw the widget on
the client-side .. i'm not sure this is the "right way" or whatever -
it might be simple to implement though .. *browses cells-internals*

in any case..we might be talking past each other; because i do not
understand everything you're telling me..

-- 
Lars Rune Nøstdal
http://lars.nostdal.org/
From: Lars Rune Nøstdal
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <1149950942.344040.68400@u72g2000cwu.googlegroups.com>
Lars Rune Nøstdal wrote:
> maybe a quick solution would be to "tag" a cell .. when setf'ing a

i meant tagging a value
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date: 
Message-ID: <MoDig.84$qx6.68@fe09.lga>
Lars Rune N�stdal wrote:
> Ken Tilton wrote:
> 
>>To avoid even the beginnings of a roundtrip, hey, have the observer take
>>a peek at where it is about to write and decide if it needs to. Not as
>>much fun as letting Cells worry about it, but this one observer (I
>>gather) is handling all the HTML input fields, so...?
> 
> 
> yes, this is almost what i'm looking for .. but instead of looking
> where it is about to _write_, i'm interested in where the data _came
> from_
> 
> maybe a quick solution would be to "tag" a cell .. when setf'ing a
> cell, can i somehow tag it with something that will stick on in while
> it's sent through the cell-network (i'm not sure how this stuff is
> implemented)?   i could then tag it with `:from-client' so the
> observer-code will later know that it should not redraw the widget on
> the client-side .. i'm not sure this is the "right way" or whatever -
> it might be simple to implement though .. *browses cells-internals*
> 
> in any case..we might be talking past each other; because i do not
> understand everything you're telling me..
> 

Yes, and it goes both ways since I do not fully understand the tools you 
are using -- but we will converge on a solution, i think.

If you are just worried about unnecessary trips to the client because of 
the obvious waste of bandwidth, then I think the 'unchanged-if' bit will 
work fine. There /is/ an unnecessary visit, but it all happens on the 
server where a "client" instance is playing the part of the real client.

This is like Celtk or Cells-Gtk, where CLOS widgets mirror one-for-one 
widgets in the C framework. First, the CLOS widget at make-instance time 
tells, say, Tk to make a widget. Then, as slots of the CLOS widget 
change (accomplishing nothing on screen) observers on each field issue 
Tk "configure" commands for the mirrored slot (again, one CLOS slot per 
Tk configuration option).

I get a kick out of this because Cells is performing two distinct, hefty 
jobs here. First, the usual Cells thang of supporting the declarative 
style, propagating state around the application model. Second, it is 
propagating state to and from (by mechanisms not yet discussed) Tk, 
keeping the CLOS mirror-model in synch with the Tk model actually 
constituting the GUI.

Your idea of tagging the values is reasonable if nothing else will work, 
but then the advantage of Cells starts to fade -- I hate for 
productivity tools to make me work harder. :)

Getting back to your project: I think what might be ging on here is that 
your design thinking is like the original LTk design. That does have 
mirror classes, but not mirror slots. An apparent setf of the mirror 
slot actually talked directly to Tk and configured the C instance. 
Reading such an apparent slot on the Lisp side /each time/ went across 
to TK to ask what value the C instance had for that configuration 
option. ie, LTk instances were nicely lightweight. But when I did 
Cells-Ltk (Celtk), I knew I wanted to do the usual Cells thang on the 
Lisp side and just drive Tk as a slave to handle the GUI for me.

With my approach, things happen very fast in the Tk mirror-world I have 
in Lisp, so something like an unnecessary roundtrip (halted when the 
'unchanged' test returns t) is not worth bothering about.

I am quite interested in doing this with Ajax, could be another killer 
app for Cells. need a snappy name, tho. Carjax? Have to justify that R 
somehow.

kzo



-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon