Merge lp:~salgado/launchpad/real-breadcrumbs into lp:launchpad

Proposed by Guilherme Salgado
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~salgado/launchpad/real-breadcrumbs
Merge into: lp:launchpad
Diff against target: None lines
To merge this branch: bzr merge lp:~salgado/launchpad/real-breadcrumbs
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+10609@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote :

= Summary =

When I redesigned our breadcrumb infrastructure I made the
BreadcbrumbBuilder abstraction unnecessary, but my branch was already
too big so I left it to be removed later.

== Proposed fix ==

Remove BreadcrumbBuilder by directly adapting objects into IBreadcrumbs,
instead of adapting them into IBreadcrumbBuilder and calling
make_breadcrumb() on it.

In order to actually complete this I had to change all existing adapters
so that they inherit from Breadcrumb rather than BreadcrumbBuilder, but
that brought the diff to 1.4KLOC, so I'll ask for a review of that
later.

== Pre-implementation notes ==

== Implementation details ==

== Tests ==

./bin/test -vvt hierarchical-menu.txt

== Demo and Q/A ==

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/webapp/interfaces.py
  lib/canonical/launchpad/browser/launchpad.py
  lib/canonical/launchpad/webapp/breadcrumb.py
  lib/canonical/launchpad/zcml/personproduct.zcml
  lib/canonical/launchpad/browser/personproduct.py
  lib/canonical/launchpad/doc/hierarchical-menu.txt

== Pyflakes Doctest notices ==

lib/canonical/launchpad/doc/hierarchical-menu.txt
    144: 'canonical_url' imported but unused

This has been removed already.

== Pylint notices ==

lib/canonical/launchpad/browser/launchpad.py
    103: [F0401] Unable to import 'lazr.uri' (No module named uri)

Revision history for this message
Aaron Bentley (abentley) wrote :
Download full text (17.8 KiB)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

 status approved

The only think I see that could be improved is in Heirarchy.items, noted
below.

Guilherme Salgado wrote:
> Guilherme Salgado has proposed merging lp:~salgado/launchpad/real-breadcrumbs into lp:launchpad/devel.
>
> Requested reviews:
> Canonical Launchpad Engineering (launchpad)
>
> = Summary =
>
> When I redesigned our breadcrumb infrastructure I made the
> BreadcbrumbBuilder abstraction unnecessary, but my branch was already
> too big so I left it to be removed later.
>
> == Proposed fix ==
>
> Remove BreadcrumbBuilder by directly adapting objects into IBreadcrumbs,
> instead of adapting them into IBreadcrumbBuilder and calling
> make_breadcrumb() on it.
>
> In order to actually complete this I had to change all existing adapters
> so that they inherit from Breadcrumb rather than BreadcrumbBuilder, but
> that brought the diff to 1.4KLOC, so I'll ask for a review of that
> later.
>
> == Pre-implementation notes ==
>
> == Implementation details ==
>
> == Tests ==
>
> ./bin/test -vvt hierarchical-menu.txt
>
> == Demo and Q/A ==
>
>
> = Launchpad lint =
>
> Checking for conflicts. and issues in doctests and templates.
> Running jslint, xmllint, pyflakes, and pylint.
> Using normal rules.
>
> Linting changed files:
> lib/canonical/launchpad/webapp/interfaces.py
> lib/canonical/launchpad/browser/launchpad.py
> lib/canonical/launchpad/webapp/breadcrumb.py
> lib/canonical/launchpad/zcml/personproduct.zcml
> lib/canonical/launchpad/browser/personproduct.py
> lib/canonical/launchpad/doc/hierarchical-menu.txt
>
>
> == Pyflakes Doctest notices ==
>
> lib/canonical/launchpad/doc/hierarchical-menu.txt
> 144: 'canonical_url' imported but unused
>
> This has been removed already.
>
>
> == Pylint notices ==
>
> lib/canonical/launchpad/browser/launchpad.py
> 103: [F0401] Unable to import 'lazr.uri' (No module named uri)
>
>
> === modified file 'lib/canonical/launchpad/browser/launchpad.py'
> --- lib/canonical/launchpad/browser/launchpad.py 2009-08-20 05:06:34 +0000
> +++ lib/canonical/launchpad/browser/launchpad.py 2009-08-24 17:47:34 +0000
> @@ -96,7 +96,7 @@
> StandardLaunchpadFacets, canonical_name, canonical_url, custom_widget,
> stepto)
> from canonical.launchpad.webapp.interfaces import (
> - IBreadcrumbBuilder, ILaunchBag, ILaunchpadRoot, INavigationMenu,
> + IBreadcrumb, ILaunchBag, ILaunchpadRoot, INavigationMenu,
> NotFoundError, POSTToNonCanonicalURL)
> from canonical.launchpad.webapp.publisher import RedirectionView
> from canonical.launchpad.webapp.authorization import check_permission
> @@ -226,39 +226,31 @@
> The list starts with the breadcrumb closest to the hierarchy root.
> """
> breadcrumbs = []
> - for builder in self._getBreadcrumbBuilders():
> - crumb = builder.make_breadcrumb()
> - if crumb is not None:
> - breadcrumbs.append(crumb)
> - return breadcrumbs
> -
> - def _getBreadcrumbBuilders(self):
> - builders = []
> for obj in self.objects:
> - builder = queryAdapter(obj, IBreadcrumbBuilder)
> -...

review: Approve
Revision history for this message
Guilherme Salgado (salgado) wrote :

Hi Aaron,

I did the change you suggested, but I need to cast the result of enumerate(breadcrumbs) into a list so that I can feed it to reversed().

Also, https://pastebin.canonical.com/21444/ has the remaining changes. It is huge but has just mechanical changes I did using sed. To test, './bin/test -vvt test_breadcrumbs'.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/browser/launchpad.py'
--- lib/canonical/launchpad/browser/launchpad.py 2009-08-20 05:06:34 +0000
+++ lib/canonical/launchpad/browser/launchpad.py 2009-08-24 17:47:34 +0000
@@ -96,7 +96,7 @@
96 StandardLaunchpadFacets, canonical_name, canonical_url, custom_widget,96 StandardLaunchpadFacets, canonical_name, canonical_url, custom_widget,
97 stepto)97 stepto)
98from canonical.launchpad.webapp.interfaces import (98from canonical.launchpad.webapp.interfaces import (
99 IBreadcrumbBuilder, ILaunchBag, ILaunchpadRoot, INavigationMenu,99 IBreadcrumb, ILaunchBag, ILaunchpadRoot, INavigationMenu,
100 NotFoundError, POSTToNonCanonicalURL)100 NotFoundError, POSTToNonCanonicalURL)
101from canonical.launchpad.webapp.publisher import RedirectionView101from canonical.launchpad.webapp.publisher import RedirectionView
102from canonical.launchpad.webapp.authorization import check_permission102from canonical.launchpad.webapp.authorization import check_permission
@@ -226,39 +226,31 @@
226 The list starts with the breadcrumb closest to the hierarchy root.226 The list starts with the breadcrumb closest to the hierarchy root.
227 """227 """
228 breadcrumbs = []228 breadcrumbs = []
229 for builder in self._getBreadcrumbBuilders():
230 crumb = builder.make_breadcrumb()
231 if crumb is not None:
232 breadcrumbs.append(crumb)
233 return breadcrumbs
234
235 def _getBreadcrumbBuilders(self):
236 builders = []
237 for obj in self.objects:229 for obj in self.objects:
238 builder = queryAdapter(obj, IBreadcrumbBuilder)230 breadcrumb = queryAdapter(obj, IBreadcrumb)
239 if builder is not None:231 if breadcrumb is not None:
240 builders.append(builder)232 breadcrumbs.append(breadcrumb)
241233
242 host = URI(self.request.getURL()).host234 host = URI(self.request.getURL()).host
243 if (len(builders) == 0235 if (len(breadcrumbs) == 0
244 or host == allvhosts.configs['mainsite'].hostname):236 or host == allvhosts.configs['mainsite'].hostname):
245 return builders237 return breadcrumbs
246238
247 # If we got this far it means we have breadcrumbs and we're not on the239 # If we got this far it means we have breadcrumbs and we're not on the
248 # mainsite, so we'll sneak an extra breadcrumb for the vhost we're on.240 # mainsite, so we'll sneak an extra breadcrumb for the vhost we're on.
249 vhost = host.split('.')[0]241 vhost = host.split('.')[0]
250242
251 # Iterate over the context of our builders in reverse order and for243 # Iterate over the context of our breadcrumbs in reverse order and for
252 # the first one we find an adapter named after the vhost we're on,244 # the first one we find an adapter named after the vhost we're on,
253 # generate an extra breadcrumb and insert it in our list.245 # generate an extra breadcrumb and insert it in our list.
254 for idx in reversed(xrange(len(builders))):246 for idx in reversed(xrange(len(breadcrumbs))):
255 builder = builders[idx]247 breadcrumb = breadcrumbs[idx]
256 extra_builder = queryAdapter(248 extra_breadcrumb = queryAdapter(
257 builder.context, IBreadcrumbBuilder, name=vhost)249 breadcrumb.context, IBreadcrumb, name=vhost)
258 if extra_builder is not None:250 if extra_breadcrumb is not None:
259 builders.insert(idx + 1, extra_builder)251 breadcrumbs.insert(idx + 1, extra_breadcrumb)
260 break252 break
261 return builders253 return breadcrumbs
262254
263 def render(self):255 def render(self):
264 """Render the hierarchy HTML.256 """Render the hierarchy HTML.
265257
=== modified file 'lib/canonical/launchpad/browser/personproduct.py'
--- lib/canonical/launchpad/browser/personproduct.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/launchpad/browser/personproduct.py 2009-08-24 17:47:34 +0000
@@ -5,7 +5,7 @@
55
6__metaclass__ = type6__metaclass__ = type
7__all__ = [7__all__ = [
8 'PersonProductBreadcrumbBuilder',8 'PersonProductBreadcrumb',
9 'PersonProductFacets',9 'PersonProductFacets',
10 'PersonProductNavigation',10 'PersonProductNavigation',
11 ]11 ]
@@ -17,7 +17,7 @@
17from lp.code.interfaces.branchnamespace import (17from lp.code.interfaces.branchnamespace import (
18 get_branch_namespace)18 get_branch_namespace)
19from canonical.launchpad.interfaces.personproduct import IPersonProduct19from canonical.launchpad.interfaces.personproduct import IPersonProduct
20from canonical.launchpad.webapp.breadcrumb import BreadcrumbBuilder20from canonical.launchpad.webapp.breadcrumb import Breadcrumb
21from canonical.launchpad.webapp import (21from canonical.launchpad.webapp import (
22 Link, Navigation, StandardLaunchpadFacets)22 Link, Navigation, StandardLaunchpadFacets)
23from canonical.launchpad.webapp.interfaces import NotFoundError23from canonical.launchpad.webapp.interfaces import NotFoundError
@@ -38,8 +38,8 @@
38 return branch38 return branch
3939
4040
41class PersonProductBreadcrumbBuilder(BreadcrumbBuilder):41class PersonProductBreadcrumb(Breadcrumb):
42 """Builds a breadcrumb for an `IPersonProduct`."""42 """Breadcrumb for an `IPersonProduct`."""
4343
44 @property44 @property
45 def text(self):45 def text(self):
4646
=== modified file 'lib/canonical/launchpad/doc/hierarchical-menu.txt'
--- lib/canonical/launchpad/doc/hierarchical-menu.txt 2009-08-17 19:22:06 +0000
+++ lib/canonical/launchpad/doc/hierarchical-menu.txt 2009-08-24 17:47:34 +0000
@@ -60,7 +60,7 @@
6060
61The Hierarchy class builds the breadcrumbs by looking at each object in61The Hierarchy class builds the breadcrumbs by looking at each object in
62the request.traversed_objects attribute. If a traversed object can be62the request.traversed_objects attribute. If a traversed object can be
63adapted to IBreadcrumbBuilder, then it is added to the breadcrumbs list.63adapted to IBreadcrumb, then it is added to the breadcrumbs list.
6464
65We'll add the objects to the request's list of traversed objects so65We'll add the objects to the request's list of traversed objects so
66the hierarchy will discover them.66the hierarchy will discover them.
@@ -71,38 +71,35 @@
71 ... [root, cookbook, recipe])71 ... [root, cookbook, recipe])
7272
73The hierarchy's list of breadcrumbs is empty since none of the objects73The hierarchy's list of breadcrumbs is empty since none of the objects
74have an IBreadcrumbBuilder adapter.74have an IBreadcrumb adapter.
7575
76 >>> hierarchy = getMultiAdapter((recipe, request), name='+hierarchy')76 >>> hierarchy = getMultiAdapter((recipe, request), name='+hierarchy')
77 >>> hierarchy.items()77 >>> hierarchy.items()
78 []78 []
7979
80The ICookbook and IRecipe breadcrumb objects show up in the hierarchy80The ICookbook and IRecipe breadcrumb objects show up in the hierarchy after
81after IBreadcrumbBuilder adapters are registered for them. The81IBreadcrumb adapters are registered for them. The hierarchy builds a list of
82hierarchy builds a list of breadcrumbs starting with the breadcrumb82breadcrumbs starting with the breadcrumb closest to the hierarchy root.
83closest to the hierarchy root.
8483
85 >>> from canonical.launchpad.webapp.breadcrumb import BreadcrumbBuilder84 >>> from canonical.launchpad.webapp.breadcrumb import Breadcrumb
8685
87 # Note that the Hierarchy assigns the breadcrumb's URL, but we need to86 # Note that the Hierarchy assigns the breadcrumb's URL, but we need to
88 # give it a valid .text attribute.87 # give it a valid .text attribute.
89 >>> class TextualBreadcrumbBuilder(BreadcrumbBuilder):88 >>> class TextualBreadcrumb(Breadcrumb):
90 ... @property89 ... @property
91 ... def text(self):90 ... def text(self):
92 ... return self.context.name.capitalize().replace('-', ' ')91 ... return self.context.name.capitalize().replace('-', ' ')
9392
94 >>> from canonical.launchpad.webapp.interfaces import IBreadcrumbBuilder93 >>> from canonical.launchpad.webapp.interfaces import IBreadcrumb
9594
96 >>> provideAdapter(95 >>> provideAdapter(TextualBreadcrumb, [ICookbook], IBreadcrumb)
97 ... TextualBreadcrumbBuilder, [ICookbook], IBreadcrumbBuilder)96 >>> provideAdapter(TextualBreadcrumb, [IRecipe], IBreadcrumb)
98 >>> provideAdapter(
99 ... TextualBreadcrumbBuilder, [IRecipe], IBreadcrumbBuilder)
10097
101 >>> hierarchy.items()98 >>> hierarchy.items()
102 [<Breadcrumb99 [<TextualBreadcrumb
103 url='http://launchpad.dev/joy-of-cooking'100 url='http://launchpad.dev/joy-of-cooking'
104 text='Joy of cooking'>,101 text='Joy of cooking'>,
105 <Breadcrumb102 <TextualBreadcrumb
106 url='http://launchpad.dev/joy-of-cooking/spam'103 url='http://launchpad.dev/joy-of-cooking/spam'
107 text='Spam'>]104 text='Spam'>]
108105
@@ -114,59 +111,45 @@
114 ... 'http://launchpad.dev/+cooker/jamie',111 ... 'http://launchpad.dev/+cooker/jamie',
115 ... [root, cooker])112 ... [root, cooker])
116113
117 >>> provideAdapter(114 >>> provideAdapter(TextualBreadcrumb, [ICooker], IBreadcrumb)
118 ... TextualBreadcrumbBuilder, [ICooker], IBreadcrumbBuilder)
119115
120 >>> cooker_hierarchy = getMultiAdapter(116 >>> cooker_hierarchy = getMultiAdapter(
121 ... (cooker, cooker_request), name='+hierarchy')117 ... (cooker, cooker_request), name='+hierarchy')
122 >>> cooker_hierarchy.items()118 >>> cooker_hierarchy.items()
123 [<Breadcrumb url='http://launchpad.dev/+cooker/jamie' text='Jamie'>]119 [<TextualBreadcrumb url='http://launchpad.dev/+cooker/jamie' text='Jamie'>]
124120
125121
126== Building IBreadcrumb objects ==122== Building IBreadcrumb objects ==
127123
128The construction of breadcrumb objects is handled by an124The construction of breadcrumb objects is handled by an IBreadcrumb adapter,
129IBreadcrumbBuilder factory. The factory adapts a context object and125which adapts a context object and produces an IBreadcrumb object for that
130produces an IBreadcrumb object for that context.126context. The default adapter provides the url attribute, but the breadcrumb's
131127text must be overriden in subclasses.
132The builder holds a temporary copy of a breadcrumb until the breadcrumb is in
133a usable state. We can ask the builder for a finished breadcrumb after both
134the text and the URL have been specified.
135128
136 >>> from canonical.launchpad.webapp.interfaces import IBreadcrumb129 >>> from canonical.launchpad.webapp.interfaces import IBreadcrumb
137 >>> from zope.interface.verify import verifyObject130 >>> from zope.interface.verify import verifyObject
138131 >>> breadcrumb = Breadcrumb(cookbook)
139 >>> builder = BreadcrumbBuilder(cookbook)
140 >>> verifyObject(IBreadcrumbBuilder, builder)
141 True
142
143 >>> builder.text = 'Joy of cooking'
144
145 >>> breadcrumb = builder.make_breadcrumb()
146 >>> verifyObject(IBreadcrumb, breadcrumb)132 >>> verifyObject(IBreadcrumb, breadcrumb)
147 True133 True
134 >>> print breadcrumb.text
135 None
148 >>> breadcrumb136 >>> breadcrumb
149 <Breadcrumb137 <Breadcrumb
150 url='http://launchpad.dev/joy-of-cooking'138 url='http://launchpad.dev/joy-of-cooking'
151 text='Joy of cooking'>139 text='None'>
152140
153The breadcrumb's attributes can be overridden with subclassing and141As said above, the breadcrumb's attributes can be overridden with subclassing
154Python properties.142and Python properties.
155143
156 >>> from canonical.launchpad.webapp.publisher import canonical_url144 >>> from canonical.launchpad.webapp.publisher import canonical_url
157145 >>> class DynamicBreadcrumb(Breadcrumb):
158 >>> class DynamicBreadcrumbBuilder(BreadcrumbBuilder):
159 ... @property146 ... @property
160 ... def text(self):147 ... def text(self):
161 ... return self.context.name.capitalize().replace('-', ' ')148 ... return self.context.name.capitalize().replace('-', ' ')
162 ...
163 ... @property
164 ... def url(self):
165 ... return canonical_url(self.context)
166149
167 >>> builder = DynamicBreadcrumbBuilder(cookbook)150 >>> breadcrumb = DynamicBreadcrumb(cookbook)
168 >>> builder.make_breadcrumb()151 >>> breadcrumb
169 <Breadcrumb152 <DynamicBreadcrumb
170 url='http://launchpad.dev/joy-of-cooking'153 url='http://launchpad.dev/joy-of-cooking'
171 text='Joy of cooking'>154 text='Joy of cooking'>
172155
@@ -186,9 +169,9 @@
186 >>> provideAdapter(169 >>> provideAdapter(
187 ... RecipeImageDisplayAPI, [IRecipe], IPathAdapter, 'image')170 ... RecipeImageDisplayAPI, [IRecipe], IPathAdapter, 'image')
188171
189 >>> builder = DynamicBreadcrumbBuilder(recipe)172 >>> breadcrumb = DynamicBreadcrumb(recipe)
190 >>> builder.make_breadcrumb()173 >>> breadcrumb
191 <Breadcrumb174 <DynamicBreadcrumb
192 url='http://launchpad.dev/joy-of-cooking/spam'175 url='http://launchpad.dev/joy-of-cooking/spam'
193 text='Spam'176 text='Spam'
194 icon='<img src="/@@/recipe"/>'>177 icon='<img src="/@@/recipe"/>'>
@@ -204,9 +187,9 @@
204 >>> print queryAdapter(cookbook, IPathAdapter, name='image').icon()187 >>> print queryAdapter(cookbook, IPathAdapter, name='image').icon()
205 None188 None
206189
207 >>> builder = DynamicBreadcrumbBuilder(cookbook)190 >>> breadcrumb = DynamicBreadcrumb(cookbook)
208 >>> builder.make_breadcrumb()191 >>> breadcrumb
209 <Breadcrumb192 <DynamicBreadcrumb
210 url='http://launchpad.dev/joy-of-cooking'193 url='http://launchpad.dev/joy-of-cooking'
211 text='Joy of cooking'>194 text='Joy of cooking'>
212195
@@ -221,7 +204,6 @@
221consistency across the site.204consistency across the site.
222205
223 >>> from canonical.launchpad.browser.launchpad import Hierarchy206 >>> from canonical.launchpad.browser.launchpad import Hierarchy
224
225 >>> class CustomHierarchy(Hierarchy):207 >>> class CustomHierarchy(Hierarchy):
226 ... @property208 ... @property
227 ... def objects(self):209 ... def objects(self):
@@ -229,7 +211,7 @@
229211
230 >>> spammy_hierarchy = CustomHierarchy(root, request)212 >>> spammy_hierarchy = CustomHierarchy(root, request)
231 >>> spammy_hierarchy.items()213 >>> spammy_hierarchy.items()
232 [<Breadcrumb214 [<TextualBreadcrumb
233 url='http://launchpad.dev/joy-of-cooking/spam'215 url='http://launchpad.dev/joy-of-cooking/spam'
234 text='Spam'216 text='Spam'
235 icon='<img src="/@@/recipe"/>'>]217 icon='<img src="/@@/recipe"/>'>]
@@ -276,7 +258,7 @@
276 >>> breadcrumb_no_icon, breadcrumb_with_icon = hierarchy.items()258 >>> breadcrumb_no_icon, breadcrumb_with_icon = hierarchy.items()
277259
278 >>> breadcrumb_no_icon260 >>> breadcrumb_no_icon
279 <Breadcrumb261 <TextualBreadcrumb
280 url='http://launchpad.dev/joy-of-cooking'262 url='http://launchpad.dev/joy-of-cooking'
281 text='Joy of cooking'>263 text='Joy of cooking'>
282264
@@ -284,7 +266,7 @@
284 False266 False
285267
286 >>> breadcrumb_with_icon268 >>> breadcrumb_with_icon
287 <Breadcrumb269 <TextualBreadcrumb
288 url='http://launchpad.dev/joy-of-cooking/spam'270 url='http://launchpad.dev/joy-of-cooking/spam'
289 text='Spam'271 text='Spam'
290 icon='<img src="/@@/recipe"/>'>272 icon='<img src="/@@/recipe"/>'>
291273
=== modified file 'lib/canonical/launchpad/webapp/breadcrumb.py'
--- lib/canonical/launchpad/webapp/breadcrumb.py 2009-08-17 20:20:17 +0000
+++ lib/canonical/launchpad/webapp/breadcrumb.py 2009-08-24 17:47:34 +0000
@@ -7,7 +7,6 @@
77
8__all__ = [8__all__ = [
9 'Breadcrumb',9 'Breadcrumb',
10 'BreadcrumbBuilder',
11 ]10 ]
1211
1312
@@ -16,38 +15,15 @@
16from zope.interface import implements15from zope.interface import implements
1716
18from canonical.launchpad.webapp import canonical_url17from canonical.launchpad.webapp import canonical_url
19from canonical.launchpad.webapp.interfaces import (18from canonical.launchpad.webapp.interfaces import IBreadcrumb
20 IBreadcrumb, IBreadcrumbBuilder)
2119
2220
23class Breadcrumb:21class Breadcrumb:
24 """See `IBreadcrumb`."""22 """See `IBreadcrumb`.
25 implements(IBreadcrumb)
26
27 def __init__(self, url, text, icon=None):
28 self.url = url
29 self.text = text
30 self.icon = icon
31
32 def __repr__(self):
33 if self.icon is not None:
34 icon_repr = " icon='%s'" % self.icon
35 else:
36 icon_repr = ""
37
38 return "<%s url='%s' text='%s'%s>" % (
39 self.__class__.__name__, self.url, self.text, icon_repr)
40
41
42# XXX: salgado, 2009-08-17: Since this adapter now provides a default
43# value for the 'url' attribute, we could easily convert it into an
44# adapter for IBreadcrumb, just changing the Hierarchy view.
45class BreadcrumbBuilder:
46 """See `IBreadcrumbBuilder`.
4723
48 This class is intended for use as an adapter.24 This class is intended for use as an adapter.
49 """25 """
50 implements(IBreadcrumbBuilder)26 implements(IBreadcrumb)
5127
52 rootsite = 'mainsite'28 rootsite = 'mainsite'
53 text = None29 text = None
@@ -66,14 +42,11 @@
66 return queryAdapter(42 return queryAdapter(
67 self.context, IPathAdapter, name='image').icon()43 self.context, IPathAdapter, name='image').icon()
6844
69 def make_breadcrumb(self):45 def __repr__(self):
70 """See `IBreadcrumbBuilder.`"""46 if self.icon is not None:
71 if self.text is None:47 icon_repr = " icon='%s'" % self.icon
72 raise AssertionError(48 else:
73 "The builder has not been given valid text for the "49 icon_repr = ""
74 "breadcrumb.")
75 if self.url is None:
76 raise AssertionError(
77 "The builder has not been given a valid breadcrumb URL.")
7850
79 return Breadcrumb(self.url, self.text, icon=self.icon)51 return "<%s url='%s' text='%s'%s>" % (
52 self.__class__.__name__, self.url, self.text, icon_repr)
8053
=== modified file 'lib/canonical/launchpad/webapp/interfaces.py'
--- lib/canonical/launchpad/webapp/interfaces.py 2009-08-18 14:36:16 +0000
+++ lib/canonical/launchpad/webapp/interfaces.py 2009-08-24 17:47:34 +0000
@@ -259,17 +259,6 @@
259 icon = Attribute("An <img> tag showing this breadcrumb's 14x14 icon.")259 icon = Attribute("An <img> tag showing this breadcrumb's 14x14 icon.")
260260
261261
262class IBreadcrumbBuilder(IBreadcrumb):
263 """An object that builds `IBreadcrumb` objects."""
264 # We subclass IBreadcrumb to minimize interface drift.
265
266 def make_breadcrumb():
267 """Return an object implementing the `IBreadcrumb` interface.
268
269 If for any reason no IBreadcrumb object can be created, return None.
270 """
271
272
273#262#
274# Canonical URLs263# Canonical URLs
275#264#
276265
=== modified file 'lib/canonical/launchpad/zcml/personproduct.zcml'
--- lib/canonical/launchpad/zcml/personproduct.zcml 2009-07-17 00:26:05 +0000
+++ lib/canonical/launchpad/zcml/personproduct.zcml 2009-08-24 17:47:34 +0000
@@ -28,9 +28,9 @@
28 />28 />
2929
30 <adapter30 <adapter
31 provides="canonical.launchpad.webapp.interfaces.IBreadcrumbBuilder"31 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
32 for="canonical.launchpad.interfaces.personproduct.IPersonProduct"32 for="canonical.launchpad.interfaces.personproduct.IPersonProduct"
33 factory="canonical.launchpad.browser.personproduct.PersonProductBreadcrumbBuilder"33 factory="canonical.launchpad.browser.personproduct.PersonProductBreadcrumb"
34 permission="zope.Public"34 permission="zope.Public"
35 />35 />
3636
3737