惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security @ Cisco Blogs
H
Hacker News: Front Page
P
Privacy International News Feed
N
News and Events Feed by Topic
T
Threatpost
Simon Willison's Weblog
Simon Willison's Weblog
S
Schneier on Security
K
Kaspersky official blog
S
Secure Thoughts
V2EX - 技术
V2EX - 技术
Security Latest
Security Latest
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CERT Recently Published Vulnerability Notes
L
Lohrmann on Cybersecurity
Jina AI
Jina AI
P
Proofpoint News Feed
AI
AI
雷峰网
雷峰网
T
Tailwind CSS Blog
Engineering at Meta
Engineering at Meta
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 叶小钗
Webroot Blog
Webroot Blog
Apple Machine Learning Research
Apple Machine Learning Research
SecWiki News
SecWiki News
罗磊的独立博客
N
Netflix TechBlog - Medium
Martin Fowler
Martin Fowler
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
Schneier on Security
Schneier on Security
The GitHub Blog
The GitHub Blog
S
Security Affairs
Blog — PlanetScale
Blog — PlanetScale
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
月光博客
月光博客
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Securelist
W
WeLiveSecurity
T
Troy Hunt's Blog
A
Arctic Wolf
博客园 - 司徒正美

Secret Weblog

Becoming More Xee: A Modern XPath and XSLT Engine in Rust Looking for new challenges! Repeat Yourself, A Bit The Curious Case of Quentell The Humble For Loop in Rust The Humble For Loop in JavaScript Don Question Best Practices I Was a 1980s Teenage Programmer Part 5: Achieving Assembly I Was a 1980s Teenage Programmer Part 4: The Call of Assembly The Tooling Shift I Was a 1980s Teenage Programmer Part 3: MSX-2 JavaScript: when you need two ways to do it! Empowering Programming Languages Bloat and Retrofuturism Refreshing my Blog Again Random Rust Impressions Apilar: An Alife System I Was a 1980s Teenage Programmer Part 2: Olivetti M24 I Was a 1980s Teenage Programmer: the Alphatronic SolidJS fits my brain Is premature optimization the root of all evil? Framework Patterns: JavaScript edition Roll Your Own Frameworks Framework Patterns Secret Weblog Highlights Refactoring to Multiple Exit Points mstform: a form library for mobx-state-tree Seven Years: A Very Personal History of the Web Morepath 0.16 released! Is Morepath Fast Yet? Introducing Bob Strongpinion Punctuated Equilibrium in Software Morepath 0.15 released! Impressions of React Europe 2016 Morepath 0.14 released! Morepath 0.13 now with Dectate Dectate: advanced configuration for Python code JavaScript Dependencies Revisited: An Example Project The Incredible Drifting Cyber A Brief History of Reselect The Emerging GraphQL Python stack Thoughts about React Europe Build a better batching UI with Morepath and Jinja2 GraphQL and REST Server Templating in Morepath 0.10 10 reasons to check out the Morepath web framework in 2015 A Review of the Web and how Morepath fits in Morepath 0.9 released! Better REST with Morepath 0.8 Morepath 0.7: new inter-app linking They say something I don Life at the Boundaries: Conversion and Validation BowerStatic 0.4 released! Morepath 0.6 released! Morepath 0.5(.1) and friends released! New HTTP 1.1 RFCs versus WSGI Against On Naming In Open Source My visit to EuroPython 2014 Morepath 0.4.1 released (with Python 3 fixes) Morepath 0.4 and breaking changes Announcing BowerStatic Morepath 0.3 released! Morepath 0.2 Morepath Python 3 support The Call of Python 2.8 Morepath 0.1 released! WebOb and Werkzeug compared Morepath: from Werkzeug to WebOb Racing the Morepath: SQLAlchemy Integration The Centre Cannot Hold Breaking Morepath Changes Morepath Update How to do REST with Morepath Morepath Security the Gravity of Python 2 #python2.8 discussion channel on freenode Alex Gaynor on Python 3 Morepath Documentation Starting to Take Shape Back to the Center Morepath App Reuse Implementing Grok Grok: the Idea Why Linux Works for Me On the Morepath Reg, Now With More Generic! The New Zope as a Web Framework Jim Fulton, Zope Architect Renewing Zope Object Publishing The Weirdness of Zope The Rise of Zope My Exit from Zope Reg: Component Architecture Reimagined JSConf EU 2013 impressions Obviel 1.0! JS Dependency Tools Redux Succinct data structures
Grok
Martijn Faassen · 2009-01-10 · via Secret Weblog

I've been following with interest a number of posts that talk about creating a simple REST-based web application that persists the number of plays various songs have had. Here's the history:

First a few comments on the protocol: the RESTfulness of this protocol could definitely be improved. As was remarked by a comment on the original post, REST-based apps return lists of URLs in overviews and the overview in this app doesn't. I'd also modify the way new songs get registered with the app and make that a POST request on the song container, instead of implicity creating such resources by the mere act of traversing. I haven't made any such modifications to the protocol even though the last improvement would simplify my code as it'd let me get rid of the traverse method.

I thought it'd be interesting to implement the same using Grok (1.0a1). Without more ado, here it is:

import grok
from zope.app.publication.interfaces import IBeforeTraverseEvent

class App(grok.Application, grok.Container):
    def traverse(self, id):
        if id not in self:
            song = self[id] = Song()
            return song
        return self[id]

@grok.subscribe(App, IBeforeTraverseEvent)
def applySkin(obj, event):
    # make rest layer the default if necessary
    if not IRESTLayer.providedBy(event.request):
        grok.util.applySkin(event.request, IRESTLayer, grok.IRESTSkinType)

class IRESTLayer(grok.IRESTLayer):
    grok.restskin('main')

class AppREST(grok.REST):
    grok.context(App)
    grok.layer(IRESTLayer)

    def GET(self):
        return ','.join(['%s=%s' % (k, v.count) for k, v in self.context.items()])

    def DELETE(self):
        for key in list(self.context.keys()):
            del self.context[key]

class Song(grok.Model):
    def __init__(self):
        self.count = 0

class SongREST(grok.REST):
    grok.context(Song)
    grok.layer(IRESTLayer)

    def GET(self):
        return str(self.context.count)

    def POST(self):
        self.context.count += 1
        return str(self.context.count)

Before I go into the good news (Grok gives you two important features here that the other frameworks examples don't have), first the bad news.

It's about 10 lines longer than the CherryPy and Restish examples (the Werkzeug example is shorter still but rather low-level).

Performance-wise it's the slowest of the bunch, on my machine, which is comparable to the machines of the others (in my case an Intel Core 2 Duo 2400 MHz Linux box) I get about 580 requests per second (not too shabby):

Concurrency Level:      1
Time taken for tests:   17.39500 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      2500000 bytes
HTML transferred:       10000 bytes
Requests per second:    586.87 [#/sec] (mean)
Time per request:       1.704 [ms] (mean)
Time per request:       1.704 [ms] (mean, across all concurrent requests)
Transfer rate:          143.26 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     1    1   1.1      1      61
Waiting:        0    1   1.0      1      60
Total:          1    1   1.1      1      61

Now to the good news. The other examples all store their information in a global variable in the form of a dictionary. Gulp. This application actually features true persistence. When you restart your server, the counted information is still there - it's in the database. This means that the benchmark actually includes database access (to the ZODB).

You may have noted that there isn't much database access code there. That's because the ZODB allows transparent persistence of Python objects. This actually made it trivially easy to write this application with true persistence.

The other has more to do with framework power. This is not low-level code, and that shouldn't be underestimated. We have available to us a framework that offers a ton of features, both out of the box and as extensions. I'll talk about some out-of-the-box features here.

Grok's REST system allows you to extend existing (persistent) objects in your application with RESTful behavior. These objects can retain their original UI entirely. If I actually left out the applySkin code above, the RESTful URLs would look like this:

http://localhost:8080/++rest++main/song/1

and the normal URLs to the objects would look like this:

http://localhost:8080/main/song/1

This means that you could give your app both a normal UI as well as REST-based access. In the example I've used the applySkin line to consolidate them into a single URL space however.

In addition, Grok's REST support also features a powerful and built-in security system. You can give each access a permission by adding the line @grok.require:

@grok.require('some.permission')
def GET(self):
    ...

Grok also takes care of URL management for you out of the box. The objects in the app all have a URL automatically. Should I want to display the URL of each song object in addition to its count I'd change the GET line of SongREST to this:

def GET(self):
    return grok.url(self.request, self.context) + ' ' + str(self.context.count)

If you want to see a much bigger REST-ful app I've written with Grok for a customer (ID-StudioLab at the Technical University of Delft), please check out imageSTORE (it comes with a lot of doctests). It's a RESTful persistent storage of image information.