[Checkins] SVN: hurry.resource/trunk/s Decouple hurry.resource from ZCA. Still need to make sure tests run
Martijn Faassen
faassen at startifact.com
Mon Jul 19 18:16:04 EDT 2010
Log message for revision 114857:
Decouple hurry.resource from ZCA. Still need to make sure tests run
without zope.interface, but dependency is still pulled in (by testrunner
itself?)
Changed:
U hurry.resource/trunk/setup.py
U hurry.resource/trunk/src/hurry/resource/README.txt
U hurry.resource/trunk/src/hurry/resource/__init__.py
U hurry.resource/trunk/src/hurry/resource/core.py
U hurry.resource/trunk/src/hurry/resource/interfaces.py
A hurry.resource/trunk/src/hurry/resource/zca.py
-=-
Modified: hurry.resource/trunk/setup.py
===================================================================
--- hurry.resource/trunk/setup.py 2010-07-19 22:06:28 UTC (rev 114856)
+++ hurry.resource/trunk/setup.py 2010-07-19 22:16:03 UTC (rev 114857)
@@ -31,10 +31,9 @@
zip_safe=False,
install_requires=[
'setuptools',
- 'zope.interface',
- 'zope.component',
],
extras_require = dict(test=['WebOb'],
- wsgi=['WebOb']),
+ wsgi=['WebOb'],
+ zca=['zope.interface', 'zope.component']),
entry_points={},
)
Modified: hurry.resource/trunk/src/hurry/resource/README.txt
===================================================================
--- hurry.resource/trunk/src/hurry/resource/README.txt 2010-07-19 22:06:28 UTC (rev 114856)
+++ hurry.resource/trunk/src/hurry/resource/README.txt 2010-07-19 22:16:03 UTC (rev 114857)
@@ -123,23 +123,24 @@
>>> y1.need()
Traceback (most recent call last):
...
- ComponentLookupError: (<InterfaceClass hurry.resource.interfaces.ICurrentNeededInclusions>, '')
+ NotImplementedError: need to implement plugin.get_current_needed_inclusions()
We get an error because we haven't configured the framework yet. The
-system says it cannot find the utility component
-``ICurrentNeededInclusions``. This is the utility we need to define
-and register so we can tell the system how to obtain the current
-``NeededInclusions`` object. Our task is therefore to provide a
-``ICurrentNeededInclusions`` utility that can give us the current
-needed inclusions object.
+system says we need to implement
+``plugin.get_current_needed_inclusions()`` first. This is a method
+that we need to implement so we can tell the system how to obtain the
+current ``NeededInclusions`` object.
This needed inclusions should be maintained on an object that is going
to be present throughout the request/response cycle that generates the
-web page that has the inclusions on them. The most obvious place where
+web page that has the inclusions on them. One place where
we can maintain the needed inclusions is the request object
-itself. Let's introduce such a simple request object (your mileage may
-vary in your own web framework)::
+itself, if we indeed have global access to it. Alternatively you could
+store the currently needed inclusions in a thread local variable.
+Let's introduce a simple request object (your mileage may vary in your
+own web framework)::
+
>>> class Request(object):
... def __init__(self):
... self.needed = NeededInclusions()
@@ -149,26 +150,24 @@
>>> request = Request()
-We should define a ``ICurrentNeededInclusion`` utility that knows how
-to get the current needed inclusions from that request::
+We now define a plugin class that implements the
+``get_current_needed_inclusions()`` method by obtaining it from the
+request::
- >>> def currentNeededInclusions():
- ... return request.needed
+ >>> class Plugin(object):
+ ... def get_current_needed_inclusions(self):
+ ... return request.needed
- >>> c = currentNeededInclusions
+We now need to register this plugin with the framework::
-We need to register the utility to complete plugging into the
-``INeededInclusions`` pluggability point of the ``hurry.resource``
-framework::
+ >>> from hurry.resource import register_plugin
+ >>> register_plugin(Plugin())
- >>> from zope import component
- >>> from hurry.resource.interfaces import ICurrentNeededInclusions
- >>> component.provideUtility(currentNeededInclusions,
- ... ICurrentNeededInclusions)
-
-Let's check which resources our request needs currently::
-
- >>> c().inclusions()
+There is an API to retrieve the current needed inclusions, so let's
+check which resources our request needs currently::
+
+ >>> from hurry.resource import get_current_needed_inclusions
+ >>> get_current_needed_inclusions().inclusions()
[]
Nothing yet. We now make ``y1`` needed using our simplified spelling::
@@ -177,20 +176,11 @@
The resource inclusion will now indeed be needed::
- >>> c().inclusions()
+ >>> get_current_needed_inclusions().inclusions()
[<ResourceInclusion 'b.css' in library 'foo'>,
<ResourceInclusion 'a.js' in library 'foo'>,
<ResourceInclusion 'c.js' in library 'foo'>]
-In this document we already have a handy reference to ``c`` to obtain
-the current needed inclusions, but that doesn't work in a larger
-codebase. How do we get this reference back, for instance to obtain those
-resources needed? Here is how you can obtain a utility by hand::
-
- >>> c_retrieved = component.getUtility(ICurrentNeededInclusions)
- >>> c_retrieved is c
- True
-
Let's go back to the original spelling of ``needed.need(y)``
now. While this is a bit more cumbersome to use in application code, it is
easier to read for the purposes of this document.
@@ -446,8 +436,8 @@
Let's look at the resources needed by default::
- >>> c = component.getUtility(ICurrentNeededInclusions)
- >>> c().inclusions()
+ >>> c = get_current_needed_inclusions()
+ >>> c.inclusions()
[<ResourceInclusion 'l1.js' in library 'foo'>]
Let's now change the mode using the convenience
@@ -458,7 +448,7 @@
When we request the resources now, we get them in the ``debug`` mode::
- >>> c().inclusions()
+ >>> c.inclusions()
[<ResourceInclusion 'l1-debug.js' in library 'foo'>]
"Rollups"
@@ -708,27 +698,25 @@
>>> print needed.render()
Traceback (most recent call last):
...
- TypeError: ('Could not adapt', <hurry.resource.core.Library object at ...>, <InterfaceClass hurry.resource.interfaces.ILibraryUrl>)
+ AttributeError: 'Plugin' object has no attribute 'get_library_url'
That didn't work. In order to render an inclusion, we need to tell
``hurry.resource`` how to get the URL for a resource inclusion. We
already know the relative URL, so we need to specify how to get a URL
to the library itself that the relative URL can be added to.
-For the purposes of this document, we define a function that renders
-resources as some static URL on localhost::
+We'll extend the existing plugin that already knows how to obtain the
+current needed inclusions. For the purposes of this document, we
+define a function that renders resources as some static URL on
+localhost::
- >>> def get_library_url(library):
- ... return 'http://localhost/static/%s' % library.name
+ >>> class NewPlugin(Plugin):
+ ... def get_library_url(self, library):
+ ... return 'http://localhost/static/%s' % library.name
-We should now register this function as a``ILibraryUrl`` adapter for
-``Library`` so the system can find it::
+Let's register the plugin::
- >>> from hurry.resource.interfaces import ILibraryUrl
- >>> component.provideAdapter(
- ... factory=get_library_url,
- ... adapts=(Library,),
- ... provides=ILibraryUrl)
+ >>> register_plugin(NewPlugin())
Rendering the inclusions now will result in the HTML fragments we
need to include on the top of our page (just under the ``<head>`` tag
@@ -1001,8 +989,8 @@
Let's look at the resources needed by default::
- >>> c = component.getUtility(ICurrentNeededInclusions)
- >>> top, bottom = c().render_topbottom()
+ >>> c = get_current_needed_inclusions()
+ >>> top, bottom = c.render_topbottom()
>>> print top
<script type="text/javascript" src="http://localhost/static/foo/l1.js"></script>
>>> print bottom
@@ -1016,7 +1004,7 @@
Re-rendering will show it's honoring the bottom setting::
- >>> top, bottom = c().render_topbottom()
+ >>> top, bottom = c.render_topbottom()
>>> print top
<BLANKLINE>
>>> print bottom
Modified: hurry.resource/trunk/src/hurry/resource/__init__.py
===================================================================
--- hurry.resource/trunk/src/hurry/resource/__init__.py 2010-07-19 22:06:28 UTC (rev 114856)
+++ hurry.resource/trunk/src/hurry/resource/__init__.py 2010-07-19 22:16:03 UTC (rev 114857)
@@ -10,4 +10,8 @@
from hurry.resource.core import (sort_inclusions_topological,
sort_inclusions_by_extension,
generate_code)
+
+from hurry.resource.core import (register_plugin,
+ get_current_needed_inclusions)
+
from hurry.resource.wsgi import Middleware
Modified: hurry.resource/trunk/src/hurry/resource/core.py
===================================================================
--- hurry.resource/trunk/src/hurry/resource/core.py 2010-07-19 22:06:28 UTC (rev 114856)
+++ hurry.resource/trunk/src/hurry/resource/core.py 2010-07-19 22:16:03 UTC (rev 114857)
@@ -1,9 +1,13 @@
import os
from types import TupleType
-from zope.interface import implements
-from zope import component
-
+try:
+ from zope.interface import implements
+except ImportError:
+ # fallback in case zope.interface isn't present
+ def implements(iface):
+ pass
+
from hurry.resource import interfaces
EXTENSIONS = ['.css', '.kss', '.js']
@@ -14,7 +18,10 @@
def __init__(self, name):
self.name = name
-class ResourceInclusion(object):
+class InclusionBase(object):
+ implements(interfaces.IInclusion)
+
+class ResourceInclusion(InclusionBase):
"""Resource inclusion
A resource inclusion specifies how to include a single resource in
@@ -109,7 +116,7 @@
return self.library.name, self.relpath
def need(self):
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
needed.need(self)
def inclusions(self):
@@ -121,30 +128,16 @@
result.append(self)
return result
-def register_get_current_needed_inclusions(func):
- pass
-
-def register_get_library_url(library):
- pass
-
-def get_current_needed_inclusions():
- return component.getUtility(interfaces.ICurrentNeededInclusions)()
-
-def get_library_url(library):
- return interfaces.ILibraryUrl(library)
-
-class GroupInclusion(object):
+class GroupInclusion(InclusionBase):
"""An inclusion used to group resources together.
It doesn't define a resource itself.
"""
- implements(interfaces.IInclusion)
-
def __init__(self, depends):
self.depends = depends
def need(self):
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
needed.need(self)
def inclusions(self):
@@ -160,7 +153,9 @@
for inclusion in inclusions]
def normalize_inclusion(library, inclusion):
- if interfaces.IInclusion.providedBy(inclusion):
+ # XXX we don't want dependency on zope.interface but
+ # it'd be better to say IInclusion.providedBy
+ if isinstance(inclusion, InclusionBase):
return inclusion
assert isinstance(inclusion, basestring)
return ResourceInclusion(library, inclusion)
@@ -253,36 +248,63 @@
html = html.replace('</body>', '%s</body>' % bottom, 1)
return html
+
+class PluginNotImplemented(object):
+ """Plug-in point for hurry.resource.
+
+ Frameworks that want to plug into hurry.resource need to
+ implement these two methods.
+
+ There's no need to subclass this in your own code; just implement
+ the methods.
+ """
+ def get_current_needed_inclusions(self):
+ raise NotImplementedError(
+ "need to implement plugin.get_current_needed_inclusions()")
+
+ def get_library_url(self, library):
+ raise NotImplementedError(
+ "need to implement plugin.get_library_url()")
+
+_plugin = PluginNotImplemented()
+
+def register_plugin(plugin):
+ global _plugin
+ _plugin = plugin
+
+def get_current_needed_inclusions():
+ return _plugin.get_current_needed_inclusions()
+
def mode(mode):
"""Set the mode for the currently needed resources.
"""
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
needed.mode(mode)
def bottom(force=False, disable=False):
"""Try to include resources at the bottom of the page, not just on top.
"""
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
needed.bottom(force, disable)
def rollup(disable=False):
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
needed.rollup(disable)
def render():
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
return needed.render()
def render_into_html(html):
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
return needed.render_into_html(html)
def render_topbottom():
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
return needed.render_topbottom()
def render_topbottom_into_html(html):
- needed = get_current_needed_inclusions()
+ needed = _plugin.get_current_needed_inclusions()
return needed.render_topbottom_into_html(html)
def apply_mode(inclusions, mode):
@@ -397,7 +419,7 @@
library_url = library_urls.get(library.name)
if library_url is None:
# if we can't find it, recalculate it
- library_url = get_library_url(library)
+ library_url = _plugin.get_library_url(library)
if not library_url.endswith('/'):
library_url += '/'
library_urls[library.name] = library_url
Modified: hurry.resource/trunk/src/hurry/resource/interfaces.py
===================================================================
--- hurry.resource/trunk/src/hurry/resource/interfaces.py 2010-07-19 22:06:28 UTC (rev 114856)
+++ hurry.resource/trunk/src/hurry/resource/interfaces.py 2010-07-19 22:16:03 UTC (rev 114857)
@@ -1,4 +1,12 @@
-from zope.interface import Interface, Attribute
+try:
+ from zope.interface import Interface, Attribute
+except ImportError:
+ # fallback in case zope.interface isn't present
+ class Interface(object):
+ pass
+ class Attribute(object):
+ def __init__(s):
+ pass
class ILibrary(Interface):
"""A library contains one or more resources.
Added: hurry.resource/trunk/src/hurry/resource/zca.py
===================================================================
--- hurry.resource/trunk/src/hurry/resource/zca.py (rev 0)
+++ hurry.resource/trunk/src/hurry/resource/zca.py 2010-07-19 22:16:03 UTC (rev 114857)
@@ -0,0 +1,15 @@
+from zope import component
+from hurry.resource import interfaces
+
+class Plugin(object):
+ """Zope Component Architecture implementation of plugin.
+
+ This implementation lets you register a utility that
+ implements (in its __call___) get_current_needed_inclusions
+ and an adapter that implements get_library_url().
+ """
+ def get_current_needed_inclusions(self):
+ return component.getUtility(interfaces.ICurrentNeededInclusions)()
+
+ def get_library_url(self, library):
+ return interfaces.ILibraryUrl(library)
More information about the checkins
mailing list