the html behind brubeck.io
Pull request Compare This branch is 11 commits behind j2labs:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
img
js
media
.gitignore
demos.html
design.html
index.html
installing.html
license.html
readme.html

readme.html

<html>
  <head>
    <title>Brubeck: a Mongrel2 handler</title>
    <link href='http://fonts.googleapis.com/css?family=Josefin+Slab' rel='stylesheet' type='text/css'>
    <link rel=stylesheet type="text/css" href="media/style.css"> 
    <link rel=stylesheet type="text/css" href="media/prettify.css"> 
    <meta name="viewport" content="width=600;" /> 
    <script type="text/javascript" src="js/prettify.js"></script>
  </head>

  <body onload="prettyPrint()">
    <div id="title">
      <a href="index.html"><img class="logo" src="img/logo.png" /></a>
    </div>

    <p class="links"><a href="readme.html">Readme</a> &nbsp;|&nbsp; <a href="design.html">Design</a> &nbsp;|&nbsp; <a href="installing.html">Installing</a> &nbsp;|&nbsp; <a href="demos.html">Demos</a> &nbsp;|&nbsp; <a href="https://github.com/j2labs/brubeck">Code</a></p>

<h1>What Is Brubeck?</h1>

<p><strong>Brubeck</strong> is a flexible Python web framework that aims to make the process of building scalable web services easy.</p>

<p>The Brubeck model resembles what companies build when they operate at large scale, but working with it will feel like what you're used to from other frameworks.</p>

<ul>
<li>No confusing callbacks</li>
<li>No database opinions</li>
<li>Built-in distributed load balancing</li>
</ul>

<h2>Example: Hello World</h2>

<p>This is a whole Brubeck application. </p>

<pre><code class="prettyprint">class DemoHandler(WebMessageHandler):
    def get(self):
        self.set_body('Hello world')
        return self.render()

urls = [(r'^/', DemoHandler)]
mongrel2_pair = ('ipc://127.0.0.1:9999', 'ipc://127.0.0.1:9998')

app = Brubeck(mongrel2_pair=mongrel2_pair,
              handler_tuples=urls)
app.run()
</code></pre>

<h2>Features</h2>

<p>Brubeck gets by with a little help from its friends:</p>

<ul>
<li><a href="http://mongrel2.org">Mongrel2</a>: lean &amp; fast, asynchronous web serving</li>
<li><a href="https://github.com/j2labs/dictshield">DictShield</a>: data modeling &amp; validation with no database opinions</li>
<li><a href="http://zeromq.org">ZeroMQ</a>: fast messaging &amp; supports most languages</li>
<li><a href="http://eventlet.net">Eventlet</a>: non-blocking I/O, coroutines &amp; implicit scheduling</li>
<li><a href="http://gevent.org">Gevent</a>: like eventlet, but uses <code>libevent</code></li>
</ul>

<p>Please also see this completely unscientific comparison of Brubeck and Tornado:</p>

<ul>
<li><a href="https://gist.github.com/882555">500 concurrent connections for 10 seconds</a></li>
</ul>

<h1>Complete Example: Listsurf</h1>

<p><strong>Listsurf</strong> is a simple to way to save links. Yeah... another delicious clone!</p>

<p>It serves as a basic demonstration of what a complete site looks like when you build with Brubeck. It has authentication with secure cookies, offers a JSON API, uses <a href="http://jinja.pocoo.org/">Jinja2</a> for templating, <a href="http://eventlet.net">Eventlet</a> for coroutines and stores data in <a href="http://mongodb.org">MongoDB</a>.</p>

<ul>
<li><a href="https://github.com/j2labs/listsurf">Listsurf on GitHub</a></li>
</ul>

<h1>Closer Look At The Code</h1>

<p>In this section we'll discuss writing a request handler, adding user authentication and rendering pages with templates.</p>

<h2>Handling Requests</h2>

<p>The framework can be used for different requirements. It can be lean and lightweight for high throughput or you can fatten it up and use it for rendering pages in a database backed CMS.</p>

<p>The general architecture of the system is to map requests for a specific URL to some <a href="http://docs.python.org/library/functions.html#callable">callable</a> for processing the request. The configuration attempts to match handlers to URL's by inspecting a list of <code>(url pattern, callable)</code> tuples. First regex to match provides the callable.</p>

<p>Some people like to use classes as handlers. Some folks prefer to use functions. Brubeck supports both.</p>

<h3>MessageHandler Classes</h3>

<p>When a class model is used, the class will be instantiated for the life of the request and then thrown away. This keeps our memory requirements nice and light.</p>

<p>Brubeck's <code>MessageHandler</code> design is similar to what you see in <a href="https://github.com/facebook/tornado">Tornado</a>, or <a href="http://webpy.org/">web.py</a>. </p>

<p>To answer HTTP GET requests, implement <code>get()</code> on a WebMessageHandler instance.</p>

<pre><code class="prettyprint">class DemoHandler(WebMessageHandler):
    def get(self):
        self.set_body('Take five!')
        return self.render()
</code></pre>

<p>Then we add <code>DemoHandler</code> to the routing config and instantiate a Brubeck instance. </p>

<pre><code class="prettyprint">urls = [(r'^/brubeck', DemoHandler)]
config = {
    'handler_tuples': urls,
    ...
}

Brubeck(**config).run()
</code></pre>

<p>Notice the url regex is <code>^/brubeck</code>. This will put our handler code on <code>http://hostname/brubeck</code>. </p>

<ul>
<li><a href="https://github.com/j2labs/brubeck/blob/master/demos/demo_minimal.py">Runnable demo</a></li>
</ul>

<h3>Functions and Decorators</h3>

<p>If you'd prefer to just use a simple function, you instantiate a Brubeck instance and wrap your function with the <code>add_route</code> decorator. </p>

<p>Your function will be given two arguments. First, is the <code>application</code> itself. This provides the function with a hook almost all the information it might need. The second argument, the <code>message</code>, provides all the information available about the request.</p>

<p>That looks like this:</p>

<pre><code class="prettyprint">app = Brubeck(mongrel2_pair=('ipc://127.0.0.1:9999', 
                             'ipc://127.0.0.1:9998'))

@app.add_route('^/brubeck', method='GET')
def foo(application, message):
    return http_response('Take five!', 200, 'OK', {})

app.run()
</code></pre>

<ul>
<li><a href="https://github.com/j2labs/brubeck/blob/master/demos/demo_noclasses.py">Runnable demo</a></li>
</ul>

<h2>Templates</h2>

<p>Brubeck currently supports <a href="http://jinja.pocoo.org/">Jinja2</a> or <a href="http://www.tornadoweb.org/documentation#templates">Tornado</a> templates.</p>

<p>Template support is contained in <code>brubeck.templates</code> as rendering mixins. Each Mixin will attach a <code>render_template</code> function to your handler and overwrite the default <code>render_error</code> to produce templated errors messages.</p>

<p>Using a template system is then as easy as calling <code>render_template</code> with the template filename and some context, just like you're used to.</p>

<h3>Jinja2</h3>

<p>Using Jinja2 template looks like this.</p>

<pre><code class="prettyprint">from brubeck.templating import Jinja2Rendering

class DemoHandler(WebMessageHandler, Jinja2Rendering):
    def get(self):
        context = {
            'name': 'J2 D2',
        }
        return self.render_template('success.html', **context)
</code></pre>

<ul>
<li><a href="https://github.com/j2labs/brubeck/blob/master/demos/demo_jinja2.py">Runnable demo</a></li>
<li><a href="https://github.com/j2labs/brubeck/tree/master/demos/templates/jinja2">Demo templates</a></li>
</ul>

<h3>Tornado</h3>

<p>Tornado templates are supported by the TornadoRendering mixin. The code looks virtually the same to keep mixing template systems lightweight.</p>

<pre><code class="prettyprint">from brubeck.templating import TornadoRendering

class DemoHandler(WebMessageHandler, TornadoRendering):
    ...
</code></pre>

<ul>
<li><a href="https://github.com/j2labs/brubeck/blob/master/demos/demo_tornado.py">Runnable demo</a></li>
<li><a href="https://github.com/j2labs/brubeck/tree/master/demos/templates/tornado">Demo templates</a></li>
</ul>

<h3>Template Loading</h3>

<p>In addition to using a rendering mixin, you need to provide the path to your templates.</p>

<p>That looks like this:</p>

<pre><code class="prettyprint">from brubeck.templating import load_jinja2_env

config = {
    template_loader=load_jinja2_env('./templates/jinja2')
    ...
}
</code></pre>

<p>Using a function here keeps the config lightweight.</p>

<h3>Custom Rendering</h3>

<p>If you have something else in mind, you'll be glad to know <code>template_loader</code> can be any callable that loads a rendering environment. Brubeck calls this during initialization and attaches the output to <code>self.application.template_env</code>.</p>

<p>The current convention is for handlers to provide a <code>render_template</code> and <code>render_error</code> function. These functions typically use <code>self.application.template_env</code> to render request specific data.</p>

<ul>
<li><a href="https://github.com/j2labs/brubeck/blob/master/brubeck/templating.py#L1">brubeck.templating</a></li>
</ul>

<h2>Auth</h2>

<p>Authentication can be provided by decorating functions with the <code>@web_authenticated</code> decorator. This decorator expects the handler to have a <code>current_user</code> property that returns either an authenticated <code>User</code> model or None. </p>

<p>The <code>UserHandlingMixin</code> provides functionality for authenticating a user and creating the <code>current_user</code> property. </p>

<p>The work that's required will depend on how you build your system. The authentication framework uses a DictShield Document to create the <code>User</code> model, so you can implement a database query or check user information in a sorted CSV. Either way, you still get the authentication framework you need.</p>

<p>In practice, this is what your code looks like.</p>

<pre><code class="prettyprint">from brubeck.auth import web_authenticated, UserHandlingMixin

class DemoHandler(WebMessageHandler, UserHandlingMixin):
    @web_authenticated
    def post(self):
        ...
</code></pre>

<p>The <code>User</code> model in brubeck.auth will probably serve as a good basis for your needs. A Brubeck user looks roughly like below.</p>

<pre><code class="prettyprint">class User(Document):
    """Bare minimum to have the concept of a User.
    """
    username = StringField(max_length=30, required=True)
    email = EmailField(max_length=100)
    password = StringField(max_length=128)
    is_active = BooleanField(default=False)
    last_login = LongField(default=curtime)
    date_joined = LongField(default=curtime)        
    ...
</code></pre>

<ul>
<li><a href="https://github.com/j2labs/brubeck/blob/master/demos/demo_auth.py">Runnable demo</a></li>
</ul>

<h3>Database Connections</h3>

<p>Database connectivity is provided in the form of a <code>db_conn</code> member on the <code>MessageHandler</code> instances when a <code>db_conn</code> flag is passed to the Brubeck instance.</p>

<p>That looks like this:</p>

<pre><code class="prettyprint">config = {
    'db_conn': db_conn,
    ...
}

app = Brubeck(**config)
</code></pre>

<p>For people using <code>MessageHandler</code> instances, the database connection is available as <code>self.db_conn</code>.</p>

<p>For people using the function and decorator approach, you can get the database connection off the <code>application</code> argument, <code>application.db_conn</code>.</p>

<p>Query code then looks like this with the database connection as the first argument.</p>

<pre><code class="prettyprint">user = load_user(self.db_conn, username='jd')
</code></pre>

<h3>Secure Cookies</h3>

<p>If you need a session to persist, you can use Brubeck's secure cookies to track users.</p>

<p>You first add the cookie secret to your Brubeck config.</p>

<pre><code class="prettyprint">config = {
    'cookie_secret': 'OMGSOOOOSECRET',
    ...
}
</code></pre>

<p>Then retrieve the cookie value by passing the application's secret key into the <code>get_cookie</code> function.</p>

<pre><code class="prettyprint"># Try loading credentials from secure cookie
user_id = self.get_cookie('user_id',
                          secret=self.application.cookie_secret)
</code></pre>

<p>What you do from there is up to you, but you'll probably be loading the user_id from a database or cache to get the rest of the account info. </p>

  <div id="footer">
    <img class="footer" src="img/footer.png">
  </div>
  </body>
</html>