[Checkins] SVN: zmi.core/trunk/s Copy browser package from zope.app.generations.
Yusei Tahara
yusei at domen.cx
Sat Nov 21 02:14:15 EST 2009
Log message for revision 105933:
Copy browser package from zope.app.generations.
Changed:
U zmi.core/trunk/setup.py
U zmi.core/trunk/src/zmi/core/configure.zcml
A zmi.core/trunk/src/zmi/core/generations/__init__.py
A zmi.core/trunk/src/zmi/core/generations/configure.zcml
A zmi.core/trunk/src/zmi/core/generations/managerdetails.pt
A zmi.core/trunk/src/zmi/core/generations/managerdetails.py
A zmi.core/trunk/src/zmi/core/generations/managers.pt
A zmi.core/trunk/src/zmi/core/generations/managers.py
A zmi.core/trunk/src/zmi/core/generations/tests.py
-=-
Modified: zmi.core/trunk/setup.py
===================================================================
--- zmi.core/trunk/setup.py 2009-11-21 06:58:58 UTC (rev 105932)
+++ zmi.core/trunk/setup.py 2009-11-21 07:14:14 UTC (rev 105933)
@@ -59,6 +59,7 @@
'zope.app.exception',
'zope.app.file',
'zope.app.folder',
+ 'zope.app.generations',
'zope.app.i18n',
'zope.app.i18nfile',
'zope.app.intid',
Modified: zmi.core/trunk/src/zmi/core/configure.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/configure.zcml 2009-11-21 06:58:58 UTC (rev 105932)
+++ zmi.core/trunk/src/zmi/core/configure.zcml 2009-11-21 07:14:14 UTC (rev 105933)
@@ -10,6 +10,7 @@
<include package=".exception" />
<include package=".file" />
<include package=".folder" />
+ <include package=".generations" />
<include package=".i18n" />
<include package=".i18nfile" />
<include package=".intid" />
Added: zmi.core/trunk/src/zmi/core/generations/__init__.py
===================================================================
--- zmi.core/trunk/src/zmi/core/generations/__init__.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/generations/__init__.py 2009-11-21 07:14:14 UTC (rev 105933)
@@ -0,0 +1 @@
+# Make this directory a package.
Added: zmi.core/trunk/src/zmi/core/generations/configure.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/generations/configure.zcml (rev 0)
+++ zmi.core/trunk/src/zmi/core/generations/configure.zcml 2009-11-21 07:14:14 UTC (rev 105933)
@@ -0,0 +1,23 @@
+<zope:configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser"
+ >
+
+<page
+ name="generations.html"
+ menu="zmi_views" title="Database Schemas"
+ for="zope.app.applicationcontrol.interfaces.IApplicationControl"
+ class=".managers.Managers"
+ template="managers.pt"
+ permission="zope.ManageApplication"
+ />
+
+<page
+ name="generations_managerdetails.html"
+ for="zope.app.applicationcontrol.interfaces.IApplicationControl"
+ class=".managerdetails.ManagerDetails"
+ template="managerdetails.pt"
+ permission="zope.ManageApplication"
+ />
+
+</zope:configure>
Added: zmi.core/trunk/src/zmi/core/generations/managerdetails.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/generations/managerdetails.pt (rev 0)
+++ zmi.core/trunk/src/zmi/core/generations/managerdetails.pt 2009-11-21 07:14:14 UTC (rev 105933)
@@ -0,0 +1,30 @@
+<html metal:use-macro="context/@@standard_macros/view" i18n:domain="zope">
+<head>
+<title metal:fill-slot="title">Manager Details</title>
+</head>
+<body>
+<div metal:fill-slot="body">
+
+<h1 i18n:translate="">
+ <b i18n:name="application_id" tal:content="view/id" />
+ Application Manager Details
+</h1>
+<br/>
+
+<tal:block repeat="evolver view/getEvolvers">
+
+<h2 i18n:translate="">
+ Evolver
+ from Generation <b i18n:name="from" tal:content="evolver/from"/>
+ to Generation <b i18n:name="to" tal:content="evolver/to"/>
+</h2>
+<br />
+<div tal:content="structure evolver/info">
+ Evolution information.
+</div>
+
+</tal:block>
+
+</div>
+</body>
+</html>
Added: zmi.core/trunk/src/zmi/core/generations/managerdetails.py
===================================================================
--- zmi.core/trunk/src/zmi/core/generations/managerdetails.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/generations/managerdetails.py 2009-11-21 07:14:14 UTC (rev 105933)
@@ -0,0 +1,87 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Manager Details View
+
+$Id: managerdetails.py 85608 2008-04-22 18:34:09Z lgs $
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+
+from zope.app.generations.interfaces import ISchemaManager
+from zope.app.renderer.rest import ReStructuredTextToHTMLRenderer
+
+class ManagerDetails(object):
+ r"""Show Details of a particular Schema Manager's Evolvers
+
+ This method needs to use the component architecture, so
+ we'll set it up:
+
+ >>> from zope.app.testing.placelesssetup import setUp, tearDown
+ >>> setUp()
+
+ We need to define some schema managers. We'll define just one:
+
+ >>> from zope.app.generations.generations import SchemaManager
+ >>> from zope.app.testing import ztapi
+ >>> app1 = SchemaManager(0, 3, 'zope.app.generations.demo')
+ >>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1')
+
+ Now let's create the view:
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> details = ManagerDetails()
+ >>> details.context = None
+ >>> details.request = TestRequest(environ={'id': 'foo.app1'})
+
+ Let's now see that the view gets the ID correctly from the request:
+
+ >>> details.id
+ 'foo.app1'
+
+ Now check that we get all the info from the evolvers:
+
+ >>> info = details.getEvolvers()
+ >>> for item in info:
+ ... print sorted(item.items())
+ [('from', 0), ('info', u'<p>Evolver 1</p>\n'), ('to', 1)]
+ [('from', 1), ('info', u'<p>Evolver 2</p>\n'), ('to', 2)]
+ [('from', 2), ('info', ''), ('to', 3)]
+
+ We'd better clean up:
+
+ >>> tearDown()
+ """
+
+ id = property(lambda self: self.request['id'])
+
+ def getEvolvers(self):
+ id = self.id
+ manager = zope.component.getUtility(ISchemaManager, id)
+
+ evolvers = []
+
+ for gen in range(manager.minimum_generation, manager.generation):
+
+ info = manager.getInfo(gen+1)
+ if info is None:
+ info = ''
+ else:
+ # XXX: the renderer *expects* unicode as input encoding (ajung)
+ renderer = ReStructuredTextToHTMLRenderer(
+ unicode(info), self.request)
+ info = renderer.render()
+
+ evolvers.append({'from': gen, 'to': gen+1, 'info': info})
+
+ return evolvers
Added: zmi.core/trunk/src/zmi/core/generations/managers.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/generations/managers.pt (rev 0)
+++ zmi.core/trunk/src/zmi/core/generations/managers.pt 2009-11-21 07:14:14 UTC (rev 105933)
@@ -0,0 +1,62 @@
+<html metal:use-macro="context/@@standard_macros/view" i18n:domain="zope">
+<head>
+<title metal:fill-slot="title">Database Generations</title>
+</head>
+<body>
+<div metal:fill-slot="body">
+
+<span i18n:translate="">Database generations</span>
+
+<form tal:attributes="action request/URL"
+ tal:define="status view/evolve"
+ >
+
+<p tal:condition="status">
+<span tal:condition="status/to" i18n:translate=""
+ >The database was updated to generation <span
+ i18n:name=generation
+ tal:content="status/to">2</span> for <span
+ i18n:name=application
+ tal:content="status/app"
+ >foo.bar</span>.</span>
+<span tal:condition="not: status/to" i18n:translate=""
+ >The database is up to date for <span
+ i18n:name=application
+ tal:content="status/app"
+ >foo.bar</span>.</span>
+</p>
+
+<table border="1">
+
+<tr>
+ <th i18n:translate="">Application</th>
+ <th i18n:translate="">Minimum Generation</th>
+ <th i18n:translate="">Maximum Generation</th>
+ <th i18n:translate="">Current Database Generation</th>
+ <th i18n:translate="">Evolve?</th>
+</tr>
+<tr tal:repeat="app view/applications">
+ <td>
+ <a href=""
+ tal:attributes=
+ "href string:generations_managerdetails.html?id=${app/id}"
+ tal:content="app/id">foo.bar</a>
+ </td>
+ <td tal:content="app/min">1</td>
+ <td tal:content="app/max">10</td>
+ <td tal:content="app/generation">2</td>
+ <td>
+ <input type="submit" value=" evolve " name="evolve"
+ tal:condition="app/evolve"
+ tal:attributes="name app/evolve"
+ >
+ <span tal:condition="not: app/evolve"
+ i18n:translate=""
+ >No, up to date</span>
+ </td>
+</tr>
+</table>
+</form>
+</div>
+</body>
+</html>
Added: zmi.core/trunk/src/zmi/core/generations/managers.py
===================================================================
--- zmi.core/trunk/src/zmi/core/generations/managers.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/generations/managers.py 2009-11-21 07:14:14 UTC (rev 105933)
@@ -0,0 +1,298 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""UI for browsing database schema managers
+
+$Id: managers.py 85608 2008-04-22 18:34:09Z lgs $
+"""
+__docformat__ = 'restructuredtext'
+
+import transaction
+
+import zope.component
+
+from zope.app.generations.interfaces import ISchemaManager
+from zope.app.generations.generations import generations_key, Context
+
+request_key_format = "evolve-app-%s"
+
+class Managers(object):
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def _getdb(self):
+ # TODO: There needs to be a better api for this
+ return self.request.publication.db
+
+ def evolve(self):
+ """Perform a requested evolution
+
+ This method needs to use the component architecture, so
+ we'll set it up:
+
+ >>> from zope.app.testing.placelesssetup import setUp, tearDown
+ >>> setUp()
+
+ We also need a test request:
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+
+ We also need to give it a publication with a database:
+
+ >>> class Publication(object):
+ ... pass
+
+ >>> request.setPublication(Publication())
+ >>> from ZODB.tests.util import DB
+ >>> db = DB()
+ >>> request.publication.db = db
+
+ We need to define some schema managers. We'll define two
+ using the demo package:
+
+ >>> from zope.app.generations.generations import SchemaManager
+ >>> from zope.app.testing import ztapi
+ >>> app1 = SchemaManager(0, 1, 'zope.app.generations.demo')
+ >>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1')
+ >>> app2 = SchemaManager(0, 0, 'zope.app.generations.demo')
+ >>> ztapi.provideUtility(ISchemaManager, app2, 'foo.app2')
+
+ And we need to record some data for them in the database.
+
+ >>> from zope.app.generations.generations import evolve
+ >>> evolve(db)
+
+ This sets up the data and actually evolves app1:
+
+ >>> conn = db.open()
+ >>> conn.root()[generations_key]['foo.app1']
+ 1
+ >>> conn.root()[generations_key]['foo.app2']
+ 0
+
+ To evolve a data base schema, the user clicks on a submit
+ button. If they click on the button for add1, a item will
+ be added to the request, which we simulate:
+
+ >>> request.form['evolve-app-foo.app1'] = 'evolve'
+
+ We'll also increase the generation of app1:
+
+ >>> app1.generation = 2
+
+ Now we can create our view:
+
+ >>> view = Managers(None, request)
+
+ Now, if we call its `evolve` method, it should see that the
+ app1 evolve button was pressed and evolve app1 to the next
+ generation.
+
+ >>> status = view.evolve()
+ >>> conn.sync()
+ >>> conn.root()[generations_key]['foo.app1']
+ 2
+
+ The demo evolver just writes the generation to a database key:
+
+ >>> from zope.app.generations.demo import key
+ >>> conn.root()[key]
+ ('installed', 'installed', 2)
+
+ Note that, because the demo package has an install script,
+ we have entries for that script.
+
+ Which the returned status should indicate:
+
+ >>> status['app']
+ u'foo.app1'
+ >>> status['to']
+ 2
+
+ Now, given that the database is at the maximum generation
+ for app1, we can't evolve it further. Calling evolve again
+ won't evolve anything:
+
+ >>> status = view.evolve()
+ >>> conn.sync()
+ >>> conn.root()[generations_key]['foo.app1']
+ 2
+ >>> conn.root()[key]
+ ('installed', 'installed', 2)
+
+ as the status will indicate by returning a 'to' generation
+ of 0:
+
+ >>> status['app']
+ u'foo.app1'
+ >>> status['to']
+ 0
+
+ If the request doesn't have the key:
+
+ >>> request.form.clear()
+
+ Then calling evolve does nothing:
+
+ >>> view.evolve()
+ >>> conn.sync()
+ >>> conn.root()[generations_key]['foo.app1']
+ 2
+ >>> conn.root()[key]
+ ('installed', 'installed', 2)
+
+ We'd better clean upp:
+
+ >>> db.close()
+ >>> tearDown()
+ """
+
+ self.managers = managers = dict(
+ zope.component.getUtilitiesFor(ISchemaManager))
+ db = self._getdb()
+ conn = db.open()
+ try:
+ generations = conn.root().get(generations_key, ())
+ request = self.request
+ for key in generations:
+ generation = generations[key]
+ rkey = request_key_format % key
+ if rkey in request:
+ manager = managers[key]
+ if generation >= manager.generation:
+ return {'app': key, 'to': 0}
+ context = Context()
+ context.connection = conn
+ generation += 1
+ manager.evolve(context, generation)
+ generations[key] = generation
+ transaction.commit()
+ return {'app': key, 'to': generation}
+
+ return None
+ finally:
+ transaction.abort()
+ conn.close()
+
+ def applications(self):
+ """Get information about database-generation status
+
+ This method needs to use the component architecture, so
+ we'll set it up:
+
+ >>> from zope.app.testing.placelesssetup import setUp, tearDown
+ >>> setUp()
+
+ We also need a test request:
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+
+ We also need to give it a publication with a database:
+
+ >>> class Publication(object):
+ ... pass
+
+ >>> request.setPublication(Publication())
+ >>> from ZODB.tests.util import DB
+ >>> db = DB()
+ >>> request.publication.db = db
+
+ We need to define some schema managers. We'll define two
+ using the demo package:
+
+ >>> from zope.app.generations.generations import SchemaManager
+ >>> from zope.app.testing import ztapi
+ >>> app1 = SchemaManager(0, 1, 'zope.app.generations.demo')
+ >>> ztapi.provideUtility(ISchemaManager, app1, 'foo.app1')
+ >>> app2 = SchemaManager(0, 0, 'zope.app.generations.demo')
+ >>> ztapi.provideUtility(ISchemaManager, app2, 'foo.app2')
+
+ And we need to record some data for them in the database.
+
+ >>> from zope.app.generations.generations import evolve
+ >>> evolve(db)
+
+ This sets up the data and actually evolves app1:
+
+ >>> conn = db.open()
+ >>> conn.root()[generations_key]['foo.app1']
+ 1
+ >>> conn.root()[generations_key]['foo.app2']
+ 0
+
+ Now, let's increment app1's generation:
+
+ >>> app1.generation += 1
+
+ so we can evolve it.
+
+ Now we can create our view:
+
+ >>> view = Managers(None, request)
+
+ We call its applications method to get data about
+ application generations. We are required to call evolve
+ first:
+
+ >>> view.evolve()
+ >>> data = list(view.applications())
+ >>> data.sort(lambda d1, d2: cmp(d1['id'], d2['id']))
+
+ >>> for info in data:
+ ... print info['id']
+ ... print info['min'], info['max'], info['generation']
+ ... print 'evolve?', info['evolve'] or None
+ foo.app1
+ 0 2 1
+ evolve? evolve-app-foo.app1
+ foo.app2
+ 0 0 0
+ evolve? None
+
+ We'd better clean up:
+
+ >>> db.close()
+ >>> tearDown()
+
+ """
+ result = []
+
+ db = self._getdb()
+ conn = db.open()
+ try:
+ managers = self.managers
+ generations = conn.root().get(generations_key, ())
+ for key in generations:
+ generation = generations[key]
+ manager = managers.get(key)
+ if manager is None:
+ continue
+
+ result.append({
+ 'id': key,
+ 'min': manager.minimum_generation,
+ 'max': manager.generation,
+ 'generation': generation,
+ 'evolve': (generation < manager.generation
+ and request_key_format % key
+ or ''
+ ),
+ })
+
+ return result
+ finally:
+ conn.close()
Added: zmi.core/trunk/src/zmi/core/generations/tests.py
===================================================================
--- zmi.core/trunk/src/zmi/core/generations/tests.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/generations/tests.py 2009-11-21 07:14:14 UTC (rev 105933)
@@ -0,0 +1,92 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Generation-browser tests
+
+$Id: tests.py 95085 2009-01-27 08:43:42Z icemac $
+"""
+import unittest
+from zope.testing.doctestunit import DocTestSuite
+from zope.app.generations.testing import GenerationsLayer
+from zope.app.testing import ztapi, functional
+from zope.app.generations.generations import SchemaManager, generations_key
+from zope.app.generations.interfaces import ISchemaManager
+
+class TestDatabaseSchema(functional.BrowserTestCase):
+
+ def test(self):
+ root = self.getRootFolder()._p_jar.root()
+ appkey = 'zope.app.generations.demo'
+ root[generations_key][appkey] = 0
+ self.commit()
+ manager = SchemaManager(0, 3, 'zope.app.generations.demo')
+
+ ztapi.provideUtility(ISchemaManager, manager, appkey)
+
+ response = self.publish('/++etc++process/@@generations.html',
+ basic='globalmgr:globalmgrpw')
+ body = response.getBody()
+ body = ' '.join(body.split())
+ expect = ('zope.app.generations.demo</a> </td> '
+ '<td>0</td> <td>3</td> <td>0</td> '
+ '<td> <input type="submit" value=" evolve " '
+ 'name="evolve-app-zope.app.generations.demo"> </td>')
+ self.assert_(body.find(expect) > 0)
+
+ response = self.publish('/++etc++process/@@generations.html'
+ '?evolve-app-zope.app.generations.demo=evolve',
+ basic='globalmgr:globalmgrpw')
+ body = response.getBody()
+ body = ' '.join(body.split())
+ expect = ('zope.app.generations.demo</a> </td> '
+ '<td>0</td> <td>3</td> <td>1</td> '
+ '<td> <input type="submit" value=" evolve " '
+ 'name="evolve-app-zope.app.generations.demo"> </td>')
+ self.assert_(body.find(expect) > 0)
+
+ response = self.publish('/++etc++process/@@generations.html'
+ '?evolve-app-zope.app.generations.demo=evolve',
+ basic='globalmgr:globalmgrpw')
+ body = response.getBody()
+ body = ' '.join(body.split())
+ expect = ('zope.app.generations.demo</a> </td> '
+ '<td>0</td> <td>3</td> <td>2</td> '
+ '<td> <input type="submit" value=" evolve " '
+ 'name="evolve-app-zope.app.generations.demo"> </td>')
+ self.assert_(body.find(expect) > 0)
+
+ response = self.publish('/++etc++process/@@generations.html'
+ '?evolve-app-zope.app.generations.demo=evolve',
+ basic='globalmgr:globalmgrpw')
+ body = response.getBody()
+ body = ' '.join(body.split())
+ expect = ('zope.app.generations.demo</a> </td> '
+ '<td>0</td> <td>3</td> <td>3</td> '
+ '<td> <span>')
+ self.assert_(body.find(expect) > 0)
+
+ ztapi.unprovideUtility(ISchemaManager, appkey)
+
+
+
+def test_suite():
+ TestDatabaseSchema.layer = GenerationsLayer
+ return unittest.TestSuite((
+ DocTestSuite('zmi.core.generations.managers'),
+ DocTestSuite('zmi.core.generations.managerdetails'),
+ unittest.makeSuite(TestDatabaseSchema),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
More information about the checkins
mailing list