Glad to see I have been using best practices... I am in complete agreement about form field names, and I like the added benefit that they're obscured.
One area where the continuations are very useful is in multipage forms. I like the idea of being able to, for example, stop halfway through a multipage survey and come back to it later. But for this, the continuation has to be a little more permanent. Is there any facility built into Lift for this? I played around with Seaside briefly until I couldn't stand the Squeak IDE anymore (one seriously ugly application -- looks like it was designed by Mickey Mouse). But I thought that the continuations were persistent. Years and years ago I used to work with Apache Cocoon, which was my first exposure to continuations (I'm such a newbie), and again, you could stop and restart a process at any time. Chas. David Pollak wrote: > > > On Tue, Mar 24, 2009 at 11:02 AM, Charles F. Munat <[email protected] > <mailto:[email protected]>> wrote: > > > I remain confused by a lot of this stuff. > > Here is an example: > > I have a site that creates pages dynamically based on information in a > database. A good example is bio pages. Users add their bios, and then > the bios are available through a URL. > > Now if I use, for example, the user's ID in the URL, thus: > > members/666 > > Then it is a simple matter of a lookup in the database and spit out the > code. The page is easily bookmarked and returned to. It's even > reasonably human readable (members/beelzebub would be even better). I > can do some simple things to prevent scripting attacks. I've used this > system for many years and have never had a problem. > > If I use a closure instead I end up with something like this: > > members/?F92019795619530=_ > > Can't be bookmarked. Not easily human readable. Expires. For me, this is > essentially unworkable. > > I see the benefit to continuations, but they don't seem like the best > solution in every instance. Even in a simple CRUD app, I like being able > to bookmark the edit page for an item and come back to it. Where I tend > to use continuations is for deletes, or for pages I specifically want to > expire. > > So what is the best practice here, and why? How can I create my dynamic > member pages and make them bookmarkable and non-expiring? Is there a > Lift way that I'm missing? > > > No. You got the balance just right. For stuff that you bookmark and > things that are stable, use stable identifiers. That's totally cool. > But when you're building a page with a form, you should worry about the > name of the fields on the form. You should only have to worry about > what happens when the form is submitted. But the user never bookmarks > the form field. The user never looks at the form (unless they're a > hyper-geek). > > Bright line: for stuff that's intended for bookmarking or for stuff that > conveys meaning to a human, generate the stable identifier yourself and > deal with the HTTP request appropriately (e.g., a rewrite rule.) For > the plumbing and stuff that's related to sessions, let Lift generate > GUIDs and associate them with functions. > > Cool? > > > > > Chas. > > marius d. wrote: > > > > > > On Mar 24, 6:53 pm, Alex <[email protected] > <mailto:[email protected]>> wrote: > >> Perhaps this debate has been had before but I didn't see it. I > have a > >> feeling this might be contentious. > >> > >> Doesn't this violate the basic tenet that you should not use server > >> side state when you don't have to? > > > > No. As long as you are binding a function we are talking about state. > > > >> There is no reason for instance > >> when someone is submitting a news group posting to bind an empty > >> posting in a closure and store it on the server, thereby > breaking the > >> application in any number of cases: > >> > >> 1) The session times out because the user got distracted or lost > their > >> connection temporarily > >> 2) The user abuses the back button > >> 3) The server fails over (assuming there is no memory replication) > >> 4) There is a temporary connection failure and the user reloads > >> 5) The server is restarted > > > > I don't see how that would break the application. The function > mapping > > is cleared is the session times out or it is terminated. Furthermore > > Lift has a garbage collection mechanism that removes the functions > > that are not utilized. > > None of the above cases breaks a Lift application ... if the > > application itself is correctly designed and implemented. If we are > > talking about clustered applications Lift currently uses sticky > > sessions mechanism meaning that all requests pertaining to a given > > session are processed by the same node. This can be easily ensured by > > load balancing rules. If a node breaks part of the conversation state > > can be ensured by application design when processed by a different > > node ...of course in the context of a different HttpSession. > > > >> All of the above cases are recoverable with what, IMO, are properly > >> designed frameworks that minimize server-side state. Those are the > >> things that taught us state should be stored in the client whenever > >> possible. > > > > Yes and no. I agree with state minimization but adding burden to > > client side doesn't solve much in many cases. With Lift and Scala we > > are leveraging functions and partial functions to process > requests. If > > you want that bad purely stateless handling you can also do that with > > Lift. Please see LiftRules.statelessDispatchTable. > > > >> I saw a bit of a discussion distinguishing REST from human > >> interaction, saying human interactions are not stateless, but that > >> does not excuse the application being so vulnerable to breakage. > > > > I still don't get where your vulnerability comes from. > > > >> This reminds me of why I dislike many ASP applications I've used - > >> they always break when users don't behave well or when some kicks a > >> server - and users, myself included, always misbehave. I use > the back > >> button when I shouldn't, I leave forms half filled out when I answer > >> the phone, and as a frequent traveler I tend to use web applications > >> from places with shoddy internet. I also don't like to lose my work > >> when a server fails over or is restarted. > > > > With a bit of care for design this can be taken care. For instance if > > you have an Ajax form and the request fails, Lift will try to resend > > that ... if the retry request qoes to a new node then we are talking > > about the application design if the application has enough contextual > > information to handle that or not. Such as in forms that do not > > require logged in users (i.e. posting a comment on a blog or forum as > > anonymous) the application can safely process the form request in the > > context of a new HttpSession/LiftSession. > > > >> There may be a few gains from designing things the lift way - > >> simplicity, security, etc - but they seem to come at too great a > cost. > > > > What is this "great cost" ... one can write poor designed > applications > > in any language or framework on this Earth (dunno about other > > planets). A framework can only do so much but it does not guarantee > > flawless applications. > > > >> There maybe some solutions to this, such as serialization of > this data > >> to the client, but I'm not sure I like any of them. > > > > That would be a performance killer. > > > >> -Alex > >> > >> On Mar 24, 2:46 am, "marius d." <[email protected] > <mailto:[email protected]>> wrote: > >> > >>> The foo reference is captured in checkAndSave which is saved on > >>> LiftSession in function mapping. > >>> Br's, > >>> Marius > >>> On Mar 24, 10:23 am, Alex <[email protected] > <mailto:[email protected]>> wrote: > >>>> If I have this in a (not Stateful) Snippet, is the checkAndSave > >>>> closure saving state on the server? Where is the reference to foo > >>>> saved? > >>>> def add(form: NodeSeq) : NodeSeq = { > >>>> val foo = Foo.create > >>>> def checkAndSave(): Unit = foo.validate match { > >>>> case Nil => foo.save ; S.notice("Started foo: " + foo.body) > >>>> case xs => S.error(xs) ; S.mapSnippet("Foo.add", doBind) > >>>> } > >>>> def doBind(form: NodeSeq) = > >>>> bind("foo", form, > >>>> "body" -> foo.body.toForm, > >>>> "submit" -> submit("Submit", checkAndSave)) > >>>> doBind(form) > >>>> } > > > > > > > > > -- > Lift, the simply functional web framework http://liftweb.net > Beginning Scala http://www.apress.com/book/view/1430219890 > Follow me: http://twitter.com/dpp > Git some: http://github.com/dpp > > > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Lift" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~----------~----~----~----~------~----~------~--~---
