Author: jmorliaguet Date: Mon Jun 5 23:27:23 2006 New Revision: 3329 Modified: cpsskins/branches/paris-sprint-2006/storage/__init__.py cpsskins/branches/paris-sprint-2006/storage/configure.zcml cpsskins/branches/paris-sprint-2006/storage/storage.py cpsskins/branches/paris-sprint-2006/tests/test_storages.py
Log: - added a name chooser for storages Modified: cpsskins/branches/paris-sprint-2006/storage/__init__.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/storage/__init__.py (original) +++ cpsskins/branches/paris-sprint-2006/storage/__init__.py Mon Jun 5 23:27:23 2006 @@ -19,5 +19,5 @@ from zope.deferredimport import defineFrom -defineFrom('cpsskins.storage.storage', 'Storage') +defineFrom('cpsskins.storage.storage', 'Storage', 'NameChooser') Modified: cpsskins/branches/paris-sprint-2006/storage/configure.zcml ============================================================================== --- cpsskins/branches/paris-sprint-2006/storage/configure.zcml (original) +++ cpsskins/branches/paris-sprint-2006/storage/configure.zcml Mon Jun 5 23:27:23 2006 @@ -2,6 +2,13 @@ xmlns="http://namespaces.zope.org/zope" xmlns:cpsskins="http://namespaces.zope.org/cpsskins"> + <!-- Name chooser for storage items --> + <adapter + provides="zope.app.container.interfaces.INameChooser" + for=".interfaces.IStorage" + factory=".storage.NameChooser" + /> + <!-- Portlet storage --> <cpsskins:storage id="portlets" Modified: cpsskins/branches/paris-sprint-2006/storage/storage.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/storage/storage.py (original) +++ cpsskins/branches/paris-sprint-2006/storage/storage.py Mon Jun 5 23:27:23 2006 @@ -17,6 +17,7 @@ """ __docformat__ = "reStructuredText" +import re from zope.app.container.btree import BTreeContainer from zope.app.container.contained import Contained from zope.app.container.contained import ObjectAddedEvent @@ -31,8 +32,8 @@ from cpsskins.storage.interfaces import IStorage class Storage(BTreeContainer, Contained): - """A base storage class""" - + """A base storage class. + """ implements(IStorage) def add(self, object, name=u''): @@ -76,3 +77,60 @@ def purge(self): self.remove(list(self)) + + +class NameChooser(object): + """Name chooser for storage items. + + Allowed characters are alpha-numeric or minus (-) characters. + + minus characters may not be used at the beginning or at the end of the name + and they may not be repeated. + + If a name is already taken, a minus (-) and a number will be appended. + + If a name already ends with a minus and a number, both will be removed to + avoid generating names such as: ....-1-2-1-3-3 + + """ + implements(INameChooser) + + def __init__(self, context): + self.context = context + + def checkName(self, name, object): + if not name: + raise ValueError("Empty names are not allowed") + + if not isinstance(name, basestring): + raise TypeError("Item names must be strings") + + if self._sanatize(name) != name: + raise ValueError("Incorrect name") + + if name in self.context: + raise ValueError("The name is already taken") + + def chooseName(self, name, object): + if not name: + raise ValueError("Empty names are not allowed") + + if not isinstance(name, basestring): + raise TypeError("Item names must be strings") + + i = 1 + n = candidate = re.sub('-[0-9]+$', '', self._sanatize(name)) + while n in self.context: + n = u'%s-%s' % (candidate, i) + i += 1 + + self.checkName(n, object) + return n + + def _sanatize(self, name): + name = unicode(name) + n = re.sub('[^A-Za-z0-9]+', '-', name) + n = re.sub('-+', '-', n) + n = re.sub('^-', '', n) + n = re.sub('-$', '', n) + return n Modified: cpsskins/branches/paris-sprint-2006/tests/test_storages.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/tests/test_storages.py (original) +++ cpsskins/branches/paris-sprint-2006/tests/test_storages.py Mon Jun 5 23:27:23 2006 @@ -20,14 +20,56 @@ import unittest from zope.testing.doctestunit import DocTestSuite -from zope.app.testing.placelesssetup import PlacelessSetup from cpsskins.relations import Predicate, TestRelate as Relate from cpsskins.relations import MonadicRelation as Monad from cpsskins.relations import DyadicRelation as Dyad, TriadicRelation as Triad +from cpsskins.storage import Storage, NameChooser from cpsskins.storage.relations import RelationStorage -class TestRelationStorage(PlacelessSetup, unittest.TestCase): +class TestStorageNameChooser(unittest.TestCase): + + def setUp(self): + self.storage = Storage() + self.chooseName = NameChooser(self.storage).chooseName + + def test_1(self): + self.assertEqual(u'123abc', self.chooseName(u'123abc', object())) + + def test_2(self): + self.assertEqual(u'123-abc', self.chooseName(u'123-abc', object())) + + def test_3(self): + self.assertEqual(u'a', self.chooseName(u'a:/@+', object())) + + def test_4(self): + self.assertEqual(u'a-b', self.chooseName(u'a:/@+b', object())) + + def test_5(self): + self.assertEqual(u'a-b-c', self.chooseName(u'a:/@+b-c', object())) + + def test_6(self): + self.assertEqual(u'a', self.chooseName(u'-a-', object())) + + def test_7(self): + self.assertEqual(u'a', self.chooseName(u'a-42', object())) + + def test_8(self): + self.assertEqual(u'a-b', self.chooseName(u'a---b', object())) + + def test_9(self): + self.storage[u'a'] = object() + self.assertEqual(u'a-1', self.chooseName(u'a', object())) + + def test_10(self): + self.storage[u'a'] = object() + self.storage[u'a-1'] = object() + self.assertEqual(u'a-2', self.chooseName(u'a-1', object())) + + def teardown(self): + del self.storage + +class TestRelationStorage(unittest.TestCase): def makeTestObject(self): return RelationStorage() @@ -195,6 +237,7 @@ def test_suite(): return unittest.TestSuite(( DocTestSuite('cpsskins.storage.relations'), + unittest.makeSuite(TestStorageNameChooser), unittest.makeSuite(TestRelationStorage), )) -- http://lists.nuxeo.com/mailman/listinfo/z3lab-checkins