Minor bit of tidy up (all the stuff I noticed when demoing to francis)

This commit is contained in:
Tom Christie 2011-01-28 17:42:57 +00:00
parent 2e9fd9c6b9
commit 40f47a9fb3
8 changed files with 47 additions and 62 deletions

View File

@ -1,8 +1,6 @@
:mod:`emitters`
===============
.. module:: emitters
The emitters module provides a set of emitters that can be plugged in to a :class:`.Resource`. An emitter is responsible for taking the output of a and serializing it to a given media type. A :class:`.Resource` can have a number of emitters, allow the same content to be serialized in a number of different formats depending on the requesting client's preferences, as specified in the HTTP Request's Accept header.
.. automodule:: emitters

View File

@ -13,8 +13,30 @@ Some of FlyWheel's features:
* Optional support for forms as input validation.
* Modular architecture - Easy to extend and modify.
Installation & Setup
--------------------
blah di blah
Getting Started
---------------
Blah di blah
Examples
--------
Blah blah blah, examples here.
Add a toctree for examples
Library Reference
-----------------
.. toctree::
:maxdepth: 1
:maxdepth: 2
resource
modelresource

View File

@ -15,57 +15,11 @@ class PygmentsForm(forms.Form):
The code to be highlighted can be specified either in a text field, or by URL.
We do some additional form validation to ensure clients see helpful error responses."""
code_url = forms.URLField(required=False, label='Code URL',
help_text='eg. https://bitbucket.org/tomchristie/flywheel/raw/cc266285d879/flywheel/resource.py')
code_text = forms.CharField(widget=forms.Textarea, required=False, label='Code Text',
help_text='Either supply a URL for the code to be highlighted or copy and paste the code text here.')
title = forms.CharField(required=False, help_text='(Optional)')
code = forms.CharField(widget=forms.Textarea, label='Code Text', max_length=1000000,
help_text='(Copy and paste the code text here.)')
title = forms.CharField(required=False, help_text='(Optional)', max_length=100)
linenos = forms.BooleanField(label='Show Line Numbers', required=False)
lexer = forms.ChoiceField(choices=LEXER_CHOICES, initial='python')
style = forms.ChoiceField(choices=STYLE_CHOICES, initial='friendly')
def clean_code_url(self):
"""Custom field validation.
Ensure that code URLs really are valid, and return the content they point to in the cleaned_data,
rather than returning the URL itself."""
cleaned_data = self.cleaned_data
url = cleaned_data.get('code_url')
if not url:
return ''
try:
http = httplib.Http('.cache')
resp, content = http.request(url)
except:
raise forms.ValidationError('The URL supplied cannot be reached')
if int(resp.status/100) != 2:
raise forms.ValidationError('The URL supplied does not return successfully')
if not content:
raise forms.ValidationError('The URL supplied returns no content')
return content
def clean(self):
"""Custom form validation.
Ensure that only one of code_url and code_text is set, and return the content of whichever is set in 'code'."""
cleaned_data = self.cleaned_data
code_url = cleaned_data.get('code_url')
code_text = cleaned_data.get('code_text')
if not code_url and not code_text:
raise forms.ValidationError('Either the URL or the code text must be supplied')
if code_url and code_text:
raise forms.ValidationError('You may not specify both the URL and the code text')
if code_url:
cleaned_data['code'] = code_url
del cleaned_data['code_url']
else:
cleaned_data['code'] = code_text
del cleaned_data['code_text']
return cleaned_data

View File

@ -24,7 +24,7 @@ class HTMLEmitter(BaseEmitter):
class PygmentsRoot(Resource):
"""This example demonstrates a simple RESTful Web API aound the awesome pygments library.
This top level resource is used to create """
This top level resource is used to create highlighted code snippets."""
form = PygmentsForm
allowed_methods = anon_allowed_methods = ('POST',)

View File

@ -7,6 +7,7 @@ urlpatterns = patterns('',
(r'^pygments-example/', include('pygments_api.urls')),
(r'^blog-post-example/', include('blogpost.urls')),
(r'^object-store-example/', include('objectstore.urls')),
(r'^testarchive-example/', include('testarchive.urls')),
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
(r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
(r'^admin/doc/', include('django.contrib.admindocs.urls')),

View File

@ -32,6 +32,8 @@ class BaseEmitter(object):
self.resource = resource
def emit(self, output=NoContent, verbose=False):
"""By default emit simply returns the ouput as-is.
Override this method to provide for other behaviour."""
if output is NoContent:
return ''
@ -81,7 +83,8 @@ class DocumentingTemplateEmitter(BaseEmitter):
provide a form that can be used to submit arbitrary content."""
# Get the form instance if we have one bound to the input
form_instance = resource.form_instance
print form_instance
# Otherwise if this isn't an error response
# then attempt to get a form bound to the response object
if not form_instance and resource.response.has_content_body:
@ -89,6 +92,8 @@ class DocumentingTemplateEmitter(BaseEmitter):
form_instance = resource.get_form(resource.response.raw_content)
except:
pass
if form_instance and not form_instance.is_valid():
form_instance = None
# If we still don't have a form instance then try to get an unbound form
if not form_instance:

View File

@ -58,14 +58,19 @@ class Resource(object):
CSRF_PARAM = 'csrfmiddlewaretoken' # Django's CSRF token used in form params
def __new__(cls, request, *args, **kwargs):
def __new__(cls, *args, **kwargs):
"""Make the class callable so it can be used as a Django view."""
self = object.__new__(cls)
self.__init__(request)
return self._handle_request(request, *args, **kwargs)
if args:
request = args[0]
self.__init__(request)
return self._handle_request(request, *args[1:], **kwargs)
else:
self.__init__()
return self
def __init__(self, request):
def __init__(self, request=None):
""""""
# Setup the resource context
self.request = request

View File

@ -30,9 +30,9 @@
<h1>{{ resource.name }}</h1>
<p>{{ resource.description|linebreaksbr }}</p>
<pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %}
{% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
{% endfor %}
{{ content|urlize_quoted_links }}</pre>{% endautoescape %}
{% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
{% endfor %}
{{ content|urlize_quoted_links }}</pre>{% endautoescape %}
{% if 'GET' in resource.allowed_methods %}
<div class='action'>
@ -40,7 +40,7 @@
<ul class="accepttypes">
{% for media_type in resource.emitted_media_types %}
{% with resource.ACCEPT_QUERY_PARAM|add:"="|add:media_type as param %}
<li>[<a href='{{ request.path|add_query_param:param }} rel="nofollow"'>{{ media_type }}</a>]</li>
<li>[<a href='{{ request.path|add_query_param:param }}' rel="nofollow">{{ media_type }}</a>]</li>
{% endwith %}
{% endfor %}
</ul>