Commits

Jesper Nøhr committed 2264a6c

adding documentation template for generating documentation for the handlers you've defined, activated it in the blogserver example

Comments (0)

Files changed (5)

examples/blogserver/api/handlers.py

 from blogserver.blog.models import Blogpost
 
 class AnonymousBlogpostHandler(AnonymousBaseHandler):
+    """
+    Anonymous entrypoint for blogposts.
+    """
     model = Blogpost
     fields = ('id', 'title', 'content', 'created_on')
 
+    @classmethod
+    def resource_uri(self):
+        return ('blogposts', [ 'format', ])
+
 class BlogpostHandler(BaseHandler):
+    """
+    Authenticated entrypoint for blogposts.
+    """
     model = Blogpost
     anonymous = AnonymousBlogpostHandler
     fields = ('title', 'content', ('author', ('username',)), 
               'created_on', 'content_length')
     
+    def read(self, title=None):
+        """
+        Returns a blogpost, if `title` is given,
+        otherwise all the posts.
+        
+        Parameters:
+         - `title`: The title of the post to retrieve.
+        """
+        base = Blogpost.objects
+        
+        if title:
+            return base.get(title=title)
+        else:
+            return base.all()
+    
     def content_length(self, blogpost):
         return len(blogpost.content)
         
     @require_extended
     def create(self, request):
+        """
+        Creates a new blogpost.
+        """
         attrs = self.flatten_dict(request.POST)
 
         if self.exists(**attrs):
             post.save()
             
             return post
-        
+    
+    @classmethod
+    def resource_uri(self):
+        return ('blogposts', [ 'format', ])

examples/blogserver/api/urls.py

 from django.conf.urls.defaults import *
 from piston.resource import Resource
 from piston.authentication import HttpBasicAuthentication
+from piston.doc import documentation_view
 
 from blogserver.api.handlers import BlogpostHandler
 
 urlpatterns = patterns('',
     url(r'^posts/$', blogposts),
     url(r'^posts/(?P<emitter_format>.+)/$', blogposts),
-    url(r'^posts\.(?P<emitter_format>.+)', blogposts),
+    url(r'^posts\.(?P<emitter_format>.+)', blogposts, name='blogposts'),
+
+    # automated documentation
+    url(r'^$', documentation_view),
 )

examples/blogserver/settings.py

 
 TEMPLATE_DIRS = (
     os.path.join(BASE_DIR, 'templates'),
+    os.path.join(BASE_DIR, '../../piston/templates'),
 )
 
 INSTALLED_APPS = (
     'django.contrib.sessions',
     'django.contrib.sites',
     'django.contrib.admin',
+    'django.contrib.markup',
     'blogserver.blog',
     'blogserver.api',
 )
 import inspect, handler
 
+from piston.handler import typemapper
+
 from django.core.urlresolvers import get_resolver, get_callable, get_script_prefix
+from django.shortcuts import render_to_response
+from django.template import RequestContext
 
 def generate_doc(handler_cls):
     """
             met = getattr(self.handler, method)
             stale = inspect.getmodule(met) is handler
 
-            if met and (not stale or include_default):
-                yield HandlerMethod(met, stale)
+            if not self.handler.is_anonymous:
+                if met and (not stale or include_default):
+                    yield HandlerMethod(met, stale)
+            else:
+                if not stale or met.__name__ == "read" \
+                    and 'GET' in self.allowed_methods:
+                    
+                    yield HandlerMethod(met, stale)
+        
+    def get_all_methods(self):
+        return self.get_methods(include_default=True)
         
     @property
     def is_anonymous(self):
-        return False
+        return handler.is_anonymous
 
     def get_model(self):
         return getattr(self, 'model', None)
     def name(self):
         return self.handler.__name__
     
+    @property
+    def allowed_methods(self):
+        return self.handler.allowed_methods
+    
     def get_resource_uri_template(self):
         """
         URI template processor.
         
         See http://bitworking.org/projects/URI-Templates/
         """
-        
         def _convert(template, params=[]):
             """URI template converter"""
             paths = template % dict([p, "{%s}" % p] for p in params)
             resource_uri = self.handler.resource_uri()
             
             components = [None, [], {}]
+
             for i, value in enumerate(resource_uri):
                 components[i] = value
         
     
     def __repr__(self):
         return u'<Documentation for "%s">' % self.name
+
+def documentation_view(request):
+    """
+    Generic documentation view. Generates documentation
+    from the handlers you've defined.
+    """
+    docs = [ ]
+
+    for handler, (model, anonymous) in typemapper.iteritems():
+        docs.append(generate_doc(handler))
+        
+    return render_to_response('documentation.html', 
+        { 'docs': docs }, RequestContext(request))

piston/templates/documentation.html

+{% load markup %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+	<head>
+		<title>
+			Piston generated documentation
+		</title>
+		<style type="text/css">
+			body {
+				background: #fffff0;
+				font: 1em "Helvetica Neue", Verdana;
+				padding: 0 0 0 25px;
+			}
+		</style>
+	</head>
+	<body>
+		<h1>API Documentation</h1>
+		
+		{% for doc in docs %}
+		
+			<h3>{{ doc.name|cut:"Handler" }}:</h3>
+
+			<p>
+				{{ doc.get_doc|default:""|restructuredtext }}
+			</p>
+			
+			<p>
+				URL: <b>{{ doc.get_resource_uri_template }}</b>
+			</p>
+			
+			<p>
+				Accepted methods: {% for meth in doc.allowed_methods %}<b>{{ meth }}</b>{% if not forloop.last %}, {% endif %}{% endfor %}
+			</p>
+					
+			<dl>
+				{% for method in doc.get_all_methods %}
+				
+					<dt>
+						method <i>{{ method.name }}</i>({{ method.signature }}){% if method.stale %} <i>- inherited</i>{% else %}:{% endif %}
+						
+					</dt>				
+										
+					{% if method.get_doc %}
+						<dd>
+							{{ method.get_doc|default:""|restructuredtext }}
+						<dd>
+					{% endif %}
+				
+				{% endfor %}
+			</dl>
+		
+		{% endfor %}
+	</body>
+</html>