Thank you very much for providing this fine implementation. There are many
places where I discovered you had implemented the feature as I would hope
(using RegistryEditFormView, and making the table sortable). I think there
is a chance for an oops in the code, and there are some historic code that
I hope you can clean up.
The page titles in the browser are broken, breadcrumbs are missing. This is
because the there is no breadcrumb adapter This might help:
{{{
@adapter(INameBlackListSet)
class NameBlacklistSetBreadcrumb(Breadcrumb):
"""Return a breadcrumb for an `INameBlackListSet`."""
text = "Name Blacklist"
@adapter(INameBlackList)
class NameBlacklistBreadcrumb(Breadcrumb):
"""Return a breadcrumb for an `INameBlackList`."""
> +class NameBlacklistSetView(LaunchpadView):
> + """View for /+nameblacklists top level collection."""
> +
> + page_title = (
> + 'Blacklist for names of Launchpad pillars, persons, and teams')
> + label = page_title
I think
Blacklist for names of Launchpad pillars and persons
is good enough. I think user and team are really subclasses of IPerson.
One day we will also have agent for the bots we have impersonating users.
A 404 is correct. The interface says None will be returned (404), but int()
will raise an Exception. Maybe int() should be pushed into get() or this
method chooses to raise the 404 itself when the name cannot be cast.
I like @@+related-pages in the side bar, but I do not see anything registered
to provide this menu. Is this needed?
...
> === modified file 'lib/lp/registry/tests/test_nameblacklist.py'
> --- lib/lp/registry/tests/test_nameblacklist.py 2010-10-04 19:50:45 +0000
> +++ lib/lp/registry/tests/test_nameblacklist.py 2010-11-09 05:26:23 +0000
...
> @@ -52,21 +64,25 @@
> # A name that is blacklisted returns the id of the row in the
> # NameBlacklist table that matched. Rows are tried in order, and the
> # first match is returned.
> - self.failUnlessEqual(self.name_blacklist_match("foobar"), -200)
> - self.failUnlessEqual(self.name_blacklist_match("barfoo"), -100)
> + self.failUnlessEqual(
> + self.name_blacklist_match("foobar"),
> + self.caret_foo_exp.id)
> + self.failUnlessEqual(
> + self.name_blacklist_match("barfoo"),
> + self.foo_exp.id)
Hi Edwin.
Thank you very much for providing this fine implementation. There are many mView, and making the table sortable). I think there
places where I discovered you had implemented the feature as I would hope
(using RegistryEditFor
is a chance for an oops in the code, and there are some historic code that
I hope you can clean up.
The page titles in the browser are broken, breadcrumbs are missing. This is
because the there is no breadcrumb adapter This might help:
{{{
@adapter( INameBlackListS et) tBreadcrumb( Breadcrumb) : Set`."" "
class NameBlacklistSe
"""Return a breadcrumb for an `INameBlackList
text = "Name Blacklist"
@adapter( INameBlackList) eadcrumb( Breadcrumb) : `."""
class NameBlacklistBr
"""Return a breadcrumb for an `INameBlackList
@property
def text(self):
return self.context.regexp
}}}
> === added file 'lib/lp/ registry/ browser/ nameblacklist. py' registry/ browser/ nameblacklist. py 1970-01-01 00:00:00 +0000 registry/ browser/ nameblacklist. py 2010-11-09 05:26:23 +0000 dView(NameBlack listValidationM ixin, LaunchpadFormView): widget( 'regexp' , TextWidget, displayWidth=60)
> --- lib/lp/
> +++ lib/lp/
...
> +class NameBlacklistAd
> + """View for adding a blacklist expression."""
> +
> + schema = INameBlacklist
> + field_names = ['regexp', 'comment']
> + label = "Add a new blacklist expression"
> +
> + custom_
> +
> + @property
> + def page_title(self):
> + """The page title."""
> + return self.label
This could be
page_title = label
...
> +class NameBlacklistSe tView(Launchpad View):
> + """View for /+nameblacklists top level collection."""
> +
> + page_title = (
> + 'Blacklist for names of Launchpad pillars, persons, and teams')
> + label = page_title
I think
Blacklist for names of Launchpad pillars and persons
is good enough. I think user and team are really subclasses of IPerson.
One day we will also have agent for the bots we have impersonating users.
> +class NameBlacklistSe tNavigation( Navigation) : get(int( name))
> +
> + usedfor = INameBlacklistSet
> +
> + def traverse(self, name):
> + return self.context.
Do I get a 404 or a 500 for launchpad. net/+nameblackl ist/word
http://
A 404 is correct. The interface says None will be returned (404), but int()
will raise an Exception. Maybe int() should be pushed into get() or this
method chooses to raise the 404 itself when the name cannot be cast.
...
> === added file 'lib/lp/ registry/ model/nameblack list.py' registry/ model/nameblack list.py 1970-01-01 00:00:00 +0000 registry/ model/nameblack list.py 2010-11-09 05:26:23 +0000 Set`."" " NameBlacklist) NameBlacklist, NameBlacklist.id == id).one()
> --- lib/lp/
> +++ lib/lp/
...
> + def get(self, id):
> + """See `INameBlacklist
> + store = IStore(
> + return store.find(
I think this method could cast the id to an int and return None when an
exception is raised to avoid a query that will always return None.
...
> === added file 'lib/lp/ registry/ templates/ nameblacklists- index.pt' registry/ templates/ nameblacklists- index.pt 1970-01-01 00:00:00 +0000 registry/ templates/ nameblacklists- index.pt 2010-11-09 05:26:23 +0000 www.w3. org/1999/ xhtml" xml.zope. org/namespaces/ tal" xml.zope. org/namespaces/ metal" xml.zope. org/namespaces/ i18n" macro=" view/macro: page/main_ side" "launchpad" > slot="side" > +global- actions" /> @@+related- pages" />
> --- lib/lp/
> +++ lib/lp/
> @@ -0,0 +1,40 @@
> +<html
> + xmlns="http://
> + xmlns:tal="http://
> + xmlns:metal="http://
> + xmlns:i18n="http://
> + xml:lang="en"
> + lang="en"
> + dir="ltr"
> + metal:use-
> + i18n:domain=
> +
> + <body>
> +
> + <tal:side metal:fill-
> + <tal:menu replace="structure view/@@
> + <tal:menu replace="structure context/
I like @@+related-pages in the side bar, but I do not see anything registered
to provide this menu. Is this needed?
...
> === modified file 'lib/lp/ registry/ tests/test_ nameblacklist. py' registry/ tests/test_ nameblacklist. py 2010-10-04 19:50:45 +0000 registry/ tests/test_ nameblacklist. py 2010-11-09 05:26:23 +0000 Equal(self. name_blacklist_ match(" foobar" ), -200) Equal(self. name_blacklist_ match(" barfoo" ), -100) Equal( blacklist_ match(" foobar" ), foo_exp. id) Equal( blacklist_ match(" barfoo" ),
> --- lib/lp/
> +++ lib/lp/
...
> @@ -52,21 +64,25 @@
> # A name that is blacklisted returns the id of the row in the
> # NameBlacklist table that matched. Rows are tried in order, and the
> # first match is returned.
> - self.failUnless
> - self.failUnless
> + self.failUnless
> + self.name_
> + self.caret_
> + self.failUnless
> + self.name_
> + self.foo_exp.id)
from https:/ /dev.launchpad. net/TestsStyleG uide:
When asserting for equality use the form assertEqual
Can you update all these old tests to assertEqual?
...
> +class TestNameBlackli stSet(TestCaseW ithFactory) : nalLayer lacklistSet, self).setUp() ILaunchpadCeleb rities) .registry_ experts makePerson( ) experts. addMember( registry_ expert, registry_expert) registry_ expert)
> +
> + layer = DatabaseFunctio
> +
> + def setUp(self):
> + super(TestNameB
> + registry_experts = getUtility(
> + registry_expert = self.factory.
> + login(ADMIN_EMAIL)
> + registry_
> + login_person(
You can replace the last 5 lines with
login_ celebrity( 'registry_ experts' )
> + self.name_ blacklist_ set = getUtility( INameBlacklistS et) with_one_ arg(self) : t.create( regexp) . blacklist_ set.create( u'foo') (verifyObject( INameBlacklist, name_blacklist)) ls(u'foo' , name_blacklist. regexp) comment)
> +
> + def test_create_
> + # Test NameBlacklistSe
> + name_blacklist = self.name_
> + self.assertTrue
> + self.assertEqua
> + self.assertIs(None, name_blacklist.
Use the singular form of assertEqual().
> + def test_create_ with_two_ args(self) : t.create( regexp, comment). blacklist_ set.create( u'foo', u'bar') (verifyObject( INameBlacklist, name_blacklist)) ls(u'foo' , name_blacklist. regexp) ls(u'bar' , name_blacklist. comment) t.get() . blacklist_ set.create( u'foo', u'bar') name_blacklist) blacklist_ set.get( name_blacklist. id) ls(name_ blacklist, retrieved)
> + # Test NameBlacklistSe
> + name_blacklist = self.name_
> + self.assertTrue
> + self.assertEqua
> + self.assertEqua
> +
> + def test_get(self):
> + # Test NameBlacklistSe
> + name_blacklist = self.name_
> + store = IStore(
> + store.flush()
> + retrieved = self.name_
> + self.assertEqua
If you inline the cast to int() we need a variation of this test.