Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
308 lines (256 sloc) 25.1 KB
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"0>
<title>tubes web library</title>
<link rel="stylesheet" href="css/code.css" type="text/css" media="screen" charset="utf-8"/>
<link rel="stylesheet" href="css/master.css" type="text/css" media="screen" charset="utf-8"/>
<script type="text/javascript" src="js/jquery-1.3.2.js" charset="utf-8"></script>
</head>
<body>
<div id="outer">
<div id="header">
<h1 id="heading">tubes!</h1>
<p>a series of tubes...</p>
</div>
<div id="main">
<div class="question">
<h2>what is it?</h2>
<p>
it&apos;s a small library on top of
<a href="http://werkzeug.pocoo.org/">werkzeug</a>
to create
<a href="http://en.wikipedia.org/wiki/REST">REST</a>
APIs (and websites) using
<a href="http://en.wikipedia.org/wiki/JSON">JSON</a>
</p>
</div>
<div class="question">
<h2>why?</h2>
<p>because I make websites and I don't like big frameworks, and I don't like the webpy way of doing things</p>
</div>
<div class="question">
<h2>no, really, why??</h2>
<p>because I&apos;m tired of coding the same stuff all over again, so I decided to put them together for great justice</p>
</div>
<div class="question">
<h2>why the name?</h2>
<p>because as everyone knows, <a href="http://www.youtube.com/watch?v=f99PcP0aFNE">internet is a series of tubes</a></p>
</div>
<div class="question">
<h2>where is the code?</h2>
<p>the code is hosted at <a href="http://github.com/marianoguerra/tubes">github</a>, you can submit bugs or send me messages there</p>
</div>
<div class="question">
<h2>features?</h2>
<p>
in no particular order
<ul>
<li><em>simple</em> sintax using decorators to map an url to a method</li>
<li>match urls with regular expression</li>
<li>automatic marshalling and unmarshalling of objects</li>
<li>based on <a href="http://werkzeug.pocoo.org/">werkzeug</a> (which is <em>awesome</em>)</li>
<li>dispatch based on content type</li>
<li>based on <a href="http://wsgi.org/wsgi/What_is_WSGI">wsgi</a></li>
<li>google appengine ready</li>
<li><em>choose</em> your template engine and ORM</li>
<li>automatic generation of code for requests, classes and API tests</li>
</ul>
</p>
</div>
<div class="question">
<h2>examples?</h2>
<h3>hello world</h3>
<div class="highlight"><pre><span class="k">import</span> <span class="nn">tubes</span>
<span class="n">handler</span> <span class="o">=</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Handler</span><span class="p">()</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;^.*$&quot;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello_world</span><span class="p">(</span><span class="n">handler</span><span class="p">):</span>
<span class="k">return</span> <span class="s">&quot;hello world!&quot;</span>
<span class="n">tubes</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">handler</span><span class="p">)</span>
</pre></div>
<h4>run it</h4>
<pre>
$ python hello_world.py
* Running on http://0.0.0.0:8000/
</pre>
<h4>test it</h4>
<p>point your browser to that address, you can add anything after the url, that handler will match any url</p>
</div>
<a href="#" onclick="$(this).fadeOut();$('#moreexamples').fadeIn();return false;">more!</a>
<div class="more" id="moreexamples">
<h3>hello name</h3>
<div class="highlight"><pre><span class="k">import</span> <span class="nn">tubes</span>
<span class="n">handler</span> <span class="o">=</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Handler</span><span class="p">()</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;^/?$&quot;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello_world</span><span class="p">(</span><span class="n">handler</span><span class="p">):</span>
<span class="k">return</span> <span class="s">&quot;hello world!&quot;</span>
<span class="c"># match one or more characters of any type, optionally an ending slash</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;^/(.+?)/?$&quot;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello_name</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="s">&quot;hello &quot;</span> <span class="o">+</span> <span class="n">name</span>
<span class="n">tubes</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">handler</span><span class="p">)</span>
</pre></div>
<h4>run it</h4>
<pre>
# same as before, just change the file name in the following examples
$ python hello_name.py
* Running on http://0.0.0.0:8000/
</pre>
<h4>test it</h4>
<p>
point your browser to that address, you will se a hello world, now go to
<a href="http://0.0.0.0:8000/yourname">http://0.0.0.0:8000/yourname</a> you
should see "hello yourname". you can change <em>yourname</em> for any
string or path
</p>
<h3>responses</h3>
<div class="highlight"><pre><span class="k">import</span> <span class="nn">tubes</span>
<span class="n">handler</span> <span class="o">=</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Handler</span><span class="p">()</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;^/?$&quot;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello_world</span><span class="p">(</span><span class="n">handler</span><span class="p">):</span>
<span class="k">return</span> <span class="s">&quot;hello world!&quot;</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;^/fail/?$&quot;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">fail</span><span class="p">(</span><span class="n">handler</span><span class="p">):</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Response</span><span class="p">(</span><span class="s">&quot;introductory example fail&quot;</span><span class="p">,</span> <span class="mf">500</span><span class="p">)</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;^/not-here/?$&quot;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">not_here</span><span class="p">(</span><span class="n">handler</span><span class="p">):</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Response</span><span class="p">(</span><span class="s">&quot;nothing to see here, please move along&quot;</span><span class="p">,</span> <span class="mf">404</span><span class="p">)</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&quot;^/redirect/?$&quot;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">redirect</span><span class="p">(</span><span class="n">handler</span><span class="p">):</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">redirect</span><span class="p">(</span><span class="s">&quot;/&quot;</span><span class="p">,</span> <span class="n">code</span><span class="o">=</span><span class="mf">301</span><span class="p">)</span>
<span class="c"># you can make werkzeug reload the server everytime a change is made</span>
<span class="c"># also you can enable a debugger useful when developing to inspect exceptions</span>
<span class="c"># on the browser</span>
<span class="n">tubes</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span> <span class="n">use_reloader</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">use_debugger</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>
<h4>test it</h4>
<p>
point your browser to that the following addresses
<a href="http://0.0.0.0:8000/">hello world</a>,
<a href="http://0.0.0.0:8000/fail">fail (500 error)</a>,
<a href="http://0.0.0.0:8000/not-here">not found (404 error)</a>,
<a href="http://0.0.0.0:8000/redirect">redirect (to hello world)</a>
</p>
<h3>advanced</h3>
<p>
<div class="highlight"><pre><span class="k">import</span> <span class="nn">re</span>
<span class="k">import</span> <span class="nn">tubes</span>
<span class="c"># this module generates code for us (you can ignore it if you want)</span>
<span class="k">import</span> <span class="nn">intertubes</span>
<span class="c"># decorator to allow python&lt;-&gt;json&lt;-&gt;javascript</span>
<span class="nd">@tubes</span><span class="o">.</span><span class="n">JsonClass</span><span class="p">()</span>
<span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;a class that represents an user&quot;&quot;&quot;</span>
<span class="n">USER_FORMAT</span> <span class="o">=</span> <span class="s">&#39;[a-z][a-zA-Z0-9]*&#39;</span>
<span class="n">ALL_USER_FORMAT</span> <span class="o">=</span> <span class="s">&#39;^&#39;</span> <span class="o">+</span> <span class="n">USER_FORMAT</span> <span class="o">+</span> <span class="s">&#39;$&#39;</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">mail</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">firstname</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">lastname</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">website</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;constructor&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">user</span> <span class="o">=</span> <span class="n">user</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mail</span> <span class="o">=</span> <span class="n">mail</span>
<span class="bp">self</span><span class="o">.</span><span class="n">firstname</span> <span class="o">=</span> <span class="n">firstname</span>
<span class="bp">self</span><span class="o">.</span><span class="n">lastname</span> <span class="o">=</span> <span class="n">lastname</span>
<span class="bp">self</span><span class="o">.</span><span class="n">website</span> <span class="o">=</span> <span class="n">website</span>
<span class="nd">@classmethod</span>
<span class="k">def</span> <span class="nf">is_valid_username</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">username</span><span class="p">):</span>
<span class="sd">&#39;&#39;&#39;return True if username matches User.USER_FORMAT</span>
<span class="sd"> &#39;&#39;&#39;</span>
<span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">ALL_USER_FORMAT</span><span class="p">,</span> <span class="n">username</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span>
<span class="n">handler</span> <span class="o">=</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Handler</span><span class="p">()</span>
<span class="c"># if the path starts with /files serve</span>
<span class="c"># as static files from the files/ directory</span>
<span class="n">handler</span><span class="o">.</span><span class="n">register_static_path</span><span class="p">(</span><span class="s">&#39;/files&#39;</span><span class="p">,</span> <span class="s">&#39;files/&#39;</span><span class="p">)</span>
<span class="c"># a dict to simulate a database</span>
<span class="n">USERS</span> <span class="o">=</span> <span class="p">{}</span>
<span class="c"># when a post is made to this url the body is extracted and procesed by running</span>
<span class="c"># the User.from_json method, that way we receive a User object</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s">&#39;^/user/?$&#39;</span><span class="p">,</span> <span class="n">accepts</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">JSON</span><span class="p">,</span> <span class="n">transform_body</span><span class="o">=</span><span class="n">User</span><span class="o">.</span><span class="n">from_json</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">new_user</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span> <span class="n">user</span><span class="p">):</span>
<span class="c"># add the new user</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">User</span><span class="o">.</span><span class="n">is_valid_username</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">user</span><span class="p">):</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Response</span><span class="p">(</span><span class="s">&#39;Invalid username&#39;</span><span class="p">,</span> <span class="mf">500</span><span class="p">)</span>
<span class="n">USERS</span><span class="p">[</span><span class="n">user</span><span class="o">.</span><span class="n">user</span><span class="p">]</span> <span class="o">=</span> <span class="n">user</span>
<span class="c"># update user</span>
<span class="nd">@handler</span><span class="o">.</span><span class="kp">put</span><span class="p">(</span><span class="s">&#39;^/user/?$&#39;</span><span class="p">,</span> <span class="n">accepts</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">JSON</span><span class="p">,</span> <span class="n">transform_body</span><span class="o">=</span><span class="n">User</span><span class="o">.</span><span class="n">from_json</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">update_user</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span> <span class="n">user</span><span class="p">):</span>
<span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">user</span> <span class="ow">in</span> <span class="n">USERS</span><span class="p">:</span>
<span class="n">USERS</span><span class="p">[</span><span class="n">user</span><span class="o">.</span><span class="n">user</span><span class="p">]</span> <span class="o">=</span> <span class="n">user</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Response</span><span class="p">(</span><span class="s">&quot;user not found&quot;</span><span class="p">,</span> <span class="mf">404</span><span class="p">)</span>
<span class="c"># match a valid username identifier</span>
<span class="c"># if no produces keyword is given assume JSON</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;^/user/(&#39;</span> <span class="o">+</span> <span class="n">User</span><span class="o">.</span><span class="n">USER_FORMAT</span> <span class="o">+</span> <span class="s">&#39;)/?&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_user</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span> <span class="n">username</span><span class="p">):</span>
<span class="k">if</span> <span class="n">username</span> <span class="ow">in</span> <span class="n">USERS</span><span class="p">:</span>
<span class="k">return</span> <span class="n">USERS</span><span class="p">[</span><span class="n">username</span><span class="p">]</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Response</span><span class="p">(</span><span class="s">&quot;user not found&quot;</span><span class="p">,</span> <span class="mf">404</span><span class="p">)</span>
<span class="c"># return all users</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;^/users/?&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_users</span><span class="p">(</span><span class="n">handler</span><span class="p">):</span>
<span class="c"># isn&#39;t this easy?</span>
<span class="k">return</span> <span class="n">User</span><span class="o">.</span><span class="n">to_json_list</span><span class="p">(</span><span class="n">USERS</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
<span class="nd">@handler</span><span class="o">.</span><span class="kp">delete</span><span class="p">(</span><span class="s">&#39;^/user/(&#39;</span> <span class="o">+</span> <span class="n">User</span><span class="o">.</span><span class="n">USER_FORMAT</span> <span class="o">+</span> <span class="s">&#39;)/?&#39;</span><span class="p">,</span> <span class="n">transform_body</span><span class="o">=</span><span class="n">User</span><span class="o">.</span><span class="n">from_json</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">remove_user</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span> <span class="n">username</span><span class="p">):</span>
<span class="k">if</span> <span class="n">username</span> <span class="ow">in</span> <span class="n">USERS</span><span class="p">:</span>
<span class="k">del</span> <span class="n">USERS</span><span class="p">[</span><span class="n">username</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Response</span><span class="p">(</span><span class="s">&quot;user not found&quot;</span><span class="p">,</span> <span class="mf">404</span><span class="p">)</span>
<span class="c"># return the generated requests javascript code on that url</span>
<span class="c"># (on production you should save it to a file)</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;^/requests.js/?$&#39;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">JS</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">requests</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="sd">&#39;&#39;&#39;return the requests.js file to interact with this API&#39;&#39;&#39;</span>
<span class="k">return</span> <span class="n">tubes</span><span class="o">.</span><span class="n">Response</span><span class="p">(</span><span class="n">REQUESTS</span><span class="p">)</span>
<span class="c"># return the generated model javascript code on that url</span>
<span class="c"># (on production you should save it to a file)</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;^/model.js/?$&#39;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">JS</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">model</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="sd">&#39;&#39;&#39;return the model.js file to interact with this API&#39;&#39;&#39;</span>
<span class="k">return</span> <span class="n">MODEL</span>
<span class="c"># show the API test page here</span>
<span class="nd">@handler</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;^/test.html/?$&#39;</span><span class="p">,</span> <span class="n">produces</span><span class="o">=</span><span class="n">tubes</span><span class="o">.</span><span class="n">HTML</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="sd">&#39;&#39;&#39;return a dummy html to play with the API&#39;&#39;&#39;</span>
<span class="k">return</span> <span class="n">TEST_PAGE</span>
<span class="n">REQUESTS</span> <span class="o">=</span> <span class="n">intertubes</span><span class="o">.</span><span class="n">generate_requests</span><span class="p">(</span><span class="n">handler</span><span class="p">)</span>
<span class="n">MODEL</span> <span class="o">=</span> <span class="n">intertubes</span><span class="o">.</span><span class="n">generate_model</span><span class="p">([</span><span class="n">User</span><span class="p">])</span>
<span class="n">TEST_PAGE</span> <span class="o">=</span> <span class="n">intertubes</span><span class="o">.</span><span class="n">generate_html_example</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span>
<span class="p">(</span><span class="s">&#39;/files/json2.js&#39;</span><span class="p">,</span> <span class="s">&#39;/model.js&#39;</span><span class="p">))</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
<span class="n">tubes</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">handler</span><span class="p">,</span> <span class="n">use_reloader</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>
<h4>test it</h4>
<p>
point your browser to
<a href="http://0.0.0.0:8000/test.html">http://0.0.0.0:8000/test.html</a>
to test the API from the generated test page.
</p>
<p>
you can also see the code generated for the
<a href="http://0.0.0.0:8000/requests.js">requests</a> and
<a href="http://0.0.0.0:8000/model.js">model</a>
</p>
<p>
try to add a new user with something like
<pre>
model.User('marianoguerra', 'user@account')
</pre>
on the newUser form. That content will be evaluated and sent to the server, then try with getUser/removeUser (using the username you used above) and getUsers.
</p>
<p>
keep adding methods and reloading the test page to test them
</p>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-10841887-1");
pageTracker._trackPageview();
} catch(err) {}</script>
</body>
</html>
Something went wrong with that request. Please try again.