def __init__(self, dsn): self.em = EntryManager(dsn) self.format = get_format('json')
class JSONStore(object): """ A RESTful store based on JSON. """ def __init__(self, dsn): self.em = EntryManager(dsn) self.format = get_format('json') def __call__(self, environ, start_response): self.environ = environ self.start = start_response path_info = environ.get('PATH_INFO', '/') dispatchers = [ ('/search/(?P<filters>.+)', self.search), ('/(?P<id>.+)?', self.default), ] for regexp, func in dispatchers: p = re.compile(regexp) m = p.match(path_info) if m: return func(**m.groupdict()) # 404. raise httpexceptions.HTTPNotFound() def default(self, id): """ Dispatcher that uses the request method. """ func = getattr(self, '_%s' % self.environ['REQUEST_METHOD']) return func(id) def _GET(self, id=None): """ Return entry. """ if id is None: query = parse_dict_querystring(self.environ) size = int(query.get("size", DEFAULT_NUMBER_OF_ENTRIES)) offset = int(query.get("offset", 0)) # Read members from the collection. We get the requested number of # entries plus the next one. entries = self.em.get_entries(size+1, offset) if len(entries) == size+1: entries.pop() # remove "next" entry next = "?size=%d&offset=%d" % (size, offset+size) else: next = None output = {"collection": entries, "next": next} else: try: output = self.em.get_entry(id) except KeyError: raise httpexceptions.HTTPNotFound() # 404 app = self.format.responder(output, content_type='application/json') return app(self.environ, self.start) def _POST(self, id): """ Create a new entry. """ entry = parse_request(self.environ, output_type='python') # Set id, if POSTed to specific resource. if id is not None: entry.setdefault('id', id) if not id == entry['id']: raise httpexceptions.HTTPConflict() # Create the entry. entry = self.em.create_entry(entry) # Generate new resource location. store = construct_url(self.environ, with_query_string=False, with_path_info=False) location = urljoin(store, entry['id']) app = self.format.responder(entry, content_type='application/json', headers=[('Location', location)]) # Fake start response to return 201 status. def start(status, headers): return self.start("201 Created", headers) return app(self.environ, start) def _PUT(self, id): """ Update an existing entry. """ entry = parse_request(self.environ, output_type='python') if id is not None: entry.setdefault('id', id) if not id == entry['id']: raise httpexceptions.HTTPConflict() # Update entry. entry = self.em.update_entry(entry) app = self.format.responder(entry, content_type='application/json') return app(self.environ, self.start) def _DELETE(self, id): """ Delete an existing entry. """ self.em.delete_entry(id) app = self.format.responder(None, content_type='application/json') return app(self.environ, self.start) def _HEAD(self, id): """ Return headers only. """ headers = [('Content-Encoding', 'utf-8'), ('Content-Type', 'application/json')] self.start('200 OK', headers) return [] def search(self, filters): query = parse_dict_querystring(self.environ) size = int(query.get("size", DEFAULT_NUMBER_OF_ENTRIES)) offset = int(query.get("offset", 0)) filters = urllib.unquote(filters) filters = simplejson.loads(filters) entries = self.em.search(filters, re.IGNORECASE, size, offset) if len(entries) == size+1: entries.pop() # remove "next" entry next = "?size=%d&offset=%d" % (size, offset+size) else: next = None output = {"collection": entries, "next": next} app = self.format.responder(output, content_type='application/json') return app(self.environ, self.start) def close(self): self.em.close()