<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Paul Osman</title>
  <link href="http://paulosman.me/atom.xml" rel="self" />
  <link href="http://paulosman.me/" />
  <updated>2012-06-13T15:49:29-04:00</updated>
  <id>http://paulosman.me/</id>
  <author>
    <name>Paul Osman</name>
    <email>paul@eval.ca</email>
  </author>

  
  <entry>
    <title>Great Engineering Teams</title>
    <link href="http://paulosman.me/2012/01/31/great-engineering-teams.html" />
    <updated>2012-01-31T00:00:00-05:00</updated>
    <id>http://paulosman.me/2012/01/31/great-engineering-teams</id>
    <content type="html">&lt;p&gt;Building great teams is hard. We all know this. Nobody has the perfect
recipe for creating a great engineering team, but I'm fairly certain
that some things are obvious and should be heeded. What follows is a
list of observations I've made throughout my career about what seems
to work and what doesn't. Perfect excuse for a good old &quot;Do&quot; and
&quot;Don't&quot; list:&lt;/p&gt;

&lt;h2&gt;Do: Encourage Side Projects&lt;/h2&gt;

&lt;p&gt;All good developers run into things that piss them off from time to
time. Great developers build tools that help them fix the shit that
pisses them off. Give developers space and time to work on things not
on the roadmap and they'll probably end up building tools that solve
unseen business problems. Intentionally under-load developers (or go
one step further and do away with deadlines) and you'll start to see
deploys run more smoothly, metrics start being captured, open source
libraries and tools start being published and everyone is happier for
it. Allowing developers time for side projects is an intangible, but
I'm convinced it's an investment that pays for itself and then some.&lt;/p&gt;

&lt;h2&gt;Don't: Hire Asymmetric Managers&lt;/h2&gt;

&lt;p&gt;One of my pet peeves is seeing a shop employ a &quot;manager&quot; who's job is
to pester developers and make sure they meet deadlines. The best
managers I've worked with always ask two questions during check-ins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How are you doing?&lt;/li&gt;
&lt;li&gt;What can I do to help you?&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Good managers know that their job is to enable people to do great
things by clearing roadblocks and getting the hell out of their
way. If you have a manager who is constantly &quot;whipping&quot; their team
into shape, things are going to go very wrong. Hire managers who help
programmers as much as possible. We're all on the same team.&lt;/p&gt;

&lt;h2&gt;Do: Support Async Workflows&lt;/h2&gt;

&lt;p&gt;Being a software developer involves blocking out large chunks of time
to get into periods of deep concentration. Requiring developers to
break for meetings or respond to emails / im messages / impromptu
conversations immediately breaks concentration and can ruin a big part
of the day. It also screws your remote employees if you have
any. &lt;a href=&quot;http://zachholman.com/posts/how-github-works-asynchronous/&quot;&gt;Zach
Holman&lt;/a&gt;
and &lt;a href=&quot;http://www.paulgraham.com/makersschedule.html&quot;&gt;Paul Graham&lt;/a&gt; have
written about this much more elequently than I could hope to.&lt;/p&gt;

&lt;h2&gt;Don't: Hire &quot;B&quot;s&lt;/h2&gt;

&lt;p&gt;Comprimising on hiring is the quickest way to build a shitty team. It
can be tempting to hire some &quot;juniors&quot; that will need supervision, and
there's definitely room for people with a variety of experience, but
you need to shoot for the best and hire nothing but. Hiring second
rate developers will hurt your productivity and effect the morale of
your best folks. Good people like to work with good people. Don't fuck
with that.&lt;/p&gt;

&lt;h2&gt;Do: Encourage a devops culture&lt;/h2&gt;

&lt;p&gt;As I mentioned, great programmers scratch their own itches. Hire
people who know the whole stack. Great programmers should write great
code, but also care about how it's deployed and write or use tools
that help you do that effectively and safely. Having a divide between
developers and people who make sure their code can be shipped creates
two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your developers stop thinking about ops.&lt;/li&gt;
&lt;li&gt;You create an &quot;us&quot; vs &quot;them&quot; attitude. Developers complain about
&quot;conservative&quot; ops people and ops people complain about &quot;sloppy&quot;
developers.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Bottom line: making sure your production environment is healthy is
everybodys job.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Most of this boils down to respect. If you're shipping software,
you're an engineering company. Engineering companies need to be
managed as such. Don't treat your developers as second tier. Give them
what they need, support them and your chances of building a great
culture where people want to come and do great work will be greatly
increased.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Building Services with Apache Thrift</title>
    <link href="http://paulosman.me/2011/12/12/services-with-apache-thrift.html" />
    <updated>2011-12-12T00:00:00-05:00</updated>
    <id>http://paulosman.me/2011/12/12/services-with-apache-thrift</id>
    <content type="html">&lt;p&gt;I've always been interested in cross-language service frameworks. I
believe in using the best tools for a job, instead of being limited to
a specific language or framework, so being able to write components of
a service in whatever langauge makes the most sense is attractive to
me. In past lives, I've developed software that used
&lt;a href=&quot;http://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture&quot;&gt;CORBA&lt;/a&gt;,
&lt;a href=&quot;http://en.wikipedia.org/wiki/SOAP&quot;&gt;SOAP&lt;/a&gt;,
&lt;a href=&quot;http://en.wikipedia.org/wiki/Component_Object_Model&quot;&gt;COM&lt;/a&gt; and
&lt;a href=&quot;https://developer.mozilla.org/en/XPCOM&quot;&gt;XPCOM&lt;/a&gt; and found that they
all suck in different, significant ways. Because of this, I've been
interested in the Apache Thrift project originally developed at
Facebook.&lt;/p&gt;

&lt;p&gt;At its core, Thrift is an interface definition language, a code
generation tool and a set of libraries that take care of serialization
and implement the transport protocol.&lt;/p&gt;

&lt;p&gt;In order to generate code from a thrift service definition, you'll
need to install the thrift compiler. You can install it with
&lt;a href=&quot;http://mxcl.github.com/homebrew/&quot;&gt;homebrew&lt;/a&gt; on a mac or download it
from the &lt;a href=&quot;http://thrift.apache.org/download/&quot;&gt;project site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thrift services and datatypes are defined using an
&lt;a href=&quot;http://en.wikipedia.org/wiki/Interface_description_language&quot;&gt;IDL&lt;/a&gt;
that should look pretty familiar to anyone who has worked with CORBA
or similar technologies. Let's say you want to create a service that
handles creation and storage of user objects.  We'll write the service
in Python and a client in Ruby. This is obviously a contrived example,
but imagine that we're using some kind of data store that has a
library written in Python and all of our application code is in
Ruby. It'd be great to be able to use the data store from our Ruby
code without having to develop and maintain our own library.&lt;/p&gt;

&lt;p&gt;We start by defining the datatypes and service interface:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;thrift&quot;&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;                                                                                                      
  &lt;span class=&quot;mf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                                                                                             
  &lt;span class=&quot;mf&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;                                                                                              
  &lt;span class=&quot;mf&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;                                                                                                   
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;                                                                                                                  
                                                                                                                   
&lt;span class=&quot;k&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;                                                                                              
  &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;                                                                                      
  &lt;span class=&quot;kt&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;                                                                                           
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;                                                                                                                  
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Save the above code in a file called users.thrift, then generate
the Python code by running &lt;code&gt;thrift --gen py users.thrift&lt;/code&gt;.
Because we'll be writing a client in Ruby, we'll also want to run
&lt;code&gt;thrift --gen rb users.thrift&lt;/code&gt; to generate the Ruby code.&lt;/p&gt;

&lt;p&gt;Next, write the server in Python. This requires a bit of boilerplate
and a service handler that gets passed to the Thrift processor. Notice
that thrift threw the generated code in a directory called 'gen-py'.
It's just a Python module, so you can move it wherever you'd like, but
in this example I'll just modify sys.path appropriately.&lt;/p&gt;

&lt;p&gt;There are a variety of options for the server itself. For simplicity,
we'll just use &lt;code&gt;TServer.SimpleServer&lt;/code&gt; which is a
straightforward single threaded server. There's also a multithreaded
server and a forking server.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abspath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__file__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;gen-py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;thrift.transport&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TSocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TTransport&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;thrift.protocol&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TBinaryProtocol&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;thrift.server&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TServer&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;userservice&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserService&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserServiceHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;users&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9090&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserServiceHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TSimpleServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Processor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TSocket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TServerSocket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TTransport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TBufferedTransportFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TBinaryProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TBinaryProtocolFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Finally we write the client in Ruby. Ignore the boilerplate client
code for now, and focus on the part where we create a user, add it
using the service, then get a list of users from the service. This
part looks like normal Ruby code which is part of what I really like
about Thrift. There are no special types to use, users as returned
from the get_users method is a simple Array and everything feels
really natural.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;vg&quot;&gt;$:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;./gen-rb&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;thrift&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;user_service&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;userservice_types&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9090&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thrift&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thrift&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BufferedTransport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thrift&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BinaryProtocol&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Paul&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Osman&amp;#39;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I found a &lt;a href=&quot;http://jnb.ociweb.com/jnb/jnbJun2009.html&quot;&gt;few&lt;/a&gt;
&lt;a href=&quot;http://wiki.apache.org/hadoop/Hbase/ThriftApi&quot;&gt;good&lt;/a&gt;
&lt;a href=&quot;http://diwakergupta.github.com/thrift-missing-guide/thrift.pdf&quot;&gt;resources&lt;/a&gt;
&lt;a href=&quot;http://yannramin.com/2008/07/19/using-facebook-thrift-with-python-and-hbase/&quot;&gt;out
there&lt;/a&gt;,
but most were either a bit outdated, or too generic or went into areas
that I wasn't immediately interested in. I just wanted the straightest
line to seeing what Thrift was all about, so I thought I'd write that
up here.&lt;/p&gt;

&lt;p&gt;If you're curious about Thrift, I would encourage you to read this
&lt;a href=&quot;http://thrift.apache.org/static/thrift-20070401.pdf&quot;&gt;whitepaper&lt;/a&gt;
before diving any deeper. It provides a good overview of how Thrift
works and why it was designed the way it was. I'm glad to see that
Thrift is gaining some adoption, appearing in projects like &lt;a href=&quot;http://wiki.apache.org/hadoop/Hbase/ThriftApi&quot;&gt;HBase&lt;/a&gt;, &lt;a href=&quot;https://github.com/nathanmarz/storm/wiki/Using-non-JVM-languages-with-Storm&quot;&gt;Storm&lt;/a&gt; and &lt;a href=&quot;https://github.com/twitter/flockdb&quot;&gt;FlockDB&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Storing Extra Data in Django Join Tables</title>
    <link href="http://paulosman.me/2011/10/27/storing-extra-data-in-django-join-tables.html" />
    <updated>2011-10-27T00:00:00-04:00</updated>
    <id>http://paulosman.me/2011/10/27/storing-extra-data-in-django-join-tables</id>
    <content type="html">&lt;p&gt;I've had some people ask me about the &lt;code&gt;through&lt;/code&gt; argument supported by Django's &lt;code&gt;ManyToManyField&lt;/code&gt; class. This option supports a very simple use case: when you want to store additional data in a join table.&lt;/p&gt;

&lt;p&gt;Imagine, for instance, that we're building a simple course registration tool. Let's call the Django app &lt;code&gt;courses&lt;/code&gt;. We'll define the following models:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Course&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;A course offered at our school.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__unicode__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;u&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;A student registered with our school.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;courses&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Course&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;registered_on&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__unicode__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;u&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; -&amp;gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;When we run &lt;code&gt;syncdb&lt;/code&gt; or generate a migration for these
models, Django will automatically create the following tables:
&lt;code&gt;courses_course, courses_student, and
courses_student_courses&lt;/code&gt;. The first two should be
self-explanatory. The third table will store information about our
many-to-many relationship. Each row will associate a student id with a
course id.&lt;/p&gt;

&lt;p&gt;Now let's say we want to store the date and time whenever a student
registers for a course. We'd want something like the following model:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Registration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Student registration for a course.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;student&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;course&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForeignKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Course&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;registered_on&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTimeField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auto_now_add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;In order to get Django to use our model as a join table, we can use
&lt;code&gt;through&lt;/code&gt; by changing a single line in our student model:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;courses&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ManyToManyField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Course&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;through&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;Registration&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And that's it. Now, Django will automatically generate a &lt;code&gt;courses_registration&lt;/code&gt;
table and use it instead of &lt;code&gt;courses_student_courses&lt;/code&gt;, allowing us to store
extra data about the registration.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>New Gig - Joining Krux Digital</title>
    <link href="http://paulosman.me/2011/10/11/new-gig-joining-krux.html" />
    <updated>2011-10-11T00:00:00-04:00</updated>
    <id>http://paulosman.me/2011/10/11/new-gig-joining-krux</id>
    <content type="html">&lt;p&gt;I'm really excited to announce that I've joined the team at &lt;a href=&quot;http://www.kruxdigital.com&quot;&gt;Krux Digital&lt;/a&gt;. Krux was founded last year and have since been busy building products that help content publishers responsibly manage their consumer data.&lt;/p&gt;

&lt;p&gt;Krux's work touches on two areas very near and dear to my heart: user privacy and the proliferation of third-party Javascript. Writing third-party Javascript (social widgets, analytics, ad scripts, etc) is a great way to build distributable web applications (Ben Vinegar and Anton Koyalyov from Disqus are even &lt;a href=&quot;http://www.manning.com/vinegar/&quot;&gt;writing a book on the subject&lt;/a&gt;). I think content publishers deserve good tools to manage and monitor code included on their sites. I'm thrilled that Krux is creating these and I look forward to helping them. I'm also really excited that they're &lt;a href=&quot;http://www.kruxdigital.com/broadcasts/toms_blog/time_to_move_beyond_the_ad_frontier/&quot;&gt;looking way beyond ads&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Krux is based in San Francisco. They've got a great office in SoMa. I won't be moving there though, instead I'll be the first Toronto employee.&lt;/p&gt;

&lt;p&gt;Tech-wise, I'll still be writing a lot of Python code. I'm sure I'll get my hands dirty in some less familiar areas though, as they &lt;a href=&quot;http://engineering.kruxdigital.com/technology&quot;&gt;use a lot of cool tech&lt;/a&gt;. One of the first things I'm tackling is their deployment process. At the moment, some pieces of Krux's infrastructure are being deployed continuously and some are not. I want to see every project deployed continuously with thorough test coverage and aggressive monitoring. I expect I'll have a few blog posts about that coming up :-)&lt;/p&gt;

&lt;p&gt;I'm excited to be joining Krux at such an early stage. They've done a ton of work already, but I feel like they're just getting started. I'll be joining talented folks like &lt;a href=&quot;http://www.oscon.com/oscon2009/public/schedule/speaker/4676&quot;&gt;Jos Boumans&lt;/a&gt;, &lt;a href=&quot;http://nick.sullivanflock.com/resume/&quot;&gt;Nick Sullivan&lt;/a&gt; and Krux's founders, &lt;a href=&quot;http://www.kruxdigital.com/broadcasts/toms_blog/&quot;&gt;Tom Chavez&lt;/a&gt; and &lt;a href=&quot;http://www.kruxdigital.com/broadcasts/viveks_blog/&quot;&gt;Vivek Vaidya&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Shipped New Drumbeat.org</title>
    <link href="http://paulosman.me/2011/02/14/shipped-new-drumbeat.html" />
    <updated>2011-02-14T09:17:19-05:00</updated>
    <id>http://paulosman.me/2011/02/14/shipped-new-drumbeat</id>
    <content type="html">&lt;p&gt;A week ago we shipped the new version of &lt;a href=&quot;http://drumbeat.org/&quot;&gt;drumbeat.org&lt;/a&gt;. This was a ground up rewrite and represents a significant change in the direction of the site. Before I go into too many details, or start talking about what's next, I want to thank everyone who has committed code to the project so far:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a title=&quot;Jaco Joubert&quot; href=&quot;http://www.designisreason.com/&quot;&gt;Jaco Joubert&lt;/a&gt; - Design&lt;/li&gt;
    &lt;li&gt;&lt;a title=&quot;Ned Schwartz&quot; href=&quot;http://theinterned.net/&quot;&gt;Ned Schwartz&lt;/a&gt; - Front end hacking&lt;/li&gt;
    &lt;li&gt;&lt;a title=&quot;Brian Brennan&quot; href=&quot;http://words.brianloves.it/&quot;&gt;Brian Brennan&lt;/a&gt; - Initial markup and css cutup, front end rescue operative&lt;/li&gt;
    &lt;li&gt;&lt;a title=&quot;Cillian O'Ruanaidh&quot; href=&quot;http://cilliano.com/&quot;&gt;Cillian O'Ruanaidh&lt;/a&gt; - Tester, Django hacker&lt;/li&gt;
    &lt;li&gt;&lt;a title=&quot;Guillermo Movia&quot; href=&quot;http://identi.ca/deimidis&quot;&gt;Guillermo Movia&lt;/a&gt; - Markup and css for those welcome and error messages.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;So, What's New?&lt;/h3&gt;


&lt;p&gt;When we decided to rewrite drumbeat.org in Django, we also decided to revisit our goals for the site. The old site tried to do a lot of things and we'd prefer to do fewer things and do them well. We decided that drumbeat.org is a social web application that should allow a user to link to content all over the web and have their activities (blog posts, wiki edits, github commits) aggregated in one place. With that in mind, we built the new drumbeat.org. There's a lot of refining to do, but that's the essence of the new site: find people and projects to follow, create projects, and aggregate activity relevant to your project.&lt;/p&gt;

&lt;h3&gt;What about my blog posts / pictures / text?&lt;/h3&gt;


&lt;p&gt;Most data, including user accounts and projects was automatically migrated over. You should even be able to log in with your old password. Some things however, like blog posts, don't exist on the new site. The old site will be available at old.drumbeat.org until March 15th, 2011. You'll be able to salvage whatever content you may have on the site before then.&lt;/p&gt;

&lt;h3&gt;What now?&lt;/h3&gt;


&lt;p&gt;Getting to a point where we were comfortable shipping was the first step. We'll now be shipping updates as often as we can. See the (constantly in progress) &lt;a href=&quot;https://wiki.mozilla.org/Drumbeat/Batucada/Roadmap&quot;&gt;Roadmap&lt;/a&gt; for an idea of what we'd like to do over the next couple of quarters. We're also &lt;a href=&quot;http://hire.jobvite.com/CompanyJobs/Careers.aspx?c=qpX9Vfwa&amp;amp;cs=9Kt9Vfw1&amp;amp;page=Job%20Description&amp;amp;j=ouAzVfwi&quot;&gt;hiring a full-time front-end dev&lt;/a&gt; which will double the number of full-time technical staff we have dedicated to drumbeat.org. As such, I'm planning to start having weekly planning calls that will be open to the community. One week will be dedicated to planning an iteration (deciding on what bugs / features to tackle for a specific iteration) and the next one will serve as a status check-in. 2011 is going to be an exciting time for drumbeat.org. I'm so glad to be out of the building phase and into the shipping phase.&lt;/p&gt;

&lt;h3&gt;This is really great, but what it's really missing is _____.&lt;/h3&gt;


&lt;p&gt;I love feedback and I love ideas about what would be useful for drumbeat.org. If you have some feature that you feel is absolutely essential, there are a few things you can do:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Check the &lt;a href=&quot;https://wiki.mozilla.org/Drumbeat/Batucada/Roadmap&quot;&gt;Roadmap&lt;/a&gt;. If it's already there, we're planning to add it.&lt;/li&gt;
    &lt;li&gt;Check &lt;a href=&quot;https://bugzilla.mozilla.org/buglist.cgi?product=Websites&amp;amp;component=www.drumbeat.org&amp;amp;resolution=---&quot;&gt;Bugzilla&lt;/a&gt;. It's possible someone has already thought of this and decided to file a bug on it.&lt;/li&gt;
    &lt;li&gt;Bring it up on the &lt;a href=&quot;http://www.mozilla.org/about/forums/#drumbeat-website&quot;&gt;mailing list.&lt;/a&gt; This is a good chance to see if other people share your passion for the feature you're thinking of.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://bugzilla.mozilla.org/enter_bug.cgi?product=Websites&amp;amp;component=www.drumbeat.org&quot;&gt;File a bug&lt;/a&gt;. If you feel like it's worth lobbying for, file a bug requesting the feature.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;It's important to note that simply requesting a feature will not make it so. It's entirely possible that a particular feature does not align with the goals of the project and will therefore not be implemented.&lt;/p&gt;

&lt;h3&gt;I found a bug&lt;/h3&gt;


&lt;p&gt;Fantastic. This is extremely helpful. If you feel you've found a bug, the process is similar to above, only shorter!&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Check &lt;a href=&quot;https://bugzilla.mozilla.org/buglist.cgi?product=Websites&amp;amp;component=www.drumbeat.org&amp;amp;resolution=---&quot;&gt;Bugzilla&lt;/a&gt;. It's possible someone has already filed a bug about the issue you've encountered. If they have, feel free to comment on the bug.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://bugzilla.mozilla.org/enter_bug.cgi?product=Websites&amp;amp;component=www.drumbeat.org&quot;&gt;File a bug&lt;/a&gt;. Include a description of what you were doing, as well as steps to reproduce (if possible).&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;That's it! Filing bugs is a great way to help the project.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Drumbeat.org Relaunch</title>
    <link href="http://paulosman.me/2011/01/03/drumbeat-org-relaunch.html" />
    <updated>2011-01-03T10:47:45-05:00</updated>
    <id>http://paulosman.me/2011/01/03/drumbeat-org-relaunch</id>
    <content type="html">&lt;p&gt;Let the countdown begin! We're into the final stretch of the Django rewrite of drumbeat.org, so I thought I'd write a bit about our plans for the next month or so. I'll be staging the application this week and submitting a ticket for security review and working on a deployment plan so we'll be ready to flip the switch when the time comes. Depending on these external factors, we hope to launch by the end of the month. I'll be posting a final list of blockers this week.&lt;/p&gt;

&lt;h3&gt;Batucada&lt;/h3&gt;


&lt;p&gt;The new software that will power drumbeat.org is going to feel different. This isn't just a technology change, but a new vision and direction for the site as well. The user experience is much more social; content will be generated by people and projects in your network. Instead of a heavy content site, we're aiming for small pieces loosely joined. Users and projects will be able to add links to external content, and have content from those links show up in the news feeds of people in their network.&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;/images/batucada.png&quot;&gt;&lt;img class=&quot;aligncenter&quot; title=&quot;Screenshot&quot; src=&quot;/images/batucada.png&quot; alt=&quot;Drumbeat.org Dashboard&quot; width=&quot;556.5&quot; height=&quot;384&quot; /&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3 style=&quot;text-align: left;&quot;&gt;What to Expect&lt;/h3&gt;


&lt;p&gt;We're aiming to make the transition to the new site as seamless as possible for existing drumbeat.org users. At a minimum, user accounts and projects will be moved over to the new site. You will be able to log in with your existing credentials and your projects will already exist. Contributors to projects on the old site will become followers, and updates from your project will appear in their dashboards.&lt;/p&gt;

&lt;p&gt;Project and profile metadata (images, bios, etc) will not be moved over. We will be keeping the old, Drupal site around for a limited time, at an alternative URL, so that people can grab their data that doesn't get automatically migrated to the new site.&lt;/p&gt;

&lt;h3&gt;Future Development&lt;/h3&gt;


&lt;p&gt;2011 will be a year of rapid iterations on the drumbeat.org website and platform. I'm going to push for us to do releases at a minimum every two weeks with bug fixes and feature enhancements. What we launch later this month will not be perfect and there'll be a lot of work to do, but we'll now be in a position to make constant improvements. Our roadmap and goals will be posted to the &lt;a title=&quot;Batucada Wiki&quot; href=&quot;http://wiki.mozilla.org/Drumbeat/Batucada&quot;&gt;wiki&lt;/a&gt; as we plan each iteration. Some of the stuff I already know will be in scope for Q1 and Q2: l10n/i18n, improvements to the user experience, addressing scalability and performance issues as they arise, etc.&lt;/p&gt;

&lt;p&gt;I'm looking forward to an exciting year for drumbeat.org. As always, ping me if you'd like to get involved!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Status Report - What I've Been Up To</title>
    <link href="http://paulosman.me/2010/11/15/status-report-what-ive-been-up-to.html" />
    <updated>2010-11-15T12:08:02-05:00</updated>
    <id>http://paulosman.me/2010/11/15/status-report-what-ive-been-up-to</id>
    <content type="html">&lt;p&gt;I've got a huge backlog of things I've been meaning to blog about, so to hopefully kick things off, here's a short list of things I've been up to in the last few months:&lt;/p&gt;

&lt;h4&gt;P2PU Development Sprint&lt;/h4&gt;


&lt;p&gt;I spent a few days in Barcelona (before the Drumbeat festival) working with some &lt;a title=&quot;John D Britton&quot; href=&quot;http://www.johndbritton.com/&quot;&gt;very&lt;/a&gt; &lt;a title=&quot;Jessy Cowan-Sharp&quot; href=&quot;http://jessykate.com/&quot;&gt;talented&lt;/a&gt; &lt;a title=&quot;Brian J Brennan&quot; href=&quot;http://brianlovesthings.com/&quot;&gt;developers &lt;/a&gt;on a development sprint for the &lt;a title=&quot;P2PU&quot; href=&quot;http://p2pu.org&quot;&gt;P2PU Website&lt;/a&gt;. We split our time between fixing bugs in the existing Drupal code base and working on (as well as deciding) the redevelopment of the site on a new platform. After some refreshingly practical debate, we decided to build on &lt;a title=&quot;Batucada&quot; href=&quot;http://blog.eval.ca/2010/07/02/batucada-marching-towards-drumbeat-org-1-0/&quot;&gt;batucada&lt;/a&gt;, the Drumbeat.org rewrite I've been leading. Being on the same code base and of course the same platform (Django) means the two teams will be able to share code fairly easily, which is really exciting.&lt;/p&gt;

&lt;p&gt;The sprint was a huge success. We had a great venue which we affectionately referred to as the &quot;Hacker Dungeon&quot; (there were no windows!). We fixed some major problems on the current website and left knowing that P2PU has a great core development team and it's really exciting that the batucada and lernanta (codename for the new P2PU code base) teams are going to get to work so closely together (particularly great for me, as I'm on both teams!).&lt;/p&gt;

&lt;h4&gt;Drumbeat Festival&lt;/h4&gt;


&lt;p&gt;The Drumbeat Festival, also in Barcelona, was scheduled immediately after the P2PU development sprint. This was a massive event, and I'm incredibly proud to work with the people who helped pull it all together. It was a 3 day event with over 400 participants from over 40 countries in 3 different venues and somehow, it all worked. People self-organized into fairly focused groups, all working on different projects. It was very unconference style, but my sense was that most people stuck with the same group rather than cross-pollinating. The theme this year was Learning, Freedom and the Web. Attendants were from all over, but could roughly be categorized into hackers and educators. Despite this, I was pleasantly surprised to walk away with more new technical contacts than I get at most technical conferences. I also had a few people approach me about volunteering their web development skills for Drumbeat, which was fantastic.&lt;/p&gt;

&lt;p&gt;I spent nearly all of my time working in the Badge Lab. &lt;a title=&quot;Dan Mills&quot; href=&quot;http://blog.sandmill.org/&quot;&gt;Dan Mills&lt;/a&gt;, &lt;a title=&quot;Joshua Gay&quot; href=&quot;http://joshuagay.org/&quot;&gt;Joshua Gay&lt;/a&gt;, a wonderful group of volunteers and myself found a corner to hide in and worked on a functional demo of a federated badge system. More on that below!&lt;/p&gt;

&lt;h4&gt;Badge Backpack&lt;/h4&gt;


&lt;p&gt;Thanks to a great group of folks working with us, we were successful in creating a functional demo of a federated badge system. The system uses HTML5 postMessage to enable cross-origin site communication. Badges can be issued by any site, stored in a &quot;backpack&quot; (which simply stores the badge data in localStorage) and consumer sites can show badges stored in the backpack with just a few lines of Javascript. We're continuing development on the idea, and have a couple of Github repositories (&lt;a href=&quot;https://github.com/drumbeat-badge-sprint/hub&quot;&gt;here&lt;/a&gt;, and &lt;a href=&quot;https://github.com/drumbeat-badge-sprint/backpack&quot;&gt;here&lt;/a&gt;) and of course a &lt;a title=&quot;Badge Lab&quot; href=&quot;http://groups.google.com/group/badge-lab&quot;&gt;mailing list&lt;/a&gt;. Thanks to &lt;a href=&quot;http://twitter.com/#!/carl0s_&quot;&gt;Carlo Frinolli&lt;/a&gt;, &lt;a href=&quot;http://twitter.com/#!/mrbichel&quot;&gt;Johan Bichel&lt;/a&gt;, &lt;a href=&quot;http://manufacturaindependente.org/&quot;&gt;Ana Carvalho&lt;/a&gt; and others (so sorry if I didn't catch your name) for joining the code sprint and helping us out!&lt;/p&gt;

&lt;h4&gt;Batucada / Drumbeat.org&lt;/h4&gt;


&lt;p&gt;Batucada development is coming along nicely. We've got some new folks contributing code and are laser focused on delivering an initial production release by the end of the year. We're working with a designer to finalize the interface, and are really excited to get this thing going. The existing Drupal based site will be kept available for some time, albeit at a different URL, so people will be able to access it at least even after we launch the new version (signups will be disabled though).&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Making Signups Easier With WebFinger</title>
    <link href="http://paulosman.me/2010/10/04/making-signups-easier-with-webfinger.html" />
    <updated>2010-10-04T12:17:12-04:00</updated>
    <id>http://paulosman.me/2010/10/04/making-signups-easier-with-webfinger</id>
    <content type="html">&lt;p&gt;I've been thinking about signup processes lately and what we can do with &lt;a title=&quot;Batucada &quot; href=&quot;http://www.drumbeat.org/project/batucada&quot;&gt;batucada&lt;/a&gt; (the next drumbeat.org) to make it easier, while encouraging users to use open, decentralized identity technologies like &lt;a href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;The Problem&lt;/h3&gt;


&lt;p&gt;Whenever I come across a new service that I want to create an account on, I dread the idea of creating a new set of credentials and re-entering a bunch of information I have already entered elsewhere. OpenID solves part of this for me, and I'm always happy to see sites supporting it. The problem, however, is that OpenID only solves one piece of the puzzle (authentication) and there are usability problems abound. The latter problem is well known, and people have come up with a bunch of innovative ways of tackling it, including intelligent OpenID selectors.&lt;/p&gt;

&lt;p&gt;The core of the problem is that people don't tend to think of themselves as a URL, which is what OpenID uses as a supplied identifier. It's been really hard to get the average web user to think of a URL as part of their identity, and frankly, they're usually pretty hard to remember. There are ways around this, including hosting your own OpenID provider, or using delegation, so that you can choose an easy-to-remember URL, but it's unrealistic to expect non-technical web users to be able to do this.&lt;/p&gt;

&lt;h3&gt;The Solution&lt;/h3&gt;


&lt;p&gt;Enter &lt;a href=&quot;http://code.google.com/p/webfinger/&quot;&gt;WebFinger&lt;/a&gt;. The idea behind WebFinger is that, given an email-like identifier, an application can discover information about that user (including their OpenID provider). I really like this idea. I think people have an easier time with email addresses than with URLs. A bunch of providers, including AOL, Gmail and Yahoo have implemented WebFinger support, which means that there are a ton of people out there who already have WebFinger identifiers, even if they don't know it (and they shouldn't have to know it).&lt;/p&gt;

&lt;p&gt;So what would a WebFinger based signup process look like? The first step would be to capture an email address and find out if it is a valid WebFinger identifier. If it is, the next step would be to find out of the user has an OpenID provider. If their email address is not a WebFinger identifier, the workflow would fall back to a more traditional password based signup form. Imagine clicking on a &quot;Sign up&quot; link and being presented with a form asking for your email address.&lt;/p&gt;

&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;/images/login_step_one.png&quot;&gt;&lt;img class=&quot;size-medium wp-image-450 aligncenter&quot; title=&quot;login_step_one&quot; src=&quot;/images/login_step_one.png&quot; alt=&quot;Step One&quot; width=&quot;473&quot; height=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p style=&quot;text-align: left;&quot;&gt;Once submitted, the web application would perform WebFinger based discovery on the identifier in the background. If an OpenID provider is found, the user could be presented with a friendly message asking them if they'd like to use it to sign in to the site.&lt;/p&gt;


&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;/images/mockup.png&quot;&gt;&lt;img class=&quot;size-medium wp-image-451  aligncenter&quot; title=&quot;Step Two&quot; src=&quot;/images/mockup.png&quot; alt=&quot;Step Two&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p style=&quot;text-align: left;&quot;&gt;If the email address is not a WebFinger identifier, or if the user opts not to use their OpenID, fall back to a more traditional sign up form.&lt;/p&gt;


&lt;p style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;


&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;/images/webfinger_step_two.png&quot;&gt;&lt;img class=&quot;size-medium wp-image-453 aligncenter&quot; title=&quot;webfinger_step_two&quot; src=&quot;/images/webfinger_step_two.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p style=&quot;text-align: left;&quot;&gt;These are obviously very rough, but I'm interested to know what people think of this idea. Specifically how the process could be made easier for non-technical users. A few concerns I have (and note that UX / Usability is certainly NOT my specialty, so help is appreciated!):&lt;/p&gt;




&lt;ol&gt;
    &lt;li&gt;A service provider shouldn't coerce a user into using an OpenID vs creating a new set of credentials, the user must always have a choice. Unfortunately, this means asking another question, and questions are confusing.&lt;/li&gt;
    &lt;li&gt;Presuming a user has an OpenID, but does not have an active session with their provider, they are going to be sent to their provider to log in after Step Two. This could be confusing to users. We're sending them into the OpenID &quot;Chasm of Death&quot;. Is there anything that can be done to ease this transition / manage user expectations?&lt;/li&gt;
    &lt;li&gt;Once users have signed up using an OpenID, how do we let them know that they should use that OpenID from now on? I suppose we could just prompt for an email address on sign in and perform discovery again. This introduces another step for those who aren't using an OpenID though (step one: input email address, step two on next screen: enter password).&lt;/li&gt;
    &lt;li&gt;Users could be confused immediately when we ask for their email address and not a password. This is breaking a familiar pattern, which is always potentially jarring.&lt;/li&gt;
    &lt;li&gt;What about users who know what OpenID is, and have an OpenID that they'd like to use, but don't have a WebFinger identifier that refers to their OpenID? Again, more questions to ask, which adds more confusion.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Again, I'm certainly not a UX person, so I'd be very curious to hear what people think of this idea and if people have ideas about how to improve the flow.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Batucada - Tools &amp; Vision</title>
    <link href="http://paulosman.me/2010/07/07/batucada-tools-vision.html" />
    <updated>2010-07-07T14:56:48-04:00</updated>
    <id>http://paulosman.me/2010/07/07/batucada-tools-vision</id>
    <content type="html">&lt;p&gt;I mentioned in the &lt;a href=&quot;http://blog.eval.ca/2010/07/02/batucada-marching-towards-drumbeat-org-1-0/&quot;&gt;announcement of batucada&lt;/a&gt;, that we are dropping &lt;a title=&quot;Drupal&quot; href=&quot;http://drupal.org&quot;&gt;Drupal&lt;/a&gt; for the next phase of development on the Drumbeat website project. We've decided to build Batucada with the Python web framework &lt;a title=&quot;Django&quot; href=&quot;http://www.djangoproject.com/&quot;&gt;django&lt;/a&gt;. There are a bunch of reasons why I think django is a good choice, but the primary one is that people are already using it for a number of Mozilla web projects, including AMO and SUMO. Having other people in the organization who are familiar with the framework is helpful, of course, and it means we can reuse work done elsewhere within the webdev team (particularly in fairly generic areas like localization, caching, etc). It also means that our IT department is already familiar with deployment issues.&lt;/p&gt;

&lt;p&gt;Django is also simply a great framework. The ability to package up reusable pieces of a project as applications means that we should be able to take advantage of a lot of great work done by folks working on similar web applications, and where we build applications ourselves, we'll be able to release those as separate reusable components. If the work we do can help other people build Django based social web applications that use open web technologies, then we will be further working towards Mozilla's mission of promoting openness, innovation and opportunity on the web.&lt;/p&gt;

&lt;p&gt;In order to stay on track, I'm working on a roadmap and a set of core principles that will guide our development of batucada. The roadmap is a work in progress, but I think we've got a good set of principles hashed out:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Social - Batucada will be a social application. People and activities will be at the centre of the experience.&lt;/li&gt;
    &lt;li&gt;Intelligent - Batucada should be able to suggest connections based on a users activity and profile information.&lt;/li&gt;
    &lt;li&gt;Data Ownership - Users will control who has access to their personal data and will be able to export their data easily.&lt;/li&gt;
    &lt;li&gt;Open Web - Batucada will be built using Open Web technologies and will use open standards to integrate with other applications whenever it makes sense to do so.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;It goes without saying that Batucada will be Open Source as well of course!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Batucada - Marching towards Drumbeat.org 1.0</title>
    <link href="http://paulosman.me/2010/07/02/batucada-marching-towards-drumbeat-org-1-0.html" />
    <updated>2010-07-02T07:34:14-04:00</updated>
    <id>http://paulosman.me/2010/07/02/batucada-marching-towards-drumbeat-org-1-0</id>
    <content type="html">&lt;p&gt;I'm excited to announce a new phase of development for the Drumbeat website project. We are about to begin development on a new project, code named &lt;em&gt;batucada&lt;/em&gt;, that will eventually power drumbeat.org. With &lt;em&gt;batucada&lt;/em&gt;, the future of drumbeat.org will be more social and will have the open web at its core.&lt;/p&gt;

&lt;p&gt;To understand how we got here, it's important to give a brief history of the drumbeat website. The current alpha and beta phases of development were very experimental. We didn't have a clear idea of what the end result would be. We wanted something that would allow us to experiment and iterate quickly and so building on a CMS was a natural decision. Drupal was a good choice for an open source CMS product. Building the site on Drupal allowed us to get something up and running quickly and gave us a good chance to see how people interacted with the site. We always knew that the technology choices we had made were probably not going to be permanent. Any CMS product is going to introduce tight coupling between code and content changes, which means that developers need access not just to code, but also to the configuration data stored in a database. Providing this in a way that does not compromise users privacy has been a challenge and a barrier for people wanting to contribute code to the project. Additionally, while Drupal's module system is very handy for getting something up and running quickly, at times it makes adding custom functionality and integrations more difficult than if we were using a standard web framework. The lack of unit testing in Drupal also means that we can't create a robust set of test cases for each feature we add to the site, and can't take advantage of things like Continuous Integration, which we're big fans of. I'm aware that many of these issues are going to be further addressed in Drupal 7, but given the amount we're looking to change in the next major release, waiting and upgrading presents its own set of challenges.&lt;/p&gt;

&lt;p&gt;We now have dedicated technical staff and a much clearer idea of what drumbeat.org should be, so it's a good time to take the user feedback we've collected from the alpha and beta versions of the site, and apply our experimental results to a production ready, 1.0 product. Batucada will represent a radical shift for drumbeat.org. The vision is a much simpler, user centric site that integrates well with other web applications.&lt;/p&gt;

&lt;h3&gt;Social and the Open Web&lt;/h3&gt;


&lt;p&gt;Thinking about where we want to take the site next, one thing is really clear to me, drumbeat.org is a great use case for a social web application: connecting people with projects and local events. Furthermore, if we are to promote the open web, we should be using open web technologies wherever they make sense. &lt;em&gt;Batucada &lt;/em&gt;will use open web standards to integrate with other web applications whenever possible. Users should not have to do everything on drumbeat.org. Instead, drumbeat.org should be a &quot;launch pad&quot; for projects, and activities performed on other sites should make their way back to drumbeat.org.&lt;/p&gt;

&lt;p&gt;The road-map hasn't been completed yet, but we'll be examining everything from discovery (&lt;a title=&quot;WebFinger&quot; href=&quot;http://code.google.com/p/webfinger/&quot;&gt;WebFinger&lt;/a&gt;), real-time activity streaming (&lt;a title=&quot;Activity Streams&quot; href=&quot;http://activitystrea.ms/&quot;&gt;Activity Streams&lt;/a&gt;, &lt;a title=&quot;PubSubHubbub&quot; href=&quot;http://code.google.com/p/pubsubhubbub/&quot;&gt;PubSubHubbub&lt;/a&gt;), distributed updates (&lt;a title=&quot;OStatus&quot; href=&quot;http://ostatus.org/&quot;&gt;OStatus&lt;/a&gt; / &lt;a title=&quot;Salmon Protocol&quot; href=&quot;http://www.salmon-protocol.org/&quot;&gt;Salmon&lt;/a&gt;, &lt;a title=&quot;Portable Contacts&quot; href=&quot;http://portablecontacts.net/&quot;&gt;Portable Contacts&lt;/a&gt;) and decentralized identity (&lt;a title=&quot;The OpenId Foundation&quot; href=&quot;http://openid.net/&quot;&gt;OpenID&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The next version of drumbeat.org will have social at its core and will be built on Open Web goodness. Where the current version is pretty document centric, the new version will be entirely people centric.&lt;/p&gt;

&lt;h3&gt;Global Community&lt;/h3&gt;


&lt;p&gt;The Drumbeat community is not centered in any one country or region. Drumbeat participants come from all corners of the globe. Events have been held in Brazil, Canada, Germany and community members are currently organizing events in France and the United States. Given the global nature of the movement, we will be building in support for localization and internationalization from day one and will be seeking help from localizers who want to contribute to the project. The code name for the project, &lt;em&gt;batucada&lt;/em&gt;, is a nod to Drumbeat community members in Brazil who kicked off the first two Drumbeat events.&lt;/p&gt;

&lt;h3&gt;Open Source and Community Driven&lt;/h3&gt;


&lt;p&gt;Another thing that is clear is that drumbeat.org should be an open source project. We are Mozilla, we make open source software. For a variety of reasons, we failed to fully capitalize on this with the current version of the site. The next version will be launched as an open source project and we will be actively encouraging ideas and contributions. We want to build a community of people actively building the software behind the project. If this sounds exciting to you, join us! We'll need software developers, front end designers, UX engineers, QA testers, localizers and a whole host of other skill-sets.&lt;/p&gt;

&lt;p&gt;I'll be publishing an initial roadmap in the near future and will be looking for feedback.&lt;/p&gt;

&lt;h3&gt;Using Drumbeat to build the next Drumbeat&lt;/h3&gt;


&lt;p&gt;Drumbeat.org is all about projects. It makes perfect sense to us to make &lt;em&gt;batucada &lt;/em&gt;itself a Drumbeat project. Using existing tools to build new one will allow us to step into the shoes of project maintainers on the site - a perfect way to experience first hand how a future version of the site could better serve the needs of our users. Plus, as a programmer, I like recursion! The batucada &lt;a title=&quot;Batucada&quot; href=&quot;http://www.drumbeat.org/project/batucada&quot;&gt;project page&lt;/a&gt; on Drumbeat is a work in progress, but will be used to facilitate work on the project. Join up on the project page to help build this, and share ideas and feedback.&lt;/p&gt;

&lt;p&gt;So there you have it! I'm really excited to kick off this next phase of development. This is the first of a series of posts, in the near future I'll be outlining some of the technical choices we've made (language, framework, etc) as well as a set of values I'm going to pitch for us to follow while we work on this project. Much more to come.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Motivation</title>
    <link href="http://paulosman.me/2010/05/10/motivation.html" />
    <updated>2010-05-10T04:24:48-04:00</updated>
    <id>http://paulosman.me/2010/05/10/motivation</id>
    <content type="html">&lt;p&gt;Reading posts like this make me feel pretty damn good about decisions I've made recently.&lt;/p&gt;




&lt;p&gt;&lt;a href=&quot;http://www.thewavingcat.com/2010/05/09/drumbeat-berlin/&quot;&gt;&quot;And in this mission, I guess, is the key to what makes Drumbeat special: rather than an initiative, it felt more like there’s a movement building up.&quot;&lt;/a&gt;&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Joining Mozilla</title>
    <link href="http://paulosman.me/2010/05/02/joining-mozilla.html" />
    <updated>2010-05-02T23:35:44-04:00</updated>
    <id>http://paulosman.me/2010/05/02/joining-mozilla</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;/images/mozilla-foundation.png&quot; alt=&quot;&quot; title=&quot;mozilla-foundation&quot; class=&quot;alignnone size-full wp-image-346&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I'm very happy to announce that I've taken a position as a Senior Web Developer for the Mozilla Foundation. I'm sure I'll be getting into all sorts of things, but job number one is going to be helping out with the &lt;a href=&quot;http://drumbeat.org&quot;&gt;Drumbeat&lt;/a&gt; community. If you've never heard of it, Mozilla Drumbeat is a launch-pad and playground for every day web users working on preserving the openness of the web. It's kick-ass and extremely important and I'm proud to be a part of it.&lt;/p&gt;


&lt;p&gt;Deciding to leave FreshBooks was a tough call to make. I worked with some amazing people, and I've been able to build &lt;a href=&quot;http://developers.freshbooks.com/blog/view/freshbooks_now_supports_oauth/&quot;&gt;Some&lt;/a&gt; &lt;a href=&quot;http://www.freshbooks.com/blog/2010/03/10/google-apps-marketplace-live-with-freshbooks/&quot;&gt;Really&lt;/a&gt; &lt;a href=&quot;http://developers.freshbooks.com/blog/view/on_implementing_webhooks/&quot;&gt;Cool&lt;/a&gt; &lt;a href=&quot;http://www.freshbooks.com/blog/2010/01/19/where-in-the-world-is-freshbooks/&quot;&gt;Things&lt;/a&gt;. FreshBooks is truly a cool place to work, and I continue to encourage people to check them out. In the end though, the idea of working for a &lt;a href=&quot;http://blog.lizardwrangler.com/2010/04/23/lets-speak-up-about-mozillas-public-benefit-status/&quot;&gt;public benefit organization&lt;/a&gt; was just too good to pass up. Mozilla is an organization that I've always had a profound amount of respect for and I look forward to helping them &lt;a href=&quot;http://www.mozilla.org/about/mission.html&quot;&gt;promote openness, innovation and opportunity on the web&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I won't be moving (a number of people have asked me this). I'm going to be working out of the Toronto office, which I'm very glad to say has gone through some serious beautification since &lt;a href=&quot;http://valleywag.gawker.com/391711/techs-worst-workspace-mozilla&quot;&gt;being written about by Valleywag&lt;/a&gt;. (No seriously, it's a lot nicer now. There's even an espresso machine!)&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>On Implementing WebHooks</title>
    <link href="http://paulosman.me/2010/05/01/on-implementing-webhooks.html" />
    <updated>2010-05-01T22:25:38-04:00</updated>
    <id>http://paulosman.me/2010/05/01/on-implementing-webhooks</id>
    <content type="html">&lt;p&gt;I wrote about our experiences implementing WebHooks at FreshBooks yesterday. You can read all the gory details &lt;a href=&quot;http://developers.freshbooks.com/blog/view/on_implementing_webhooks/&quot;&gt;here&lt;/a&gt;. Quick summary is: use a Message Queue, send HTTP requests asynchronously, have a retry queue, verify ownership of URIs.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>#OpenWebTO: Discovery Edition</title>
    <link href="http://paulosman.me/2010/04/08/openwebto-discovery-edition.html" />
    <updated>2010-04-08T00:22:16-04:00</updated>
    <id>http://paulosman.me/2010/04/08/openwebto-discovery-edition</id>
    <content type="html">&lt;p&gt;The second #OpenWebTO meetup is happening on Tuesday, April 20th at 7pm. We're at the &lt;a href=&quot;http://socialinnovation.ca/&quot;&gt;Centre for Social Innovation&lt;/a&gt; again but this time on the 1st floor.&lt;/p&gt;




&lt;p&gt;For this meetup, James Walker (&lt;a href=&quot;http://walkah.net&quot;&gt;walkah&lt;/a&gt;) is going to be presenting &lt;a href=&quot;http://code.google.com/p/webfinger/&quot;&gt;WebFinger&lt;/a&gt; / &lt;a href=&quot;http://hueniverse.com/2009/11/the-discovery-protocol-stack-redux/&quot;&gt;The Hammer Stack&lt;/a&gt;. This will be a fairly in-depth look at the various pieces that make up Hammer Discovery Protocol Stack (XRD, LRDD, /.well-known, etc) and how they fit together. I've spent some time looking at the big picture of this stuff, but have yet to dive into the minutiae, so I'm looking forward to this.&lt;/p&gt;




&lt;p&gt;James works for &lt;a href=&quot;http://status.net&quot;&gt;StatusNet&lt;/a&gt; and is one of the authors of the &lt;a href=&quot;http://ostatus.org/&quot;&gt;OStatus 1.0 Draft&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;There's still plenty of space, so if you're interested in discovery, or just want to chat about Open Web stuff, &lt;a href=&quot;http://guestlistapp.com/events/18687&quot;&gt;sign up on Guestlist&lt;/a&gt;. See you there!&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Community and Platform Building</title>
    <link href="http://paulosman.me/2010/03/16/community-and-platform-building.html" />
    <updated>2010-03-16T00:29:25-04:00</updated>
    <id>http://paulosman.me/2010/03/16/community-and-platform-building</id>
    <content type="html">&lt;p&gt;In June 2009, I started working for &lt;a href=&quot;http://www.freshbooks.com&quot;&gt;FreshBooks&lt;/a&gt; &amp;mdash; an online billing service based here in Toronto. My official job title is Team Lead, API &amp;amp; Integrations, but in &lt;a href=&quot;http://www.freshbooks.com/our-team.php#corey&quot;&gt;FreshBooks&lt;/a&gt; &lt;a href=&quot;http://www.freshbooks.com/our-team.php#sunir&quot;&gt;tradition&lt;/a&gt; I'll probably come up with something more clever sounding eventually. When I started, I was mostly doing bug fixes and &lt;a href=&quot;http://developers.freshbooks.com/blog/view/staff_api_access_no_more_restrictions/&quot;&gt;enhancements&lt;/a&gt;&gt; to the API, but my role has become a little higher level now and my focus has shifted to building the platform in general, as well as the community of developers who are using it to add value for our customers.&lt;/p&gt;

&lt;p&gt;So the way I see it, there are two facets to my job: platform development (coding) and community development (evangelism, support). On the technical side, this involves building things like our recently announced &lt;a href=&quot;http://developers.freshbooks.com/blog/view/keep_in_sync_with_freshbooks_webhooks/&quot;&gt;webhooks implementation&lt;/a&gt;, and helping make decisions about the direction of our API. Because we don't want to waste our time inventing &lt;a href=&quot;http://twitter.com/kevinmarks/statuses/1068364292&quot;&gt;Beautiful Fucking Snowflakes&lt;/a&gt;, it also involves keeping an eye on developments in various open web communities, and deciding what standards, specifications and ideas can be usefully implemented at FreshBooks. On the community side, things are decidedly less clear to me. I have some thoughts (open source example projects, better library support, more forums for developers to talk, etc) but I'm mostly interested in hearing from others. What do you look for from service providers that have an API? What do you wish that companies would do to help and encourage developers working with their products? What companies do this particularly well, and why? I have some ideas of my own, but I'm really interested in what other people think. You can comment here or hit me up at paul at freshbooks dot com.&lt;/p&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>#OpenWebTO Meetup</title>
    <link href="http://paulosman.me/2010/02/23/openwebto-meetup.html" />
    <updated>2010-02-23T02:37:38-05:00</updated>
    <id>http://paulosman.me/2010/02/23/openwebto-meetup</id>
    <content type="html">&lt;p&gt;On Tuesday, March 2nd at 7pm, I'll be hosting the first #OpenWebTO Meetup at the Centre for Social Innovation. My goal is to get a group of people interested in open web tech together to present, chat, and share ideas. The first meeting will be focused on figuring out what people want out of such a gathering, and what topics in particular are popular. I'm going to do a quick presentation on how I see the current landscape of open web tech and hopefully get some discussion going. Open web technology is a pretty broad topic, so it's probably helpful to say that I'm thinking specifically of protocols like OpenID, OAuth, Portable Contacts, WebFinger, Webhooks / PubSubHubbub, Activity Streams, and similar. &lt;/p&gt;




&lt;p&gt;The more the merrier of course, so if you're in the Toronto area and have an interest in open web technologies, come on out and join us! Details are on &lt;a href=&quot;http://guestlistapp.com/events/14898&quot;&gt;Guestlist&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Google Webfinger</title>
    <link href="http://paulosman.me/2010/02/01/google-webfinger.html" />
    <updated>2010-02-01T12:43:16-05:00</updated>
    <id>http://paulosman.me/2010/02/01/google-webfinger</id>
    <content type="html">&lt;p&gt;This is &lt;a href=&quot;http://groups.google.com/group/webfinger/browse_thread/thread/46fe84c1e38ab715/fa625d20f4d963e4&quot;&gt;pretty old news&lt;/a&gt;, but I missed the original announcement and I think it's pretty interesting.&lt;/p&gt;




&lt;p&gt;Google have implemented an alpha of the &lt;a href=&quot;http://code.google.com/p/webfinger/&quot;&gt;WebFinger protocol&lt;/a&gt;.  If you have a Google profile, click on &quot;Edit your profile&quot; and add 'webfingeralpha' as an interest. Congrats, your gmail address is now a WebFinger identifier and will resolve to an XRD file containing information about services you use (if you've included that information in your Google profile).&lt;/p&gt;




&lt;p&gt;This is pretty fun to play around with. Given an email-like identifier, such as evalpaul@gmail.com, get the host-meta XRD file for the domain:&lt;/p&gt;




&lt;pre lang=&quot;bash&quot;&gt;
paul@knuth ~ $ curl -i http://gmail.com/.well-known/host-meta
HTTP/1.1 200 OK
Content-Type: application/xrd+xml
Transfer-Encoding: chunked
Date: Mon, 01 Feb 2010 12:36:47 GMT
Expires: Mon, 01 Feb 2010 12:36:47 GMT
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Server: GFE/2.0
...
&lt;/pre&gt;




&lt;pre lang=&quot;xml&quot;&gt;
&amp;lt;?xml version='1.0' encoding='UTF-8'?&amp;gt;
&amp;lt;!-- NOTE: this host-meta end-point is a pre-alpha work in progress.   Don't rely on it. --&amp;gt;
&amp;lt;!-- Please follow the list at http://groups.google.com/group/webfinger --&amp;gt;
&amp;lt;XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0' 
     xmlns:hm='http://host-meta.net/xrd/1.0'&amp;gt;
  &amp;lt;hm:Host xmlns='http://host-meta.net/xrd/1.0'&amp;gt;gmail.com&amp;lt;/hm:Host&amp;gt;
  &amp;lt;Link rel='lrdd' 
        template='http://www.google.com/s2/webfinger/?q={uri}'&amp;gt;
    &amp;lt;Title&amp;gt;Resource Descriptor&amp;lt;/Title&amp;gt;
  &amp;lt;/Link&amp;gt;
&amp;lt;/XRD&amp;gt;
&lt;/pre&gt;




&lt;p&gt;Now that you have the URI template, get the XRD file for the specific user:&lt;/p&gt;




&lt;pre lang=&quot;bash&quot;&gt;
curl &quot;http://www.google.com/s2/webfinger/?q=acct%3Aevalpaul%40gmail.com&quot;
&lt;/pre&gt;




&lt;pre lang=&quot;xml&quot;&gt;
&amp;lt;?xml version='1.0'?&amp;gt;
&amp;lt;XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'&amp;gt;
    &amp;lt;Subject&amp;gt;acct:evalpaul@gmail.com&amp;lt;/Subject&amp;gt;
    &amp;lt;Alias&amp;gt;http://www.google.com/profiles/evalpaul&amp;lt;/Alias&amp;gt;
    &amp;lt;Link rel='http://portablecontacts.net/spec/1.0' href='http://www-opensocial.googleusercontent.com/api/people/'/&amp;gt;
    &amp;lt;Link rel='http://webfinger.net/rel/profile-page' href='http://www.google.com/profiles/evalpaul' type='text/html'/&amp;gt;
    &amp;lt;Link rel='http://microformats.org/profile/hcard' href='http://www.google.com/profiles/evalpaul' type='text/html'/&amp;gt;
    &amp;lt;Link rel='http://gmpg.org/xfn/11' href='http://www.google.com/profiles/evalpaul' type='text/html'/&amp;gt;
    &amp;lt;Link rel='http://specs.openid.net/auth/2.0/provider' href='http://www.google.com/profiles/evalpaul'/&amp;gt;
    &amp;lt;Link rel='describedby' href='http://www.google.com/profiles/evalpaul' type='text/html'/&amp;gt;
    &amp;lt;Link rel='describedby' href='http://s2.googleusercontent.com/webfinger/?q=evalpaul%40gmail.com&amp;amp;fmt=foaf' type='application/rdf+xml'/&amp;gt;
&amp;lt;/XRD&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; Google have since rolled out WebFinger support for everyone with a Google Profile. You no longer need to add 'webfingeralpha' to your interests.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Social Web Workshop</title>
    <link href="http://paulosman.me/2009/10/26/social-web-workshop.html" />
    <updated>2009-10-26T04:09:24-04:00</updated>
    <id>http://paulosman.me/2009/10/26/social-web-workshop</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.factoryjoe.com&quot;&gt;Chris Messina&lt;/a&gt; was in Toronto last week and ran a full day workshop on social web technologies at the &lt;a href=&quot;http://socialinnovation.ca/&quot;&gt;Centre for Social Innovation&lt;/a&gt;. He had delivered a talk the night before called &quot;Identity is the Platform&quot; (&lt;a href=&quot;http://www.scribd.com/doc/20475401/Identity-is-the-Platform&quot;&gt;slides&lt;/a&gt;) and the workshop focused on many of the same topics. The simplest way that I can think of to explain the premise of both is: 1) The web is becoming more social and 2) The data you create on the web has value.&lt;/p&gt;




&lt;p&gt;The workshop was fairly interactive with people from the audience asking questions or raising points every so often. It was a pretty diverse group professionally speaking, made up mostly of software developers, technologists, designers and some marketing people. The material wasn't very technical, which I found disappointing, but given the crowd it was the only thing that made sense (I guess for some reason I expected more developers).&lt;/p&gt;




&lt;p&gt;The real take-away of the workshop was an overview of various open standards and protocols that can be used together as a sort of Open Web stack. The usual suspects were discussed: &lt;a href=&quot;http://openid.net&quot;&gt;OpenID&lt;/a&gt;, &lt;a href=&quot;http://oauth.net&quot;&gt;OAuth&lt;/a&gt;, &lt;a href=&quot;http://pubsubhubbub.googlecode.com/&quot;&gt;PubSubHubub&lt;/a&gt; and a few that were fairly new to me: &lt;a href=&quot;http://portablecontacts.net/&quot;&gt;Portable Contacts&lt;/a&gt;, &lt;a href=&quot;http://activitystrea.ms/&quot;&gt;Activity Streams&lt;/a&gt; and the &lt;a href=&quot;http://www.salmon-protocol.org/&quot;&gt;Salmon Protocol&lt;/a&gt;. Chris also spoke a bit about the &lt;a href=&quot;https://wiki.mozilla.org/drumbeat&quot;&gt;Mozilla Drumbeat&lt;/a&gt; project which certainly looks interesting.&lt;/p&gt;




&lt;p&gt;At the end of the workshop we got into groups and brainstormed about how social web technologies could be used to create a better web. I co-opted my group to talk about how &lt;a href=&quot;http://www.freshbooks.com&quot;&gt;FreshBooks&lt;/a&gt; (my employer) could be made more social. Some really interesting ideas were brought up and I think it's a great case study for working with a community that already exists (as opposed to building a community for a new social application).&lt;/p&gt;




&lt;p&gt;The workshop was great. I'm glad I had the opportunity to participate and I definitely came away with a few thoughts and ideas. For one, it could be a worthwhile project to document existing or create new open source projects that use open web technologies. Often it's easy to find sample code, but it's usually stripped down and not very useful. Real, concrete examples from open source projects could be extremely useful for developers looking to implement support for open technologies and protocols.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Wrapping C++ Classes in a PHP Extension</title>
    <link href="http://paulosman.me/2009/04/21/wrapping-c-classes-in-a-php-extension.html" />
    <updated>2009-04-21T13:51:33-04:00</updated>
    <id>http://paulosman.me/2009/04/21/wrapping-c-classes-in-a-php-extension</id>
    <content type="html">&lt;p&gt;I have a tutorial at the &lt;a href=&quot;http://devzone.zend.com&quot;&gt;Zend Developer Zone&lt;/a&gt; called &lt;a href=&quot;http://devzone.zend.com/article/4486-Wrapping-C-Classes-in-a-PHP-Extension&quot;&gt;Wrapping C++ Classes in a PHP Extension&lt;/a&gt;. It walks you through writing a simple PHP extension that wraps a class written in C++. This is a useful thing to be able to do, especially when exposing an already existing library's API to PHP userland scripts, which seems like a fairly common task.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Bad Signs - PHP's "Shut Up" (@) Operator</title>
    <link href="http://paulosman.me/2009/01/04/bad-signs-phps-shut-up-operator.html" />
    <updated>2009-01-04T03:22:21-05:00</updated>
    <id>http://paulosman.me/2009/01/04/bad-signs-phps-shut-up-operator</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://derickrethans.nl&quot;&gt;Derick Rethans&lt;/a&gt; has a &lt;a href=&quot;http://derickrethans.nl/five_reasons_why_the_shutop_operator_@_should_be_avoided.php&quot;&gt;post&lt;/a&gt; about PHP's &quot;shut-up&quot; operator (@) and why it should be avoided. He outlines a fairly common debugging scenario and gives some &quot;under-the-hood&quot; explanations on why that particular operator sucks. I couldn't agree more (that it should be avoided) and I want to go further and talk about something that has always bugged me about that operator. In my opinion, it's a hackish, band-aid fix to a much larger, much more worrisome problem: horrendous API design.&lt;/p&gt;




&lt;p&gt;First, a disclaimer. I know that there are some historical reasons for a lot of these things (i.e. Exceptions not appearing until PHP5) but that doesn't change the current reality. Consider for instance filesystem functions in the standard extension:&lt;/p&gt;




&lt;pre lang=&quot;php&quot;&gt;
$lines = file(&quot;/tmp/file.txt&quot;);
&lt;/pre&gt;


&lt;p&gt;If the file &quot;/tmp/file.txt&quot; does not exist, depending on your php.ini settings (because display_errors should always be 'Off' on production systems), you may get something similar to the following output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Warning: file(/tmp/file.txt) [function.file]: failed to open stream: No such file or directory in /path/to/your/docroot/test.php on line 3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the proper thing to do of course would be to verify up front that the file exists and is readable with a call to &lt;a href=&quot;http://php.net/is_readable&quot;&gt;is_readable()&lt;/a&gt; or something similar, but imagine this is a case where there is no way to determine ahead of time if a warning or error will be generated (Derick mentions silencing network errors with &lt;a href=&quot;http://php.net/stream_socket_client&quot;&gt;stream_socket_client()&lt;/a&gt; as one example). In this case, you might be tempted to use the shut-up operator and depend on the return value as the one true way to see if something was succesful or not.&lt;/p&gt;




&lt;p&gt;Now compare this with what languages like Python (for example) do, If you were to write similar code in Python:&lt;/p&gt;




&lt;pre lang=&quot;python&quot;&gt;
fp = file(&quot;/tmp/file.txt&quot;)
&lt;/pre&gt;


&lt;p&gt;If the file did not exist, you'll get an IOError which, if left uncaught, will result in the following being written to standard error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Traceback (most recent call last):
File &quot;test.py&quot;, line 1, in &amp;lt;module&amp;gt;
IOError: [Errno 2] No such file or directory: '/tmp/test.txt'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The difference here of course is that an IOError can be caught, ignored or whatever best suits your needs. It's a part of the language, the API is not just spewing out garbage to standard output or standard error. More importantly, having a function (or technically a constructor) like file() throw an exception follows a consistent design philosophy whereas PHP's error system always leaves me guessing or consulting the docs.&lt;/p&gt;




&lt;p&gt;I guess what I'm saying is that I think the shut-up operator is just one of those signs that PHP is suffering from some serious cruft and I don't think those kinds of things bode well for a language.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>What drove me away from Apple</title>
    <link href="http://paulosman.me/2008/11/30/what-drove-me-away-from-apple.html" />
    <updated>2008-11-30T17:32:39-05:00</updated>
    <id>http://paulosman.me/2008/11/30/what-drove-me-away-from-apple</id>
    <content type="html">&lt;p&gt;I recently replaced my Macbook Pro with a Thinkpad x200 running Arch Linux and Windows Vista. After 5 years of using Apple products I decided to switch &amp;ndash; to borrow one of their marketing terms &amp;ndash; because frankly, I was sick of being abused as a customer and the lock-in was making me less and less comfortable. Here's a list of the things that really did it for me:&lt;/p&gt;




&lt;ul&gt;
  &lt;li&gt;Pricing the Macbook Air so high with such low specs.&lt;/li&gt;
  &lt;li&gt;Releasing an pitiful update to the air along with a nice $100 price increase.&lt;/li&gt; 
  &lt;li&gt;Doing away with the matte screen option.&lt;/li&gt;
  &lt;li&gt;Pulling the remotes from being included with laptops (admittedly not a huge deal, but felt like salt on a wound).&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Is it just me or is Apple becoming a little too arrogant? Anyway, I know Lenovo won't be saints either, but at least in the non-Mac OS X world I can easily switch vendors down the road if I ever need to.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>PHP is Lazy</title>
    <link href="http://paulosman.me/2008/10/30/php-is-lazy.html" />
    <updated>2008-10-30T20:10:54-04:00</updated>
    <id>http://paulosman.me/2008/10/30/php-is-lazy</id>
    <content type="html">&lt;p&gt;Last week I started reading &lt;a href=&quot;http://www.amazon.ca/Extending-Embedding-PHP-Sara-Golemon/dp/067232704X/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1225346121&amp;amp;sr=8-1&quot;&gt;Extending and Embedding PHP&lt;/a&gt;. I really like extending scripting languages for a number of reasons, most of all because it's a great way to learn about the more esoteric features of a language. So far I've discovered two neat things about PHP that made me smile, so I thought I'd share them here.&lt;/p&gt;

&lt;h3&gt;Copy on Write&lt;/h3&gt;


&lt;p&gt;In an effort to save CPU cycles, PHP uses an optimization strategy called &quot;Copy on Write&quot;. Basically what this means is that variable data isn't copied until it needs to be. Using an example similar to the one found in the book:&lt;/p&gt;


&lt;pre lang=&quot;php&quot;&gt;
$a = 50;
$b = $a;
$b += 5;
&lt;/pre&gt;


&lt;p&gt;
At the first line, the variable $a is created and memory is allocated for it. When we set $b to equal $a however, you might think that the data in $a is copied to $b. PHP doesn't actually copy the data until the 3rd line when $b is incremented by 5. The value in this is that data is not copied unnecessarily. If, for some reason, we took out the 3rd line and $b was never modified after the initial assignment, the data would actually never have to be copied.  
&lt;/p&gt;




&lt;h3&gt;Return Value Used?&lt;/h3&gt;




&lt;p&gt;The second, admittedly more surprising thing I learned was that PHP provides internal functions a parameter called &lt;code&gt;return_value_used&lt;/code&gt; whose value can be inspected to determine whether the return value being prepared by the function is actually being saved or not. Imagine you have a function that does some heavy crunching to compute a return value, and a caller (for some reason beyond us) does this:&lt;/p&gt;




&lt;pre lang=&quot;php&quot; escaped=&quot;true&quot;&gt;
&amp;lt;?php
...
myextension_compute_something();
...
?&amp;gt;
&lt;/pre&gt;




&lt;p&gt;In other words, they call the function but don't store the return value. Well, being a thoughtful internal function author, you can check to see that &lt;code&gt;return_value_used&lt;/code&gt; evaluates to false and just skip doing any work, saving CPU cycles and time, like so:
&lt;/p&gt;




&lt;pre lang=&quot;c&quot;&gt;
PHP_FUNCTION(myextension_compute_something)
{
    if (return_value_used) {
        // some fancy but expensive algorithm
        // ...
        RETURN_DOUBLE(some_value);
    } else {
        RETURN_NULL();
    }
}
&lt;/pre&gt;

</content>
  </entry>
  
  <entry>
    <title>ZendCon 2008 - Day 2</title>
    <link href="http://paulosman.me/2008/09/18/zendcon-2008-day-2.html" />
    <updated>2008-09-18T18:23:37-04:00</updated>
    <id>http://paulosman.me/2008/09/18/zendcon-2008-day-2</id>
    <content type="html">&lt;p&gt;Harold Goldberg, the CEO of Zend Technologies, started the day off with a talk called &quot;Insights from the Experts: How PHP Leaders Are Transforming High-Impact PHP Applications&quot;. The talk was basically a run-down of how PHP is doing &lt;i&gt;out there&lt;/i&gt;. (&lt;small&gt;Answer: Fairly well!&lt;/small&gt;). There were a few case studies of larger companies using PHP (and of course Zend products) and a few reports / surveys / etc. Nothing particularly groundbreaking but it was good to start the day feeling pumped about being a PHP programmer...&lt;/p&gt;




&lt;p&gt;Later on I attended &quot;Tiery-Eyed&quot;, a talk given by a Zend employee, Kevin Schroeder on ... you guessed it, tiered application development. It was pretty decent, Kevin walked the audience through the creation of a micro-blogging application using a variety of different backend tiers. I left with an urge to create the smallest application I could think off with one SOAP backend, one XML-RPC backend and one REST backend.&lt;/p&gt;




&lt;p&gt;The highlight of the day was Sara Goleman's talk on PHP Extension Writing. This was the most in-depth of the talks I attended and despite a few technical problems, the talk went smoothly. It was pretty broad but still managed to be quite inspiring.&lt;/p&gt;




&lt;p&gt;The day ended with a reception in the exhibit hall. Free drinks and food is never a bad way to end a day...&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>ZendCon 2008 - Day 1</title>
    <link href="http://paulosman.me/2008/09/16/zendcon-2008-day-1.html" />
    <updated>2008-09-16T19:32:51-04:00</updated>
    <id>http://paulosman.me/2008/09/16/zendcon-2008-day-1</id>
    <content type="html">&lt;p&gt;Well yesterday was day one of ZendCon 2008. Actually, the conference proper started today, yesterday was just tutorials. 
&lt;/p&gt;


&lt;p&gt;
So far I'm pretty impressed. I attended the &lt;a href=&quot;http://www.zendcon.com/ZendCon08/public/schedule/detail/95&quot;&gt;PHP Developer Best Practices&lt;/a&gt; tutorial in the morning and the &lt;a href=&quot;http://www.zendcon.com/ZendCon08/public/schedule/detail/204&quot;&gt;Quality Assurance in PHP Projects&lt;/a&gt; tutorial in the afternoon.&lt;/p&gt;




&lt;p&gt;The Best Practices tutorial was a little broad and most of it was focused on topics that were pretty common / basic. I'm not sure exactly what I wanted to get out of it, but for somebody who already uses an SCM system, who already tests (although not as much as I should) and writes api documentation it wasn't the most informative tutorial. 
&lt;/p&gt;




&lt;p&gt;Sebastian Bergmann's QA tutorial was excellent. Of course, as the primary author of &lt;a href=&quot;http://phpunit.de/&quot;&gt;PHPUnit&lt;/a&gt;, I assumed Sebastian would know what he was talking about and I was right. He offered a really good, in depth look at &lt;a href=&quot;http://phpunit.de/&quot;&gt;PHPUnit&lt;/a&gt; and covered some of the newer features. He also covered &lt;a href=&quot;http://selenium.openqa.org/&quot;&gt;Selenium&lt;/a&gt; and specifically Selenium RC in quite a bit of detail. &lt;/p&gt;




&lt;p&gt;Looking forward to more sessions... the day of tutorials was a great start, can't wait to see what the conference proper has to offer.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Using YAML with the Zend Framework</title>
    <link href="http://paulosman.me/2008/02/18/using-yaml-with-the-zend-framework.html" />
    <updated>2008-02-18T18:23:18-05:00</updated>
    <id>http://paulosman.me/2008/02/18/using-yaml-with-the-zend-framework</id>
    <content type="html">&lt;p&gt;One of the many great components provided by the &lt;a href=&quot;http://framework.zend.com&quot;&gt;Zend Framework&lt;/a&gt; is &lt;a href=&quot;http://framework.zend.com/manual/en/zend.config.html&quot;&gt;Zend_Config&lt;/a&gt;. In a nutshell, this component allows you to access configuration data (from a file, array, etc) through a nested object property based interface. Out of the box, the component supports working with XML and INI files via the &lt;a href=&quot;http://framework.zend.com/manual/en/zend.config.adapters.ini.html&quot;&gt;Zend_Config_Ini&lt;/a&gt; and &lt;a href=&quot;http://framework.zend.com/manual/en/zend.config.adapters.xml.html&quot;&gt;Zend_Config_Xml&lt;/a&gt; Config adapters.&lt;/p&gt;




&lt;p&gt;If you're familiar with frameworks like &lt;a href=&quot;http://www.rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt; or &lt;a href=&quot;http://www.symfony-project.org/&quot;&gt;Symfony&lt;/a&gt; however, you may notice that &lt;a href=&quot;http://www.yaml.org/&quot;&gt;YAML&lt;/a&gt; support is missing. This is mostly because there is no one YAML parsing library for PHP. If you are willing to introduce another dependency to your application however, there is a really easy way to use &lt;a href=&quot;http://spyc.sourceforge.net/&quot;&gt;Spyc&lt;/a&gt; to bridge YAML and Zend_Config. One of the nice things about the Zend_Config component is that it can take data right out of a PHP array (as opposed to an XML or INI file). This gives us quite a lot of flexibility, especially considering that Spyc returns parsed YAML data as a PHP array. Armed with this knowledge, the solution becomes really, really simple:&lt;/p&gt;




&lt;pre lang=&quot;php&quot; escaped=&quot;true&quot;&gt;
&amp;lt;?php
require_once 'Zend/Config.php';
require_once 'spyc.php';

$configFile = &quot;config.yml&quot;;
$config = new Zend_Config(Spyc::YAMLLoad($confFile));
?&amp;gt;
&lt;/pre&gt;




&lt;p&gt;Remarkably simple isn't it? Now, given the YAML config file:&lt;/p&gt;




&lt;pre lang=&quot;yaml&quot;&gt;
 webhost: www.example.com
 database: 
     adapter: pdo_mysql
     host: db.example.com
     username: dbuser
     password: secret
     dbname: mydatabase
&lt;/pre&gt;




&lt;p&gt;The above PHP code will create a config object that can then be used like this:&lt;/p&gt;




&lt;pre lang=&quot;php&quot; escaped=&quot;true&quot;&gt;
&amp;lt;?php
print $config-&gt;webhost;        // prints 'www.example.com'
print $config-&gt;database-&gt;host; // prints 'db.example.com'
?&amp;gt;
&lt;/pre&gt;

</content>
  </entry>
  
  <entry>
    <title>Letting the masses decide...</title>
    <link href="http://paulosman.me/2008/01/08/letting-the-masses-decide.html" />
    <updated>2008-01-08T06:54:22-05:00</updated>
    <id>http://paulosman.me/2008/01/08/letting-the-masses-decide</id>
    <content type="html">&lt;p&gt;
&lt;a href=&quot;http://kuwamoto.org/&quot;&gt;Sho Kuwamoto&lt;/a&gt; has been doing some excellent work on implementing improved pluralization support for PHP, Ruby on Rails and AppleScript. When I put together a &lt;a href=&quot;/articles/php-pluralize&quot;&gt;quick solution&lt;/a&gt; for this in PHP, I couldn't help but think that there might be some missing special cases or what have you. Well, Sho has now put together a &lt;a href=&quot;http://kuwamoto.org/2008/01/04/mob-intelligence-please-feed-the-pluralizer/&quot;&gt;feeder app&lt;/a&gt; for the pluralizer. What a neat experiment in collaborative web production! &lt;a href=&quot;http://kuwamoto.org/2008/01/04/mob-intelligence-please-feed-the-pluralizer/&quot;&gt;Go on and give it a try.&lt;/a&gt;
&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>The Journey Back to Linux</title>
    <link href="http://paulosman.me/2008/01/04/the-journey-back-to-linux.html" />
    <updated>2008-01-04T17:35:54-05:00</updated>
    <id>http://paulosman.me/2008/01/04/the-journey-back-to-linux</id>
    <content type="html">&lt;p&gt;As part of starting my new job I've had to decide what kind of workstation to order. I'm fortunate in that my manager has given me the choice between a generic PC and an Apple machine. There was a time when I used Linux exclusively for all of my desktop and development needs, but about 4 years ago I purchased my first Powerbook and I really haven't touched anything other than Mac OS X on Apple hardware since. &lt;/p&gt;




&lt;p&gt;Naturally my first instinct was simple: Get a Mac. I do all of my work on a MacBook Pro and I've grown to appreciate many things about Mac OS X. I fired up my browser and went over to the Apple Store Online to look at my options. What I'd forgotten was that the cost of even the lowest-spec Mac Pro is pretty prohibitive, so I'd have to choose between an iMac and a Mac Mini. Display size is pretty important to me, and the specs on a maxed-out Mini seem so piss-poor, so my best option was a 24&quot; iMac. I've never been a fan of the iMacs (for a variety of reasons) so the fact that this was my best and only option was a bit disappointing to me. Why the hell is it not possible to purchase a Mac Pro for less than $2500 sans-display?&lt;/p&gt;




&lt;p&gt;Faced with these limitations from my vendor of choice, I started to consider a PC. After looking around a bit, the prospect of purchasing a PC and running Linux again started to become strangely compelling. Perhaps years of limiting my hardware choices has taken it's toll on me after all. As one is want to do when faced with such a conundrum, I decided to do a little bit of soul searching and figure out exactly what it was (other than the price) that was attracting me to the option of a Lintel PC.&lt;/p&gt;




&lt;p&gt;Now before anybody says anything, I'm perfectly aware that I could run Linux on Apple hardware, but I really don't consider that an option. I acknowledge that Apple hardware is generally of good quality, but I really do think it's overpriced. I'm fine paying a fee for a machine that runs OS X but I just can't justify the cost in order to just run Linux on the thing. If I'm going to run Linux, it's going to be on a Frankenstein-ish PC and that's that.&lt;/p&gt;




&lt;p&gt;After some thought I decided that the big thing drawing me towards the Lintel option was choice. I didn't realize how much I missed piecing together a system, choosing a processor, shopping for a motherboard and all the fun things that Apple shields me from. I started to imagine my dream system, running Gentoo Linux (with XGL), maybe compiling a xen patched kernel and running a few virtual machines... it all made me feel... happy. The other unexpected realization I made was this: Running Linux on a PC I specced out and built myself makes me feel more... nerdy. For some reason, and I know that this is very unscientific, I just feel more encouraged to tinker on Linux... to try things out, to learn about new corners of the OS or a particular development environment or programming language or whatever the case may be...&lt;/p&gt;




&lt;p&gt;Maybe it's the unpolished nature (kind of like living in a fixer-upper house) that does it, but Linux just makes me want to hack more. Apple hardware and Mac OS X is very good at making me feel comfortable or at home in the way a warm bath does... or the smell of bread baking, but it just doesn't inspire me to hack. At least... not anymore. So, we'll see if I still feel this way after a couple of months of customizing my system, emerging portage ebuilds, configuring USE flags, etc. But for now, this is the way I'm going and I'm a little excited to get back into it.&lt;/p&gt;




&lt;p&gt;&lt;b&gt;Disclaimer:&lt;/b&gt; I'm not leaving OS X behind. I still have my MacBook Pro, I suspect I'll still do a lot of my word processing in iWork and well, it's hard to do anything with Cocoa on Linux... I'm just diversifying my desktop experience.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Perforce Knowledge Base Launched</title>
    <link href="http://paulosman.me/2007/11/04/perforce-knowledge-base-launched.html" />
    <updated>2007-11-04T10:21:59-05:00</updated>
    <id>http://paulosman.me/2007/11/04/perforce-knowledge-base-launched</id>
    <content type="html">&lt;p&gt;In case you missed the announcement, the Perforce Knowledge Base is now &lt;a href=&quot;http://kb.perforce.com/&quot;&gt;available&lt;/a&gt;. Click &lt;a href=&quot;http://www.perforce.com/perforce/conferences/us/2007/presentations/kbpresentation.pdf&quot;&gt;here&lt;/a&gt; for more information about the technology driving the site.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Perspectives on SCM</title>
    <link href="http://paulosman.me/2007/06/20/perspectives-on-scm.html" />
    <updated>2007-06-20T20:33:40-04:00</updated>
    <id>http://paulosman.me/2007/06/20/perspectives-on-scm</id>
    <content type="html">&lt;p&gt;So I know this is old news by now, but Linus Torvalds gave a talk last month on &lt;a href=&quot;http://git.or.cz/&quot;&gt;git&lt;/a&gt;, the version management system he wrote. He spends quite a lot of time bashing other SCM systems, particularly &lt;a href=&quot;http://www.perforce.com&quot;&gt;Perforce&lt;/a&gt; and &lt;a href=&quot;http://subversion.tigris.org/&quot;&gt;Subversion&lt;/a&gt;. He writes off CVS entirely, which is perhaps warranted, but I thought his views on the other two products were way off. He doesn't really explain why he thinks that Perforce &quot;should just go away&quot;, so I can't really comment on that, but curiously he does go on to complain about things that Perforce actually does rather well (like branching and merging). He also says clearly that he &quot;is not an SCM person&quot;, so I guess that explains it.&lt;/p&gt;


&lt;p&gt;
&lt;object width=&quot;425&quot; height=&quot;350&quot;&gt;&lt;param name=&quot;movie; value=;http://www.youtube.com/v/4XpnKHJAok8&quot;&gt;&lt;/param&gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot;&gt;&lt;/param&gt;&lt;embed src=&quot;http://www.youtube.com/v/4XpnKHJAok8&quot; type=&quot;application/x-shockwave-flash&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;350&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;/p&gt;


&lt;p&gt;
For a much more informative and useful presentation on SCM, check out Laura Wingerd's (someone who &lt;em&gt;is&lt;/em&gt; an SCM person) presentation at Google Talks:
&lt;/p&gt;


&lt;p&gt;
&lt;embed style=&quot;width:400px; height:326px;&quot; id=&quot;VideoPlayback&quot; type=&quot;application/x-shockwave-flash&quot; src=&quot;http://video.google.com/googleplayer.swf?docId=-577744660535947210&quot; flashvars=&quot;&quot; /&gt;
&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Using Smarty with the Zend Framework</title>
    <link href="http://paulosman.me/2007/05/15/using-smarty-with-the-zend-framework.html" />
    <updated>2007-05-15T19:15:22-04:00</updated>
    <id>http://paulosman.me/2007/05/15/using-smarty-with-the-zend-framework</id>
    <content type="html">&lt;p&gt;
So it appears that the &lt;a href=&quot;http://framework.zend.com&quot;&gt;Zend Framework&lt;/a&gt; is approaching a 1.0 release, which is very exciting. The last time I worked with the ZF, I found the capabilities of the Zend_View class to be a little bit limited. Luckily, somebody has written a tutorial on using the &lt;a href=&quot;http://smarty.php.net&quot;&gt;Smarty&lt;/a&gt; template engine with the Zend Framework. Read more about it &lt;a href=&quot;http://devzone.zend.com/node/view/id/2028&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>PHP Pluralize Method</title>
    <link href="http://paulosman.me/2007/03/03/php-pluralize-method.html" />
    <updated>2007-03-03T19:06:41-05:00</updated>
    <id>http://paulosman.me/2007/03/03/php-pluralize-method</id>
    <content type="html">&lt;p&gt;I recently found myself wanting a rails-esque pluralize function like that found in the Rails Inflector class. After inspecting the Rails implementation, and playing around a bit, I was able to get a PHP version working as a Smarty variable modifier. Thank goodness for the rails version, I have no idea how I would go about listing all the various kinds of singular / plural nouns. Where would one go for a list of those kinds of things?&lt;/p&gt;




&lt;pre lang=&quot;php&quot;&gt;

class MyClass 
{
    public static function conditionallyPluralize( $string, $count )
    {
        if ( intval( $count ) !== 0 )
            return MyClass::pluralize( $string );

        return $string; 
    }

    public static function pluralize( $string ) 
    {

        $plural = array(
            array( '/(quiz)$/i',               &quot;$1zes&quot;   ),
        array( '/^(ox)$/i',                &quot;$1en&quot;    ),
        array( '/([m|l])ouse$/i',          &quot;$1ice&quot;   ),
        array( '/(matr|vert|ind)ix|ex$/i', &quot;$1ices&quot;  ),
        array( '/(x|ch|ss|sh)$/i',         &quot;$1es&quot;    ),
        array( '/([^aeiouy]|qu)y$/i',      &quot;$1ies&quot;   ),
        array( '/([^aeiouy]|qu)ies$/i',    &quot;$1y&quot;     ),
            array( '/(hive)$/i',               &quot;$1s&quot;     ),
            array( '/(?:([^f])fe|([lr])f)$/i', &quot;$1$2ves&quot; ),
            array( '/sis$/i',                  &quot;ses&quot;     ),
            array( '/([ti])um$/i',             &quot;$1a&quot;     ),
            array( '/(buffal|tomat)o$/i',      &quot;$1oes&quot;   ),
            array( '/(bu)s$/i',                &quot;$1ses&quot;   ),
            array( '/(alias|status)$/i',       &quot;$1es&quot;    ),
            array( '/(octop|vir)us$/i',        &quot;$1i&quot;     ),
            array( '/(ax|test)is$/i',          &quot;$1es&quot;    ),
            array( '/s$/i',                    &quot;s&quot;       ),
            array( '/$/',                      &quot;s&quot;       )
        );

        $irregular = array(
        array( 'move',   'moves'    ),
        array( 'sex',    'sexes'    ),
        array( 'child',  'children' ),
        array( 'man',    'men'      ),
        array( 'person', 'people'   )
        );

        $uncountable = array( 
        'sheep', 
        'fish',
        'series',
        'species',
        'money',
        'rice',
        'information',
        'equipment'
        );

        // save some time in the case that singular and plural are the same
        if ( in_array( strtolower( $string ), $uncountable ) )
        return $string;

        // check for irregular singular forms
        foreach ( $irregular as $noun )
        {
        if ( strtolower( $string ) == $noun[0] )
            return $noun[1];
        }

        // check for matches using regular expressions
        foreach ( $plural as $pattern )
        {
        if ( preg_match( $pattern[0], $string ) )
            return preg_replace( $pattern[0], $pattern[1], $string );
        }
    
        return $string;
    }

}
&lt;/pre&gt;




&lt;p&gt;Works like a charm!&lt;/p&gt;




&lt;p&gt;&lt;b&gt;EDIT:&lt;/b&gt; &lt;a href=&quot;http://kuwamoto.org/&quot;&gt;Sho Kuwamoto&lt;/a&gt; took this example and ran with it, creating a really robust and complete pluralization solution for PHP (and ActionScript!). Check out his improvements &lt;a href=&quot;http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>PAM Authentication for Apache, Trac and SVN</title>
    <link href="http://paulosman.me/2007/01/04/pam-authentication-for-apache-trac-and-svn.html" />
    <updated>2007-01-04T20:45:38-05:00</updated>
    <id>http://paulosman.me/2007/01/04/pam-authentication-for-apache-trac-and-svn</id>
    <content type="html">&lt;p&gt;
I&amp;#8217;m going to describe how to get two of my favourite development tools, Trac and Subversion, to authenticate against PAM (Pluggable Authentication Module). For those who might not be familiar, &lt;a href=&quot;http://trac.edgewall.org/&quot; title=&quot;Trac&quot;&gt;Trac&lt;/a&gt; is a wiki that has integrated SCM and issue tracking. It&amp;#8217;s written in Python and it&amp;#8217;s incredibly useful. I&amp;#8217;ve been using Subversion for version control for a long time and Trac is perfect for documenting a project as you go, and for keeping track of tasks and bugs. It&amp;#8217;s access control settings allow you to specify levels of access for different users, so I often create one group for me and any other developers, and another group for my clients. The clients group can update the wiki, create tickets and get reports or look at milestones, etc. The developers group can administer all aspects of the site. Anyway, as useful as Trac and SVN are, I started to get really sick of handling authentication for them. I used to set up htpasswd files for each repository and wiki and it got to be a real pain, especially when I wanted my clients or developers to also have email, shell access, etc. So I decided to try and get mod_pam_auth working so I could use existing system accounts for Trac and Subversion access (over SSL of course). 
&lt;/p&gt;




&lt;p&gt;
mod_auth_pam is an Apache module that implements Basic authentication on top of the Pluggable Authentication Module. Unfortunately, as the &lt;a href=&quot;http://pam.sourceforge.net/mod_auth_pam/&quot;&gt;project&lt;/a&gt; page says, the module is no longer being maintained which is unfortunate, but it works well enough with Apache 2.0. 
&lt;/p&gt;


&lt;p&gt;
Installing the module is pretty straightforward if you&amp;#8217;re familiar installing Apache modules. I won&amp;#8217;t go into too much detail, but as usual you&amp;#8217;ll need to load the module in your Apache configuration:
&lt;/p&gt;


&lt;pre lang=&quot;apache&quot;&gt;
LoadModule auth_pam_module modules/mod_auth_pam.so
LoadModule auth_sys_group_module modules/mod_auth_sys_group.so
&lt;/pre&gt;


&lt;p&gt;
Once that&amp;#8217;s done you can easily set up basic authentication with PAM. Because Basic authentication involves sending a username + password combination in plain text, this setup should &lt;b&gt;not&lt;/b&gt; be used without SSL. Within my VirtualHost configuration, I define separate location configs for each trac site and svn repository. It all looks something like this:
&lt;/p&gt;


&lt;pre lang=&quot;apache&quot; escaped=&quot;true&quot;&gt;
&amp;lt;VirtualHost 123.321.123.321:443&amp;gt;
    ServerName host.domain.tld
    SSLEngine On
    ...
    # Trac config
    &amp;lt;Location /trac&amp;gt;
       SetHandler mod_python
       PythonHandler trac.web.modpython_frontend 
       PythonOption TracEnvParentDir /var/lib/trac
       PythonOption TracUriRoot /trac
    &amp;lt;/Location&amp;gt;

    &amp;lt;Location &quot;/trac/tracsiteone/login&quot;&amp;gt;
       AuthPAM_Enabled On
       AuthType Basic
       AuthName &quot;trac site # 1&quot;
       Require user paul
    &amp;lt;/Location&amp;gt;

    &amp;lt;Location &quot;/trac/tracsitetwo/login&quot;&amp;gt;
       AuthPAM_Enabled On
       AuthType Basic
       AuthName &quot;trac site # 2&quot;
       Require group developers
    &amp;lt;/Location&amp;gt;

    # Subversion config
    &amp;lt;Location /svn&amp;gt;
       DAV svn
       SVNParentPath /var/svn
       SVNListParentPath On
       SVNAutoVersioning On
    &amp;lt;/Location&amp;gt;

    &amp;lt;Location &quot;/svn/repositoryone&quot;&amp;gt;
       AuthPAM_Enabled On
       AuthType Basic
       AuthName &quot;Repo # 1&quot;
       Require user paul
    &amp;lt;/Location&amp;gt;

    &amp;lt;Location &quot;/svn/repositorytwo&quot;&amp;gt;
       AuthPAM_Enabled On
       AuthType Basic
       AuthName &quot;Repo # 2&quot;
       Require group developers
    &amp;lt;/Location&amp;gt;
&amp;lt;/VirtualHost&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
So what we have now is two SVN repositories and two Trac wikis. For the first trac wiki and the first subversion repository, only the user 'paul' is given access. For the second, any valid user in the 'developers' group has access. Unfortunately there&amp;#8217;s an issue with shadow passwords and this module and I&amp;#8217;m not entirely happy with the work-around so I may have to edit this setup to use &lt;a href=&quot;http://unixpapa.com/mod_authnz_external/&quot;&gt;mod_authnz_external&lt;/a&gt; or maybe I&amp;#8217;ll eventually move to LDAP. Regardless, I find this works well enough for now and saves a lot of hassle maintaining separate authentication files. 
&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Xen Hosting</title>
    <link href="http://paulosman.me/2007/01/03/xen-hosting.html" />
    <updated>2007-01-03T20:40:56-05:00</updated>
    <id>http://paulosman.me/2007/01/03/xen-hosting</id>
    <content type="html">&lt;p&gt;
 I recently made the decision to move from a shared hosting account to a dedicated server. I was very happy with my shared hosting provider (&lt;a href=&quot;http://www.webfaction.com&quot; title=&quot;WebFaction&quot;&gt;WebFaction&lt;/a&gt;) but it&amp;#8217;s not uncommon for me to have several websites or trac wikis or subversion repositories all going at once. I need these to be accessible to me and my clients at all times and my home DSL connection just isn&amp;#8217;t dependable enough for hosting. I started looking into dedicated hosting packages but decided to save myself about $80 / month and go for a VPS (Virtual Private Server) package. 
&lt;/p&gt;




&lt;p&gt;
 When it comes to virtualization, I'm a pretty big fan of &lt;a href=&quot;http://www.cl.cam.ac.uk/research/srg/netos/xen/&quot; title=&quot;Xen Hypervisor&quot;&gt;Xen&lt;/a&gt;. If anyone is interested in the subject and hasn&amp;#8217;t already, it&amp;#8217;s really worth checking out. At a really high level, here&amp;#8217;s how it works: basically you have a DOM0 (Domain-0) patched kernel that you run on your physical machine which you allocate a specific amount of RAM to. Once you have your DOM0 set up correctly you can install any number (RAM permitting) of guest hosts running a DOMU (unprivileged domain) patched kernel. You specify how much RAM you want to allocate to each DOMU and there are a number of ways to handle filesystems. The DOM0 (host OS) manages access to hardware and other low-level stuff and basically makes it completely transparent when working within a DOMU. It comes with some handy userland tools to manage guest hosts and there are 3rd party packages like &lt;a href=&quot;http://www.enomalism.com&quot; title=&quot;Enomalism&quot;&gt;enomalism&lt;/a&gt; (Yes I just plugged my former employer!) that make it really simple. 
&lt;/p&gt;


&lt;p&gt;
So I started shopping around for hosting companies offering VPS packages that used Xen and I found one that looked a little nickle and dime, but were local, had reasonable prices and offered my favourite Linux distribution (&lt;a href=&quot;http://www.gentoo.org&quot; title=&quot;Gentoo Linux&quot;&gt;Gentoo!&lt;/a&gt;). I figured I&amp;#8217;d give them a shot. One week and 14 hours of downtime later I canceled my account, got a refund and signed up for an account with &lt;a href=&quot;http://rimuhosting.com&quot; title=&quot;RimuHosting&quot;&gt;RimuHosting&lt;/a&gt;. Now these people know how to do support! Within minutes of signing up I got an email with a link to setup my PayPal subscription. I setup my subscription and less than an hour later had my account details. First things first, I tried to connect to my host via SSH and got a timeout... good opportunity to try out their support! I emailed their tech support and we were able to figure out that the problem was a lack of a reverse DNS entry on the VPS' IP address. I didn&amp;#8217;t know that Mac OS X won&amp;#8217;t open an SSH connection in these circumstances. Okay, easy to solve... there&amp;#8217;s a handy utility in their control panel to do just that. Once that was working I was up and running and started to set up Apache, Postfix and all the other wonderful software I use. 
&lt;/p&gt;


&lt;p&gt;
Now I&amp;#8217;m happily hosting several websites (each with MySQL or PostgreSQL databases), Trac wikis, SVN repositories, I&amp;#8217;m running AWStats, hosting my mail, and generally loving the VPS life, all for at least a 5th of what a dedicated package would cost!
&lt;/p&gt;

</content>
  </entry>
  
  <entry>
    <title>Introduction to mod_python</title>
    <link href="http://paulosman.me/2007/01/01/introduction-to-mod_python.html" />
    <updated>2007-01-01T19:56:10-05:00</updated>
    <id>http://paulosman.me/2007/01/01/introduction-to-mod_python</id>
    <content type="html">&lt;p class=&quot;summary&quot;&gt;This article will provide a brief introduction to mod_python, a tour of what you can do with it, and some pointers to further resources should you want to explore it in more depth. I will assume that the reader is comfortable programming in Python (although no specific knowledge is required) and is familiar with Apache and basic web concepts. This article is not intended to be a complete reference for mod_python. Instead it is meant to consolidate information available from other sources, to hopefully whet your appetite and encourage you to read more from the official documentation (links are in the Resources section). 
&lt;/p&gt;




&lt;h3&gt;Prerequisites and Assumptions&lt;/h3&gt;




&lt;p&gt;
I am going to assume that you have a working installation of Apache 2.0.47 or higher and have Python 2.2.1 or later installed. To make things simple, I&amp;#8217;m going to assume you are working from the same machine that Apache is installed on, so all URLs will have 'localhost' as the server name. Replace this with your server name if this is not the case. 
&lt;/p&gt;




&lt;h3&gt;What is mod_python?&lt;/h3&gt;




&lt;p&gt;
In the bad old days, most web development was done using CGI (Common Gateway Interface). Writing a CGI program meant creating an executable (script or binary) that the web server called to handle a request. The output generated by the CGI program would then be returned to the user via their browser. Think about that process for a second: a) the user requests a page from the web server, perhaps with some arguments sent via GET or POST, b) the web server recognizes that the requested page is handled by a CGI script and invokes the CGI process, c) the CGI program collects information from the web server using some mechanism (usually environment variables), does some processing and prints out a bunch of HTML (usually),  if everything went alright, the web server takes the output and sends it to the user. 
&lt;/p&gt;




&lt;p&gt;
Sounds pretty cumbersome when you think about it, doesn&amp;#8217;t it? It&amp;#8217;s not only cumbersome, it&amp;#8217;s also slow and very error prone. mod_python saves us from having to go through this process by integrating the Python programming language right into the Apache HTTP server. This provides a much faster way for Apache to execute python handlers, and as an added bonus, gives us complete access to the Apache internals. Imagine mod_python as a little guy (or ... a Python?) stuck inside your web server intercepting certain requests and allowing you to do really cool things with them. Okay, so that may not be a very good technical description, but we&amp;#8217;ll get to that. Sound interesting? It is! 
&lt;/p&gt;




&lt;p&gt;
It&amp;#8217;s important to understand that writing applications with mod_python is not the same as writing applications with a server-side scripting language like PHP. Instead, with mod_python you specify handlers in the Apache configuration file(s) that allow you to customize how a request is handled. This allows you to do a variety of neat things like implement protocols other than HTTP, filter the request and response, determine a document&amp;#8217;s content-type, etc.
&lt;/p&gt;




&lt;p&gt;
So let&amp;#8217;s get mod_python installed and take a tour of how it works. In order to follow along with this tutorial, you&amp;#8217;ll need to have Apache and Python installed, and then install mod_python. 
&lt;/p&gt;




&lt;h2&gt;Installation&lt;/h2&gt;




&lt;p&gt;
There are a few different ways to install mod_python, depending on what Operating System you are running. If you are using a distribution of Linux that has a decent package management system (any Red Hat / Fedora, Debian or Gentoo based system for instance) then there will likely already be a package available for you to install. On Gentoo I just emerge the mod_python ebuild with the USE flags I want and portage automatically adds configuration files to be included into my Apache configuration. On Red Hat Enterprise Linux I use a mod_python RPM that depends on having the python-devel and httpd-devel packages. For the sake of brevity I&amp;#8217;m only going to cover installing from source here. Consult your OS&amp;#8217; documentation to see if there is an easier way for you. (There is a way to install mod_python on Windows but I am not going to cover that here). 
&lt;/p&gt;




&lt;h4&gt;Compiling from Source&lt;/h4&gt;




&lt;p&gt;
In order to compile mod_python, you&amp;#8217;ll need to grab the latest stable source release from the &lt;a href=&quot;http://www.modpython.org&quot; title=&quot;mod_python&quot;&gt;mod_python website&lt;/a&gt;. At the time of this writing, the latest stable release was 3.2.10.
&lt;/p&gt;




&lt;p&gt;
I&amp;#8217;m going to assume you have Apache2 already installed (if not you can get it from the &lt;a href=&quot;http://httpd.apache.org/&quot; title=&quot;Apache HTTP Server&quot;&gt;Apache website&lt;/a&gt;). I am using Apache 2.0.59 but the process should be the same for any version of Apache above 2.0.47. I have Apache installed in /usr/local/apache2. You will need to adjust the path to match your installation.
&lt;/p&gt;




&lt;p&gt;
Once you&amp;#8217;ve downloaded the source tarball for mod_python, untar it and run the 'configure' script (feel free to run ./configure --help to see what other configuration options are available):
&lt;/p&gt;




&lt;pre lang=&quot;bash&quot;&gt;
pike:/usr/local/src paul$ tar xfz mod_python-3.2.10.tgz 
pike:/usr/local/src paul$ cd mod_python-3.2.10
pike:/usr/local/src/mod_python-3.2.10 paul$ ./configure --with-apxs=/usr/local/apache2/bin/apxs
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
...
&lt;/pre&gt;




&lt;p&gt;
If everything went okay, and you didn&amp;#8217;t get any error messages from configure, you should then run 'make' to compile mod_python and 'make install'. You will likely need to run 'make install' as root, so you can use &quot;su -c 'make install'&quot;:
&lt;/p&gt;




&lt;pre lang=&quot;bash&quot;&gt;
pike:/usr/local/src/mod_python-3.2.10 paul$ make
... 
(a whole bunch of output you can ignore unless you know what you&amp;#8217;re doing)
pike:/usr/local/src/mod_python-3.2.10 paul$ su -c 'make install'
...
(more output that we won&amp;#8217;t concern ourselves with)
&lt;/pre&gt;




&lt;p&gt;
&lt;b&gt;Safety Note:&lt;/b&gt; Never run a configure script as root. It&amp;#8217;s always possible that the host you retrieved the source distribution from has been compromised and therefore you don&amp;#8217;t know for sure what could be hiding in that script.
&lt;/p&gt;




&lt;p&gt;
If the compilation process didn&amp;#8217;t report any errors, you should be ready to go. Next we have to open the Apache2 configuration file (usually called httpd.conf) and add the following:
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot;&gt;
LoadModule python_module      modules/mod_python.so
&lt;/pre&gt;




&lt;p&gt;
&lt;b&gt;Note:&lt;/b&gt; the path to mod_python.so may very. Check your Apache2 installation root and find out if it was actually put there or somewhere else.
&lt;/p&gt;




&lt;p&gt;
As with any time you edit the Apache configuration, you will have to restart Apache before the changes take effect. Now we want to verify that our installation went smoothly. There are a variety of ways to do this, I always like to grab the default headers from Apache to see what is installed. I do this by telnetting into port 80 and typing 'HEAD / HTTP/1.0':
&lt;/p&gt;




&lt;pre lang=&quot;bash&quot;&gt;
pike:/usr/local/src/mod_python-3.2.10 paul$ su -c '/usr/local/apache2/bin/apachectl restart'
pike:/usr/local/src/mod_python-3.2.10 paul$ telnet localhost 80
Trying ::1...
Connected to localhost.
Escape character is '^]'.
HEAD / HTTP/1.0

HTTP/1.1 200 OK
Date: Mon, 01 Jan 2007 04:28:24 GMT
Server: Apache/2.0.59 (Unix) mod_python/3.2.10 Python/2.3.5 PHP/5.2.0
Last-Modified: Sat, 20 Nov 2004 20:16:24 GMT
ETag: &quot;ab5da-2c-4c23b600&quot;
Accept-Ranges: bytes
Content-Length: 44
Connection: close
Content-Type: text/html

Connection closed by foreign host.
&lt;/pre&gt;




&lt;p&gt;
As you can see from the &quot;Server&quot; header, mod_python version 3.2.10 is installed. Now to really test it! Open the Apache2 configuration file again and add the following location directive:
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot; escaped=&quot;true&quot;&gt;
&amp;lt;Location /mpinfo&amp;gt;
    SetHandler mod_python
    PythonHandler mod_python.testhandler
&amp;lt;/Location&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
Restart Apache again and point your browser to &lt;a href=&quot;http://localhost/mpinfo&quot;&gt;http://localhost/mpinfo&lt;/a&gt; and you should see a test page with a lot of useful information about your server environment. This information can come in very handy when debugging problems so I tend to keep it around. If you don&amp;#8217;t see this page, something must have gone wrong. Go over the instructions again or consult your operating system documentation.  If you see the page, congratulations, you have installed mod_python! Now let&amp;#8217;s move on and start learning about mod_python.
&lt;/p&gt;




&lt;h3&gt;Handlers&lt;/h3&gt;




&lt;p&gt;
In order to truly understand the power offered by mod_python, a basic understanding of Apache handlers is required. Essentially you can think of a handler as a processing &quot;phase&quot;. Apache gets a request, and then initiates a number of handlers (or &quot;phases&quot;) to do something. The handlers can either be built into Apache, or included as modules. Apache handlers may be configured explicitly, based on either filename extensions or location. Examples of functionality that takes place in handlers may include authenticating a user, invoking a cgi script, getting the server&amp;#8217;s status, etc. mod_python allows you to tap into any handler used by Apache. mod_python also provides a few standard handlers to help you with some common tasks. 
&lt;/p&gt;




&lt;h4&gt;Publisher Handler&lt;/h4&gt;




&lt;p&gt;
The publisher handler is available so you don&amp;#8217;t always have to worry about writing your own handlers and can instead focus on developing your application. It&amp;#8217;s very handy. In order to use the publisher handler, you have to something like this to your Apache configuration:
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot; escaped=&quot;true&quot;&gt;
&amp;lt;Directory /usr/local/apache2/htdocs/PublisherExample&amp;gt;
    AddHandler mod_python .py
    PythonDebug On
    PythonHandler mod_python.publisher
&amp;lt;/Directory&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
&lt;b&gt;Note: &lt;/b&gt;The &quot;PythonDebug On&quot; line makes mod_python output errors to the browser when possible, instead of the Apache server logs. This is useful while we&amp;#8217;re developing. 
&lt;/p&gt;




&lt;p&gt;
Now create a file called 'ModPythonExample.py' in the directory 'PublisherExample' and type the following:
&lt;/p&gt;




&lt;pre lang=&quot;python&quot; escaped=&quot;true&quot;&gt;
from time import strftime, localtime

def publisher_example(req):
    req.content_type = 'text/html'
    time_str = strftime(&quot;%a %b %d %H:%M:%S %Y&quot;, localtime())
    message = &quot;&amp;lt;h1&amp;gt;Hello from mod_python!&amp;lt;/h1&amp;gt;&quot;
    message += &quot;&amp;lt;p&amp;gt;The time on this server is %s&amp;lt;/p&amp;gt;&quot; % (time_str)
    return message
&lt;/pre&gt;




&lt;p&gt;
Because we have modified the Apache configuration we will need to restart it once again. Now point your browser to &lt;a href=&quot;http://localhost/PublisherExample/ModPythonExample.py/publisher_example&quot;&gt;http://localhost/PublisherExample/ModPythonExample.py/publisher_example&lt;/a&gt; and you should see a message telling you what time it is.
&lt;/p&gt;




&lt;p&gt;
As we can see from this example, the publisher handler calls a function and just sends the return value to the client. The function receives a request object as an argument. We set the request object&amp;#8217;s content_type to 'text/html' because we want the output to be handled as HTML. We then construct a message including the current time and date and return it. Notice the structure of the URL. The first part after the directory (ModPythonExample.py) is the name of our file, and the second part (&quot;publisher_example&quot;) is the name of the function to call. 
&lt;/p&gt;




&lt;p&gt;
Obviously this is only a simple example. There are many great things you can do with the publisher handler. See the documentation for more information (&lt;a href=&quot;#&quot;&gt;Resources&lt;/a&gt;). 
&lt;/p&gt;




&lt;h4&gt;CGI Handler&lt;/h4&gt;




&lt;p&gt;
The CGI Handler is provided as a stepping stone away from traditional CGI. It is not intended as a final solution for using mod_python. Basically, the CGI Handler emulates a CGI environment from within mod_python, allowing you to migrate CGI based Python applications to mod_python with little or no modification. Read the documentation to learn about limitations of this handler. To use it, just add a Directory directive to your Apache config (like we did for the Publisher Handler) and include the following:
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot;&gt;
SetHandler mod_python
PythonHandler mod_python.cgihandler
&lt;/pre&gt;




&lt;p&gt;
Once again, this should not be considered a final solution but can certainly improve the performance of your existing CGI code without too much modification.
&lt;/p&gt;




&lt;h4&gt;Custom Handlers&lt;/h4&gt;




&lt;p&gt;
Using a standard handler may be appropriate in many scenarios, but sometimes you might find it more appropriate to write your own handler. mod_python allows you to do this (of course) and in fact makes it pretty easy! Let&amp;#8217;s create the following directory directive in our Apache configuration:
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot; escaped=&quot;true&quot;&gt;
&amp;lt;Directory /usr/local/apache2/htdocs/CustomExample&amp;gt;
    AddHandler mod_python .py
    PythonDebug On
    PythonHandler customexample
&amp;lt;/Directory&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
    This tells Apache that any requests for a file with a .py extension will be served by mod_python. (It is worth noting that the file does not actually have to exist, and that in fact a request for /CustomExample/myfile.py and /CustomExample/myotherfile.py will both be handled the same with this configuration). The &quot;PythonHandler customexample&quot; line tells Apache to hand requests for files with a .py extension to this module (which we will be writing). The actual process goes something like this: a) Apache receives a request for a file in the CustomExample directory that has an extension of .py. b) Apache recognizes that this request is to be handled by mod_python and attempts to import a module called &quot;customexample&quot;. Apache looks for this module in sys.path (with our directory prepended to it so anything in there will be found first). c) Apache will then look for a function called &quot;handler&quot; in the module and execute it, passing the request object as an argument. Okay, enough details, let&amp;#8217;s write our handler module. Create a file called &quot;customexample.py&quot; in the &quot;CustomExample&quot; directory:
&lt;/p&gt;




&lt;pre lang=&quot;python&quot;&gt;
from mod_python import apache

def handler(req):
    req.content_type = 'text/plain'
    req.write('Hello from mod_python!')
    return apache.OK
&lt;/pre&gt;




&lt;p&gt;
    A few things to notice here: a) we use the request object to write output to the client instead of just returning content, b) we return a constant from the apache module. The apache.OK constant corresponds to an HTTP 200 response code. Other constants are defined for 404, 302, etc response codes. Of course, this example doesn&amp;#8217;t really do anything new, so to demonstrate the real power of writing our own handlers we are now going to create a super simple (and not very secure) MySQL authentication handler. If you have MySQL installed, create a database called &quot;mptutorial&quot; and add the following table and records:
&lt;/p&gt;




&lt;pre lang=&quot;sql&quot;&gt;
CREATE TABLE `users` (
    `id` int(11) unsigned NOT NULL auto_increment,
    `username` varchar(50) NOT NULL,
    `password` varchar(50) NOT NULL,
    PRIMARY KEY  (`id`),
    UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO `users` (`username`, `password`) VALUES ('john', MD5('secret'));
&lt;/pre&gt;




&lt;p&gt;
   Then add the following to your Apache configuration: 
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot; escaped=&quot;true&quot;&gt;
&amp;lt;Directory /usr/local/apache2/htdocs/AuthenticateExample&amp;gt;
    AddHandler mod_python .py
    PythonDebug On
    PythonAuthenHandler authuser
    AuthType Basic
    AuthName &quot;Secure Area&quot;
    Require valid-user
&amp;lt;/Directory&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
    Now let&amp;#8217;s write our example code. We need to write an authentication handler that retrieves the username and password entered 
    &lt;b&gt;Note: &lt;/b&gt; This example uses the &lt;a href=&quot;http://sourceforge.net/projects/mysql-python&quot;&gt;MySQLdb&lt;/a&gt; module. 
&lt;/p&gt;




&lt;pre lang=&quot;python&quot;&gt;
import MySQLdb
from mod_python import apache

def verify_user(username, password):
    db = MySQLdb.Connect(host='localhost',user='mpuser',passwd='mppassword',db='mptutorial')
    cur = db.cursor()
    sql = &quot;SELECT * FROM users WHERE username = '%s' AND password = MD5('%s');&quot; % (MySQLdb.escape_string(username), MySQLdb.escape_string(password))
    cur.execute(sql) 
    results = cur.fetchall()
    db.close()
    return len(results) &gt; 0
    

def authenhandler(req):
    username = req.user
    password = req.get_basic_auth_pw()
    
    if verify_user(username, password):
        return apache.OK
    else:
        return apache.HTTP_UNAUTHORIZED
&lt;/pre&gt;




&lt;p&gt;
    Restart Apache and point your browser to &lt;a href=&quot;http://localhost/AuthenticateExample/&quot;&gt;http://localhost/AuthenticateExample/&lt;/a&gt;. You should be prompted with a username and password dialog. Try entering a wrong password, then enter the username 'john' and the password 'secret'. See how mod_python handled the authentication? Neat huh?
&lt;/p&gt;




&lt;h3&gt;Python Server Pages (PSP)&lt;/h3&gt;




&lt;p&gt;
 There is one standard handler that I did not cover in the previous section. The PSP Handler allows you to use the PSP class in the mod_python.psp module. PSP stands for Python Server Pages. Python Server Pages allow you to inline Python code in HTML (or any other kind of document) as you would if you were using PHP, ASP (Active Server Pages), JSP (Java Server Pages) or something similar. Some people argue against the practice of mixing markup and code, and I&amp;#8217;d be one of them. I personally advocate the use of server pages as a view mechanism with very little control logic. Of course, to demonstrate the functionality of PSPs I will likely break my own rule. 
&lt;/p&gt;




&lt;p&gt;
    As with any mod_python handler, we have to edit our Apache configuration before we can use Python Server Pages. Open your Apache configuration and add a Directory directive similar to the following:
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot; escaped=&quot;true&quot;&gt;
&amp;lt;Directory /usr/local/apache2/htdocs/PSPExample&amp;gt;
    AddHandler mod_python .psp
    PythonHandler mod_python.psp
    PythonDebug On
&amp;lt;/Directory&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
    This should all look pretty familiar by now! Basically we tell Apache that any files with a .psp extension are handled by mod_python. We then tell Apache that the mod_python.psp module will be the generic handler for these files. Also, because we&amp;#8217;ve told Apache that PSP files will have a .psp extension, let&amp;#8217;s add index.psp to our DirectoryIndex in Apache&amp;#8217;s configuration. Open your Apache configuration file again and find the line that starts with &quot;DirectoryIndex&quot; and add index.psp. Depending on what was there before, that line should now look like this:
&lt;/p&gt;




&lt;pre lang=&quot;apache&quot;&gt;
DirectoryIndex index.html index.html.var index.php index.psp
&lt;/pre&gt;




&lt;p&gt;
    We&amp;#8217;ve now set up the PSP handler, told Apache to serve index.psp files as directory indexes, all that is left to do is to write some actual code. So restart Apache and let&amp;#8217;s start exploring Python Server Pages. Create a file called index.psp in the PSPExample directory and type the following:
&lt;/p&gt;




&lt;pre lang=&quot;html4strict&quot; escaped=&quot;true&quot;&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Python Server Pages (PSP)&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;%
import time
%&amp;gt;
Hello world, the time is: &amp;lt;%=time.strftime(&quot;%Y-%m-%d, %H:%M:%S&quot;)%&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
    Save the file and point your browser to &lt;a href=&quot;http://localhost/PSPExample&quot;&gt;http://localhost/PSPExample&lt;/a&gt;. Your server should give you the index.psp file, because we added it to the list of files in DirectoryIndex. If all went well, you should get a message with the current time on your server. If you are familiar with JSP, ASP, etc, then the above code should look very similar. Basically, anything in between the &amp;lt;% %&amp;gt; tags is interpreted as Python code. Whatever is between the &amp;lt;%= %&amp;gt; tags is replaced with the result of the expression. This saves you from typing a lot of write() or print statements. 
&lt;/p&gt;


&lt;p&gt;
    Indentation can be pretty tricky in Python Server Pages. Because PSP allows you to mix Python code and HTML / XML / Anything else, you often find that you need a way to terminate a for iteration or if statement. There&amp;#8217;s a simple, albeit cumbersome way to do this:
&lt;/p&gt;




&lt;pre lang=&quot;python&quot; escaped=&quot;true&quot;&gt;
&amp;lt;%
a = [1,2,3,4,5,6]
for number in a:
%&amp;gt;
This is a number: &amp;lt;%=number %&amp;gt; &amp;lt;br /&amp;gt;
&amp;lt;%
# this terminates the iteration
%&amp;gt;
&amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
&lt;/pre&gt;




&lt;p&gt;
    If we left the comment out of the above example, the Hello header element would be written to the output for each iteration of the list. That&amp;#8217;s obviously not what we want, so add a comment to tell PSP that the for iteration has ended. 
&lt;/p&gt;




&lt;h3&gt;Conclusion&lt;/h3&gt;


&lt;p&gt;
We&amp;#8217;ve just taken a whirl-wind tour of some of the features of mod_python. Leveraging the power of the Python programming language and the Apache HTTP server, mod_python offers an incredible amount of flexibility to a web developer. In the next article in this series, I&amp;#8217;m going to start covering some of the more framework oriented approaches to Python Web Development. Until then. 
&lt;/p&gt;


&lt;h3&gt;Resources&lt;/h3&gt;


&lt;p&gt;
I have covered a lot of material in this article, but there is still a lot more to mod_python. In order to become truly proficient, you should really take the time to read the official documentation and become more familiar with Apache. The mod_python documentation can be found &lt;a href=&quot;http://modpython.org/live/current/doc-html/&quot;&gt;here&lt;/a&gt;. You can also find more examples at the mod_python section of the apache wiki found &lt;a href=&quot;http://wiki.apache.org/mod_python/FrontPage&quot;&gt;here&lt;/a&gt;.
&lt;/p&gt;

</content>
  </entry>
  

</feed>