[Checkins] SVN: z3c.formjs/trunk/ Implemented JS functions.

Stephan Richter srichter at cosmos.phy.tufts.edu
Fri Jul 20 18:44:12 EDT 2007


Log message for revision 78227:
  Implemented JS functions.
  

Changed:
  U   z3c.formjs/trunk/TODO.txt
  U   z3c.formjs/trunk/src/z3c/formjs/interfaces.py
  A   z3c.formjs/trunk/src/z3c/formjs/jsfunction.py
  A   z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt
  U   z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py

-=-
Modified: z3c.formjs/trunk/TODO.txt
===================================================================
--- z3c.formjs/trunk/TODO.txt	2007-07-20 16:55:02 UTC (rev 78226)
+++ z3c.formjs/trunk/TODO.txt	2007-07-20 22:44:12 UTC (rev 78227)
@@ -2,22 +2,15 @@
 TODO
 ====
 
- - A way to write a javascript function in python such that you can
-   render a call to it from python
-
-    class SomeForm:
-        @jsfunction('bar') # bar is the namespace
-        def foo(self):
-            return 'alert("foo");'
-    renderJSFunction(SomeForm.foo)
-    'function z3c_formjs_foo(){ alert("foo"); }'
-    renderJSCall(SomeForm.foo)
-    'bar_foo()'
-
  - ajax form submission - ala "save" button
 
- - ajax widget switching
-
  - client side js validators for simple fields.  (maybe we can use an
    existing library?)
 
+- Make decorators less intrusive. They should return the original object, not
+  something they created, so that we can better pipe them, like::
+
+    @jsevent.handler(IFields['one'])
+    @jsevent.handler(IFields['two'])
+    def handleOneAndTwo(self, ...):
+        pass

Modified: z3c.formjs/trunk/src/z3c/formjs/interfaces.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/interfaces.py	2007-07-20 16:55:02 UTC (rev 78226)
+++ z3c.formjs/trunk/src/z3c/formjs/interfaces.py	2007-07-20 22:44:12 UTC (rev 78227)
@@ -83,21 +83,62 @@
         """Subscribe an event for a DOM element executing the handler's
         result."""
 
-    def __iter__(self):
+    def __iter__():
         """Return an iterator of all subscriptions."""
 
 
 class IRenderer(zope.interface.Interface):
     """Render a component in the intended output format."""
 
-    def update(self):
+    def update():
         """Update renderer."""
 
-    def render(self):
+    def render():
         """Render content."""
 
-# -----[ Wiidgets ]-----------------------------------------------------------
+# -----[ Javascript Functions ]----------------------------------------------
 
+class IJSFunction(zope.interface.Interface):
+    """A Javascript Function."""
+
+    name = zope.schema.BytesLine(
+        title=u"Name",
+        description=u"The name of the function.",
+        required=True)
+
+    arguments = zope.schema.List(
+        title=u"Arguments",
+        description=u"A list of arguments of the function.",
+        required=True)
+
+    def render():
+        """Render the content of the JS function."""
+
+
+class IJSFunctions(zope.interface.Interface):
+    """A manager of Javascript functions."""
+
+    def add(function, namespace=''):
+        """Add a new function to the given namespace."""
+
+    def render(self):
+        """Render all functions."""
+
+class IHaveJSFunctions(zope.interface.Interface):
+    """An component that has a JS functions manager .
+
+    This component is most often a view component. When rendering a page this
+    interface is used to check whether any functions must be rendered.
+    """
+
+    jsFunctions = zope.schema.Object(
+        title=u"Javascript Functions",
+        description=u"Attribute holding the JS Functions Manager.",
+        schema = IJSFunctions,
+        required=True)
+
+# -----[ Widgets ]------------------------------------------------------------
+
 class IWidgetSelector(ISelector):
     """Select a DOM element using the action."""
 

Added: z3c.formjs/trunk/src/z3c/formjs/jsfunction.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsfunction.py	                        (rev 0)
+++ z3c.formjs/trunk/src/z3c/formjs/jsfunction.py	2007-07-20 22:44:12 UTC (rev 78227)
@@ -0,0 +1,115 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Javascript Functions.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import inspect
+import sys
+import zope.component
+import zope.interface
+from zope.viewlet import viewlet
+from zope.publisher.interfaces.browser import IBrowserRequest
+
+from z3c.formjs import interfaces
+
+class JSFunction(object):
+    zope.interface.implements(interfaces.IJSFunction)
+
+    def __init__(self, namespace, function):
+        self.namespace = namespace
+        self.function = function
+
+    @property
+    def name(self):
+        return self.function.func_name
+
+    @property
+    def arguments(self):
+        args = inspect.getargspec(self.function)[0]
+        if args[0] is 'self':
+            del args[0]
+        return args
+
+    def render(self):
+        return self.function(*[x for x in ['self'] + self.arguments])
+
+    def __repr__(self):
+        return '<%s %s>' % (
+            self.__class__.__name__, self.name)
+
+
+class JSFunctions(object):
+    zope.interface.implements(interfaces.IJSFunctions)
+
+    def __init__(self):
+        self._functions = {}
+
+    def add(self, function, namespace=''):
+        jsFunction = JSFunction(namespace, function)
+        ns = self._functions.setdefault(namespace, [])
+        ns.append(jsFunction)
+        return jsFunction
+
+    def render(self):
+        result = ''
+        # Render non-namespaced functions
+        for func in self._functions.get('', []):
+            args = func.arguments
+            result += 'function %s(%s) {\n' %(
+                func.name, ', '.join(args) )
+            code = func.render()
+            result += '  ' + code.replace('\n', '\n  ') + '\n'
+            result += '}\n'
+        # Render namespaced functions
+        for ns, funcs in self._functions.items():
+            if ns == '':
+                continue
+            result += 'var %s = {\n' %ns
+            for func in funcs:
+                args = func.arguments
+                result += '  %s: function(%s) {\n' %(
+                    func.name, ', '.join(args) )
+                code = func.render()
+                result += '    ' + code.replace('\n', '\n    ') + '\n'
+                result += '  },\n'
+            result = result[:-2] + '\n'
+            result += '}\n'
+        return result
+
+    def __repr__(self):
+        return '<%s>' % (self.__class__.__name__)
+
+def function(namespace=''):
+    """A decorator for defining a javascript function."""
+    def createFunction(func):
+        frame = sys._getframe(1)
+        f_locals = frame.f_locals
+        funcs = f_locals.setdefault('jsFunctions', JSFunctions())
+        return funcs.add(func, namespace)
+    return createFunction
+
+
+class JSFunctionsViewlet(viewlet.ViewletBase):
+    """An viewlet for the JS viewlet manager rendering functions."""
+    zope.component.adapts(
+        zope.interface.Interface,
+        IBrowserRequest,
+        interfaces.IHaveJSFunctions,
+        zope.interface.Interface)
+
+    def render(self):
+        content = self.__parent__.jsFunctions.render()
+        return u'<script type="text/javascript">\n%s\n</script>' % content


Property changes on: z3c.formjs/trunk/src/z3c/formjs/jsfunction.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt	                        (rev 0)
+++ z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt	2007-07-20 22:44:12 UTC (rev 78227)
@@ -0,0 +1,112 @@
+====================
+JavaScript Functions
+====================
+
+When developing JavaScript-enabled user interfaces, it is often necessary to
+create small callback functions. The usual way of creating those functions is
+to write and register a resource, then create a viewlet for it that integrates
+it. Those steps can be tedious when writing small functions. Thus, this
+package provides a way to convert a Python method to a Javascript function.
+
+  >>> from z3c.formjs import jsfunction
+
+So let's create a simple view with a JavaScript function in it:
+
+  >>> class View(object):
+  ...
+  ...     @jsfunction.function('hw')
+  ...     def showHelloWorldMessage(self):
+  ...         return u"alert('Hello World!');"
+
+The argument to ``jsfunction.function`` is the namspace into which the
+function will be placed. This argument is optional. The Python method is
+expected to return the Javascript code as a string. All functions are
+collected in a special attribute called "jsFunctions"
+
+  >>> View.jsFunctions
+  <JSFunctions>
+
+The functions can be rendered directly:
+
+  >>> print View.jsFunctions.render()
+  var hw = {
+    showHelloWorldMessage: function() {
+      alert('Hello World!');
+    }
+  }
+
+Similarly to Javascript subscriptions, a JavaScript viewlet exists for any
+view containing JavaScript functions that provides the following output:
+
+  >>> viewlet = jsfunction.JSFunctionsViewlet(
+  ...     object(), object(), View(), object())
+  >>> viewlet.update()
+  >>> print viewlet.render()
+  <script type="text/javascript">
+    var hw = {
+      showHelloWorldMessage: function() {
+        alert('Hello World!');
+      }
+    }
+  </script>
+
+Let's now have a closer look that the decorator. As mentioned before, the
+namespace is option. So what happens if the namespace is not specified? Then
+the function should be declared normally:
+
+  >>> class View(object):
+  ...
+  ...     @jsfunction.function()
+  ...     def showHelloWorldMessage(self):
+  ...         return u"alert('Hello World!');"
+
+  >>> print View.jsFunctions.render()
+  function showHelloWorldMessage() {
+    alert('Hello World!');
+  }
+
+Of course you can mix namespace and non-namespace functions:
+
+  >>> class View(object):
+  ...
+  ...     @jsfunction.function()
+  ...     def show1(self):
+  ...         return u"alert('Hello World!');"
+  ...
+  ...     @jsfunction.function('ns1')
+  ...     def show1(self):
+  ...         return u"alert('Hello World!');"
+  ...
+  ...     @jsfunction.function('ns1')
+  ...     def show2(self):
+  ...         return u"alert('Hello World!');"
+
+  >>> print View.jsFunctions.render()
+  function show1() {
+    alert('Hello World!');
+  }
+  var ns1 = {
+    show1: function() {
+      alert('Hello World!');
+    },
+    show2: function() {
+      alert('Hello World!');
+    }
+  }
+
+What about arguments? The arguments are directly extracted into the
+code. Currently, keyword arguments, and variable positional and keyword
+arguments are not supported, as they are not supported by JavaScript either:
+
+  >>> class View(object):
+  ...
+  ...     @jsfunction.function()
+  ...     def show(self, title):
+  ...         return u"alert('Title' + title);"
+
+  >>> print View.jsFunctions.render()
+  function show(title) {
+    alert('Title' + title);
+  }
+
+And that is realy everything that there is to it.


Property changes on: z3c.formjs/trunk/src/z3c/formjs/jsfunction.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py	2007-07-20 16:55:02 UTC (rev 78226)
+++ z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py	2007-07-20 22:44:12 UTC (rev 78227)
@@ -34,6 +34,11 @@
             optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
                         zope.testing.doctest.ELLIPSIS),
         zope.testing.doctest.DocFileSuite(
+            '../jsfunction.txt',
+            setUp=testing.setUp, tearDown=testing.tearDown,
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+        zope.testing.doctest.DocFileSuite(
             '../jsaction.txt',
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |



More information about the Checkins mailing list