Commit 40f47a9f by Tom Christie

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

parent 2e9fd9c6
:mod:`emitters` :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. 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 .. automodule:: emitters
......
...@@ -13,8 +13,30 @@ Some of FlyWheel's features: ...@@ -13,8 +13,30 @@ Some of FlyWheel's features:
* Optional support for forms as input validation. * Optional support for forms as input validation.
* Modular architecture - Easy to extend and modify. * 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:: .. toctree::
:maxdepth: 1 :maxdepth: 2
resource resource
modelresource modelresource
......
...@@ -15,57 +15,11 @@ class PygmentsForm(forms.Form): ...@@ -15,57 +15,11 @@ class PygmentsForm(forms.Form):
The code to be highlighted can be specified either in a text field, or by URL. 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.""" We do some additional form validation to ensure clients see helpful error responses."""
code_url = forms.URLField(required=False, label='Code URL', code = forms.CharField(widget=forms.Textarea, label='Code Text', max_length=1000000,
help_text='eg. https://bitbucket.org/tomchristie/flywheel/raw/cc266285d879/flywheel/resource.py') help_text='(Copy and paste the code text here.)')
code_text = forms.CharField(widget=forms.Textarea, required=False, label='Code Text', title = forms.CharField(required=False, help_text='(Optional)', max_length=100)
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)')
linenos = forms.BooleanField(label='Show Line Numbers', required=False) linenos = forms.BooleanField(label='Show Line Numbers', required=False)
lexer = forms.ChoiceField(choices=LEXER_CHOICES, initial='python') lexer = forms.ChoiceField(choices=LEXER_CHOICES, initial='python')
style = forms.ChoiceField(choices=STYLE_CHOICES, initial='friendly') 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
...@@ -24,7 +24,7 @@ class HTMLEmitter(BaseEmitter): ...@@ -24,7 +24,7 @@ class HTMLEmitter(BaseEmitter):
class PygmentsRoot(Resource): class PygmentsRoot(Resource):
"""This example demonstrates a simple RESTful Web API aound the awesome pygments library. """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 form = PygmentsForm
allowed_methods = anon_allowed_methods = ('POST',) allowed_methods = anon_allowed_methods = ('POST',)
......
...@@ -7,6 +7,7 @@ urlpatterns = patterns('', ...@@ -7,6 +7,7 @@ urlpatterns = patterns('',
(r'^pygments-example/', include('pygments_api.urls')), (r'^pygments-example/', include('pygments_api.urls')),
(r'^blog-post-example/', include('blogpost.urls')), (r'^blog-post-example/', include('blogpost.urls')),
(r'^object-store-example/', include('objectstore.urls')), (r'^object-store-example/', include('objectstore.urls')),
(r'^testarchive-example/', include('testarchive.urls')),
(r'^accounts/login/$', 'django.contrib.auth.views.login'), (r'^accounts/login/$', 'django.contrib.auth.views.login'),
(r'^accounts/logout/$', 'django.contrib.auth.views.logout'), (r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
(r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^admin/doc/', include('django.contrib.admindocs.urls')),
......
...@@ -32,6 +32,8 @@ class BaseEmitter(object): ...@@ -32,6 +32,8 @@ class BaseEmitter(object):
self.resource = resource self.resource = resource
def emit(self, output=NoContent, verbose=False): 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: if output is NoContent:
return '' return ''
...@@ -81,6 +83,7 @@ class DocumentingTemplateEmitter(BaseEmitter): ...@@ -81,6 +83,7 @@ class DocumentingTemplateEmitter(BaseEmitter):
provide a form that can be used to submit arbitrary content.""" provide a form that can be used to submit arbitrary content."""
# Get the form instance if we have one bound to the input # Get the form instance if we have one bound to the input
form_instance = resource.form_instance form_instance = resource.form_instance
print form_instance
# Otherwise if this isn't an error response # Otherwise if this isn't an error response
# then attempt to get a form bound to the response object # then attempt to get a form bound to the response object
...@@ -89,6 +92,8 @@ class DocumentingTemplateEmitter(BaseEmitter): ...@@ -89,6 +92,8 @@ class DocumentingTemplateEmitter(BaseEmitter):
form_instance = resource.get_form(resource.response.raw_content) form_instance = resource.get_form(resource.response.raw_content)
except: except:
pass 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 we still don't have a form instance then try to get an unbound form
if not form_instance: if not form_instance:
......
...@@ -58,14 +58,19 @@ class Resource(object): ...@@ -58,14 +58,19 @@ class Resource(object):
CSRF_PARAM = 'csrfmiddlewaretoken' # Django's CSRF token used in form params 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.""" """Make the class callable so it can be used as a Django view."""
self = object.__new__(cls) self = object.__new__(cls)
if args:
request = args[0]
self.__init__(request) self.__init__(request)
return self._handle_request(request, *args, **kwargs) 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 # Setup the resource context
self.request = request self.request = request
......
...@@ -30,9 +30,9 @@ ...@@ -30,9 +30,9 @@
<h1>{{ resource.name }}</h1> <h1>{{ resource.name }}</h1>
<p>{{ resource.description|linebreaksbr }}</p> <p>{{ resource.description|linebreaksbr }}</p>
<pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %} <pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %}
{% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }} {% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
{% endfor %} {% endfor %}
{{ content|urlize_quoted_links }}</pre>{% endautoescape %} {{ content|urlize_quoted_links }}</pre>{% endautoescape %}
{% if 'GET' in resource.allowed_methods %} {% if 'GET' in resource.allowed_methods %}
<div class='action'> <div class='action'>
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
<ul class="accepttypes"> <ul class="accepttypes">
{% for media_type in resource.emitted_media_types %} {% for media_type in resource.emitted_media_types %}
{% with resource.ACCEPT_QUERY_PARAM|add:"="|add:media_type as param %} {% 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 %} {% endwith %}
{% endfor %} {% endfor %}
</ul> </ul>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment