[Repoze-checkins] r1399 - in repoze.lxmlgraph/trunk/docs: . step00 step01/myapp step02/myapp step03/myapp
Paul Everitt
paul at agendaless.com
Sat Jul 19 09:17:35 EDT 2008
Author: Paul Everitt <paul at agendaless.com>
Date: Sat Jul 19 09:17:35 2008
New Revision: 1399
Log:
Fix up some NWO bugs. Switch to using @name on the XML nodes, to correspond to __name__. Change RST to use numbered lists.
Modified:
repoze.lxmlgraph/trunk/docs/background.rst
repoze.lxmlgraph/trunk/docs/step00/simplemodel.xml
repoze.lxmlgraph/trunk/docs/step01.rst
repoze.lxmlgraph/trunk/docs/step01/myapp/configure.zcml
repoze.lxmlgraph/trunk/docs/step02.rst
repoze.lxmlgraph/trunk/docs/step02/myapp/configure.zcml
repoze.lxmlgraph/trunk/docs/step02/myapp/models.py
repoze.lxmlgraph/trunk/docs/step02/myapp/samplemodel.xml
repoze.lxmlgraph/trunk/docs/step03.rst
repoze.lxmlgraph/trunk/docs/step03/myapp/configure.zcml
repoze.lxmlgraph/trunk/docs/step03/myapp/models.py
repoze.lxmlgraph/trunk/docs/step03/myapp/samplemodel.xml
repoze.lxmlgraph/trunk/docs/step03/myapp/views.py
repoze.lxmlgraph/trunk/docs/step03/myapp/xsltview.xsl
Modified: repoze.lxmlgraph/trunk/docs/background.rst
==============================================================================
--- repoze.lxmlgraph/trunk/docs/background.rst (original)
+++ repoze.lxmlgraph/trunk/docs/background.rst Sat Jul 19 09:17:35 2008
@@ -57,7 +57,7 @@
sprinkle in some Python behavior. For example, ``repoze.bfg`` uses
``__getitem__`` to traverse the model. I need my XML data to support
this method. Moreover, I want some specific behavior: run an XPath
-express on the node to get the child with the ``@id`` attribute
+express on the node to get the child with the ``@name`` attribute
matching the URL hop.
Fortunately ``lxml`` makes this easy. I can inject my nodes with a
@@ -91,21 +91,22 @@
This demo application has the potential to show some other interesting
investigations:
-1) Authorization. By hooking up support for an ``__acl__`` property,
-I can store ACL information on a single node, on an ancestor, on the
-root, on the Python class, or any combination thereof. Additionally,
-I can wire up the ``__parent__`` attribute as a property that makes an
-lxml ``node.getparent()`` call.
-
-2) Multiple views. Instead of just having a single default view on a
-node, I can allow other view names, all pointing at the same factory
-and XSLT. I simple grab that name and pass it in as a paramter to the
-XSLT, which will run a different rule for rendering.
-
-3) Forms. To edit data in the model, I need to render a form, then
-handle post data on the way back in. For the former, it's *really*
-easy in XSLT to make a very powerful, flexible, and extensisible form
-rendering system. For the latter, I'll have to learn more about POST
-handlers in ``repoze.bfg``.
+#. **Authorization**. By hooking up support for an ``__acl__``
+ property, I can store ACL information on a single node, on an
+ ancestor, on the root, on the Python class, or any combination
+ thereof. Additionally, I can wire up the ``__parent__`` attribute
+ as a property that makes an lxml ``node.getparent()`` call.
+
+#. **Multiple views**. Instead of just having a single default view
+ on a node, I can allow other view names, all pointing at the same
+ factory and XSLT. I simple grab that name and pass it in as a
+ paramter to the XSLT, which will run a different rule for
+ rendering.
+
+#. **Forms**. To edit data in the model, I need to render a form,
+ then handle post data on the way back in. For the former, it's
+ *really* easy in XSLT to make a very powerful, flexible, and
+ extensisible form rendering system. For the latter, I'll have to
+ learn more about POST handlers in ``repoze.bfg``.
Modified: repoze.lxmlgraph/trunk/docs/step00/simplemodel.xml
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step00/simplemodel.xml (original)
+++ repoze.lxmlgraph/trunk/docs/step00/simplemodel.xml Sat Jul 19 09:17:35 2008
@@ -3,8 +3,8 @@
<site>
<title>My XMLGRAPH Website</title>
</site>
- <folder xml:id="n1" id="folder1">
- <document xml:id="n11" id="doc1">
+ <folder xml:id="n1" name="folder1">
+ <document xml:id="n11" name="doc1">
<title>doc1 in folder1</title>
<body>
<div xmlns="http://www.w3.org/1999/xhtml">
@@ -13,15 +13,15 @@
</div>
</body>
</document>
- <document xml:id="n12" id="doc2">
+ <document xml:id="n12" name="doc2">
<title>doc2 in folder1</title>
</document>
</folder>
- <folder xml:id="n2" id="folder2">
- <document xml:id="n21" id="doc1">
+ <folder xml:id="n2" name="folder2">
+ <document xml:id="n21" name="doc1">
<title>doc1 in folder2</title>
</document>
- <document xml:id="n22" id="doc2">
+ <document xml:id="n22" name="doc2">
<title>doc2 in folder2</title>
</document>
</folder>
Modified: repoze.lxmlgraph/trunk/docs/step01.rst
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step01.rst (original)
+++ repoze.lxmlgraph/trunk/docs/step01.rst Sat Jul 19 09:17:35 2008
@@ -61,18 +61,18 @@
:linenos:
:language: xml
-1) Lines 1-3 provide the root element and namespaces for the
-configuration language. ``bfg`` is the namespace for
-``repoze.bfg``-specific configuration directives.
-
-2) Line 5 initializes those ``repoze.bfg``-specific configuration
-directives.
-
-3) Lines 8-11 register a single view for model objects that support
-the IMyModel interface. In this case we made a default view, which
-means going to ``/a`` triggers the default view of instance ``a``.
-Finally, the ``factory`` attribute points at a Python function that
-does all the work for this view.
+#. Lines 1-3 provide the root element and namespaces for the
+ configuration language. ``bfg`` is the namespace for
+ ``repoze.bfg``-specific configuration directives.
+
+#. Line 5 initializes those ``repoze.bfg``-specific configuration
+ directives.
+
+#. Lines 8-11 register a single view for model objects that support
+ the IMyModel interface. In this case we made a default view, which
+ means going to ``/a`` triggers the default view of instance ``a``.
+ Finally, the ``view`` attribute points at a Python function that
+ does all the work for this view.
Module ``myapp/models.py``
@@ -86,15 +86,15 @@
.. literalinclude:: step01/myapp/models.py
:linenos:
-1) Lines 5-6 define the interface.
+#. Lines 5-6 define the interface.
-2) Lines 8-11 provide a class for that interface.
+#. Lines 8-11 provide a class for that interface.
-3) Lines 13-15 make a small tree of sample data (``/a`` and ``/b`` for
-URLs.)
+#. Lines 13-15 make a small tree of sample data (``/a`` and ``/b`` for
+ URLs.)
-4) Line 17 is a function that will be grabbed by the server when it
-wants to find the top of the model.
+#. Line 17 is a function that will be grabbed by the server when it
+ wants to find the top of the model.
Module ``myapp/views.py``
@@ -116,16 +116,16 @@
.. literalinclude:: step01/myapp/models.py
:linenos:
-1) Lines 3 provide the ``my_hello_view`` that was registered as the
-view. ``configure.zcml``, on Line 10, said that the default URL for
-IMyModel content should run this ``my_hello_view`` function.
-
-The function is handed two pieces of information: the ``context`` and
-the ``request``. The ``context`` is the data at the current hop in
-the URL. (That data comes from the model.) The request is an
-instance of a WebOb request.
+#. Lines 3 provide the ``my_hello_view`` that was registered as the
+ view. ``configure.zcml``, on Line 10, said that the default URL
+ for IMyModel content should run this ``my_hello_view`` function.
+
+ The function is handed two pieces of information: the ``context``
+ and the ``request``. The ``context`` is the data at the current
+ hop in the URL. (That data comes from the model.) The request is
+ an instance of a WebOb request.
-2) Lines 4-7 generate a WebOb Response object and return it.
+#. Lines 4-7 generate a WebOb Response object and return it.
Module ``run.py``
@@ -138,18 +138,18 @@
.. literalinclude:: step01/run.py
:linenos:
-1) Line 1 uses the web server from the Paste project.
+#. Line 1 uses the web server from the Paste project.
-2) Line 3 imports the big gun from ``repoze.bfg``.
+#. Line 3 imports the big gun from ``repoze.bfg``.
-3) Line 4 grabs the function that hands back the top of the
-application's model data.
+#. Line 4 grabs the function that hands back the top of the
+ application's model data.
-4) Line 5 imports the package that we want to "publish".
+#. Line 5 imports the package that we want to "publish".
-5) Line 7 loads the big gun with the data and the package.
+#. Line 7 loads the big gun with the data and the package.
-6) And finally, line 8 starts the web server on port 5432.
+#. And finally, line 8 starts the web server on port 5432.
Running and Browsing the Application
Modified: repoze.lxmlgraph/trunk/docs/step01/myapp/configure.zcml
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step01/myapp/configure.zcml (original)
+++ repoze.lxmlgraph/trunk/docs/step01/myapp/configure.zcml Sat Jul 19 09:17:35 2008
@@ -7,7 +7,7 @@
<!-- the default view for a MyModel -->
<bfg:view
for=".models.IMyModel"
- factory=".views.my_hello_view"
+ view=".views.my_hello_view"
/>
</configure>
Modified: repoze.lxmlgraph/trunk/docs/step02.rst
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step02.rst (original)
+++ repoze.lxmlgraph/trunk/docs/step02.rst Sat Jul 19 09:17:35 2008
@@ -32,19 +32,20 @@
:linenos:
:language: xml
-1) Line 2 provides the root of the model as an XML ``<root>`` node.
-The element name doesn't have to be ``<root>``.
+#. Line 2 provides the root of the model as an XML ``<root>`` node.
+ The element name doesn't have to be ``<root>``.
-2) In lines 3-4, the root contains 2 top-level children: a and b.
-These are provided as an element name ``<document>``. This, also, is
-meaningfless as far as ``repoze.bfg`` is concerned. However, this is
-where you compose th information model you are publishing.
+#. In lines 3-4, the root contains 2 top-level children: a and b.
+ These are provided as an element name ``<document>``. This, also,
+ is meaningfless as far as ``repoze.bfg`` is concerned. However,
+ this is where you compose th information model you are publishing.
The only special constraint is that a node that wants to be "found" by
-``repoze.bfg`` in during traversal *must* have an ``id`` attribute.
-Each hop in the URL tries to grab a child with an attribute matching
-the next hop. Also, the value of the ``@id`` should be unique in its
-containing node.
+``repoze.bfg`` in during traversal *must* have an ``name`` attribute.
+(The use of ``@name`` corresponds to ``__name__`` in the
+``repoze.bfg`` handshake.) Each hop in the URL tries to grab a child
+with an attribute matching the next hop. Also, the value of the
+``@name`` should be unique in its containing node.
Module ``myapp/models.py``
@@ -60,30 +61,30 @@
.. literalinclude:: step02/myapp/models.py
:linenos:
-1) Line 4 imports lxml.
+#. Line 4 imports lxml.
-2) Line 9 creates the custom class we are going to use to extend
-etree.ElementBase. The lxml website has great documentation on the
-various ways to inject custom Python behavior into XML.
-
-3) Just as before, line 12 says that instances of this class support a
-certain content type (interface.) In our case, instances will be XML
-nodes.
-
-4) ``repoze.bfg`` has a "protocol" where model data should have an
-``__name__`` attribute. Lines 14-16 implement this by grabbing the
-``@id`` attribute of the current node.
-
-5) URL traversal in ``repoze.bfg`` works via the ``__getitem__``
-protocol. Thus, we need a method that implements this. Lines 18-26
-use XPath to look for a direct child that has an ``@id`` matching the
-item id that's being traversed to. If it finds it, return it. If
-not, or if more than one is found, raise an error.
-
-6) As before, ``get_root`` is the function that is expected to return
-the top of the model. In lines 30-32 we do the lxml magic to get the
-custom Python class registered. We then load some XML and return the
-top of the tree.
+#. Line 9 creates the custom class we are going to use to extend
+ etree.ElementBase. The lxml website has great documentation on the
+ various ways to inject custom Python behavior into XML.
+
+#. Just as before, line 12 says that instances of this class support a
+ certain content type (interface.) In our case, instances will be
+ XML nodes.
+
+#. ``repoze.bfg`` has a "protocol" where model data should have an
+ ``__name__`` attribute. Lines 14-16 implement this by grabbing the
+ ``@name`` attribute of the current node.
+
+#. URL traversal in ``repoze.bfg`` works via the ``__getitem__``
+ protocol. Thus, we need a method that implements this. Lines
+ 18-26 use XPath to look for a direct child that has an ``@name``
+ matching the item name that's being traversed to. If it finds it,
+ return it. If not, or if more than one is found, raise an error.
+
+#. As before, ``get_root`` is the function that is expected to return
+ the top of the model. In lines 30-32 we do the lxml magic to get
+ the custom Python class registered. We then load some XML and
+ return the top of the tree.
Module `myapp/views.py``
@@ -94,11 +95,11 @@
.. literalinclude:: step02/myapp/views.py
:linenos:
-1) Line 5 grabs the element name (tag name) of the ``context``, which
-is the current XML node that we're traversing through.
+#. Line 5 grabs the element name (tag name) of the ``context``, which
+ is the current XML node that we're traversing through.
-2) Line 6 uses the special property we defined in our custom Python
-class to get the ``__name__`` of the context.
+#. Line 6 uses the special property we defined in our custom Python
+ class to get the ``__name__`` of the context.
Browsing the Model
Modified: repoze.lxmlgraph/trunk/docs/step02/myapp/configure.zcml
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step02/myapp/configure.zcml (original)
+++ repoze.lxmlgraph/trunk/docs/step02/myapp/configure.zcml Sat Jul 19 09:17:35 2008
@@ -1,13 +1,11 @@
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:bfg="http://namespaces.repoze.org/bfg">
- <!-- this must be included for the view declarations to work -->
<include package="repoze.bfg" />
- <!-- the default view for a MyModel -->
<bfg:view
for=".models.IMyModel"
- factory=".views.my_hello_view"
+ view=".views.my_hello_view"
/>
</configure>
Modified: repoze.lxmlgraph/trunk/docs/step02/myapp/models.py
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step02/myapp/models.py (original)
+++ repoze.lxmlgraph/trunk/docs/step02/myapp/models.py Sat Jul 19 09:17:35 2008
@@ -13,15 +13,15 @@
@property
def __name__(self):
- return self.xpath("@id")[0]
+ return self.xpath("@name")[0]
- def __getitem__(self, item_id):
- xp = "*[@id='%s']" % item_id
+ def __getitem__(self, child_name):
+ xp = "*[@name='%s']" % child_name
matches = self.xpath(xp)
if len(matches) == 0:
- raise KeyError('No child found for %s' % item_id)
+ raise KeyError('No child found for %s' % child_name)
elif len(matches) > 1:
- raise KeyError('More than one child for %s' % item_id)
+ raise KeyError('More than one child for %s' % child_name)
else:
return matches[0]
Modified: repoze.lxmlgraph/trunk/docs/step02/myapp/samplemodel.xml
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step02/myapp/samplemodel.xml (original)
+++ repoze.lxmlgraph/trunk/docs/step02/myapp/samplemodel.xml Sat Jul 19 09:17:35 2008
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
- <document id="a"/>
- <document id="b"/>
+ <document name="a"/>
+ <document name="b"/>
</root>
Modified: repoze.lxmlgraph/trunk/docs/step03.rst
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step03.rst (original)
+++ repoze.lxmlgraph/trunk/docs/step03.rst Sat Jul 19 09:17:35 2008
@@ -22,7 +22,7 @@
<bfg:view
for=".models.IMyModel"
- factory=".views.zpt_default_view"
+ view=".views.zpt_default_view"
/>
Here we point to a function in ``myapp/views.py`` that looks like the
@@ -74,6 +74,9 @@
between the braces. And since ``node`` is an lxml Element object,
we just ask for its ``.tag``, like regular Python lxml code.
+Viewing the ZPT
+------------------
+
With all of that in place, going to ``http://localhost:5432/a`` now
generates, via the ZPT, the following::
@@ -97,22 +100,76 @@
File ``myapp/configure.zcml``
--------------------------------
+The ZCML statement for the XSLT template looks almost exactly the same
+as the ZPT template:
+
.. literalinclude:: step03/myapp/configure.zcml
:linenos:
:language: xml
+#. Lines 10-14 wire up a new view, in addition to the default view.
+
+#. Line 13 provides the difference: ``name="xsltview.html"`` means
+ that all our URLs now can have ``/xsltview.xml`` appended to them.
+
+In the ZCML, there is no distinction between a ZPT view and an XSLT
+view. The difference is only in the function that is pointed to by
+the ``view=`` attribute.
+
Module ``myapp/views.py``
--------------------------------
+The ZCML says that our XSLT view (``xsltview.html`` on the URL) comes
+from the ``myapp.views.xslt_view`` function:
+
.. literalinclude:: step03/myapp/views.py
:linenos:
+#. Line 9 starts the Python function which serves as the view for this
+ template. The function has the same signature as the
+ ``zpt_default_view`` function we defined for the ZPT template's
+ view.
+
+#. Line 10 implements the difference. We call
+ ``render_transform_to_response`` instead of
+ ``render_template_to_response``. This tells ``repoze.bfg`` to make
+ an XSLT processor for this template, instead of a ZPT. The second
+ argument passes in ``context`` to the XSLT transform. ``context```
+ is an instance of an Element node. Namely, a node from the XML
+ document that corresponds to the current hop in the URL.
+
File ``myapp/xsltview.xsl``
--------------------------------
+How different does the XSLT itself look? At this stage, not too different:
+
.. literalinclude:: step03/myapp/xsltview.xsl
:linenos:
:language: xml
+#. Lines 1 and 2 are typical XSLT setup.
+
+#. Line 3 defines a rule to match on the node that is passed in. In
+ our case, a ``<document>`` node.
+
+#. Line 7 inserts the value of the ``@id`` attribute from the
+ "current" node at that point in the rule. We're sitting on the
+ ``<document>`` node (thanks to line 3). Thus, ``<xsl:value of
+ select="@id"/>`` inserts ``a`` or ``b``, depending on which
+ document we are sitting on.
+
+#. Line 8 shows the element name of the current node.
+
+
+Viewing the XSLT
+--------------------
+
+With this in place, runnning the application provides a URL such as
+``http://localhost:5432/a/xsltview.html``. Going to that URL should
+show::
+
+ My template is viewing item: a
+
+ The node has a name of: document.
Modified: repoze.lxmlgraph/trunk/docs/step03/myapp/configure.zcml
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step03/myapp/configure.zcml (original)
+++ repoze.lxmlgraph/trunk/docs/step03/myapp/configure.zcml Sat Jul 19 09:17:35 2008
@@ -5,12 +5,12 @@
<bfg:view
for=".models.IMyModel"
- factory=".views.zpt_default_view"
+ view=".views.zpt_default_view"
/>
<bfg:view
for=".models.IMyModel"
- factory=".views.xslt_view"
+ view=".views.xslt_view"
name="xsltview.html"
/>
Modified: repoze.lxmlgraph/trunk/docs/step03/myapp/models.py
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step03/myapp/models.py (original)
+++ repoze.lxmlgraph/trunk/docs/step03/myapp/models.py Sat Jul 19 09:17:35 2008
@@ -13,15 +13,15 @@
@property
def __name__(self):
- return self.xpath("@id")[0]
+ return self.xpath("@name")[0]
- def __getitem__(self, item_id):
- xp = "*[@id='%s']" % item_id
+ def __getitem__(self, child_name):
+ xp = "*[@name='%s']" % child_name
matches = self.xpath(xp)
if len(matches) == 0:
- raise KeyError('No child found for %s' % item_id)
+ raise KeyError('No child found for %s' % child_name)
elif len(matches) > 1:
- raise KeyError('More than one child for %s' % item_id)
+ raise KeyError('More than one child for %s' % child_name)
else:
return matches[0]
Modified: repoze.lxmlgraph/trunk/docs/step03/myapp/samplemodel.xml
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step03/myapp/samplemodel.xml (original)
+++ repoze.lxmlgraph/trunk/docs/step03/myapp/samplemodel.xml Sat Jul 19 09:17:35 2008
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
- <document id="a">xx</document>
- <document id="b">yy</document>
+ <document name="a"/>
+ <document name="b"/>
</root>
Modified: repoze.lxmlgraph/trunk/docs/step03/myapp/views.py
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step03/myapp/views.py (original)
+++ repoze.lxmlgraph/trunk/docs/step03/myapp/views.py Sat Jul 19 09:17:35 2008
@@ -1,9 +1,10 @@
-from repoze.bfg.template import render_template
-from repoze.bfg.template import render_transform
+from repoze.bfg.template import render_template_to_response
+from repoze.bfg.template import render_transform_to_response
def zpt_default_view(context, request):
- fn = "default.pt"
- return render_template(fn, name=context.__name__, node=context)
+ return render_template_to_response("default.pt",
+ name=context.__name__,
+ node=context)
def xslt_view(context, request):
- return render_transform("xsltview.xsl", context)
+ return render_transform_to_response("xsltview.xsl", context)
Modified: repoze.lxmlgraph/trunk/docs/step03/myapp/xsltview.xsl
==============================================================================
--- repoze.lxmlgraph/trunk/docs/step03/myapp/xsltview.xsl (original)
+++ repoze.lxmlgraph/trunk/docs/step03/myapp/xsltview.xsl Sat Jul 19 09:17:35 2008
@@ -4,7 +4,7 @@
<html>
<head/>
<body>
- <h1>My template is viewing item: <xsl:value-of select="@id"/></h1>
+ <h1>My template is viewing item: <xsl:value-of select="@name"/></h1>
<p>The node has a name of: <xsl:value-of select="name()"/>.</p>
</body>
</html>
More information about the Repoze-checkins
mailing list