[^/]+)/$', 'CommentInstance'),
)
diff --git a/examples/blogpost/views.py b/examples/blogpost/views.py
index 05e795fac..c5be25441 100644
--- a/examples/blogpost/views.py
+++ b/examples/blogpost/views.py
@@ -1,34 +1,15 @@
from flywheel.response import Response, status
from flywheel.resource import Resource
-from flywheel.modelresource import ModelResource, QueryModelResource
+from flywheel.modelresource import ModelResource, RootModelResource
from blogpost.models import BlogPost, Comment
-##### Root Resource #####
-
-class RootResource(Resource):
- """This is the top level resource for the API.
- All the sub-resources are discoverable from here."""
- allowed_methods = ('GET',)
-
- def get(self, request, *args, **kwargs):
- return Response(status.HTTP_200_OK,
- {'blog-posts': self.reverse(BlogPostList),
- 'blog-post': self.reverse(BlogPostCreator)})
-
-
-##### Blog Post Resources #####
-
BLOG_POST_FIELDS = ('created', 'title', 'slug', 'content', 'absolute_url', 'comment_url', 'comments_url')
+COMMENT_FIELDS = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
-class BlogPostList(QueryModelResource):
- """A resource which lists all existing blog posts."""
- allowed_methods = ('GET', )
- model = BlogPost
- fields = BLOG_POST_FIELDS
-class BlogPostCreator(ModelResource):
- """A resource with which blog posts may be created."""
- allowed_methods = ('POST',)
+class BlogPostRoot(RootModelResource):
+ """A resource with which lists all existing blog posts and creates new blog posts."""
+ allowed_methods = ('GET', 'POST',)
model = BlogPost
fields = BLOG_POST_FIELDS
@@ -38,20 +19,9 @@ class BlogPostInstance(ModelResource):
model = BlogPost
fields = BLOG_POST_FIELDS
-
-##### Comment Resources #####
-
-COMMENT_FIELDS = ('username', 'comment', 'created', 'rating', 'absolute_url', 'blogpost_url')
-
-class CommentList(QueryModelResource):
- """A resource which lists all existing comments for a given blog post."""
- allowed_methods = ('GET', )
- model = Comment
- fields = COMMENT_FIELDS
-
-class CommentCreator(ModelResource):
- """A resource with which blog comments may be created for a given blog post."""
- allowed_methods = ('POST',)
+class CommentRoot(RootModelResource):
+ """A resource which lists all existing comments for a given blog post, and creates new blog comments for a given blog post."""
+ allowed_methods = ('GET', 'POST',)
model = Comment
fields = COMMENT_FIELDS
diff --git a/examples/pygments_api/forms.py b/examples/pygments_api/forms.py
index dc9927e26..8488db062 100644
--- a/examples/pygments_api/forms.py
+++ b/examples/pygments_api/forms.py
@@ -3,23 +3,26 @@ from django import forms
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
-import httplib2 as httplib
-
-
LEXER_CHOICES = sorted([(item[1][0], item[0]) for item in get_all_lexers()])
STYLE_CHOICES = sorted((item, item) for item in list(get_all_styles()))
-
class PygmentsForm(forms.Form):
"""A simple form with some of the most important pygments settings.
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 = forms.CharField(widget=forms.Textarea, label='Code Text', max_length=1000000,
+ 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')
+ 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')
diff --git a/examples/pygments_api/urls.py b/examples/pygments_api/urls.py
index 39bcf668a..f96f4518c 100644
--- a/examples/pygments_api/urls.py
+++ b/examples/pygments_api/urls.py
@@ -2,5 +2,5 @@ from django.conf.urls.defaults import patterns
urlpatterns = patterns('pygments_api.views',
(r'^$', 'PygmentsRoot'),
- (r'^([a-zA-Z0-9]+)/$', 'PygmentsInstance'),
+ (r'^([a-zA-Z0-9-]+)/$', 'PygmentsInstance'),
)
diff --git a/examples/pygments_api/views.py b/examples/pygments_api/views.py
index 4493a8220..963221155 100644
--- a/examples/pygments_api/views.py
+++ b/examples/pygments_api/views.py
@@ -11,10 +11,19 @@ from pygments import highlight
from forms import PygmentsForm
import os
-import hashlib
+import uuid
+import operator
# We need somewhere to store the code that we highlight
HIGHLIGHTED_CODE_DIR = os.path.join(settings.MEDIA_ROOT, 'pygments')
+MAX_FILES = 5
+
+def remove_oldest_files(dir, max_files):
+ """Remove the oldest files in a directory 'dir', leaving at most 'max_files' remaining"""
+ filepaths = [os.path.join(dir, file) for file in os.listdir(dir)]
+ ctime_sorted_paths = [item[0] for item in sorted([(path, os.path.getctime(path)) for path in filepaths],
+ key=operator.itemgetter(1), reverse=True)]
+ [os.remove(path) for path in ctime_sorted_paths[max_files:]]
class HTMLEmitter(BaseEmitter):
@@ -24,38 +33,42 @@ 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 highlighted code snippets."""
+ This top level resource is used to create highlighted code snippets, and to list all the existing code snippets."""
form = PygmentsForm
- allowed_methods = anon_allowed_methods = ('POST',)
+ allowed_methods = anon_allowed_methods = ('GET', 'POST',)
+
+ def get(self, request, auth):
+ """Return a list of all currently existing snippets."""
+ unique_ids = sorted(os.listdir(HIGHLIGHTED_CODE_DIR))
+ return [self.reverse(PygmentsInstance, unique_id) for unique_id in unique_ids]
def post(self, request, auth, content):
- # Generate a unique id by hashing the input
- input_str = ''.join(['%s%s' % (key, content[key]) for key in sorted(content.keys())])
- hash = hashlib.md5()
- hash.update(input_str)
- unique_id = hash.hexdigest()
+ """Create a new highlighed snippet and return it's location.
+ For the purposes of the sandbox example, also ensure we delete the oldest snippets if we have > MAX_FILES."""
+ unique_id = str(uuid.uuid1())
pathname = os.path.join(HIGHLIGHTED_CODE_DIR, unique_id)
- if not os.path.exists(pathname):
- # We only need to generate the file if it doesn't already exist.
- options = {'title': content['title']} if content['title'] else {}
- linenos = 'table' if content['linenos'] else False
- lexer = get_lexer_by_name(content['lexer'])
- formatter = HtmlFormatter(style=content['style'], linenos=linenos, full=True, **options)
-
- with open(pathname, 'w') as outfile:
- highlight(content['code'], lexer, formatter, outfile)
-
+ lexer = get_lexer_by_name(content['lexer'])
+ linenos = 'table' if content['linenos'] else False
+ options = {'title': content['title']} if content['title'] else {}
+ formatter = HtmlFormatter(style=content['style'], linenos=linenos, full=True, **options)
+
+ with open(pathname, 'w') as outfile:
+ highlight(content['code'], lexer, formatter, outfile)
+
+ remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
+
return Response(status.HTTP_201_CREATED, headers={'Location': self.reverse(PygmentsInstance, unique_id)})
class PygmentsInstance(Resource):
"""Simply return the stored highlighted HTML file with the correct mime type.
- This Resource only emits HTML and uses a standard HTML emitter rather than FlyWheel's DocumentingHTMLEmitter class."""
+ This Resource only emits HTML and uses a standard HTML emitter rather than the emitters.DocumentingHTMLEmitter class."""
allowed_methods = anon_allowed_methods = ('GET',)
emitters = (HTMLEmitter,)
def get(self, request, auth, unique_id):
+ """Return the highlighted snippet."""
pathname = os.path.join(HIGHLIGHTED_CODE_DIR, unique_id)
if not os.path.exists(pathname):
return Resource(status.HTTP_404_NOT_FOUND)
diff --git a/examples/urls.py b/examples/urls.py
index 48af39dee..b1dec13d5 100644
--- a/examples/urls.py
+++ b/examples/urls.py
@@ -7,7 +7,6 @@ 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')),
diff --git a/flywheel/emitters.py b/flywheel/emitters.py
index 57e95ec2f..f548e1d93 100644
--- a/flywheel/emitters.py
+++ b/flywheel/emitters.py
@@ -83,16 +83,15 @@ 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:
try:
form_instance = resource.get_form(resource.response.raw_content)
+ if form_instance:
+ form_instance.is_valid()
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
diff --git a/flywheel/modelresource.py b/flywheel/modelresource.py
index 7e9c56559..d68ec79e8 100644
--- a/flywheel/modelresource.py
+++ b/flywheel/modelresource.py
@@ -1,5 +1,3 @@
-"""TODO: docs
-"""
from django.forms import ModelForm
from django.db.models.query import QuerySet
from django.db.models import Model
@@ -379,9 +377,20 @@ class ModelResource(Resource):
return
+class RootModelResource(ModelResource):
+ """A Resource which provides default operations for list and create."""
+ allowed_methods = ('GET', 'POST')
+ queryset = None
+
+ def get(self, request, *args, **kwargs):
+ queryset = self.queryset if self.queryset else self.model.objects.all()
+ return queryset
+
class QueryModelResource(ModelResource):
- allowed_methods = ('read',)
+ """Resource with default operations for list.
+ TODO: provide filter/order/num_results/paging, and a create operation to create queries."""
+ allowed_methods = ('GET',)
queryset = None
def get_form(self, data=None):
diff --git a/flywheel/templates/emitter.html b/flywheel/templates/emitter.html
index 4b0a7ce9b..d21350cd1 100644
--- a/flywheel/templates/emitter.html
+++ b/flywheel/templates/emitter.html
@@ -23,7 +23,7 @@
diff --git a/flywheel/utils.py b/flywheel/utils.py
index c0386871b..f9bbc0fe6 100644
--- a/flywheel/utils.py
+++ b/flywheel/utils.py
@@ -125,7 +125,6 @@ class XML2Dict(object):
"""
result = re.compile("\{(.*)\}(.*)").search(tag)
if result:
- print tag
value.namespace, tag = result.groups()
return (tag, value)