Replace Reference

This document covers all the possible uses of a TwiddlerElement's
'replace' method. 

We'll demonstrate this using an example similar to the one in
readme.txt:

>>> from twiddler import Twiddler
>>> t = Twiddler('''<html>
...   <body>
...   
Hello world!
...   <div name="stuff">I'm in <i id="type">Italic</i>!</div>
...   <div name="morestuff">I'm in <i>Italic</i>!</div>
...   </body>
... </html>''')

We've already seen examples of replacement, but you may be
wondering how to substitute an attribute value when the attribute
name is a reserved keyword in python. The common example of this
is when you want to set the 'class' attribute of an html element.

The answer is to take advantage of some slightly unusual python
syntax: 

>>> t['greeting'].replace('Hello user!',**{'class':'greeting'})

Of course, this doesn't help if you're trying to set an attribute
which has the same name as one of the replace method's parameters.
In this rare case, you can specify a dictionary containing the
attributes as follows:

>>> t['type'].replace(attributes={'tag':'foo'})

You may also be wondering what happens when you change the value
of an indexed attribute. The answer is that the new value becomes
indexed and the old value is no longer indexed:

>>> t['greeting'].replace(id='intro_greeting')
>>> t['intro_greeting'].replace(**{'xml:lang':'en'})
>>> t['greeting']
Traceback (most recent call last):
...
KeyError:...

Now, lets see how the changes we've made have taken effect:

>>> print t.render()
<html>
  <body>
  <div class="greeting" id="intro_greeting" xml:lang="en">Hello user!</div>
  <div name="stuff">I'm in <i id="type" tag="foo">Italic</i>!</div>
  <div name="morestuff">I'm in <i>Italic</i>!</div>
  </body>
</html>

When you call replace and specify a value, it replaces the whole
value of the tag, including any contained tags:

>>> t['stuff'].replace('My stuff')
>>> print t.render()
<html>
  <body>
  <div class="greeting" id="intro_greeting" xml:lang="en">Hello user!</div>
  <div name="stuff">My stuff</div>
  <div name="morestuff">I'm in <i>Italic</i>!</div>
  </body>
</html>

You can use the 'tag' parameter to change the tag itself:

>>> t['stuff'].replace(tag='span')
>>> print t.render()
<html>
  <body>
  <div class="greeting" id="intro_greeting" xml:lang="en">Hello user!</div>
  <span name="stuff">My stuff</span>
  <div name="morestuff">I'm in <i>Italic</i>!</div>
  </body>
</html>

If you pass True for any parameter to render, the current value is
left as it is:

>>> t['intro_greeting'].replace(True,tag=True,style=True)
>>> print t.render()
<html>
  <body>
  <div class="greeting" id="intro_greeting" xml:lang="en">Hello user!</div>
  <span name="stuff">My stuff</span>
  <div name="morestuff">I'm in <i>Italic</i>!</div>
  </body>
</html>

If you pass False for an attribute value, the attribute is
removed:

>>> t['intro_greeting'].replace(**{'xml:lang':False})
>>> print t.render()
<html>
  <body>
  <div class="greeting" id="intro_greeting">Hello user!</div>
  <span name="stuff">My stuff</span>
  <div name="morestuff">I'm in <i>Italic</i>!</div>
  </body>
</html>

If you pass False as the value for a call to 'replace', then the
contents of the tag, including any contained tags, is removed:

>>> t['morestuff'].replace(False)
>>> print t.render()
<html>
  <body>
  <div class="greeting" id="intro_greeting">Hello user!</div>
  <span name="stuff">My stuff</span>
  <div name="morestuff" />
  </body>
</html>

If you pass False for the tag itself, the tag is hidden from
rendering calls:

>>> t['intro_greeting'].replace(tag=False)
>>> print t.render()
<html>
  <body>
  Hello user!
  <span name="stuff">My stuff</span>
  <div name="morestuff" />
  </body>
</html>

However, this doesn't mean the tag no longer exists:

>>> t['intro_greeting'].replace("Still here!")
>>> print t.render()
<html>
  <body>
  Still here!
  <span name="stuff">My stuff</span>
  <div name="morestuff" />
  </body>
</html>

If you really want to get rid of a tag and all its contents, use
the 'remove' method.

Now, rendering output that combines a site template with content
for a specific page involves using the replace method of one
Twiddler where the value passed is an element from another
Twiddler. We saw an example of this in readme.txt, but there are
some examples that need further clarification.

Firstly, code blocks inside a twiddler are only executed when
either the render or execute methods are called. Comprehensive
examples of the different posibilities can be found in
templating.txt. For now, we'll just show a simple example of how
to use a site template with dynamic elements in conjunction with a
specific page, where both contain a code block.

So, here's the template:

>>> from twiddler.input.default import DefaultWithCodeBlock
>>> template = Twiddler('''<html>
... <!--twiddler
... def template_code(t,title):
...   t['title'].replace(title)
... -->
...   <body>
...   <h1 id="title">The Site Header</h1>
...   <div id="content">Content goes here</div>
...   </body>
... </html>''',input=DefaultWithCodeBlock)

And here's the specific page:

>>> page = Twiddler('''
... <html>
... <!--twiddler
... def page_code(t,content):
...   t['content'].replace(content)
... -->
...   <body>
...   <div id="content">This is our page content!</div>
...   </body>
... </html>''',input=DefaultWithCodeBlock)

Okay, so now lets define some data that we want to render with the
above page:

>>> data = {
...    'title':'The document title',
...    'content':'The document content',
... }

Now here's the code that combines them using the replace method:

>>> page_output = page.clone()
>>> page_output.execute(data['content'])
<twiddler.Twiddler instance at ...>
>>> output = template.clone()
>>> output['content'].replace(page_output['content'])
>>> print output.render(data['title'])
<html>
  <body>
  <h1 id="title">The document title</h1>
  <div id="content">The document content</div>
  </body>
</html>

This relies on the ability to pass an element from another
Twiddler to the replace method. When this is done, the element
that is passed is copied and then used to replace the node on
which the replace method was called. As the following example will
show, this means that once this operation is complete, changes to
one Twiddler will not have an effect on the other.

So, our first Twiddler:

>>> t1 = Twiddler('''<html>
...   <body>
...   <div id="t1_content">This is t1's content!</div>
...   </body>
... </html>''')

Now our second Twiddler:

>>> t2 = Twiddler('''<html>
...   <body>
...   <div id="t2_content">This is t2's content!</div>
...   </body>
... </html>''')

Now lets use a node from one in the other:

>>> t1['t1_content'].replace(t2['t2_content'])

Rendering the two now gives the following:

>>> print t1.render()
<html>
  <body>
  <div id="t2_content">This is t2's content!</div>
  </body>
</html>
>>> print t2.render()
<html>
  <body>
  <div id="t2_content">This is t2's content!</div>
  </body>
</html>

But, now we change both of them independently:

>>> t1['t2_content'].replace('The third change!',
...                          id="t1_content")
>>> t2['t2_content'].replace('The fourth change!')
>>> print t1.render()
<html>
  <body>
  <div id="t1_content">The third change!</div>
  </body>
</html>
>>> print t2.render()
<html>
  <body>
  <div id="t2_content">The fourth change!</div>
  </body>
</html>

It should also be noted that when an element from another Twiddler
is passed to the replace method, all other parameters to the
replace method will be applied after the node has been copied and
inserted:

>>> t1['t1_content'].replace(
...     t2['t2_content'],
...     **{'tag':'span',
...        'class':'fish'}
... )
>>> print t1.render()
<html>
  <body>
  <span class="fish" id="t2_content">The fourth change!</span>
  </body>
</html>

Unicode

  All information substituted into a Twiddler using the replace
  method must either be a unicode or must be decodable into a
  unicode by the default python decoder. In other cases you will
  get a unicode error:

  >>> t['stuff'].replace(u'\x82'.encode('latin-1'))
  Traceback (most recent call last):
  ...
  UnicodeDecodeError:...
  >>> t['stuff'].replace(title=u'\x82'.encode('latin-1'))
  Traceback (most recent call last):
  ...
  UnicodeDecodeError:...

Python Booleans

  As you've seen above, True and False have special meanings for
  the parameters to Twiddler's replace method.

  However, boolean expressions in python often don't return either
  of these values:

  >>> '' or 'X'
  'X'
  >>> 0 and 1
  0

  This can lead to unexpected results with Twiddler:

  >>> t = Twiddler('<node id="test" str="string" int="integer"/>')
  >>> t['test'].replace(str='' or 'X',int=0 and 1)
  >>> print t.render()
  <node id="test" int="0" str="X" />

  The solution is to cast the expression to a boolean if you're
  unsure:

  >>> bool('' or 'X')
  True
  >>> bool(0 and 1)
  False

  Using this technique with replace calls means you always get
  what you expect:

  >>> t = Twiddler('<node id="test" str="string" int="integer"/>')
  >>> t['test'].replace(str=bool('' or 'X'),
  ...                   int=bool(0 and 1))
  >>> print t.render()
  <node id="test" str="string" />
changed November 13, 2007