I've just started to use nevow i18n and I'm pretty happy with it, it works very well and the automatic detection of browser language is invisible.
But my app uses livepage a lot, and I began to face problems when trying to translate strings in "dynamic renderers". After some investigations, I found my problem : i18n uses the i18nconfig saved in context to translate strings, but livepage.ClientHandle makes a new "fake" context for each calls via the livepage.flt function.
I resolved my problem by creating my ClientHandle and ClientHandleFactory implementations, the ClientHandle saving the context at its creation (pretty easy, but I would have found it cleaner if clientHandleFactory was a class variable of LivePage, Input and Output, and not a module global). Thus, the i18nconfig remains in context.
My question is : is this choice good, or is there a good reason for ClientHandle not to keep track of the context ? If not, wouldn't it be better to save context in ClientHandle (it would useful for others things, getting session for example) ?
Thomas HERVE wrote:
I resolved my problem by creating my ClientHandle and ClientHandleFactory implementations, the ClientHandle saving the context at its creation (pretty easy, but I would have found it cleaner if clientHandleFactory was a class variable of LivePage, Input and Output, and not a module global). Thus, the i18nconfig remains in context.
My question is : is this choice good, or is there a good reason for ClientHandle not to keep track of the context ? If not, wouldn't it be better to save context in ClientHandle (it would useful for others things, getting session for example) ?
Or you can use closures and have the outer context stored in the closure hence you will be able to use it in the handler without requiring anything from the livepage.flt context
On May 30, 2005, at 2:17 PM, Thomas HERVE wrote:
I've just started to use nevow i18n and I'm pretty happy with it, it works very well and the automatic detection of browser language is invisible.
But my app uses livepage a lot, and I began to face problems when trying to translate strings in "dynamic renderers". After some investigations, I found my problem : i18n uses the i18nconfig saved in context to translate strings, but livepage.ClientHandle makes a new "fake" context for each calls via the livepage.flt function.
I resolved my problem by creating my ClientHandle and ClientHandleFactory implementations, the ClientHandle saving the context at its creation (pretty easy, but I would have found it cleaner if clientHandleFactory was a class variable of LivePage, Input and Output, and not a module global). Thus, the i18nconfig remains in context.
My question is : is this choice good, or is there a good reason for ClientHandle not to keep track of the context ? If not, wouldn't it be better to save context in ClientHandle (it would useful for others things, getting session for example) ?
You should take a look at the branches/dp/livepage-completion- notification-3 branch. It includes a lot of refactoring to livepage, one of which causes all of the livepage apis to take a JavascriptContext object instead of a ClientHandle. To use the ClientHandle, you'll have to get it out of the context, like you would other objects: IClientHandle(ctx).
This context is hooked up to the context chain from the outputConduit or inputConduit HTTP request, so it should be much more useful to you. For example, render_ methods should work properly because nevow'll be able to find the IRendererFactory (your Page instance, usually).
This branch is not yet fully backwards compatible with the previous livepage API. sendScript, call, set, and append are not present as methods of ClientHandle. Instead, there is a single ClientHandle.send method which takes any stan which can be rendered down to javascript. For example, IClientHandle(ctx).send(js("any javascript here")) will work, as will client.send(livepage.set('foo', 'bar')). I will probably add the ClientHandle convenience APIs back soon. They will just call send with the appropriate "stan for javascript" object.
In this branch, input handlers can also return javascript which will be executed as the response to the input event.
I encourage anyone who is interested in livepage to take a look at this branch to see where it is going. Take a look at the examples to see what has changed, read the code, and experiment with it. Ask questions if you have any. I'll be trying to merge this branch into the mainline (after adding some backwards compatibility) in the next week.
I'll also be adding some LivePage docs. I think the API will settle down after this last round of changes.
dp
Quoting Donovan Preston dp@ulaluma.com:
You should take a look at the branches/dp/livepage-completion- notification-3 branch. It includes a lot of refactoring to livepage, one of which causes all of the livepage apis to take a JavascriptContext object instead of a ClientHandle. To use the ClientHandle, you'll have to get it out of the context, like you would other objects: IClientHandle(ctx).
Thanks a lot for this pointer, it looks really promising. I like the unification of renders with the context as argument, even if it makes write another line to get client handle.
I'll give it a try as soon as possible for feedback.
On 5/31/05, Donovan Preston dp@ulaluma.com wrote:
In this branch, input handlers can also return javascript which will be executed as the response to the input event.
What do you mean? I don't understand... Are you talking about javascript that will be executed without round-trip to server?
I encourage anyone who is interested in livepage to take a look at this branch to see where it is going. Take a look at the examples to see what has changed, read the code, and experiment with it. Ask questions if you have any. I'll be trying to merge this branch into the mainline (after adding some backwards compatibility) in the next week.
I'll also be adding some LivePage docs. I think the API will settle down after this last round of changes.
1. nevow_insertNode from liveglue.js (and livepage.insert) is broken. And (as far as I can see) it is not used anywhere in Nevow (including examples). Remove?
2. I think you should have "nevow_" prefix for all things in liveglue.js.
3. What is the purpose of ClientHandle.transient?
4. Are these two equivalent? onsubmit=livepage.server.handle(livepage.js.onSubmit, getValue('inputline')) onsubmit=livepage.server.handle('onSubmit', getValue('inputline'))
P.S. At this time I have only read the code and checked new examples... I will try to experiment with it soon. But I must say your javascript "constructor" looks interesting (livepage.assign, livepage.var etc.)
Thank you very much for your feedback. Please, anyone else on this list who is using or attempting to use LivePage, let me know what you are doing with it or plan on doing with it. I want to take it from long-running experiment to solid, deployable code before the next release, and need to know how much API stability/backwards compatibility I should provide. I also need to know how people want to use LivePage, and if it has any usability shortcomings with this new design which I describe below. If there are any, I'd like to fix them before the next release.
On Jun 1, 2005, at 4:46 PM, Alexey Shamrin wrote:
On 5/31/05, Donovan Preston dp@ulaluma.com wrote:
In this branch, input handlers can also return javascript which will be executed as the response to the input event.
What do you mean? I don't understand... Are you talking about javascript that will be executed without round-trip to server?
When a client-side event generates a trip to the server over the input conduit, the server-side handler method which is invoked can now return javascript stan. This return result is written as the response to the HTTP request which dispatched the event to the server, and the browser evals it. Previously, the server would arrange for the handler method to be called in the next reactor iteration and close the input HTTP request, writing nothing to it. The handler method would then call methods on the client handle which would generate javascript and buffer it or write it directly to the output conduit.
So yes, one (output conduit) round trip is eliminated, and it becomes easier to support normal AJaX style events (where the browser is the only thing initiating requests, and the server does no pushing), which I call input only mode.
I encourage anyone who is interested in livepage to take a look at this branch to see where it is going. Take a look at the examples to see what has changed, read the code, and experiment with it. Ask questions if you have any. I'll be trying to merge this branch into the mainline (after adding some backwards compatibility) in the next week.
I'll also be adding some LivePage docs. I think the API will settle down after this last round of changes.
- nevow_insertNode from liveglue.js (and livepage.insert) is broken.
And (as far as I can see) it is not used anywhere in Nevow (including examples). Remove?
I think I might fix it rather than removing it. I'll also write a test for it, since livetest can now reliably test livepage apps.
- I think you should have "nevow_" prefix for all things in
liveglue.js.
I agree, except I think I want the new "server" object to keep that name. It will be much easier for people to understand and use livepage if they know that calling server.handle('foo') in javascript invokes the handle_foo method on the server. Are there other things in there that aren't prefixed with nevow_?
- What is the purpose of ClientHandle.transient?
ClientHandle.transient and LivePage.locateHandler (whose default implementation looks for handle_* methods) are the new, easier to understand and use replacements for the handler decorator. Previously, there was a problem with handler because it registered the callable you gave it in a dictionary which lived for as long as the LivePage instance it was associated with. This means if you had a really, really long lived LivePage (which LivePage is designed to allow) you would leak more and more closures that the client can't ever call again (because you replaced the client-side dom that referenced those server-side closures with something else)
Now, if you provide a handle_foo method, the client can always call server.handle('foo') as many times as it wants. However, it is still useful to register closures on the server and give the client the capability to call it. To avoid the aforementioned garbage problem, transient allows you to do this, but only allows the client to call it once, then pops the registration.
This avoids the garbage problem well; if the client closes the browser, LivePage guarantees that the ClientHandle will die no more than 90 seconds later, taking the transient closure registration dictionary with it. If you are, for example, putting an "ok/cancel" dialog on the user's screen, you can register a single transient which gets called by either the ok or cancel button, ensuring that the transient will be popped when the client no longer has the capability to call it (because the UI has gone away, by the user clicking either ok or cancel)
- Are these two equivalent?
onsubmit=livepage.server.handle(livepage.js.onSubmit, getValue ('inputline')) onsubmit=livepage.server.handle('onSubmit', getValue('inputline'))
No, because the client-side javascript "handle" function takes a string. The first would render down to:
server.handle(onSubmit, getValue('inputline'))
The second would render down to:
server.handle('onSubmit', getValue('inputLine'))
The first would barf because the global name onSubmit doesn't exist.
The livepage.js object is like a stan.Tag for javascript; it creates literal javascript. For example:
livepage.js.foo -> foo livepage.js.foo('1') -> foo('1') livepage.js('any string') -> any string livepage.js['1'] -> ['1'] livepage.js.foo['1'] -> foo['1']
There are a bunch of other js objects in the module now, such as window, document, this, server, set, append, alert, etc. They are really convenient for making your python code much shorter.
I have an incredible LivePage app I wrote recently I am calling "Pavel". I will be cleaning it up soon and I will create a new open source project for it (rather than including it as a nevow example). I think it really showcases the power of LivePage and I'm excited to show it to people, but for now it is somewhat of a secret :-)
P.S. At this time I have only read the code and checked new examples... I will try to experiment with it soon. But I must say your javascript "constructor" looks interesting (livepage.assign, livepage.var etc.)
assign and var are two experiments to get around the fact that python has no assignment overloading. Here are examples of usage:
var(js.x, 5) -> var x = 5 assign(js.x, 10) -> x = 10
One other thing while I am discussing stan for javascript. This is a pain once you start using it, but I don't think there is any way around it other than education. You need to terminate your statements, since the javascript flatteners have no idea where it is appropriate to put newlines in javascript. So if you were sending the following:
client.send([ livepage.alert('hello'), livepage.set('name', 'Donovan Preston')])
You would need to terminate each statement, otherwise the javascript won't be correct. The above example would render as:
alert('hello')set('name', 'Donovan Preston')
Right now, you need to do:
client.send([ livepage.alert('hello'), livepage.eol, livepage.set('name', 'Donovan Preston')])
Which will render down to:
alert('hello') set('name', 'Donovan Preston')
It occurs to me that it may be possible for Nevow to just infer that a newline is required between each element of a list or a tuple while in JavascriptContext. I will have to experiment and see whether this causes any inadvertent problems. If not, the requirement to manually insert newline characters will go away (horray!)
Donovan
On 6/1/05, Donovan Preston dp@ulaluma.com wrote:
Thank you very much for your feedback. Please, anyone else on this list who is using or attempting to use LivePage, let me know what you are doing with it or plan on doing with it.
I use it for http://punjab.thetofu.com
It is not stable yet. You can get the code at http://punjab.sf.net
<snip>
Thank you very much for your feedback. Please, anyone else on this list who is using or attempting to use LivePage, let me know what you are doing with it or plan on doing with it.
I'm planning to re-implement parts or all of the iPodder GUI in LivePage to overcome difficulties we're experiencing with the wxPython framework. I'll probably also roll it into PyDS.
Regards, Garth.
Hello!
Thank you for good explanations! As far as I can see you have already commited good docstrings and backward-compatibility code into your branch (r1545). It looks good!
On 6/2/05, Donovan Preston dp@ulaluma.com wrote:
I also need to know how people want to use LivePage, and if it has any usability shortcomings with this new design which I describe below.
As you ask to describe how people use LivePage, here is my small example.
In a smaill project I am doing I use LivePage as follows (simplified extract from the working code -- I haven't tested it...):
# example.py from nevow import loaders, livepage, rend, tags as T
def get_teachers_list(): # some code that returns the list of teachers goes here pass
class Editor(rend.Page): """Base class providing template for all pages""" addSlash=True
docFactory = loaders.stan(T.html[T.body[T.directive('main_area')]])
class TeachersEditor(Editor,livepage.LivePage): """This page is exposed to the web"""
def render_main_area(self, ctx, data): return livepage.glue, TeachersList(get_teachers_list())
class TeachersList(rend.Fragment): docFactory = loaders.stan([ T.ul(render=rend.sequence, id='teachers_list')[ T.li(pattern='item', render=T.directive('teacher_item'))[ T.a(render=T.directive('teacher_link'), href='#')[ T.slot(name='teacher_name')]]], T.div(id='teacher_details')])
def render_teacher_item(self, ctx, teacher): return ctx.tag(id='teacher_%d' % teacher.id)
def render_teacher_link(self, ctx, teacher): @livepage.handler def show_teacher(client): client.set('teacher_details',TeacherDetails(teacher))
ctx.fillSlots('teacher_name', teacher.name) return ctx.tag(onclick=show_teacher, id='teacher_link_%d' % teacher.id)
class TeacherDetails(rend.Fragment): # this rend.Fragment also has @livepage.handler-ed closures pass
# end of example.py
As you can see, I use LivePage from inside rend.Fragment's subclasses (TeacherDetails and TeachersList). But only the main rend.Page subclass (TeacherEditor) is a subclass of livepage.LivePage. I also use livepage.handler's as closures (TeachersList.render_teacher_link).
There were two problems with your branch and my code:
1. Backward compatible code didn't work for me (I don't remember what was the reason. If you need details, I can make a better, working, example.).
2. I tried to use new LivePage api, but I couldn't figure out how to translate my code using it. For example, your ClientHandler.transient seems to provide the ability to make browser-callable functions as closures, but I am confused about the limitation of one-time calling. Is it really necessary for a solution of garbage problem you mention?
I also can't see a way to use handler_* methods, because this methods must be inside a rend.Fragment subclass in my case.
How can use the new api in my application?
- I think you should have "nevow_" prefix for all things in liveglue.js.
I agree, except I think I want the new "server" object to keep that name. It will be much easier for people to understand and use livepage if they know that calling server.handle('foo') in javascript invokes the handle_foo method on the server. Are there other things in there that aren't prefixed with nevow_?
Lot's of them. Just look inside liveglue.js... createRequest, connect, auto_load, listener etc.
Or you can you preuso object-oriented approach like in Prototype (http://prototype.conio.net/) or dojo.
I have an incredible LivePage app I wrote recently I am calling "Pavel". I will be cleaning it up soon and I will create a new open source project for it (rather than including it as a nevow example). I think it really showcases the power of LivePage and I'm excited to show it to people, but for now it is somewhat of a secret :-)
I am looking forward for that!
So if you were sending the following:
client.send([ ��� livepage.alert('hello'), ��� livepage.set('name', 'Donovan Preston')])
You would need to terminate each statement, otherwise the javascript won't be correct. The above example would render as:
alert('hello')set('name', 'Donovan Preston')
Right now, you need to do:
client.send([ ��� livepage.alert('hello'), livepage.eol, ��� livepage.set('name', 'Donovan Preston')])
Which will render down to:
alert('hello') set('name', 'Donovan Preston')
It occurs to me that it may be possible for Nevow to just infer that a newline is required between each element of a list or a tuple while in JavascriptContext. I will have to experiment and see whether this causes any�inadvertent problems. If not, the requirement to manually insert newline characters will go away (horray!)
I am not sure, but may be something like livepage.do would do the job a little better?
client.send(livepage.do[ livepage.alert('hello'), livepage.set('name', 'Donovan Preston')])
(or even livepage.progn...)