The combination of Shane's Refresh product and Steve's code to prevent multiple names in the product add list is really useful. Thanks, guys!
I've reworked Steve's initialize code so it works for products that have multiple classes. As an added benefit, you no longer need to specify which class is being refreshed, so the meta types can be renamed or the code reused without changing a single line.
Cheers,
Stephen
-------------Here is the code: ---------------------------------------- def initialize(context): """Initialize my product""" context.registerClass(...) context.registerBaseClass(...)
# Fix Refresh's behaviour of unconditionally reading all class meta_types. # This code extends Steve Spicklemire's fix for products with multiple classes. import Products found_meta_types = [] new_meta_type_list = [] for mt in Products.meta_types: if mt['name'] in found_meta_types: pass else: found_meta_types.append(mt['name']) new_meta_type_list.append(mt) Products.meta_types = tuple(new_meta_type_list)
-----------------Steve Spicklemire wrote----------------------------- Debugging products is a pain... restarting Zope all the time is a Pain....
THANK GOD for Shane Hathaway. ;-)
I am delighted to report general success with the Refresh product! My only problem is while debugging the process of creating new EMarket instances, I find that every time I run 'Refresh' I get a new "EMarket" in the 'Available Objects' popup. A quick test of other Products shows the same behavior.
Soo... I snooped through the product creation code and found that Products.meta_types was being unconditionally appended to every time context.registerClass was called.... so I changed my initialize to this:
def initialize(context):
import Products
context.registerClass( EMarket.EMarket, permission='Add EMarkets', constructors=(EMarket.addEMarketForm, EMarket.addEMarket), icon='emarket.gif', )
found_count = 0
new_products_meta_types = []
for index in range(len(Products.meta_types)): if Products.meta_types[index]['name'] == EMarket.EMarket.meta_type: found_count = found_count + 1 if found_count == 1: new_products_meta_types.append( Products.meta_types[index] ) else: new_products_meta_types.append( Products.meta_types[index] )
Products.meta_types = tuple( new_products_meta_types)
In other words... only include the original version of EMarket.EMarket.meta_type in the Product.meta_types tuple... if there are others... leave them out.
...and that seems to have solved the problem....
Does this seem like an OK idea?
thanks, -steve
_______________________________ Stephen Simmons HealthArena B.V. phone +31 20 486 0555 mobile +31 6 1505 3086 stephen.simmons@healtharena.net
Stephen Simmons wrote:
The combination of Shane's Refresh product and Steve's code to prevent multiple names in the product add list is really useful. Thanks, guys!
I've reworked Steve's initialize code so it works for products that have multiple classes. As an added benefit, you no longer need to specify which class is being refreshed, so the meta types can be renamed or the code reused without changing a single line.
This is great, Steve and Stephen. I'll get this worked into Refresh right away.
Shane
Today is not a good day:-( I've just spent 2 days trying to force __call__ to render my product properly, got every error possible at least seven times, and wasted last night working out what was wrong with Refresh (I was calling refresh on the wrong product. Duh!).
Please, please, please, can someone point me in the right direction.
I have a class called Element which has three components: 1. purpose: a string describing the element's role (rendered as structured text) 2. template: a string that is rendered to display the element (DTML) 3. Element inherits from Folder so it can contain sub-elements which can be included in the template
For example, suppose A is an element that contains a sub-element B (i.e. B is A.B) A has template 'Element A contains <dtml-var B>' and purpose 'Contain B' B has template 'element B, which contains nix' and purpose 'Element with no subelement'
Now for the tricky bit: "<dtml-var A>" needs to be rendered one of three possible ways depending on a variable in the REQUEST. So __call__ needs to return one of - case a: the purpose property (as structured text) wrapped in [] - case b: render the template, with sub-elements properly rendered - case c: render the template, with sub-elements wrapped in [] showing their purpose
Then '<dtml-var A>' must be rendered in the three cases as: - case a: '[Contain B]' - case b: 'Element A contains element B, which contains nix' - case c: 'Element A contains [Element with no subelement]'
To do this, I need to write my own __call__ method since __call__ is what detemines how objects gets rendered in DTML (is this correct?) def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw): With this, I discovered that '<dtml-var A>' gives client=None and REQUEST={} while '<dtml-var "A(_,REQUEST)">' gets client=_ and REQUEST=REQUEST.
But I cannot for the life of me, work out how to do what I need. In particular:
1.) How do you magically get the DTML context from '<dtml-var A>' without having to resort to '<dtml-var "A(_,REQUEST)">' in the templates?
In the __call__ methods of different classes, I've seen some code like: 'if REQUEST is None and not kw: REQUEST=self.REQUEST' (from Catalog.py) and other code like 'if hasattr(self, 'aq_explicit'): bself=self.aq_explicit else: bself=self' and 'security=getSecurityManager() security.addContext(self)' (from DTMLDocument.py) I can't see any consistency in what these classes are doing, so I don't know what I need to do.
2.) How to convert an arbitrary text string into rendered DTML inside a python product method? How do you get the object's properties, REQUEST and acquisition context all in the namespace?
I've been trying code like: template = '<span class="purpose"><dtml-var purpose fmt="structured_text"></span>' dtml = HTML(template, globals()) rendered = dtml(self, REQUEST) but this doesn't work. All the example classes I've seen (such as ZWikiPage.py in ZWiki) use DTMLDocument as their base class so can call DTMLDocument's __call__ on themselves as in text = apply(DTMLDocument.__call__,(self, client, REQUEST, RESPONSE), kw) (ZWikiPage.py, line 135) This doesn't help me because I don't have DTMLDocument as a base class. Looking through the source for DTMLMethod etc is not very clear because lots of "apply(__call__,(...),kw)" calls are all mixed up with acquisition code.
3.) Do I need to write a custom tag (<dtml-element A>) to get this behaviour?
So, if anyone out in Zope land can help me, I'll be eternally grateful. I've exhausted my patience looking through the NIP mailing list archives, the How-Tos and the Zope site and need to have something to demonstrate on Thursday.
Thanks in advace,
Stephen
P.S. ChrisW, searching NIP for "__call__" returns every message with 'call' in it, which is not quite the same thing! _______________________________ Stephen Simmons HealthArena B.V. phone +31 20 486 0555 mobile +31 6 1505 3086 stephen.simmons@healtharena.net
Stephen Simmons wrote:
1.) How do you magically get the DTML context from '<dtml-var A>' without having to resort to '<dtml-var "A(_,REQUEST)">' in the templates?
Give your class a class attribute of isDocTemp=1. This tells the DTML method calling machinery that you want to receive the namespace as REQUEST as the third argument to your __call__ method.
There was a thread on this in zope-dev on and around 26 September, subject "more __call__ ..."
In the __call__ methods of different classes, I've seen some code like: 'if REQUEST is None and not kw: REQUEST=self.REQUEST' (from Catalog.py)
Your method will usually get called with its third argument as the namespace, or REQUEST. However, sometimes, it may just be called with a "self" argument. The code in Catalog.py is falling back on getting the REQUEST out of the self argument, if it is not explicitly passed a namespace to work with.
I can't see any consistency in what these classes are doing, so I don't know what I need to do.
Most developers would like more consistency here :-) I'm hoping that all this will be much much clearer for Zope 2.3. That doesn't help much for now though.
2.) How to convert an arbitrary text string into rendered DTML inside a python product method? How do you get the object's properties, REQUEST and acquisition context all in the namespace?
I've been trying code like: template = '<span class="purpose"><dtml-var purpose fmt="structured_text"></span>' dtml = HTML(template, globals())
If all you want to do is render some Structured Text, you can import and use the StructuredText module directly.
P.S. ChrisW, searching NIP for "__call__" returns every message with 'call' in it, which is not quite the same thing!
For cases like that, I have downloaded the plaintext mail archives as available from http://www.zope.org/Resources/MailingLists
I then use unix grep on them to search for just what I need.
-- Steve Alexander Software Engineer Cat-Box limited http://www.cat-box.net
Stephen Simmons wrote:
2.) How to convert an arbitrary text string into rendered DTML inside a python product method? How do you get the object's properties, REQUEST and acquisition context all in the namespace?
I've been trying code like: template = '<span class="purpose"><dtml-var purpose fmt="structured_text"></span>' dtml = HTML(template, globals()) rendered = dtml(self, REQUEST)
Here's an except from my Python interactive interpreter:
import DocumentTemplate my_dt=DocumentTemplate.HTML("""<dtml-var "1+a">""") my_dt(None, {'a':4})
'5'
The object my_dt is callable, and takes the arguments (client, namespace). You can put a REQUEST in place of the simple dictionary I used above.
If you're trying this, make sure you have the PYTHONPATH environment variable set to /your/zope/directory/lib/python
-- Steve Alexander Software Engineer Cat-Box limited http://www.cat-box.net
From: Stephen Simmons stephen.simmons@healtharena.net
1.) How do you magically get the DTML context from '<dtml-var A>' without having to resort to '<dtml-var "A(_,REQUEST)">' in the templates?
A.isDocTemp = 1 # or make it a shared class attribute
2.) How to convert an arbitrary text string into rendered DTML inside a python product method?
Here's what I use:
from Globals import HTML from AccessControl import getSecurityManager
class DTML(HTML): """DTML objects are DocumentTemplate.HTML objects that allow dynamic, temporary creation of restricted DTML.""" def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw): """Render the DTML given a client object, REQUEST mapping, Response, and key word arguments."""
security=getSecurityManager() security.addContext(self) try: return apply(HTML.__call__, (self, client, REQUEST), kw) finally: security.removeContext(self)
def validate(self, inst, parent, name, value, md): return getSecurityManager().validate(inst, parent, name, value)
Cheers,
Evan @ digicool & 4-am
Shane and Zope-Dev users of Refresh,
Have you noticed any differences refreshing objects that are in the root folder versus higher in the Zope tree?
Last week, I was debugging a class using Refresh. I had two class instances, E1 in the root directory and E2 which lived inside E1 (my class is folderish).
Trying http://localhost:8080/E1/test and http://localhost:8080/E1/E2/test gave completely different results after changing the class's python code and refreshing. E2 picked up the new class definition while E1 kept the old class definition. Aggressively flushing the cache a few times and refreshing again sometimes fixed the problem, but only temporarily. E1 would use the new code a couple of times then inexplicably switch back to the old code. Needless to say, this bamboozled me completely!
Today I am debugging the class instances one level higher in the Zope tree (http://localhost:8080/demo/E1/test and http://localhost:8080/demo/E1/E2/test). Refresh works perfectly. I don't need to flush the cashe or anything like that. Also, the reported size of the cache after a full refresh is down to 8 objects (it was around 28 before).
Strange huh? Does this mean that Zope's cache hangs on much more tightly to objects in the ZODB root that inside folders?
Cheers,
Stephen
----- Original Message ----- From: "Shane Hathaway" shane@digicool.com Newsgroups: digicool.zope.dev To: "Stephen Simmons" stephen.simmons@healtharena.net Sent: Tuesday, October 10, 2000 3:53 PM Subject: Re: [Zope-dev] Re: Refresh and debugging product creation issues.....
Stephen Simmons wrote:
The combination of Shane's Refresh product and Steve's code to prevent multiple names in the product add list is really useful. Thanks, guys!
I've reworked Steve's initialize code so it works for products that have multiple classes. As an added benefit, you no longer need to specify
which
class is being refreshed, so the meta types can be renamed or the code reused without changing a single line.
This is great, Steve and Stephen. I'll get this worked into Refresh right away.
Shane
___________________________________________ Stephen Simmons, stephen.simmons@healtharena.net HealthArena B.V., Amsterdam, The Netherlands phone +31 20 486 0555; mobile +31 6 1505 3086; fax +31 20 486 0559