[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