<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>xdissent.com &#8623; Articles</title>
	<atom:link href="http://xdissent.com/articles/" rel="self" type="application/rss+xml" />
	<link>http://xdissent.com</link>
	<description>Ride the Lightning</description>
	<lastBuildDate>Tue, 16 Nov 2010 16:53:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>GitHub Clone with Redmine</title>
		<link>http://xdissent.com/2010/05/04/github-clone-with-redmine/</link>
		<comments>http://xdissent.com/2010/05/04/github-clone-with-redmine/#comments</comments>
		<pubDate>Tue, 04 May 2010 21:17:15 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[Gitosis]]></category>
		<category><![CDATA[Redmine]]></category>
		<category><![CDATA[tools]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=170</guid>
		<description><![CDATA[I&#8217;m in love with GitHub and I don&#8217;t mind paying a few bucks a month to host private code repositories there. It&#8217;s not without its issues though, and I&#8217;ve often had trouble getting others to collaborate with me via GitHub for one reason or another. Desiring more control, I was thrilled when GitHub:FI was announced. [...]]]></description>
			<content:encoded><![CDATA[<div class="document" id="github-clone-with-redmine">


<p>I&#8217;m in <em>love</em> with <a class="reference external" href="http://github.com/">GitHub</a> and I don&#8217;t mind paying a few bucks a month
to host private code repositories there. It&#8217;s not without its issues though,
and I&#8217;ve often had trouble getting others to collaborate with me via GitHub
for one reason or another. Desiring more control, I was thrilled when
<a class="reference external" href="http://fi.github.com/">GitHub:FI</a> was <a class="reference external" href="http://github.com/blog/441-announcing-github-fi/">announced</a>. Unfortunately, the licensing fees are staggering
and put the service far out of my reach. Recently, my buddy <a class="reference external" href="http://marcuswhitney.com/">Marcus Whitney</a>
had been messing around with <a class="reference external" href="http://redmine.org/">Redmine</a> and my interest was piqued by his results.
I decided to jump in head first and try to build a reliable GitHub:FI
alternative using an <a class="reference external" href="http://ubuntu.org/">Ubuntu</a> VPS, <a class="reference external" href="http://git.gnu.org/">Git</a>, <a class="reference external" href="http://eagain.net/gitweb/?p=gitosis.git">Gitosis</a>, <a class="reference external" href="http://gmail.com/">Gmail</a> (or <a class="reference external" href="http://google.com/apps/">Google Apps
for Domains</a>), and <a class="reference external" href="http://bitnami.org/">Bitnami</a>&#8216;s <a class="reference external" href="http://bitnami.org/stack/redmine/">Redmine Stack</a>.</p>
<span id="more-170"></span>
<div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#goals-and-design-decisions" id="id1">Goals and Design Decisions</a><ul>
<li><a class="reference internal" href="#server-hygeine" id="id2">Server Hygeine</a></li>
<li><a class="reference internal" href="#stacks" id="id3">Stacks</a></li>
</ul>
</li>
<li><a class="reference internal" href="#prerequisites" id="id4">Prerequisites</a><ul>
<li><a class="reference internal" href="#fully-qualified-domain-name" id="id5">Fully Qualified Domain Name</a></li>
<li><a class="reference internal" href="#development-machine-access" id="id6">Development Machine Access</a></li>
<li><a class="reference internal" href="#sudo-configuration" id="id7">Sudo Configuration</a></li>
<li><a class="reference internal" href="#required-packages" id="id8">Required Packages</a><ul>
<li><a class="reference internal" href="#acl-mount-options" id="id9">ACL Mount Options</a></li>
<li><a class="reference internal" href="#python-packages" id="id10">Python Packages</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#install-redmine" id="id11">Install Redmine</a><ul>
<li><a class="reference internal" href="#create-a-redmine-user" id="id12">Create a <tt class="docutils literal">redmine</tt> user</a></li>
<li><a class="reference internal" href="#install-bitnami-redmine-stack" id="id13">Install Bitnami Redmine Stack</a></li>
<li><a class="reference internal" href="#a-brief-note-on-redmine-management" id="id14">A Brief Note on Redmine Management</a></li>
<li><a class="reference internal" href="#reconfigure-the-redmine-stack-to-use-system-services" id="id15">Reconfigure the Redmine Stack to Use System Services</a><ul>
<li><a class="reference internal" href="#create-a-redmine-database" id="id16">Create a Redmine Database</a></li>
<li><a class="reference internal" href="#alter-redmine-s-database-configuration" id="id17">Alter Redmine&#8217;s Database Configuration</a></li>
<li><a class="reference internal" href="#change-permissions-for-the-mongrel-cluster-proxy" id="id18">Change Permissions for the Mongrel Cluster Proxy</a></li>
<li><a class="reference internal" href="#configure-system-apache2-server" id="id19">Configure System Apache2 Server</a></li>
<li><a class="reference internal" href="#restart-apache2-and-redmine" id="id20">Restart Apache2 and Redmine</a></li>
</ul>
</li>
<li><a class="reference internal" href="#load-default-redmine-configuration" id="id21">Load Default Redmine Configuration</a></li>
<li><a class="reference internal" href="#set-up-redmine-and-gmail" id="id22">Set Up Redmine and Gmail</a><ul>
<li><a class="reference internal" href="#install-the-plugin" id="id23">Install the Plugin</a></li>
<li><a class="reference internal" href="#create-redmine-s-email-configuration" id="id24">Create Redmine&#8217;s Email Configuration</a></li>
<li><a class="reference internal" href="#restart-redmine" id="id25">Restart Redmine</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#install-gitosis" id="id26">Install Gitosis</a><ul>
<li><a class="reference internal" href="#create-the-git-user" id="id27">Create the <tt class="docutils literal">git</tt> User</a></li>
<li><a class="reference internal" href="#the-master-key-pair" id="id28">The Master Key Pair</a></li>
<li><a class="reference internal" href="#create-a-virtualenv" id="id29">Create a Virtualenv</a></li>
<li><a class="reference internal" href="#initialize-the-gitosis-repository" id="id30">Initialize the Gitosis Repository</a></li>
<li><a class="reference internal" href="#install-redmine-gitosis-plugin" id="id31">Install Redmine Gitosis Plugin</a></li>
<li><a class="reference internal" href="#patching-the-plugin" id="id32">Patching the Plugin</a><ul>
<li><a class="reference internal" href="#gitosis-configuration" id="id33">Gitosis Configuration</a></li>
<li><a class="reference internal" href="#linking-fix" id="id34">Linking Fix</a></li>
<li><a class="reference internal" href="#knowing-your-host" id="id35">Knowing Your Host</a></li>
<li><a class="reference internal" href="#migrate-the-plugin" id="id36">Migrate the Plugin</a></li>
<li><a class="reference internal" href="#redmine-the-key-master" id="id37">Redmine, the Key Master</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#working-with-redmine-and-gitosis" id="id38">Working With Redmine and Gitosis</a></li>
<li><a class="reference internal" href="#future-enhancements" id="id39">Future Enhancements</a></li>
</ul>
</div>
<div class="section" id="goals-and-design-decisions">
<h2><a class="toc-backref" href="#id1">Goals and Design Decisions</a></h2>
<p>Redmine itself supports many of the features of GitHub, specifically:</p>
<ul class="simple">
<li>Project membership</li>
<li>Issue/ticket tracker</li>
<li>Wikis</li>
<li>SCM with Git</li>
</ul>
<p>The latest stable release even includes the much anticipated Git branch
support. With a couple of plugins, Redmine can perform even more neat
tricks, like sending outgoing email through Google&#8217;s SMTP servers, and
managing your Gitosis repositories and public keys automatically. I&#8217;ve
only just begun to scratch the surface of functionality and I can already
sense the power and flexibility of Redmine is going to dramatically increase
my productivity and calm sense of well-being.</p>
<div class="section" id="server-hygeine">
<h3><a class="toc-backref" href="#id2">Server Hygeine</a></h3>
<p>There&#8217;s no reason even a fairly complex system like a Redmine installation
can&#8217;t play nice with other system services, although it takes some
planning. Much misinformation exists on the Web about running Redmine in
particular, so we must tread very lightly to sidestep the pitfalls of a
shaky system architecture. I&#8217;m of the mind that you should <em>fully</em>
understand the intricacies of a piece of software before you attempt
to run it &#8211; or at least understand clearly that you do not need to
worry yourself with certain details &#8211; and hopefully when this setup
is completely we&#8217;ll understand not only what we&#8217;ve done, but <em>why</em>
we&#8217;ve done it. That&#8217;s the key to building a solid system and is of
the highest priority.</p>
<p>Different elements of the system should be reused where appropriate as
well. We want to <em>only</em> use the system-wide versions of things like
Apache2, since they&#8217;ll probably be used for other services too and it
makes no sense to double your load when you don&#8217;t have to. By the same
token, we don&#8217;t need to install services which are easily outsourced
to existing services or the cloud (like Gmail). In reality,
our Redmine server might be moonlighting as a DNS server, firewall,
development box &#8211; <em>whatever</em> &#8211; and we need to respect that. A general
policy of software and user isolation along with well controlled shared
resource management will ensure we aren&#8217;t wasting our time on a
server that will need to be rebuilt later to play some additional role.</p>
</div>
<div class="section" id="stacks">
<h3><a class="toc-backref" href="#id3">Stacks</a></h3>
<p>A &#8220;stack&#8221; is a complete software system packaged so that, once installed,
it operates in a self contained environment that will not effect, or be
effected by, the rest of the host system. You generally don&#8217;t have to know
much more than how to run an installer script and you can be the proud admin
of your very own LAMP/Rails/etc server!</p>
<p>Stacks as a concept are almost prehistoric yet they remain an effective means
of reliable package installation. The more complex the package, the more
appealing a stack distribution becomes. The author may finely tune the most
fragile system and distribute it as a stack without having to worry about
it breaking in most cases.</p>
<p>A Redmine stack is a particularly appealing alternative to fighting Ubuntu&#8217;s
awkward Rails packages that are notoriously difficult to use and maintain.
Bitnami&#8217;s Redmine stack was chosen almost randomly, and I&#8217;m sure there are
other options out there. Bitnami has a great reputation though, and their
conventions are very clean and logical in my opinion.</p>
<p>To be honest, I&#8217;m a total Ruby-phobe. Not bringing that up would be lying
through omission, and it&#8217;s a major reason I&#8217;ve decided to go with a stack
- it&#8217;s a turn-key solution to a problem I (care to) know extremely little
about otherwise. If you&#8217;re not sold on the idea by my humility, feel
free to substitute your own Redmine installation process. Most of the
general setup steps will be similar anyway.</p>
</div>
</div>
<div class="section" id="prerequisites">
<h2><a class="toc-backref" href="#id4">Prerequisites</a></h2>
<p>While this setup procedure should be fairly universal, there are a couple of
important things that must be in place before continuing. Firstly it&#8217;s assumed
that you have an Ubuntu server somewhere and a local development machine, which
is capable of reaching the Ubuntu server using a fully qualified domain name
(FQDN). You don&#8217;t have to have working DNS entries necessarily &#8211; you can get
away with using your <tt class="docutils literal">/etc/hosts</tt> files. On both of these machines you must
have access to a non-root user, and that user must have <tt class="docutils literal">sudo</tt> access on the
server.</p>
<div class="section" id="fully-qualified-domain-name">
<h3><a class="toc-backref" href="#id5">Fully Qualified Domain Name</a></h3>
<p>Log into the non-root user account on the server via the console or an SSH
session. Check the server&#8217;s hostname to make sure a FQDN is provided:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> hostname
<span class="go">dev.local</span>
<span class="gp">xdissent@dev:~$</span> hostname -s
<span class="go">dev</span>
<span class="gp">xdissent@dev:~$</span> hostname -f
<span class="go">dev.local</span>
</pre></div>
<p>If for some reason the output of <tt class="docutils literal">hostname</tt> does not give a FQDN, you might
have to adjust the contents of your <tt class="docutils literal">/etc/hosts</tt> file and/or your
<tt class="docutils literal">/etc/hostname</tt> file. Your host must also be accessible to itself, so a quick
<tt class="docutils literal">ping</tt> is in order.</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> head -3 /etc/hosts
<span class="go">127.0.0.1       localhost</span>
<span class="go">127.0.1.1       dev.hsd1.tn.comcast.net.        dev</span>
<span class="go">127.0.1.1       dev.local</span>
<span class="gp">xdissent@dev:~$</span> cat /etc/hostname
<span class="go">dev.local</span>
<span class="gp">xdissent@dev:~$</span> ping dev.local
<span class="go">PING dev.local (127.0.1.1) 56(84) bytes of data.</span>
<span class="go">64 bytes from dev.hsd1.tn.comcast.net. (127.0.1.1): icmp_seq=1 ttl=64 time=0.021 ms</span>
<span class="go">64 bytes from dev.hsd1.tn.comcast.net. (127.0.1.1): icmp_seq=2 ttl=64 time=0.030 ms</span>
<span class="go">^C</span>
<span class="go">--- dev.local ping statistics ---</span>
<span class="go">2 packets transmitted, 2 received, 0% packet loss, time 1000ms</span>
<span class="go">rtt min/avg/max/mdev = 0.021/0.025/0.030/0.006 ms</span>
</pre></div>
<p>You may need to tell the server to take note of the changes in <tt class="docutils literal">/etc/hostname</tt>
by running <tt class="docutils literal">sudo hostname <span class="pre">-F</span> /etc/hostname</tt>. It&#8217;s pretty important to get
your FQDN set up correctly before you continue the installation, so double check
everything now.</p>
</div>
<div class="section" id="development-machine-access">
<h3><a class="toc-backref" href="#id6">Development Machine Access</a></h3>
<p>At this point you should also be able to <tt class="docutils literal">ssh</tt> into the server from your local
development machine. If you can&#8217;t <tt class="docutils literal">ssh</tt> yet but are logged in via the console,
make sure the <tt class="docutils literal"><span class="pre">openssh-server</span></tt> package is installed by running <tt class="docutils literal">sudo <span class="pre">apt-get</span>
install <span class="pre">openssh-server</span></tt>. If you&#8217;re using a FQDN that doesn&#8217;t have real-world
DNS entries, add the FQDN to your development machine&#8217;s <tt class="docutils literal">/etc/hosts</tt> file:</p>
<div class="highlight"><pre><span class="gp">Stewart:~ xdissent$</span> <span class="nb">echo</span> &lt;server-ip&gt; &lt;server-fqdn&gt; | sudo tee -a /etc/hosts &gt; /dev/null
</pre></div>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">The <tt class="docutils literal">tee</tt> command is used because root access is required to change the
<tt class="docutils literal">/etc/hosts</tt> file. STDOUT redirection is not granted the same access, so
redirecting to a protected file will still result in a permissions error.
The <tt class="docutils literal">tee</tt> command simply redirects STDIN to a file, appending the contents
of STDIN if the <tt class="docutils literal"><span class="pre">-a</span></tt> option is passed. By default, the contents are also
dumped to STDOUT, so we redirect that to <tt class="docutils literal">/dev/null</tt> to get rid of it.</p>
</div>
</div>
<div class="section" id="sudo-configuration">
<h3><a class="toc-backref" href="#id7">Sudo Configuration</a></h3>
<p>Finally, you must ensure that your non-root user on the server can gain access
to other <em>non-root</em> user accounts. Some Ubuntu virtual images come with more
restrictive <tt class="docutils literal">sudoers</tt> configurations that only let your user <tt class="docutils literal">sudo</tt> to root.
As a simple test, try any random command as a random system user (who has a
shell defined). For example, the <tt class="docutils literal"><span class="pre">www-data</span></tt> user is a decent test case:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -u www-data ls
<span class="go">Sorry, user xdissent is not allowed to execute '/bin/ls' as www-data on dev.local.</span>
</pre></div>
<p>If access is denied, you must change the <tt class="docutils literal">sudoers</tt> file using the command
<tt class="docutils literal">sudo visudo</tt>. A common fix is to append <tt class="docutils literal">%admin <span class="pre">ALL=(ALL)</span> ALL</tt> to the end
of the file, and then add your non-root user to the <tt class="docutils literal">admin</tt> group if not
already a member.</p>
</div>
<div class="section" id="required-packages">
<h3><a class="toc-backref" href="#id8">Required Packages</a></h3>
<p>The whole idea behind using Ubuntu is to take advantage of its conveniences,
like the package system. The Redmine stack comes with its own version of
many standard system services which we&#8217;re going to disable in favor of the
system versions. That way, we don&#8217;t have multiple instances of the same
service consuming precious resources, and we move further towards a generic
system that doesn&#8217;t rely on the stack for anything but the most specialized
of services.</p>
<p>The standard Ubuntu services are easy to install using the various package
management tools. Chances are, you&#8217;ve got many of these packages already
installed, but there are so many different configurations out there, it&#8217;s
easiest to just try to install them all in one go. Don&#8217;t forget to update
your Apt sources:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo apt-get update
<span class="go">Hit http://security.ubuntu.com karmic-security Release.gpg</span>
<span class="go">Ign http://security.ubuntu.com karmic-security/main Translation-en_US</span>
<span class="go">[...]</span>
<span class="go">Hit http://us.archive.ubuntu.com karmic-updates/mutiverse Packages</span>
<span class="go">Hit http://us.archive.ubuntu.com karmic-updates/mutiverse Sources</span>
<span class="go">Reading package lists... Done</span>
<span class="gp">xdissent@dev:~$</span> sudo apt-get install apache2 mysql-server git-core python-setuptools git-core build-essential acl
<span class="go">Reading package lists...</span>
<span class="go">Reading state information...</span>
<span class="go">The following extra packages will be installed:</span>
<span class="go">[...]</span>
</pre></div>
<div class="section" id="acl-mount-options">
<h4><a class="toc-backref" href="#id9">ACL Mount Options</a></h4>
<p>Access Control Lists (ACLs) are an often overlooked security feature that
allow additional, more specific access permissions to be applied to a file
or directory. They can control automatic file ownership settings inherited
from parent paths as well, which is crucial in situations where permissions
must be maintained regardless of which user is operating on a shared directory.
ACL support in Ubuntu is dependent upon the <tt class="docutils literal">acl</tt> package which we installed
earlier.</p>
<p>Simply installing the <tt class="docutils literal">acl</tt> package does not enable ACLs for all
disks. Hard disk partitions must be configured in <tt class="docutils literal">/etc/fstab</tt> to load the
ACL option and then must be remounted for the changes to take effect. Assuming
<tt class="docutils literal">/opt</tt> is on the same partition as the root filesystem, add the <tt class="docutils literal">acl</tt>
option to its <tt class="docutils literal">/etc/fstab</tt> entry (using <tt class="docutils literal">sudo</tt>):</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> cat /etc/fstab
<span class="gp">#</span> /etc/fstab: static file system information.
<span class="gp">#</span>
<span class="gp">#</span> Use <span class="s1">'blkid -o value -s UUID'</span> to print the universally unique identifier
<span class="gp">#</span> <span class="k">for </span>a device; this may be used with <span class="nv">UUID</span><span class="o">=</span> as a more robust way to name
<span class="gp">#</span> devices that works even <span class="k">if </span>disks are added and removed. See fstab<span class="o">(</span>5<span class="o">)</span>.
<span class="gp">#</span>
<span class="gp">#</span> &lt;file system&gt; &lt;mount point&gt;   &lt;<span class="nb">type</span>&gt;  &lt;options&gt;       &lt;dump&gt;  &lt;pass&gt;
<span class="go">proc            /proc           proc    defaults        0       0</span>
<span class="gp">#</span> / was on /dev/sda1 during installation
<span class="go">UUID=0c23e780-3453-4b2b-b42c-a1811722e941 /               ext4    acl,errors=remount-ro 0       1</span>
<span class="gp">#</span> swap was on /dev/sda5 during installation
<span class="go">UUID=e3eb3872-b203-46e9-b020-b3caf72ccf99 none            swap    sw              0       0</span>
<span class="go">/dev/scd0       /media/cdrom0   udf,iso9660 user,noauto,exec,utf8 0       0</span>
<span class="go">/dev/fd0        /media/floppy0  auto    rw,user,noauto,exec,utf8 0       0</span>
</pre></div>
<p>A reboot is required if ACLs were not already enabled and <tt class="docutils literal">/opt</tt> is indeed
on the root filesystem. If <tt class="docutils literal">/opt</tt> is on a different (non-root) partition,
you may get away with remounting it using <tt class="docutils literal">mount <span class="pre">-o</span> remount,acl /opt</tt> or
similar. Once you&#8217;ve got it working, the following command should give similar
output:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> mount | grep acl
<span class="go">/dev/sda1 on / type ext4 (rw,acl,errors=remount-ro)</span>
</pre></div>
<p>ACLs aren&#8217;t absolutely <em>required</em> by this setup, but it&#8217;s good practice and
will probably save us a few headaches down the road, so just bite the bullet
and load it up now. If you didn&#8217;t already see this coming, we&#8217;ll be using ACL
features to let Redmine talk to Git repositories without a file ownership
nightmare a little later.</p>
</div>
<div class="section" id="python-packages">
<h4><a class="toc-backref" href="#id10">Python Packages</a></h4>
<p>We installed the <tt class="docutils literal"><span class="pre">python-setuptools</span></tt> package mere moments ago, which
installs the very popular <tt class="docutils literal">easy_install</tt> Python package installer. While
it&#8217;s a great little dude to have around, I <em>much</em> prefer a new-comer to
the Python package tool scene, <a class="reference external" href="http://pip.openplans.org/">Pip</a>. It&#8217;s one Python package I&#8217;m comfortable
installing globally, so we&#8217;ll use <tt class="docutils literal">easy_install</tt> only once, to fetch its own
successor and install it system-wide:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo easy_install pip
<span class="go">Searching for pip</span>
<span class="go">Reading http://pypi.python.org/simple/pip/</span>
<span class="go">[...]</span>
<span class="go">Installed /usr/local/lib/python2.6/dist-packages/pip-0.7.1-py2.6.egg</span>
<span class="go">Processing dependencies for pip</span>
<span class="go">Finished processing dependencies for pip</span>
</pre></div>
<p>Now we can use the <tt class="docutils literal">pip</tt> command to install any Python package we want,
which is a much more well-advised notion than attempting to use the Ubuntu
package manager. Interestingly, the only thing the Apt system is really bad at
is Python and Ruby package management, and this project deals with both.</p>
<p>On the subject of Python, it would be a great time to familiarize yourself
with the <a class="reference external" href="http://pypi.python.org/pypi/virtualenv">Virtualenv</a> Python package, and how it&#8217;s used to create self contained
Python environments. Essentially, the <tt class="docutils literal">virtualenv &lt;path&gt;</tt> command sets up
a tiny Python distribution with its own package repository and <tt class="docutils literal"><span class="pre">&lt;path&gt;/bin</span></tt>
directory containing special binaries. The special binaries will automatically
bootstrap the intended Python environment upon execution, doing away with a
lot of path and environment variable nonsense that used to be required when
attempting advanced deployments. It really is a life saver if you run more
than a single Python app, and the idea of not impacting the system-wide Python
installation is in line with the goals we&#8217;ve stated.</p>
<p>Install Virtualenv globally, which will be used when installing Gitosis:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo pip install virtualenv
<span class="go">Downloading/unpacking virtualenv</span>
<span class="go">  Downloading virtualenv-1.4.8.tar.gz (1.5Mb): 1.5Mb downloaded</span>
<span class="go">[...]</span>
<span class="go">Successfully installed virtualenv</span>
<span class="go">Cleaning up...</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="install-redmine">
<h2><a class="toc-backref" href="#id11">Install Redmine</a></h2>
<p>Bitnami really makes it simple to get up and running with Redmine. If we didn&#8217;t
care about running &#8220;two of everything&#8221;, it could be installed and configured in
less than an hour with no sweat. Most of the extra steps we&#8217;re going to perform
will actually disable many parts of the carefully architected Bitnami system,
pointing the Redmine install to our standard system services instead.</p>
<div class="section" id="create-a-redmine-user">
<h3><a class="toc-backref" href="#id12">Create a <tt class="docutils literal">redmine</tt> user</a></h3>
<p>A dedicated user is not technically required to run the Bitnami Redmine stack.
In fact, I first ran it as <tt class="docutils literal"><span class="pre">www-data</span></tt> and it worked like a charm. Problems
arose when trying to integrate Gitosis, and it became obvious that it&#8217;s just
easier and cleaner to go ahead and set up a new system user for the redmine
application. This configuration assumes the Redmine stack, and thus, the
dedicated Redmine user&#8217;s home directory, will reside at <tt class="docutils literal">/opt/redmine</tt>. Feel
free to adjust this path at your whim. The obvious choice for the user name
was <tt class="docutils literal">redmine</tt>, but you can change that if you must as well:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo adduser --system --shell /bin/bash --gecos <span class="s1">'Redmine Administrator'</span> --group --disabled-password --home /opt/redmine redmine
<span class="go">Adding system user `redmine' (UID 106) ...</span>
<span class="go">Adding new group `redmine' (GID 115) ...</span>
<span class="go">Adding new user `redmine' (UID 106) with group `redmine' ...</span>
<span class="go">Creating home directory `/opt/redmine' ...</span>
</pre></div>
</div>
<div class="section" id="install-bitnami-redmine-stack">
<h3><a class="toc-backref" href="#id13">Install Bitnami Redmine Stack</a></h3>
<p>The Bitnami Stack system is super easy to get running. All you have to do is
download and run the installer and away you go. Before we get started,
ensure that the system Apache2 and MySQL services have been stopped.
That way the default values will be used by the Bitnami installer, which
are closer to the values we&#8217;ll ultimately want.</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo /etc/init.d/apache2 stop <span class="o">&amp;&amp;</span> sudo /etc/init.d/mysql stop
<span class="go"> * Stopping web server apache2</span>
<span class="go">   ...done.</span>
<span class="go"> * Stopping MySQL database server mysqld</span>
<span class="go">   ...done.</span>
</pre></div>
<p>Download and run the Bitnami Redmine Stack installer. Choose to install into
<tt class="docutils literal">/opt/redmine</tt>, or whichever folder you chose for the <tt class="docutils literal">redmine</tt> user&#8217;s home.
Do <em>not</em> choose to set up an SMTP server at this time, because we will handle that
with Gmail later. Be sure to use <tt class="docutils literal">sudo <span class="pre">-u</span> redmine</tt> to run the installer, so
the file ownership will be correct.</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> wget http://bitnami.org/files/stacks/redmine/0.9.3-0/bitnami-redmine-0.9.3-0-linux-installer.bin
<span class="go">--2010-05-03 18:34:05--  http://bitnami.org/files/stacks/redmine/0.9.3-0/bitnami-redmine-0.9.3-0-linux-installer.bin</span>
<span class="go">Resolving bitnami.org... 216.235.167.25</span>
<span class="go">Connecting to bitnami.org|216.235.167.25|:80... connected.</span>
<span class="go">[...]</span>
<span class="go">2010-05-03 18:38:48 (550 KB/s) - `bitnami-redmine-0.9.3-0-linux-installer.bin' saved [159459281/159459281]</span>
<span class="gp">xdissent@dev:~$</span> chmod 755 bitnami-redmine-0.9.3-0-linux-installer.bin
<span class="gp">xdissent@dev:~$</span> sudo -u redmine ./bitnami-redmine-0.9.3-0-linux-installer.bin
<span class="go">----------------------------------------------------------------------------</span>
<span class="go">Welcome to the BitNami Redmine Stack Setup Wizard.</span>
<span class="go">Created with an evaluation version of BitRock InstallBuilder</span>

<span class="go">----------------------------------------------------------------------------</span>
<span class="go">Installation folder</span>

<span class="go">Please, choose a folder to install BitNami Redmine Stack</span>

<span class="go">Select a folder [/home/xdissent/redmine-0.9.3-0]: /opt/redmine</span>

<span class="go">----------------------------------------------------------------------------</span>
<span class="go">Create Admin account</span>

<span class="go">BitNami Redmine Stack admin user creation</span>

<span class="go">Login [user]: admin</span>

<span class="go">Password :</span>
<span class="go">Please confirm your password :</span>
<span class="go">Your real name [User Name]: Redmine Administrator</span>
<span class="go">[...]</span>
<span class="go">Please wait while Setup installs BitNami Redmine Stack on your computer.</span>

<span class="go"> Installing</span>
<span class="go"> 0% ______________ 50% ______________ 100%</span>
<span class="go"> #########################################</span>

<span class="go">----------------------------------------------------------------------------</span>
<span class="go">Setup has finished installing BitNami Redmine Stack on your computer.</span>

<span class="go">Launch RedMine application. [Y/n]: Y</span>

<span class="go">Info: To access the BitNami Redmine Stack, go to</span>
<span class="go">http://localhost:8080 from your browser.</span>
<span class="go">Press [Enter] to continue :</span>
</pre></div>
<p>That&#8217;s it! Redmine is running! You <em>could</em> open up
<tt class="docutils literal"><span class="pre">http://&lt;server-fqdn&gt;:8080/redmine/</span></tt> and bask in the glory &#8211; or you could
grow a pair and just forge ahead with the setup. That&#8217;s what I thought, buddy.</p>
</div>
<div class="section" id="a-brief-note-on-redmine-management">
<h3><a class="toc-backref" href="#id14">A Brief Note on Redmine Management</a></h3>
<p>Bitnami provides a completely self contained Redmine stack with tightly coupled
components that require a very specific (but minimal) shell environment to work
together properly. For that reason, Bitnami includes a <tt class="docutils literal">use_redmine</tt> script,
which sets up the correct environment and runs a shell that is guaranteed
to work with the Bitnami system. The <tt class="docutils literal">use_redmine</tt> script should be run once
before <em>any</em> Redmine management task. If you invoke a management script from
an environment other than the one set up by <tt class="docutils literal">use_redmine</tt>, you run the risk
of corrupting your system. All Redmine management should also be done as the
<tt class="docutils literal">redmine</tt> user for obvious reasons. The <tt class="docutils literal">use_redmine</tt> script is convenient in
this regard, because you only have to run <tt class="docutils literal">sudo</tt> once per administration
session:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> <span class="nb">cd</span> ~redmine
<span class="gp">xdissent@dev:/opt/redmine$</span> sudo -H -u redmine ./use_redmine
<span class="gp">bash-4.0$</span> whoami
<span class="go">redmine</span>
</pre></div>
<p>The Redmine stack services can be managed using the <tt class="docutils literal">~redmine/ctlscript.sh</tt>
script. It follows the familiar <tt class="docutils literal">ctlscript.sh &lt;start|stop|restart|status&gt;
&lt;service&gt;</tt> syntax and, of course, <strong>must</strong> be run after <tt class="docutils literal">use_redmine</tt>.</p>
</div>
<div class="section" id="reconfigure-the-redmine-stack-to-use-system-services">
<h3><a class="toc-backref" href="#id15">Reconfigure the Redmine Stack to Use System Services</a></h3>
<p>Firstly, stop all Bitnami stack services that are currently running. We&#8217;re
going to completely reconfigure Redmine anyway, and we won&#8217;t be needing any
of the other services from this point on.</p>
<div class="warning">
<p class="first admonition-title">Warning</p>
<p class="last">These and <em>all</em> management commands must be run from within a Bitnami
management shell as the <tt class="docutils literal">redmine</tt> user. Use the following command if you
don&#8217;t understand and want to be safe: <tt class="docutils literal">sudo <span class="pre">-H</span> <span class="pre">-u</span> redmine
/opt/redmine/use_redmine</tt>.</p>
</div>
<div class="highlight"><pre><span class="gp">bash-4.0$</span> <span class="nb">pwd</span>
<span class="go">/opt/redmine</span>
<span class="gp">bash-4.0$</span> ./ctlscript.sh stop
<span class="go">/opt/redmine/subversion/scripts/ctl.sh : subversion stopped</span>
<span class="go">Syntax OK</span>
<span class="go">/opt/redmine/apache2/scripts/ctl.sh : httpd stopped</span>
<span class="go">stopping port 3001</span>
<span class="go">stopping port 3002</span>
<span class="go">/opt/redmine/mysql/scripts/ctl.sh : mysql stopped</span>
</pre></div>
<div class="section" id="create-a-redmine-database">
<h4><a class="toc-backref" href="#id16">Create a Redmine Database</a></h4>
<p>Leave the Redmine management shell and start up the system MySQL service.
Then we can create a database to host the Redmine application. The
<tt class="docutils literal">mysqladmin</tt> command can be used to create the DB.</p>
<div class="highlight"><pre><span class="gp">bash-4.0$</span> <span class="nb">exit</span>
<span class="go">exit</span>
<span class="gp">xdissent@dev:/opt/redmine$</span> sudo /etc/init.d/mysql start
<span class="go"> * Starting MySQL database server mysqld</span>
<span class="go">   ...done.</span>
<span class="go"> * Checking for corrupt, not cleanly closed and upgrade needing tables.</span>
<span class="gp">xdissent@dev:/opt/redmine$</span> mysqladmin --user<span class="o">=</span>root --host<span class="o">=</span>localhost create redmine
</pre></div>
</div>
<div class="section" id="alter-redmine-s-database-configuration">
<h4><a class="toc-backref" href="#id17">Alter Redmine&#8217;s Database Configuration</a></h4>
<p>Redmine uses YAML configuration files, which is a fairly common Rails thing
from what I understand. We need to point the database configuration towards
the system MySQL service with the default settings. Edit the file
<tt class="docutils literal">~redmine/apps/redmine/config/database.yml</tt> and alter the
<tt class="docutils literal">production</tt> section to reflect the system settings:</p>
<div class="highlight"><pre><span class="l-Scalar-Plain">production</span><span class="p-Indicator">:</span>
  <span class="l-Scalar-Plain">adapter</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">mysql</span>
  <span class="l-Scalar-Plain">database</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">redmine</span>
  <span class="l-Scalar-Plain">host</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">localhost</span>
  <span class="l-Scalar-Plain">username</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">root</span>
  <span class="l-Scalar-Plain">password</span><span class="p-Indicator">:</span>
  <span class="l-Scalar-Plain">socket</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">/var/run/mysqld/mysqld.sock</span>
</pre></div>
<p>Now we can migrate the database, installing the Redmine application data on the
system MySQL server. Rails apps use the <tt class="docutils literal">rake</tt> command for these types of
tasks, which I&#8217;ll admit is a pretty cool built-in tool. Rails also supports
the concept of deployment environments, but we can ignore that for this
setup and only pay attention to the <tt class="docutils literal">production</tt> environment.</p>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">All <tt class="docutils literal">rake</tt> commands <strong>must</strong> be run from within the application&#8217;s root
folder, which is <tt class="docutils literal">~redmine/apps/redmine/</tt> in this case.</p>
</div>
<p>Migrate the database and exit the Redmine management shell:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:/opt/redmine$</span> sudo -H -u redmine ./use_redmine
<span class="gp">bash-4.0$</span> <span class="nb">cd </span>apps/redmine
<span class="gp">bash-4.0$</span> rake db:migrate <span class="nv">RAILS_ENV</span><span class="o">=</span>production
<span class="go">(in /opt/redmine/apps/redmine)</span>
<span class="go">==  Setup: migrating ==========================================================</span>
<span class="go">-- create_table("attachments", {:force=&gt;true})</span>
<span class="go">   -&gt; 0.0151s</span>
<span class="go">[...]</span>
<span class="go">==  AddIndexOnChangesetsScmid: migrating ======================================</span>
<span class="go">-- add_index(:changesets, [:repository_id, :scmid], {:name=&gt;:changesets_repos_scmid})</span>
<span class="go">   -&gt; 0.0303s</span>
<span class="go">==  AddIndexOnChangesetsScmid: migrated (0.0305s) =============================</span>

<span class="gp">bash-4.0$</span> <span class="nb">exit</span>
</pre></div>
</div>
<div class="section" id="change-permissions-for-the-mongrel-cluster-proxy">
<h4><a class="toc-backref" href="#id18">Change Permissions for the Mongrel Cluster Proxy</a></h4>
<p>The Ubuntu Apache2 server has more restrictive proxy security settings than
is assumed by the Bitnami Redmine stack. We need to edit
<tt class="docutils literal">~redmine/apps/redmine/conf/redmine.conf</tt> and add the <tt class="docutils literal">Order</tt> and
<tt class="docutils literal">Allow</tt> setting keys to permit Apache2 to access the Mongrel cluster:</p>
<div class="highlight"><pre><span class="nb">ProxyPass</span> <span class="sx">/redmine</span> balancer://redminecluster
<span class="nb">ProxyPassReverse</span> <span class="sx">/redmine</span> balancer://redminecluster

<span class="nt">&lt;Proxy</span> <span class="s">balancer://redminecluster</span><span class="nt">&gt;</span>
  <span class="nb">BalancerMember</span> http://127.0.0.1:3001/redmine
  <span class="nb">BalancerMember</span> http://127.0.0.1:3002/redmine
  <span class="nb">Order</span> deny,allow
  <span class="nb">Allow</span> from <span class="k">all</span>
<span class="nt">&lt;/Proxy&gt;</span>
</pre></div>
<p>You don&#8217;t need to know much about Mongrel or clusters, which is another perk
of an application stack. The default Bitnami setup runs two instances of the
application and can load balance between them, which is totally fine for most
use-cases. Just change the permissions and forget the word &#8220;mongrel&#8221; forever.</p>
</div>
<div class="section" id="configure-system-apache2-server">
<h4><a class="toc-backref" href="#id19">Configure System Apache2 Server</a></h4>
<p>The Apache2 server only requires a couple of additional modules to be enabled
in order to play nice with Redmine. They&#8217;re all of the <tt class="docutils literal">mod_proxy</tt> family
and come in the default Apache2 package. The Redmine Apache2 configuration
file we just edited will need to be &#8220;included&#8221; in the main Apache2 config,
which we&#8217;ll accomplish by creating a simple file in <tt class="docutils literal">/etc/apache2/conf.d/</tt>:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:/opt/redmine$</span> sudo a2enmod proxy*
<span class="go">Enabling module proxy.</span>
<span class="go">Considering dependency proxy for proxy_ajp:</span>
<span class="go">Module proxy already enabled</span>
<span class="go">Enabling module proxy_ajp.</span>
<span class="go">Considering dependency proxy for proxy_balancer:</span>
<span class="go">Module proxy already enabled</span>
<span class="go">Enabling module proxy_balancer.</span>
<span class="go">Considering dependency proxy for proxy_connect:</span>
<span class="go">Module proxy already enabled</span>
<span class="go">Enabling module proxy_connect.</span>
<span class="go">Considering dependency proxy for proxy_ftp:</span>
<span class="go">Module proxy already enabled</span>
<span class="go">Enabling module proxy_ftp.</span>
<span class="go">Considering dependency proxy for proxy_http:</span>
<span class="go">Module proxy already enabled</span>
<span class="go">Enabling module proxy_http.</span>
<span class="go">Run '/etc/init.d/apache2 restart' to activate new configuration!</span>
<span class="gp">xdissent@dev:/opt/redmine$</span> <span class="nb">echo </span>Include /opt/redmine/apps/redmine/conf/redmine.conf | sudo tee /etc/apache2/conf.d/redmine &gt; /dev/null
</pre></div>
</div>
<div class="section" id="restart-apache2-and-redmine">
<h4><a class="toc-backref" href="#id20">Restart Apache2 and Redmine</a></h4>
<p>All that&#8217;s left to do is to restart the Apache2 server and the Redmine
application. The former must be done from outside of the Redmine
management shell, while the latter must be done from within:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:/opt/redmine$</span> sudo /etc/init.d/apache2 start
<span class="go"> * Starting web server apache2</span>
<span class="go">   ...done.</span>
<span class="gp">xdissent@dev:/opt/redmine$</span> sudo -H -u redmine ./use_redmine
<span class="gp">bash-4.0$</span> ./ctlscript.sh start redmine
<span class="go">starting port 3001</span>
<span class="go">starting port 3002</span>
</pre></div>
<p>Congratulations, Redmine is installed and running on the system Apache2 and
MySQL servers!</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Redmine-Installed1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Redmine-Installed-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Redmine-Installed-1024x870.png" style="width: 700px; height: 594px;"/></a>
</div>
</div>
<div class="section" id="load-default-redmine-configuration">
<h3><a class="toc-backref" href="#id21">Load Default Redmine Configuration</a></h3>
<p>Log into Redmine at <tt class="docutils literal"><span class="pre">http://&lt;server-fqdn&gt;/redmine/</span></tt> using the administrative
user which the installer created, in my case, <tt class="docutils literal">admin</tt>. You will be greeted with
a screen prompting you to load the Redmine default configuration. This is highly
recommended, so do it now. You should probably at least change the URL setting
for the site as well.</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Admin-Login1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Admin-Login-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Admin-Login-1024x870.png" style="width: 700px; height: 594px;"/></a>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Default-Configuration1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Default-Configuration-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Default-Configuration-1024x870.png" style="width: 700px; height: 594px;"/></a>
</div>
<div class="section" id="set-up-redmine-and-gmail">
<h3><a class="toc-backref" href="#id22">Set Up Redmine and Gmail</a></h3>
<p>Redmine occasionally needs to send users email to alert them to project changes
and other events. We chose not to set up an SMTP server when installing so that
we could hopefully interface with Gmail, avoiding yet another redundant service
running on our machine. It&#8217;s assumed that we have a Gmail or Google Apps for
Domains account specifically reserved for Redmine &#8211; ideally
<tt class="docutils literal"><span class="pre">redmine@&lt;server-fqdn&gt;</span></tt>.</p>
<p>Google&#8217;s SMTP servers require a special type of authentication called TLS. The
Ruby libraries on which Redmine is built do not support this authentication
scheme unfortunately, so we&#8217;ll need a Redmine plugin to get it working.
Specifically, the <a class="reference external" href="http://api.rubyonrails.org/classes/ActionMailer/Base.html">ActionMailer</a> class will be extended to support an optional
TLS authentication mode. The plugin that provides this extension is (creatively)
called <a class="reference external" href="http://github.com/collectiveidea/action_mailer_optional_tls">action_mailer_optional_tls</a>.</p>
<div class="section" id="install-the-plugin">
<h4><a class="toc-backref" href="#id23">Install the Plugin</a></h4>
<p>Redmine (or is it Rails?) comes with a plugin installation script that makes
it trivial to try out new extensions. Just like <tt class="docutils literal">rake</tt> commands,
the <tt class="docutils literal">script/plugin</tt> script must be run from within the application&#8217;s root
folder &#8211; <tt class="docutils literal">~redmine/apps/redmine/</tt> in our case. It accepts an <tt class="docutils literal">install</tt>
sub-command and can install plugins directly from Git repositories:</p>
<div class="highlight"><pre><span class="gp">bash-4.0$</span> <span class="nb">cd</span> ~/apps/redmine
<span class="gp">bash-4.0$</span> script/plugin install git://github.com/collectiveidea/action_mailer_optional_tls.git
<span class="go">Initialized empty Git repository in /opt/redmine/apps/redmine/vendor/plugins/action_mailer_optional_tls/.git/</span>
<span class="go">remote: Counting objects: 14, done.</span>
<span class="go">remote: Compressing objects: 100% (12/12), done.</span>
<span class="go">remote: Total 14 (delta 2), reused 2 (delta 0)</span>
<span class="go">Unpacking objects: 100% (14/14), done.</span>
<span class="go">From git://github.com/collectiveidea/action_mailer_optional_tls</span>
<span class="go"> * branch            HEAD       -&gt; FETCH_HEAD</span>
</pre></div>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">It&#8217;s standard practice to &#8220;migrate&#8221; your plugins after adding or removing
any packages. The action_mailer_optional_tls plugin does not have its own
DB schema, so it&#8217;s safe to ignore this step here.</p>
</div>
</div>
<div class="section" id="create-redmine-s-email-configuration">
<h4><a class="toc-backref" href="#id24">Create Redmine&#8217;s Email Configuration</a></h4>
<p>Since we opted out of the SMTP configuration during installation, we probably
won&#8217;t have a <tt class="docutils literal">~redmine/apps/redmine/config/email.yml</tt> file, which we&#8217;ll need
to create. It&#8217;s another YAML configuration file that should be fairly self
explanatory. Create <tt class="docutils literal">~redmine/apps/redmine/config/email.yml</tt> (as the
<tt class="docutils literal">redmine</tt> user) and add in your own Gmail settings:</p>
<div class="highlight"><pre><span class="l-Scalar-Plain">production</span><span class="p-Indicator">:</span>
  <span class="l-Scalar-Plain">delivery_method</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">:smtp</span>
  <span class="l-Scalar-Plain">smtp_settings</span><span class="p-Indicator">:</span>
    <span class="l-Scalar-Plain">tls</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">true</span>
    <span class="l-Scalar-Plain">address</span><span class="p-Indicator">:</span> <span class="s">"smtp.gmail.com"</span>
    <span class="l-Scalar-Plain">port</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">587</span>
    <span class="l-Scalar-Plain">domain</span><span class="p-Indicator">:</span> <span class="s">"gmail.com"</span>
    <span class="l-Scalar-Plain">authentication</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">:plain</span>
    <span class="l-Scalar-Plain">user_name</span><span class="p-Indicator">:</span> <span class="s">"redminelover82@gmail.com"</span>
    <span class="l-Scalar-Plain">password</span><span class="p-Indicator">:</span> <span class="s">"XXXXXXXX"</span>
</pre></div>
<p>The <tt class="docutils literal">tls: true</tt> setting is only allowed after the action_mailer_optional_tls
plugin has been installed. If you&#8217;re using Google Apps for Domains, you would
substitute your domain for <tt class="docutils literal">gmail.com</tt> in the <tt class="docutils literal">domain</tt> and <tt class="docutils literal">user_name</tt>
settings.</p>
</div>
<div class="section" id="restart-redmine">
<h4><a class="toc-backref" href="#id25">Restart Redmine</a></h4>
<p>If the settings are correct we can restart Redmine and the changes will take
effect:</p>
<div class="highlight"><pre><span class="gp">bash-4.0$</span> <span class="nb">cd</span> ~
<span class="gp">bash-4.0$</span> ./ctlscript.sh restart redmine
<span class="go">stopping port 3001</span>
<span class="go">stopping port 3002</span>
<span class="go">starting port 3001</span>
<span class="go">starting port 3002</span>
</pre></div>
<p>Try the &#8220;Send a test email&#8221; link from within the &#8220;Email Notifications&#8221; settings
tab in Redmine and you should shortly receive an email, courtesy of Gmail&#8217;s servers!</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Send-Test-Email1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Send-Test-Email-700x594.png" src="http://xdissent.com/wp-content/uploads/2010/05/Send-Test-Email-700x594.png" style="width: 700px; height: 594px;"/></a>
</div>
</div>
</div>
<div class="section" id="install-gitosis">
<h2><a class="toc-backref" href="#id26">Install Gitosis</a></h2>
<p>Gitosis is a great little piece of software that manages Git repository
access, magically storing its own configuration in a Git repository as well.
Technically, Gitosis relies on a single system user for access and manages
more fine grained access policies using public key management. It&#8217;s a very
solid system, on which GitHub itself is derived, and doesn&#8217;t clutter the
system up with unnecessary users or daemons.</p>
<p>We&#8217;ll be using a Redmine plugin to manage our Gitosis access directly through
the Redmine interface. Redmine users may manage their own public keys and they
are automatically added to Gitosis&#8217;s access control system for project
repositories of which they are members. This is similar to the way public
keys work in GitHub so it should sound vaguely familiar.</p>
<div class="section" id="create-the-git-user">
<h3><a class="toc-backref" href="#id27">Create the <tt class="docutils literal">git</tt> User</a></h3>
<p>Gitosis absolutely <strong>requires</strong> a system user &#8211; specifically one with a real
home directory somewhere. Disabling the password makes sure only those with
the Gitosis private key we&#8217;ll create will be able to gain access to the box
and change Gitosis settings. We&#8217;ll start by creating this user, just as we
did for the <tt class="docutils literal">redmine</tt> user. Feel free to select a different home directory
or name for the user, but note these differences. Also note that Bash is the
preferred shell, since it will give us a little more control over the <tt class="docutils literal">git</tt>
user environment when running the Gitosis commands, which would otherwise fail
to find the Gitosis commands on its path.</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo adduser --system --shell /bin/bash --gecos <span class="s1">'Git Administrator'</span> --group --disabled-password --home /opt/gitosis git
<span class="go">Adding system user `git' (UID 105) ...</span>
<span class="go">Adding new group `git' (GID 114) ...</span>
<span class="go">Adding new user `git' (UID 105) with group `git' ...</span>
<span class="go">Creating home directory `/opt/gitosis' ...</span>
</pre></div>
</div>
<div class="section" id="the-master-key-pair">
<h3><a class="toc-backref" href="#id28">The Master Key Pair</a></h3>
<p>Under the hood, Gitosis access is controlled with the use of private/public key
pairs. Each developer will create his own key pair, offering the public key to
the Gitosis system through SSH when a Git operation is requested. Gitosis will
check the public key against its known key list and grant or deny access
appropriately.</p>
<p>Naturally, the key management system must be accessible in some regard in order
to actually add developer public keys. Gitosis uses a single master key pair
in this case, which will always be allowed to manage keys and the Gitosis
configuration. It&#8217;s a good idea to generate this key from within a secure shell
on the server, and <strong>never</strong> transfer it over <strong>any</strong> network. Strict file
permissions are applied by default and should not be changed or the SSH system
may reject a perfectly good key out of (justified) paranoia.</p>
<p>To generate a master key pair, use the <tt class="docutils literal"><span class="pre">ssh-keygen</span></tt> command. We will be
using DSA keys, but any common scheme will work. Be sure to generate the key
as the <tt class="docutils literal">git</tt> user as to not totally blow up the file permissions:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -H -u git ssh-keygen -t dsa
<span class="go">Generating public/private dsa key pair.</span>
<span class="go">Enter file in which to save the key (/opt/gitosis/.ssh/id_dsa):</span>
<span class="go">Created directory '/opt/gitosis/.ssh'.</span>
<span class="go">Enter passphrase (empty for no passphrase):</span>
<span class="go">Enter same passphrase again:</span>
<span class="go">Your identification has been saved in /opt/gitosis/.ssh/id_dsa.</span>
<span class="go">Your public key has been saved in /opt/gitosis/.ssh/id_dsa.pub.</span>
<span class="go">The key fingerprint is:</span>
<span class="go">55:d7:4b:4c:c8:a1:f9:c3:4d:99:44:2a:af:03:71:fd git@&lt;server-fqdn&gt;</span>
<span class="go">The key's randomart image is:</span>
<span class="go">+--[ DSA 1024]----+</span>
<span class="go">|            o.B= |</span>
<span class="go">|           .++oo+|</span>
<span class="go">|         ..= o.+.|</span>
<span class="go">|         .o = +. |</span>
<span class="go">|        S.   = E |</span>
<span class="go">|          . . .  |</span>
<span class="go">|           o     |</span>
<span class="go">|            .    |</span>
<span class="go">|                 |</span>
<span class="go">+-----------------+</span>
</pre></div>
</div>
<div class="section" id="create-a-virtualenv">
<h3><a class="toc-backref" href="#id29">Create a Virtualenv</a></h3>
<p>Gitosis is &#8220;just Python&#8221;, so it can easily be installed using Pip. However,
it&#8217;s a good idea to sequester the Gitosis installation to minimize the risk
of impacting our host system&#8217;s Python setup. We&#8217;ll use Virtualenv to create
a standalone Python environment solely used by Gitosis.</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -u git virtualenv ~git/virtualenv
<span class="go">New python executable in /opt/gitosis/virtualenv/bin/python</span>
<span class="go">Installing setuptools............done.</span>
</pre></div>
<p>Now any time a Python script is run from within <tt class="docutils literal">/opt/gitosis/virtualenv/bin</tt>,
our Gitosis Python environment will be bootstrapped automatically. That means
calling <tt class="docutils literal">~git/virtualenv/bin/pip install &lt;package&gt;</tt> will install a Python
package to the Gitosis virtual environment <em>only</em>. With that in mind, we can
install Gitosis straight from its Git repository:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -u git ~git/virtualenv/bin/pip install git+git://eagain.net/gitosis.git
<span class="go">Downloading/unpacking git+git://eagain.net/gitosis.git</span>
<span class="go">  Cloning Git repository git://eagain.net/gitosis.git to /tmp/pip-JyRa0m-build</span>
<span class="go">  Running setup.py egg_info for package from git+git://eagain.net/gitosis.git</span>
<span class="go">Requirement already satisfied (use --upgrade to upgrade): setuptools&gt;=0.6c5 in /opt/gitosis/virtualenv/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg (from gitosis)</span>
<span class="go">Installing collected packages: gitosis</span>
<span class="go">  Running setup.py install for gitosis</span>
<span class="go">    Installing gitosis-init script to /opt/gitosis/virtualenv/bin</span>
<span class="go">    Installing gitosis-run-hook script to /opt/gitosis/virtualenv/bin</span>
<span class="go">    Installing gitosis-serve script to /opt/gitosis/virtualenv/bin</span>
<span class="go">Successfully installed gitosis</span>
<span class="go">Cleaning up...</span>
</pre></div>
<p>To actually force the virtual environment to take precedence over the system
environment, we can load the <tt class="docutils literal">~git/virtualenv/bin/activate</tt> script. It sets
up the user&#8217;s <tt class="docutils literal">PATH</tt> environment variable to prepend the special Python
scripts, automatically overriding the system versions. Loading the script from
a <tt class="docutils literal"><span class="pre">~/.bashrc</span></tt> file will set up the virtual environment upon login, and in our
case, immediately before looking for the Gitosis commands. We&#8217;ll need to add a
simple <tt class="docutils literal"><span class="pre">~/.bashrc</span></tt> for the <tt class="docutils literal">git</tt> user to get it all working:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> <span class="nb">echo</span> <span class="s2">"source \$HOME/virtualenv/bin/activate"</span> | sudo -u git tee -a ~git/.bashrc &gt; /dev/null
</pre></div>
</div>
<div class="section" id="initialize-the-gitosis-repository">
<h3><a class="toc-backref" href="#id30">Initialize the Gitosis Repository</a></h3>
<p>Before it can manage any Git repositories, Gitosis needs to be initialized
via the <tt class="docutils literal"><span class="pre">gitosis-init</span></tt> script. The master <strong>public</strong> key should be provided
on standard input and the <tt class="docutils literal"><span class="pre">-H</span></tt> flag for <tt class="docutils literal">sudo</tt> <strong>must</strong> be used:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -u git cat ~git/.ssh/id_dsa.pub | sudo -H -u git ~git/virtualenv/bin/gitosis-init
<span class="go">Initialized empty Git repository in /opt/gitosis/repositories/gitosis-admin.git/</span>
<span class="go">Reinitialized existing Git repository in /opt/gitosis/repositories/gitosis-admin.git/</span>
</pre></div>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">Many sources would suggest using a command similar to <tt class="docutils literal">sudo <span class="pre">-H</span> <span class="pre">-u</span> git
<span class="pre">~git/virtualenv/bin/gitosis-init</span> &lt; <span class="pre">~git/.ssh/id_dsa.pub</span></tt>. If your file
permissions are <em>correct</em>, this will <strong>not</strong> work. This is the same IO
redirection problem that required the use of <tt class="docutils literal">tee</tt> earlier, and the
<tt class="docutils literal">cat</tt> command must be used with <tt class="docutils literal">sudo</tt> to successfully read and output
the <tt class="docutils literal">git</tt> user&#8217;s public key.</p>
</div>
<p>Gitosis is now set up to manage keys and configuration through the special
<tt class="docutils literal"><span class="pre">gitosis-admin</span></tt> repository. Assuming they had loaded the master private key,
any local or remote user should now be able to clone your Gitosis repository
using <tt class="docutils literal">git clone <span class="pre">git@&lt;server-fqdn&gt;:gitosis-admin.git</span></tt>.</p>
</div>
<div class="section" id="install-redmine-gitosis-plugin">
<h3><a class="toc-backref" href="#id31">Install Redmine Gitosis Plugin</a></h3>
<p>Public key management and automatic project-to-repository linking are
provided by the <a class="reference external" href="http://github.com/rocket-rentals/redmine-gitosis/">redmine-gitosis</a> plugin. Just like before, plugin installation
must be done from within a Redmine management shell. The redmine-gitosis
plugin uses the <tt class="docutils literal">lockfile</tt> and <tt class="docutils literal">inifile</tt> Gems, so we&#8217;ll need to install
those as well:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -H -u redmine ~redmine/use_redmine
<span class="gp">bash-4.0$</span> gem install lockfile inifile
<span class="go">Successfully installed lockfile-1.4.3</span>
<span class="go">Successfully installed inifile-0.3.0</span>
<span class="go">2 gems installed</span>
<span class="go">Installing ri documentation for lockfile-1.4.3...</span>
<span class="go">Installing ri documentation for inifile-0.3.0...</span>
<span class="go">Installing RDoc documentation for lockfile-1.4.3...</span>
<span class="go">Installing RDoc documentation for inifile-0.3.0...</span>
<span class="gp">bash-4.0$</span> <span class="nb">cd</span> ~/apps/redmine
<span class="gp">bash-4.0$</span> script/plugin install git://github.com/rocket-rentals/redmine-gitosis.git
<span class="go">Initialized empty Git repository in /opt/redmine/apps/redmine/vendor/plugins/redmine-gitosis/.git/</span>
<span class="go">remote: Counting objects: 123, done.</span>
<span class="go">remote: Compressing objects: 100% (120/120), done.</span>
<span class="go">remote: Total 123 (delta 74), reused 0 (delta 0)</span>
<span class="go">Receiving objects: 100% (123/123), 13.19 KiB, done.</span>
<span class="go">Resolving deltas: 100% (74/74), done.</span>
<span class="go">From git://github.com/rocket-rentals/redmine-gitosis</span>
<span class="go"> * branch            HEAD       -&gt; FETCH_HEAD</span>
</pre></div>
<p>Don&#8217;t try to use the plugin yet though &#8211; it requires modification before
anything constructive will happen.</p>
</div>
<div class="section" id="patching-the-plugin">
<h3><a class="toc-backref" href="#id32">Patching the Plugin</a></h3>
<p>Unfortunately, the redmine-gitosis plugin is a definite work in progress (read:
<em>hack</em>) and needs to be slightly modified to work in <em>any</em> case. In the future
I&#8217;d love to have a fork available that will automatically work in this setup,
but that presents a sort of chicken-vs-egg problem, doesn&#8217;t it?</p>
<div class="section" id="gitosis-configuration">
<h4><a class="toc-backref" href="#id33">Gitosis Configuration</a></h4>
<p>Firstly, the plugin is actually configured by editing a Ruby source file
rather than a YAML configuration file. We must edit
<tt class="docutils literal"><span class="pre">~redmine/apps/redmine/vendor/plugins/redmine-gitosis/lib/gitosis.rb</span></tt> and
change the <tt class="docutils literal">GITOSIS_URI</tt> and <tt class="docutils literal">GITOSIS_BASE_PATH</tt> values to reflect our
setup. The <tt class="docutils literal">GIT_SSH</tt> environment variable needs to be slightly tweaked as
well:</p>
<div class="highlight"><pre><span class="nb">require</span> <span class="s1">'lockfile'</span>
<span class="nb">require</span> <span class="s1">'inifile'</span>
<span class="nb">require</span> <span class="s1">'net/ssh'</span>

<span class="k">module</span> <span class="nn">Gitosis</span>
  <span class="c1"># server config</span>
  <span class="no">GITOSIS_URI</span> <span class="o">=</span> <span class="s1">'git@&lt;server-fqdn&gt;:gitosis-admin.git'</span>
  <span class="no">GITOSIS_BASE_PATH</span> <span class="o">=</span> <span class="s1">'/opt/gitosis/repositories/'</span>

  <span class="c1"># commands</span>
  <span class="no">ENV</span><span class="o">[</span><span class="s1">'GIT_SSH'</span><span class="o">]</span> <span class="o">=</span> <span class="no">SSH_WITH_IDENTITY_FILE</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="no">RAILS_ROOT</span><span class="p">,</span> <span class="s1">'vendor/plugins/redmine-gitosis/extra/ssh_with_identity_file.sh'</span><span class="p">)</span>
</pre></div>
<div class="warning">
<p class="first admonition-title">Warning</p>
<p class="last">The default <tt class="docutils literal">GIT_SSH</tt> environment variable is <strong>incorrect</strong>. The path
should have a hypenated <tt class="docutils literal"><span class="pre">redmine-gitosis</span></tt> rather than the underscored
default of <tt class="docutils literal">redmine_gitosis</tt>.</p>
</div>
</div>
<div class="section" id="linking-fix">
<h4><a class="toc-backref" href="#id34">Linking Fix</a></h4>
<p>Bitnami stacks are great! The only thing I can think of to make them <em>even
better</em> would be if they linked their software differently at compile time.
It&#8217;s pretty technical (and very boring) but Bitnami could either statically
link the more common libraries or at least specify an <tt class="docutils literal">rpath</tt> for the
resulting dynamically linked binaries. That way the infamous
<tt class="docutils literal">LD_LIBRARY_PATH</tt> environment variable could be avoided entirely, promoting
responsible Linux software packaging. In the meantime, we&#8217;ll have to edit
<tt class="docutils literal"><span class="pre">~redmine/apps/redmine/vendor/plugins/redmine-gitosis/extra/ssh_with_identity_file.sh</span></tt>
to change the <tt class="docutils literal">LD_LIBRARY_PATH</tt> temporarily before calling <tt class="docutils literal">ssh</tt> to add the
private key. This prevents a linking error that would otherwise cause all
public keys to be rejected. The modified <tt class="docutils literal">ssh_with_identity_file.sh</tt> should
look like the following:</p>
<div class="highlight"><pre><span class="c">#!/bin/bash</span>
<span class="nv">LD_LIBRARY_PATH</span><span class="o">=</span><span class="s2">""</span>
<span class="nb">exec </span>ssh -i <span class="sb">`</span>dirname <span class="nv">$0</span><span class="sb">`</span>/ssh/private_key <span class="s2">"$@"</span>
</pre></div>
</div>
<div class="section" id="knowing-your-host">
<h4><a class="toc-backref" href="#id35">Knowing Your Host</a></h4>
<p>SSH maintains a list of &#8220;known hosts&#8221; for each user, which that user has
determined should be trusted. We should create a quick SSH session to the
FQDN of the server as the <tt class="docutils literal">redmine</tt> user, which will prompt SSH to add
the hostname to the list of known hosts. Redmine would not be able
to connect regardless of private key if the host is not previously known
to SSH, specifically for the <tt class="docutils literal">redmine</tt> user. After the password prompt is
displayed, simply <tt class="docutils literal"><span class="pre">ctrl-c</span></tt> to cancel the session:</p>
<div class="highlight"><pre><span class="gp">bash-4.0$</span> ssh &lt;server-fqdn&gt;
<span class="go">The authenticity of host '&lt;server-fqdn&gt; (127.0.1.1)' can't be established.</span>
<span class="go">RSA key fingerprint is d1:fb:2a:0e:53:4f:27:64:63:66:a5:09:28:bd:20:86.</span>
<span class="go">Are you sure you want to continue connecting (yes/no)? yes</span>
<span class="go">Warning: Permanently added '&lt;server-fqdn&gt;' (RSA) to the list of known hosts.</span>
<span class="go">redmine@&lt;server-fqdn&gt;'s password:</span>
<span class="gp">bash-4.0$</span>
</pre></div>
</div>
<div class="section" id="migrate-the-plugin">
<h4><a class="toc-backref" href="#id36">Migrate the Plugin</a></h4>
<p>Unlike the action_mailer_optional_tls plugin, redmine-gitosis does use the
database internally. For this reason we need to &#8220;migrate&#8221; the plugin before
attempting to use it. The <tt class="docutils literal">rake</tt> command is used again, just like when
migrating the initial Redmine application database. After the migration is
complete, it&#8217;s safe to go ahead and restart Redmine since we&#8217;re done altering
its configuration:</p>
<div class="highlight"><pre><span class="gp">bash-4.0$</span> rake db:migrate_plugins <span class="nv">RAILS_ENV</span><span class="o">=</span>production
<span class="go">(in /opt/redmine/apps/redmine)</span>
<span class="go">Migrating engines...</span>
<span class="go">[...]</span>
<span class="go">Migrating redmine-gitosis...</span>
<span class="go">==  CreateGitosisPublicKeys: migrating ========================================</span>
<span class="go">-- create_table(:gitosis_public_keys)</span>
<span class="go">   -&gt; 0.0136s</span>
<span class="go">==  CreateGitosisPublicKeys: migrated (0.0139s) ===============================</span>

<span class="go">Migrating rfpdf...</span>
<span class="go">Migrating ruby-net-ldap-0.0.4...</span>
<span class="gp">bash-4.0$</span> <span class="nb">cd</span>
<span class="gp">bash-4.0$</span> ./ctlscript.sh restart redmine
<span class="go">stopping port 3001</span>
<span class="go">stopping port 3002</span>
<span class="go">starting port 3001</span>
<span class="go">starting port 3002</span>
<span class="gp">bash-4.0$</span> <span class="nb">exit</span>
</pre></div>
</div>
<div class="section" id="redmine-the-key-master">
<h4><a class="toc-backref" href="#id37">Redmine, the Key Master</a></h4>
<p>It was mentioned that the master Gitosis key pair is used to manage the Gitosis
configuration and public keys. Redmine will need access to the <strong>private</strong> key
in order to manage the public keys for us, but we have been very careful to
prevent access to the master key from anyone except the <tt class="docutils literal">git</tt> user! This is
a perfect use-case for an ACL.</p>
<p>For some reason, the redmine-gitosis plugin comes with a default private key,
which should be removed:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -u redmine rm ~redmine/apps/redmine/vendor/plugins/redmine-gitosis/extra/ssh/private_key
</pre></div>
<p>Now we need to create a symlink from the path we just remove to the real
Gitosis master key at <tt class="docutils literal"><span class="pre">~git/.ssh/id_dsa</span></tt>. Note that you won&#8217;t currently
be able to access the file:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo -u redmine ln -s ~git/.ssh/id_dsa ~redmine/apps/redmine/vendor/plugins/redmine-gitosis/extra/ssh/private_key
<span class="gp">xdissent@dev:~$</span> sudo -u redmine cat ~redmine/apps/redmine/vendor/plugins/redmine-gitosis/extra/ssh/private_key
<span class="go">cat: /opt/redmine/apps/redmine/vendor/plugins/redmine-gitosis/extra/ssh/private_key: Permission denied</span>
</pre></div>
<p>We need to install an ACL that will allow the <tt class="docutils literal">redmine</tt> user to read the
private key. ACLs are complicated but essentially we will add a rule that
only grants the <tt class="docutils literal">redmine</tt> user read-only access to the actual key file
and the parent directory, which is also required. The <tt class="docutils literal">mask</tt> parameter
must also be modified so the effective ACL permissions will be what we
are expecting:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo setfacl -m user:redmine:r-x,mask:r-x ~git/.ssh
<span class="gp">xdissent@dev:~$</span> sudo setfacl -m user:redmine:r--,mask:r-- ~git/.ssh/id_dsa
<span class="gp">xdissent@dev:~$</span> sudo -u redmine cat ~redmine/apps/redmine/vendor/plugins/redmine-gitosis/extra/ssh/private_key
<span class="go">-----BEGIN DSA PRIVATE KEY-----</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span>
<span class="go">XXXXXXXXXXXXXXXXXXXX</span>
<span class="go">-----END DSA PRIVATE KEY-----</span>
</pre></div>
<p>The <tt class="docutils literal">redmine</tt> user now has access to the Gitosis private key, but there&#8217;s
one last ACL we need to implement before we&#8217;re done. The redmine-gitosis
plugin occasionally manipulates the Gitosis repositories directly, using
the path defined as <tt class="docutils literal">GITOSIS_BASE_PATH</tt> above. To ensure the plugin can
successfully access the repositories, we&#8217;ve got to add a &#8220;default&#8221; ACL to
the repositories directory. In the future, any repositories that are created
will inherit the &#8220;default&#8221; ACL, granting the plugin the required access:</p>
<div class="highlight"><pre><span class="gp">xdissent@dev:~$</span> sudo setfacl -m default:user::rwx,default:group::r-x,default:other:---,default:user:redmine:rwx,default:mask:rwx ~git/repositories
</pre></div>
<p>The redmine-gitosis plugin should now have all the permissions it needs
to access the Gitosis repositories, without unnecessarily exposing them to
other users. Log into Redmine using the administrator user and verify that
the redmine-gitosis plugin is active at
<tt class="docutils literal"><span class="pre">http://&lt;server-fqdn&gt;/redmine/admin/plugins</span></tt>.</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Plugins1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Plugins-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Plugins-1024x870.png" style="width: 700px; height: 594px;"/></a>
</div>
</div>
</div>
<div class="section" id="working-with-redmine-and-gitosis">
<h2><a class="toc-backref" href="#id38">Working With Redmine and Gitosis</a></h2>
<p>Believe it or not, our GitHub clone is finished! Interacting with Redmine
should be very familiar if you&#8217;ve used GitHub. To get started, we&#8217;ll need
a Redmine project and at least one project member. Create a project at
<tt class="docutils literal"><span class="pre">http://&lt;server-fqdn&gt;/redmine/projects/new/</span></tt>, noting the unique identifier.</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Create-Example-Project1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Create-Example-Project-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Create-Example-Project-1024x870.png" style="width: 700px; height: 594px;"/></a>
<p>The redmine-gitosis plugin will use the project identifier to determine the
path each project&#8217;s repository. A project whose ID is <tt class="docutils literal">example</tt> will be
available at <tt class="docutils literal"><span class="pre">git@&lt;server-fqdn&gt;:example.git</span></tt>. To actually create the
project&#8217;s repository, a Redmine user (with a valid key pair) must push a
branch to the Gitosis server. Create a developer user at
<tt class="docutils literal"><span class="pre">http://&lt;server-fqdn&gt;/redmine/users/new</span></tt> who will later create our repo.</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Create-Developer1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Create-Developer-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Create-Developer-1024x870.png" style="width: 700px; height: 594px;"/></a>
<p>Add your new developer user as a member of the project at
<tt class="docutils literal"><span class="pre">http://&lt;server-fqdn&gt;/redmine/projects/&lt;project-id&gt;/settings/members/</span></tt>,
granting Git access to the project&#8217;s repository.</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Add-Project-Member1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Add-Project-Member-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Add-Project-Member-1024x870.png" style="width: 700px; height: 594px;"/></a>
<p>Now we can log out of the administrator account, since it&#8217;s really only
useful for project and user creation from now on. To enable your developer user
to push to the Gitosis repositories, a public key must be assigned. Log in
to Redmine as your new developer user and navigate to
<tt class="docutils literal"><span class="pre">http://&lt;server-fqdn&gt;/redmine/my/public_keys</span></tt> to add a public key.</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Add-Public-Key1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Add-Public-Key-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Add-Public-Key-1024x870.png" style="width: 700px; height: 594px;"/></a>
<p>To generate a key for use from a development machine, run <tt class="docutils literal"><span class="pre">ssh-keygen</span></tt> for
your local user exactly as we did when setting up Gitosis, except without the
<tt class="docutils literal">sudo</tt>. We need to paste the <strong>public</strong> key into the Redmine interface, naming
the key appropriately. The general naming convention is
<tt class="docutils literal"><span class="pre">&lt;local-user&gt;@&lt;local-fqdn&gt;</span></tt>. When successfully added, the new public key shows
up in the key list.</p>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Added-Public-Key1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Added-Public-Key-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Added-Public-Key-1024x870.png" style="width: 700px; height: 594px;"/></a>
<p>Now our local development user should be able to create the project&#8217;s Git
repository from a development machine. It&#8217;s exactly the same process used
by GitHub, which is super convenient since I&#8217;m not sure I have the brain
space left to remember another command sequence. Once we push a branch to
the Gitosis server, the &#8220;Repository&#8221; tab in Redmine will be populated with
our brand new Git repo.</p>
<div class="highlight"><pre><span class="gp">Stewart:Code xdissent$</span> mkdir example
<span class="gp">Stewart:Code xdissent$</span> <span class="nb">cd </span>example/
<span class="gp">Stewart:example xdissent$</span> git init
<span class="go">Initialized empty Git repository in /Users/xdissent/Code/example/.git/</span>
<span class="gp">Stewart:example xdissent$</span> touch README.rst
<span class="gp">Stewart:example xdissent$</span> git add README.rst
<span class="gp">Stewart:example xdissent$</span> git commit -m <span class="s2">"Initial commit."</span>
<span class="go">[master (root-commit) 016b344] Initial commit.</span>
<span class="go"> 0 files changed, 0 insertions(+), 0 deletions(-)</span>
<span class="go"> create mode 100644 README.rst</span>
<span class="gp">Stewart:example xdissent$</span> git remote add origin git@&lt;server-fqdn&gt;:example.git
<span class="gp">Stewart:example xdissent$</span> git push origin master
<span class="go">Initialized empty Git repository in /opt/gitosis/repositories/example.git/</span>
<span class="go">Counting objects: 3, done.</span>
<span class="go">Writing objects: 100% (3/3), 219 bytes, done.</span>
<span class="go">Total 3 (delta 0), reused 0 (delta 0)</span>
<span class="go">To git@&lt;server-fqdn&gt;:example.git</span>
<span class="go"> * [new branch]      master -&gt; master</span>
</pre></div>
<a class="reference external image-reference" href="http://xdissent.com/wp-content/uploads/2010/05/Initial-Commit1.png"><img alt="http://xdissent.com/wp-content/uploads/2010/05/Initial-Commit-1024x870.png" src="http://xdissent.com/wp-content/uploads/2010/05/Initial-Commit-1024x870.png" style="width: 700px; height: 594px;"/></a>
<p>Redmine already includes great SCM tools like diff views and tracker
integration, just like GitHub. Git commit users are mapped to Redmine users
automatically as well so the interface is often even more intuitive than
GitHub&#8217;s when you&#8217;ve accidentally borked a commit or merge.</p>
</div>
<div class="section" id="future-enhancements">
<h2><a class="toc-backref" href="#id39">Future Enhancements</a></h2>
<p>It&#8217;s pretty damn close, but &#8211; even with Gitosis integration &#8211; Redmine is
<strong>not</strong> GitHub. The feature that&#8217;s most likely to be missed is the per-user
repository configuration. It might be a sizable undertaking, but this could
presumably be implemented in another Redmine plugin. Pull requests, hook
management, and pretty graphs are probably next on the list, but should be
less complicated to build.</p>
<p>Regardless, Redmine is obviously exciting and it&#8217;s my hope that more people
get involved in the development. I&#8217;ve already brushed up on my Ruby basics
to tweak a few things so &#8211; who knows &#8211; I might even try my hand at putting
together a plugin or two of my own. In the meantime, I&#8217;ve got more than
enough functionality (and the promise of flexibility) to start using Redmine
full-time in my development work.</p>
</div>
</div>
<div class="footer">
<hr class="footer"/>
<a class="reference external" href="?source=rest">View document source</a>.

</div>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2010/05/04/github-clone-with-redmine/feed/rss2/</wfw:commentRss>
		<slash:comments>40</slash:comments>
		</item>
		<item>
		<title>Bank of America Breakdown</title>
		<link>http://xdissent.com/2010/04/27/bank-of-america-breakdown/</link>
		<comments>http://xdissent.com/2010/04/27/bank-of-america-breakdown/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 22:18:05 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=165</guid>
		<description><![CDATA[I have a check written out to me from a local Nashville company, drawn on their Bank of America account. I no longer have a personal BOA account (thankfully) but I&#8217;ve never had a problem cashing a BOA check at a BOA branch, since that&#8217;s a pretty standard transaction. Regions is notorious in my mind [...]]]></description>
			<content:encoded><![CDATA[<div class="document">


<p>I have a check written out to me from a local Nashville company, drawn on their Bank of America account. I no longer have a personal BOA account (thankfully) but I&#8217;ve never had a problem cashing a BOA check at a BOA branch, since that&#8217;s a pretty standard transaction. Regions is notorious in my mind for charging $5 per-check if you don&#8217;t have a Regions account, which they upped to $7 about a month ago. This whole concept of charging a fee to cash checks drawn on their customers&#8217; accounts is fairly new, and many bank customers don&#8217;t understand that their own checks are potentially costing their payee money. I for one would be enraged if I found out my bank was stealing money intended for the person to whom I wrote a check, but that&#8217;s just me.</p>
<span id="more-165"></span>
<p>Back to my check situation: I walked into a BOA branch trying to cash this paycheck. This wasn&#8217;t my first rodeo so I immediately dropped my right thumbprint on the check front and slid my license under the glass. The teller asked if I had an account and I said no, but mentioned that I was thinking of getting an account somewhere soon (which <em>was</em> the case at that moment.) His eyes lit up at the opportunity to nab a new-account commission as he walked me through the amazing benefits of having my very own BOA accounts &#8211; personal AND business. After all, why should I be operating as a measly independent contractor/DBA/sole-proprietorship when I could become &quot;a real business&quot;? I thanked him for the advice, accepting the business card he on which he had written &quot;BUSINESS CHECKING FREE!&quot;</p>
<p>What was I there for again? Oh yeah &#8211; cashing checks. The teller punched some keys, waited, more keys, then let out a sigh. &quot;We&#8217;re going to have to charge you $6 for non-relationship transactions&quot; he regretted, obviously not understanding that I do in fact have a very long, <em>rocky</em> &quot;relationship&quot; with BOA. &quot;We just started doing that, I&#8217;m sorry. It doesn&#8217;t seem right really.&quot; &quot;No kidding&quot; I added, &quot;but I know Regions has been doing that for at least a few months.&quot; &quot;We all do it now,&quot; he confirmed. In a hurry and not wanting to press my luck, I conceded, &quot;&#8230; just take it out of the check.&quot;</p>
<p>More key punching and waiting went by, then he calmly walked over to a doorman&#8217;s bell sitting on a filing cabinet and rang it loudly. &quot;What did I win?&quot;, I laughed. However slightly amused, he straightened his face and told me he needed to ask his superior about a problem with the signature. Apparently the signee (one of the partners of the business who wrote me the check) hadn&#8217;t signed any checks recently so they couldn&#8217;t compare the signature to anything. &quot;He&#8217;s on the signatory list, but we can&#8217;t verify the signature so I can&#8217;t cash it.&quot; &quot;Um&#8230; want me to get him on the phone?&quot; I asked. &quot;That won&#8217;t be necessary, my superior can override it.&quot; I stood there waiting, looking through my iPhone for the email messages from both partners (and signatories) of the company which clearly describe the details of the check transaction since I thought those might help eventually (spoiler alert: they didn&#8217;t.) Assuming everything was going well, the teller literally started counting out my money when we both seemed to notice the woman approaching him. They spoke briefly (and quietly) before she left and he turned back to me. &quot;She denied the override,&quot; the teller lamented. &quot;You see, we&#8217;re on a brand new system, and the requirements for this type of transaction are more strict now. But I do have an idea,&quot; he added. I was listening. His plan was to try to cash the check on the one remaining terminal which still used the legacy system. He handed me off to another woman who ran the check through her machine. &quot;Ooooh, yeah I should probably call this in since we can&#8217;t verify the&#8230;&quot; I cut her off, &quot;SIGNATURE &#8211; yeah I know. Look I&#8217;ve got these emails <em>right here</em> from both signatories talking about this very check, can&#8217;t you just read those real fast and not have to bother these dudes?&quot; She looked confused. &quot;Nevermind, call him: (646) 262&#8230;&quot; She stopped me and said they had to call the number on file. &quot;Fine.&quot;</p>
<p>The phone rang, she explained that she was from BOA and there was someone trying to cash a check they had written. No surprise there! She asked the guy to verify the amount (noting that telling him the amount would be a major breach of security, right?) After she was satisfied with the legitimacy of the transaction, she began apologizing for having to call him, framing it as some kind of account security feature &#8211; not just a bullshit hassle or another example of the breakdown of trust between banks and actual people. She might as well have ended the conversation with &quot;You&#8217;re <em>welcome</em>!&quot;</p>
<p>Now that we were VERY SURE that I&#8217;m not a criminal trying to commit check fraud, they could fork over my cash &#8211; or so I thought. &quot;Do you have a second form of identification on you?&quot; she asked. I cringed. &quot;A credit card or ATM card will be fine.&quot; &quot;I don&#8217;t have those,&quot; I told her, mentioning the now-ironic bank account tips I had just received from the other guy. I offered my PADI scuba certification card (a photo ID which is NOT issued by a financial institution) but was rejected. Kroger plus cards are also not valid identification apparently. I let my passport expire last year after swearing off foreign travel for the foreseeable future because of a bad European trip, so basically I was shit out of luck. &quot;Yeah, we&#8217;re just going to need to go ahead and get a second form of ID for this transaction. Our new policy is to get those for checks over $1000,&quot; she said, doing her best Bill Lumberg impression. Realizing then that I really wasn&#8217;t going to leave there with any cash in hand, I deadpanned &quot;No it&#8217;s my fault &#8211; I&#8217;ll earn less money next time.&quot; &quot;Well, we wouldn&#8217;t want to do that would we?&quot; she laughed. &quot;I&#8217;m actually really damn <strong>hungry</strong> right now lady, so <strong>yeah</strong> &#8211; we <strong>would</strong> want to do that. Give me my check back and I&#8217;ll just get my girlfriend to deposit it in her account.&quot; I snatched the check and walked out &#8211; one full hour and zero dollars later.</p>
</div>
<div class="footer">
<hr class="footer" />
<a class="reference external" href="?source=rest">View document source</a>.

</div>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2010/04/27/bank-of-america-breakdown/feed/rss2/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mimicking Python Descriptors in PHP</title>
		<link>http://xdissent.com/2010/01/15/mimicking-python-descriptors-in-php/</link>
		<comments>http://xdissent.com/2010/01/15/mimicking-python-descriptors-in-php/#comments</comments>
		<pubDate>Sat, 16 Jan 2010 00:55:56 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[descriptors]]></category>
		<category><![CDATA[OOP]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=112</guid>
		<description><![CDATA[So you&#8217;ve been working in Python for a year and then along comes a PHP gig that you just can&#8217;t pass up. All of a sudden you&#8217;re realizing just how much you miss some of the familiar Pythonisms you&#8217;ve come to rely on. One such feature that is lacking in PHP is the concept of [...]]]></description>
			<content:encoded><![CDATA[<p>So you&#8217;ve been working in Python for a year and then along comes a PHP gig
that you just can&#8217;t pass up. All of a sudden you&#8217;re realizing just how much
you miss some of the familiar <a class="reference external" href="http://pythonism.wordpress.com">Pythonisms</a> you&#8217;ve come to rely on. One such
feature that is lacking in PHP is the concept of <a class="reference external" href="http://docs.python.org/reference/datamodel.html#descriptors">descriptors</a>. Fortunately,
it&#8217;s possible to pretty closely simulate their behaviour in PHP with just
a few special interfaces and classes.</p>
<span id="more-112"></span>
<div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#mind-blowing" id="id1">Mind Blowing</a><ul>
<li><a class="reference internal" href="#object-instances" id="id2">object instances&#8230;</a></li>
<li><a class="reference internal" href="#posing-as-dynamic-properties" id="id3">posing as dynamic properties&#8230;</a></li>
<li><a class="reference internal" href="#which-are-defined-on-a-class" id="id4">which are defined on a <em>class</em>&#8230;</a></li>
<li><a class="reference internal" href="#evaluated-at-runtime-by-a-method-on-the-descriptor-object-itself" id="id5">evaluated at runtime by a method on the descriptor object itself&#8230;</a></li>
<li><a class="reference internal" href="#that-are-accessible-through-either-an-object-instance" id="id6">that are accessible through <em>either</em> an object instance&#8230;</a></li>
<li><a class="reference internal" href="#or-statically-through-a-property-on-the-the-class-itself" id="id7">or <em>statically</em> through a property on the the class itself!</a></li>
</ul>
</li>
<li><a class="reference internal" href="#meet-the-family" id="id8">Meet the Family</a></li>
<li><a class="reference internal" href="#the-gotchas" id="id9">The Gotchas</a></li>
<li><a class="reference internal" href="#the-payoff" id="id10">The Payoff</a><ul>
<li><a class="reference internal" href="#easy-class-wide-caching" id="id11">Easy (Class-wide) Caching</a></li>
<li><a class="reference internal" href="#memory-footprint" id="id12">Memory Footprint</a></li>
<li><a class="reference internal" href="#cleaner-than-the-alternatives" id="id13">Cleaner Than the Alternatives</a></li>
<li><a class="reference internal" href="#true-object-oriented-design" id="id14">True Object Oriented Design</a></li>
</ul>
</li>
<li><a class="reference internal" href="#conclusion" id="id15">Conclusion</a></li>
</ul>
</div>
<div class="section" id="mind-blowing">
<h2>Mind Blowing</h2>
<p>For the uninitiated, there are <a class="reference external" href="http://martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/">a few</a> <a class="reference external" href="http://users.rcn.com/python/download/Descriptor.htm">great</a> <a class="reference external" href="http://mypythonnotes.wordpress.com/2008/07/05/python-descriptors/">articles</a> online that go
into great detail about what Python descriptors <em>are</em>, and what they can <em>do</em>.
Descriptors are a notoriously mind boggling topic that has left many a
curious developer cross-eyed and drooling, and usually more confused than
we he started. But they&#8217;re simple to understand, really: They&#8217;re just object
instances posing as dynamic properties which are defined on a <em>class</em>,
evaluated at runtime by a method on the descriptor object itself, that are
accessible through <em>either</em> an object instance, or <em>statically</em> through a
property on the the class itself!</p>
<div align="center" class="align-center"><img alt="http://xdissent.com/wp-content/uploads/2010/01/scanners-headexplode1.jpg" class="align-center" src="http://xdissent.com/wp-content/uploads/2010/01/scanners-headexplode1.jpg" /></div>
<p>Yeah. &quot;<strong>Simple</strong>.&quot;</p>
<p>To be honest, most of the difficulty in understanding descriptors stems
from the fact that (especially in PHP,) classes aren&#8217;t really thought of as
&quot;objects&quot; much. Classes in general are a little bit &quot;quirky&quot; in PHP, being
referenced as a quoted class name string half the time, an unquoted global
class name the other half, and as a special keyword the other-other half!
Only now are the more advanced class related tools even appearing in the PHP
world, and they&#8217;re still not complete (which will really screw us later in this
article). Plus there&#8217;s just not that much information about down and dirty
class-level manipulation for PHP out there. Where the official documentation
is lacking, it&#8217;s only supplemented by helpless user comments flailing wildly
trying to understand the murky examples. It&#8217;s a real shame because ultimately,
we as developers fall victim to the catch 22 of PHP&#8217;s evolution as a language:
Features will be added after widespread adoption of the feature.&quot; Progress
happens very slowly because developers lack the understanding (or perhaps
the vision) to take advantage of some of these features &#8211; much less
successfully lobby for improvements in the language itself. Regardless,
I&#8217;ll admit I never really even thought of classes in such a dynamic way
until I switched to Python. So, if you&#8217;ve never been exposed to Python and
its <em>very</em> object-like class syntax, then it&#8217;s going to be an even longer
road to enlightenment! No worries though, let&#8217;s just break it down slowly
and see what descriptors really look like: Descriptors <em>are</em>&#8230;</p>
<div class="section" id="object-instances">
<h3>object instances&#8230;</h3>
<p>Ok, so that means we&#8217;re dealing with an instance of a class, which will
obviously require a class definition. That&#8217;s a pretty simple concept -
define a class and create an instance of it. Somewhere along the line,
we&#8217;re going to have to define some kind of &quot;descriptor&quot; class, and
instantiate it. No big deal. With that in mind, let&#8217;s move on: Descriptors
are object instances&#8230;</p>
</div>
<div class="section" id="posing-as-dynamic-properties">
<h3>posing as dynamic properties&#8230;</h3>
<p>Dynamic properties are a slightly advanced concept, and usually make use of
the <tt class="docutils literal">__get</tt> <a class="reference external" href="http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members">magic method</a> (and friends), which PHP automatically calls
when you try to access a non-existant property (member variable) on an object
instance. For example, in the following code, <tt class="docutils literal">$obj</tt> will be an instance of
<tt class="docutils literal">TheClass</tt> and <tt class="docutils literal">the_property</tt> will be normal, <em>non-dynamic</em> instance
property:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define the class.</span>
    <span class="k">class</span> <span class="nc">TheClass</span>
    <span class="p">{</span>
        <span class="c">// Declare the property.</span>
        <span class="k">public</span> <span class="nv">$the_property</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c">// Create the object instance.</span>
    <span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">TheClass</span><span class="p">();</span>

    <span class="c">// Access the object instance&#39;s property.</span>
    <span class="k">echo</span> <span class="s1">&#39;The property: &#39;</span> <span class="o">.</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">the_property</span><span class="p">;</span>
</pre></div>
<p>The property is accessed as <tt class="docutils literal"><span class="pre">$obj-&gt;the_property</span></tt>. The following call will
issue a <tt class="docutils literal">Notice</tt> level error by default, since we attempt to access
an undeclared property on <tt class="docutils literal">$obj</tt>:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Access a nonexistent property.</span>
    <span class="k">echo</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">bogus_property</span><span class="p">;</span>
</pre></div>
<p>However, if the class defines an instance method named <tt class="docutils literal">__get</tt>, it can
intercept requests for undefined properties, and return a dynamic value.
That means we can calculate a value at runtime for what the world will
think is a normal instance property. The end product looks something like this:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define the class.</span>
    <span class="k">class</span> <span class="nc">DynamicClass</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Returns the value of a dynamic property.</span>
<span class="sd">         *</span>
<span class="sd">         * This method will issue a &#39;Notice&#39; level error if a nonexistent</span>
<span class="sd">         * property is requested.</span>
<span class="sd">         *</span>
<span class="sd">         * @param string $name The name of the property to get.</span>
<span class="sd">         *</span>
<span class="sd">         * @return mixed</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__get</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c">// Check the name of the requested property.</span>
            <span class="k">if</span> <span class="p">(</span><span class="nv">$name</span> <span class="o">==</span> <span class="s1">&#39;the_property&#39;</span><span class="p">)</span> <span class="p">{</span>

                <span class="c">// Return the dynamic value for &#39;the_property&#39; property.</span>
                <span class="k">return</span> <span class="nb">time</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="c">// Trigger an error if no property was found with the name.</span>
            <span class="nb">trigger_error</span><span class="p">(</span><span class="nb">sprintf</span><span class="p">(</span>
                <span class="s1">&#39;Undefined property: %s::$%s&#39;</span><span class="p">,</span>
                <span class="nb">get_class</span><span class="p">(</span><span class="nv">$this</span><span class="p">),</span>
                <span class="nv">$name</span>
            <span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c">// Create the object instance.</span>
    <span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DynamicClass</span><span class="p">();</span>

    <span class="c">// Access the object instance&#39;s property.</span>
    <span class="k">echo</span> <span class="s1">&#39;The property: &#39;</span> <span class="o">.</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">the_property</span><span class="p">;</span>
</pre></div>
<p>Now every time we access the <tt class="docutils literal"><span class="pre">$obj-&gt;the_property</span></tt> property, the <tt class="docutils literal">__get</tt>
method will be evaluated, and &#8211; specifically &#8211; the current time will be
returned.</p>
<p>That&#8217;s really all there is to the concept of dynamic properties. They&#8217;re
incredibly convenient though, and <em>indispensable</em> when you want to define
a clean and easy to understand API or standard interface to some functionality.
There are in fact even more magic methods, allowing you to control property
access in contexts other than &quot;getting.&quot; We&#8217;ll deal with a few of them later.</p>
<p>Let&#8217;s get back to unraveling the mystery of descriptors. We know that they&#8217;re
object instances (duh), and that they <em>themselves</em> somehow pose as dynamic
properties. That means that, similarly to the <tt class="docutils literal">__get</tt> method we&#8217;ve just
seen, descriptors handle access requests for undefined properties.
It&#8217;s not clear how an object instance actually <em>does</em> this amazing feat yet,
but let&#8217;s take it one step at a time: Descriptors are object instances
posing as dynamic properties&#8230;</p>
</div>
<div class="section" id="which-are-defined-on-a-class">
<h3>which are defined on a <em>class</em>&#8230;</h3>
<p>Holy crap, say what? Defined on a <em>class</em>? How do you define a property on
a class? Well, in the biz, we call a property defined on a <em>class</em>, a static
property. &quot;Class members&quot;, &quot;class properties&quot;, &quot;static properties&quot; &#8211; it&#8217;s all
the same. In fact, the PHP documentation can&#8217;t even make up its mind what
to call them. The premise is simple though. Remember that in PHP, classes are
defined globally. It might help to think of the class definition itself as
just a block of sourcecode that creates a single global instance of a &quot;class&quot;
type object the first time it is run. These global &quot;class&quot; object instances
have their own properties and methods, <em>plus</em> they know the how to do the
voodoo (that they do) to create a local instance of the class they represent
(so well). To declare a static class property, you just use the <tt class="docutils literal">static</tt>
keyword in the class definition, and that property will now only be accessible
through the class itself and <em>not</em> through instances. The <cite>scope resolution</cite>
operator can be used to get the static property&#8217;s value:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="k">class</span> <span class="nc">StaticPropertyClass</span>
    <span class="p">{</span>
        <span class="c">// Declare the static property.</span>
        <span class="k">public</span> <span class="k">static</span> <span class="nv">$the_static_property</span><span class="p">;</span>

        <span class="c">// Declare the property.</span>
        <span class="k">public</span> <span class="nv">$the_property</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c">// Create the object instance.</span>
    <span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">StaticPropertyClass</span><span class="p">();</span>

    <span class="c">// Access the object instance&#39;s property.</span>
    <span class="k">echo</span> <span class="s1">&#39;The property: &#39;</span> <span class="o">.</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">the_property</span><span class="p">;</span>

    <span class="c">// Access the static class property.</span>
    <span class="k">echo</span> <span class="s1">&#39;The class property: &#39;</span> <span class="o">.</span> <span class="nx">StaticPropertyClass</span><span class="o">::</span><span class="nv">$the_static_property</span><span class="p">;</span>

    <span class="c">// This will produce an error.</span>
    <span class="k">echo</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">the_static_property</span><span class="p">;</span>

    <span class="c">// And so will this.</span>
    <span class="k">echo</span> <span class="nx">StaticPropertyClass</span><span class="o">::</span><span class="nv">$the_property</span><span class="p">;</span>
</pre></div>
<p>As you can see, class properties are completely separate from (and oblivious
to) instance properties. They represent a unique global value, associated
with the theoretical &quot;global instance&quot; of the &quot;class&quot; type object. Got it? Good.
To review, a descriptor is really just an object that happens to be stored
in a class property on some random class. It also will intercept and
specially handle requests for some sort of dynamic property, which we haven&#8217;t
really talked about yet. We do know that the value for the hypothetical
property will be&#8230;</p>
</div>
<div class="section" id="evaluated-at-runtime-by-a-method-on-the-descriptor-object-itself">
<h3>evaluated at runtime by a method on the descriptor object itself&#8230;</h3>
<p>That means that every time we try to get the value of the dynamic property,
the descriptor instance will run an instance method to determine the returned
value. So a descriptor has to define some kind of processing method, which
is loosely equivalent to the <tt class="docutils literal">__get</tt> method we used earlier. The real
difference is that the PHP magic methods only automatically handle access
to dynamic properties on the exact instance on which the property is accessed.
The descriptor differs in that (somehow) it intercepts property access for
a <em>different</em> object, not itself.</p>
<p>Now our picture of a descriptor is complete, even though we don&#8217;t really
know how it&#8217;s going to work or what it&#8217;s for yet. According to what we
just discussed, we&#8217;re simply dealing with an instance of a class, with
a method that will calculate and return the dynamic value of the property
which the descriptor instance represents. And it goes a little something
like this:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define the descriptor class.</span>
    <span class="k">class</span> <span class="nc">DescriptorClass</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Calculates and returns the dynamic value for the property</span>
<span class="sd">         * associated with the descriptor instance.</span>
<span class="sd">         *</span>
<span class="sd">         * @return mixed</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">getDescriptor</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="c">// Do some special calculations.</span>
            <span class="k">return</span> <span class="nb">time</span><span class="p">();</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c">// Create a descriptor.</span>
    <span class="nv">$the_descriptor</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DescriptorClass</span><span class="p">();</span>

    <span class="c">// Add the descriptor to the class.</span>
    <span class="nx">StaticPropertyClass</span><span class="o">::</span><span class="nv">$the_static_property</span> <span class="o">=</span> <span class="nv">$the_descriptor</span><span class="p">;</span>
</pre></div>
<p>That&#8217;s it! Now <tt class="docutils literal">$the_descriptor</tt> object fits all of the criteria of a
descriptor so far. It&#8217;s stored in a static class property
(<tt class="docutils literal"><span class="pre">StaticPropertyClass::$the_static_property</span></tt>), it&#8217;s an instance
of a class and it has an instance method called <tt class="docutils literal">getDescriptor</tt> that
will return a dynamic value for some property somewhere. Ah yes &#8211; we have
to figure out how the descriptor relates to <em>the property</em>. Recall that
descriptors intercept requests for dynamic properties&#8230;</p>
</div>
<div class="section" id="that-are-accessible-through-either-an-object-instance">
<h3>that are accessible through <em>either</em> an object instance&#8230;</h3>
<p>Hold it right there! Forget the &quot;either&quot; for right now and let&#8217;s focus on
this part. The dynamic properties we&#8217;re going to be using with descriptors
are accessible through an object instance. So really, when we&#8217;ve got our
descriptor working correctly, we should be able to access a dynamic instance
property just like we did using <tt class="docutils literal">__get</tt> earlier. The important thing to
note is that the <em>name</em> of the static class property in which the descriptor
is stored should be the name of the instance property we use to get the
dynamic value from the descriptor instance. I&#8217;ll say that again &#8211; store
a descriptor instance in a class property, use the name of that class
property to access the descriptor in an instance of that class. For
example, using the descriptor we stored in
<tt class="docutils literal"><span class="pre">StaticPropertyClass::$the_static_property</span></tt>, we would access the
descriptor&#8217;s calculated property value using <tt class="docutils literal"><span class="pre">$obj-&gt;the_static_property</span></tt>:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Create the object instance.</span>
    <span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">StaticPropertyClass</span><span class="p">();</span>

    <span class="c">// Access the dynamic property, calculated by the descriptor instance.</span>
    <span class="k">echo</span> <span class="s1">&#39;Current time: &#39;</span> <span class="o">.</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">the_static_property</span><span class="p">;</span>
</pre></div>
<p>Every time we access <tt class="docutils literal"><span class="pre">$obj-&gt;the_static_property</span></tt>, we need
<tt class="docutils literal"><span class="pre">StaticPropertyClass::$the_static_property-&gt;getDescriptor()</span></tt> to run. Now
that we&#8217;re clear, we can actually go about implementing some interfaces and
a class that will take care of the actual property request handling. It&#8217;s
not as hard as it sounds! Let&#8217;s start by defining a <tt class="docutils literal">DescriptorInterface</tt>
interface, which will need to be implemented by each descriptor class we
create.</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// An interface that all descriptors will implement.</span>
    <span class="k">interface</span> <span class="nx">DescriptorInterface</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Returns the value of the descriptor when accessed.</span>
<span class="sd">         *</span>
<span class="sd">         * @param object $instance The object instance on which the descriptor was</span>
<span class="sd">         *                         accessed. &#39;null&#39; will be passed if accessed the</span>
<span class="sd">         *                         descriptor was accessed statically.</span>
<span class="sd">         * @param string $owner    The name of the class which owns the descriptor.</span>
<span class="sd">         *</span>
<span class="sd">         * @return mixed</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">getDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">,</span> <span class="nv">$owner</span><span class="p">);</span>
    <span class="p">}</span>
</pre></div>
<p>Notice that we added parameters to the <tt class="docutils literal">getDescriptor</tt> method. These are
the parameters specified by the Python descriptor interface and they&#8217;ll
<em>really</em> come in handy later. For now just understand that the first
parameter is always the object instance whose descriptor property is being
accessed and the second parameter will always be the name of the class on
which the descriptor is defined. For example:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define a descriptor.</span>
    <span class="k">class</span> <span class="nc">ChattyDescriptor</span> <span class="k">implements</span> <span class="nx">DescriptorInterface</span>
    <span class="p">{</span>
        <span class="c">// Calculate the dynamic value.</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">getDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">,</span> <span class="nv">$owner</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="nb">sprintf</span><span class="p">(</span>
                <span class="s2">&quot;Hey %s, you&#39;re a %s.&quot;</span><span class="p">,</span>
                <span class="nv">$instance</span><span class="o">-&gt;</span><span class="na">name</span><span class="p">,</span>
                <span class="nv">$owner</span>
            <span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c">// Define a simple class.</span>
    <span class="k">class</span> <span class="nc">Dude</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nv">$name</span><span class="p">;</span>
        <span class="k">public</span> <span class="k">static</span> <span class="nv">$descriptor</span><span class="p">;</span>

        <span class="c">// Assign the name at creation.</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">name</span> <span class="o">=</span> <span class="nv">$name</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c">// Add the descriptor instance to the class.</span>
    <span class="nx">Dude</span><span class="o">::</span><span class="nv">$descriptor</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ChattyDescriptor</span><span class="p">();</span>

    <span class="c">// Create an object instance.</span>
    <span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Dude</span><span class="p">(</span><span class="s1">&#39;Broseph&#39;</span><span class="p">);</span>

    <span class="c">// Access the descriptor.</span>
    <span class="k">echo</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">descriptor</span><span class="p">;</span>
</pre></div>
<p>The previous example will output <tt class="docutils literal">Hey Broseph, You're a Dude.</tt> if
everything goes according to plan &#8211; which it won&#8217;t, until we sprinkle some
magic code dust on the <tt class="docutils literal">Dude</tt> class. What we need <tt class="docutils literal">Dude</tt> to take care
of is the hand-off from the dynamic property request to the descriptor&#8217;s
<tt class="docutils literal">getDescriptor</tt> method. Since we&#8217;re definitely not going to want to add
extra code to <em>every single</em> class we use a descriptor with, let&#8217;s agree
that we&#8217;ll need a single base class from which all descriptor-bearing classes
will be extended. In this class, we want to intercept requests for undefined
instance properties, check for a descriptor with the same name in the static
<em>class</em> properties of the instance&#8217;s class, and then call the class property&#8217;s
<tt class="docutils literal">getDescriptor</tt> method if we do in fact find a descriptor object. Here&#8217;s
one implementation of just such a base class:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define a base class for objects that will use descriptors.</span>
    <span class="k">abstract</span> <span class="k">class</span> <span class="nc">Descriptable</span>
    <span class="p">{</span>
        <span class="c">// Intercepts requests for nonexistent properties.</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__get</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c">// Get the name of this class.</span>
            <span class="nv">$class</span> <span class="o">=</span> <span class="nb">get_class</span><span class="p">(</span><span class="nv">$this</span><span class="p">);</span>

            <span class="c">// Get an introspection reflection for the class.</span>
            <span class="nv">$class_ref</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReflectionClass</span><span class="p">(</span><span class="nv">$class</span><span class="p">);</span>

            <span class="c">// Check for a static class property with the given name.</span>
            <span class="nv">$attr</span> <span class="o">=</span> <span class="nv">$class_ref</span><span class="o">-&gt;</span><span class="na">getStaticPropertyValue</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span> <span class="k">null</span><span class="p">);</span>

            <span class="c">// If the class property is an object, check for a descriptor.</span>
            <span class="k">if</span> <span class="p">(</span><span class="nb">is_object</span><span class="p">(</span><span class="nv">$attr</span><span class="p">))</span> <span class="p">{</span>

                <span class="c">// Get an introspection reflection for the property.</span>
                <span class="nv">$attr_ref</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReflectionClass</span><span class="p">(</span><span class="nb">get_class</span><span class="p">(</span><span class="nv">$attr</span><span class="p">));</span>

                <span class="c">// Check to see if the static property is a descriptor.</span>
                <span class="k">if</span> <span class="p">(</span><span class="nv">$attr_ref</span><span class="o">-&gt;</span><span class="na">implementsInterface</span><span class="p">(</span><span class="s1">&#39;DescriptorGet&#39;</span><span class="p">))</span> <span class="p">{</span>

                    <span class="c">// Call the descriptor method and return the value.</span>
                    <span class="k">return</span> <span class="nv">$attr</span><span class="o">-&gt;</span><span class="na">getDescriptor</span><span class="p">(</span><span class="nv">$this</span><span class="p">,</span> <span class="nv">$class</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>

            <span class="c">// Trigger an error if no descriptor was found.</span>
            <span class="nb">trigger_error</span><span class="p">(</span><span class="nb">sprintf</span><span class="p">(</span><span class="s1">&#39;Undefined property: %s::$%s&#39;</span><span class="p">,</span> <span class="nv">$class</span><span class="p">,</span> <span class="nv">$name</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>
</pre></div>
<p>Subclasses of <tt class="docutils literal">Descriptable</tt> will now correctly handle descriptors with no
further fiddling. PHP&#8217;s new <a class="reference external" href="http://www.php.net/manual/en/book.reflection.php">reflection</a> API (speaking of undocumented
features) is used to inspect the subclass and the descriptor instance to
determine whether descriptor handling should take place. Here&#8217;s a final,
working example that will correctly implement a descriptor according to the
Python rules we&#8217;ve been over so far:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define a simple class.</span>
    <span class="k">class</span> <span class="nc">WorkingDude</span> <span class="k">extends</span> <span class="nx">Descriptable</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nv">$name</span><span class="p">;</span>
        <span class="k">public</span> <span class="k">static</span> <span class="nv">$descriptor</span><span class="p">;</span>

        <span class="c">// Assign the name at creation.</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">name</span> <span class="o">=</span> <span class="nv">$name</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c">// Add the descriptor instance to the class.</span>
    <span class="nx">WorkingDude</span><span class="o">::</span><span class="nv">$descriptor</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ChattyDescriptor</span><span class="p">();</span>

    <span class="c">// Create an object instance.</span>
    <span class="nv">$obj</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WorkingDude</span><span class="p">(</span><span class="s1">&#39;Brosephus&#39;</span><span class="p">);</span>

    <span class="c">// Access the descriptor (works!)</span>
    <span class="k">echo</span> <span class="nv">$obj</span><span class="o">-&gt;</span><span class="na">descriptor</span><span class="p">;</span>
</pre></div>
<p>Brilliant! We&#8217;ve made a descriptor! The only thing we need to remember about
using the <tt class="docutils literal">Descriptable</tt> class is that if we override the <tt class="docutils literal">__get</tt> method,
we <em>must</em> call the <tt class="docutils literal">__get</tt> method inherited from <tt class="docutils literal">Descriptable</tt>. <em>When</em>
we call it in the overridden method will essentially effect the resolution
order of dynamic properties, so do tread lightly in these cases.</p>
<p>So far we&#8217;ve built a descriptor class that emulates Python&#8217;s descriptors in
all cases but one. Python descriptor properties may be accessed <strong>either</strong>
through an object instance (as we&#8217;ve just shown)&#8230;</p>
</div>
<div class="section" id="or-statically-through-a-property-on-the-the-class-itself">
<h3>or <em>statically</em> through a property on the the class itself!</h3>
<p>And here&#8217;s where we hit the proverbial brick wall. Basically what we need is
a way to trigger a descriptor&#8217;s dynamic processing when accessing a static
class property. The really confusing part is that generally, the static
property will actually contain the descriptor instance itself! Mind Blow
part II, anyone? So every time we access a static class property that
contains a descriptor instance, we want to receive the dynamic value of
the descriptor, not the descriptor instance itself. If we could get that to
work, it would look like this:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define a simple class.</span>
    <span class="k">class</span> <span class="nc">UnemployedDude</span> <span class="k">extends</span> <span class="nx">Descriptable</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="nv">$name</span><span class="p">;</span>
        <span class="k">public</span> <span class="k">static</span> <span class="nv">$descriptor</span><span class="p">;</span>

        <span class="c">// Assign the name at creation.</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__construct</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">name</span> <span class="o">=</span> <span class="nv">$name</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c">// Add the descriptor instance to the class.</span>
    <span class="nx">UnemployedDude</span><span class="o">::</span><span class="nv">$descriptor</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ChattyDescriptor</span><span class="p">();</span>

    <span class="c">// Access the descriptor on the class (doesn&#39;t work!)</span>
    <span class="k">echo</span> <span class="nx">UnemployedDude</span><span class="o">::</span><span class="nv">$descriptor</span> <span class="o">.</span> <span class="s2">&quot; You don&#39;t even exist, you lazy bum!&quot;</span><span class="p">;</span>
</pre></div>
<p>Unfortunately, PHP has a problem. It provides no <tt class="docutils literal">__get</tt> equivalent magic
method for static access. Try as we might, we&#8217;ll <em>never</em> get a dynamic value
for a class property (defined or otherwise). There has been <a class="reference external" href="http://marc.info/?t=121875368600002&amp;r=1&amp;w=2">great
discussion</a>, a <a class="reference external" href="http://bugs.php.net/bug.php?id=45002">legendary bug ticket</a> and even <a class="reference external" href="http://wiki.php.net/rfc/static-classes#class_model_rules">an RFC</a> dealing with the
addition of a <tt class="docutils literal">__getStatic</tt> magic method to the PHP core, but PHP 5.3
shipped without even a hint of progress in this area. Quel bummer, man! So
all static class properties <em>must</em> be declared in the class definition,
period. In fact, our descriptor implementation subtley <em>relies</em> on this
quirk. When <tt class="docutils literal">__getStatic</tt> is finally available our descriptor class will
require that the class property be undefined in the class definition, and
assigned later. The assignment would take place in the <tt class="docutils literal">__setStatic</tt> magic
method, which would be tasked with keeping track of added descriptors, most
likely in an array keyed off the property name for each descriptor. Yes, it
will be a brave new world for sure! Oh well, it looks like we were just
wasting our time on this descriptor pipe dream.</p>
<p>Not so fast! Don&#8217;t throw your keyboard in the trashcan yet &#8211; there is hope.
First, let&#8217;s examine precisely how big of a deal this one restriction on
our PHP descriptors is. Why would you even want static access to a dynamic
descriptor property? Coincidentally, you <em>probably</em> wouldn&#8217;t
want access to the dynamic value <em>at all</em>! In practice, descriptors rarely
define special handling for the class itself, rather they focus on manipulating
object instances. So the use cases are few in which you will even be dealing
with a descriptor <em>value</em> statically. What you&#8217;ll see far more often is
a descriptor returning its own <em>instance</em> instead of a value when it is
accessed as a static class property. If you think about it, this makes
total sense. How else are you supposed to ever get at the descriptor instance
otherwise? If <tt class="docutils literal"><span class="pre">UnemployedDude::$descriptor</span></tt> returned a <em>value</em>, there
would be no way to get at the descriptor instance at all, since that&#8217;s the
only way we know how to refer to the damn thing. This is just how PHP works
(which we&#8217;re stuck with) and it happens to correlate with the most likely
use case for a descriptor (luckily) so our descriptor class is still very
faithful to the Python equivalent.</p>
<p>One quick thing to note is that the dynamic accessor method required by the
descriptor interface accepts an instance parameter which will contain the
object instance whose descriptor property was requested. In a class descriptor
context, this parameter would always be <tt class="docutils literal">null</tt> since there is no instance
in a static context.</p>
</div>
</div>
<div class="section" id="meet-the-family">
<h2>Meet the Family</h2>
<p>Up to this point, we&#8217;ve ignored a <em>huge</em> detail. In reality, &quot;getting&quot; isn&#8217;t
the only access method supported by Python descriptors. There is also support
for dynamic value &quot;setting&quot; and &quot;deleting.&quot; These actions roughly correspond
to the PHP magic methods <tt class="docutils literal">__set</tt> and <tt class="docutils literal">__unset</tt>, and should be fairly self
explanatory. Descriptors intercept property &quot;setting&quot; and &quot;unsetting&quot; just as
they do for property &quot;getting&quot; currently. To fill out our descriptor
implementation, we should define some new interfaces to define how these new
methods will be called by the descriptable class.</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// The descriptor interface for &quot;set&quot; access.</span>
    <span class="k">interface</span> <span class="nx">DescriptorSet</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Sets the value of the descriptor.</span>
<span class="sd">         *</span>
<span class="sd">         * @param object $instance The object instance on which the descriptor was</span>
<span class="sd">         *                         accessed.</span>
<span class="sd">         * @param mixed  $value    The value that was given to the descriptor.</span>
<span class="sd">         *</span>
<span class="sd">         * @return null</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">setDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">,</span> <span class="nv">$value</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c">// The descriptor interface for &quot;unset&quot; access.</span>
    <span class="k">interface</span> <span class="nx">DescriptorUnset</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Unsets the descriptor&#39;s value.</span>
<span class="sd">         *</span>
<span class="sd">         * @param object $instance The object instance on which the descriptor was</span>
<span class="sd">         *                         accessed.</span>
<span class="sd">         *</span>
<span class="sd">         * @return null</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">unsetDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">);</span>
    <span class="p">}</span>
</pre></div>
<p>The <tt class="docutils literal">setDescriptor</tt> method will be called when setting the value of a
descriptor property like <tt class="docutils literal"><span class="pre">$obj-&gt;descriptor</span> = 'new value';</tt> and will receive
the new property value in the <tt class="docutils literal">$value</tt> parameter. Note that the
<tt class="docutils literal">unsetDescriptor</tt> method only receives the object instance as a parameter.
If you&#8217;re wondering what happened to the <tt class="docutils literal">$class</tt> parameter that we used
in <tt class="docutils literal">getDescriptor</tt>, good catch! It turns out, Python descriptors do
not allow static access for &quot;setting&quot; and &quot;deleting (unsetting)&quot; descriptor
properties. The reasoning is simple: if these methods were allowed for
static property access, you could never remove the descriptor instance from
the class. A full restart of the program would be required to empty the
static class property. That just isn&#8217;t practical, so the Python authors left
the feature out completely to prevent confusion. This is an extra bonus for
us, since we can&#8217;t use static class property access <em>at all</em> with descriptors
in PHP. That means we&#8217;re really only missing out on the one single use-case.</p>
<p>There is one other detail to descriptors that&#8217;s specific to PHP. Many PHP
developers use <tt class="docutils literal">isset()</tt> to evaluate whether a property exists and is
valid. Unfortunately <tt class="docutils literal">isset()</tt> will return <tt class="docutils literal">false</tt> for <em>any</em> undeclared
property, even if we&#8217;ve overridden the <tt class="docutils literal">__get</tt> method to return a value.
To accurately simulate a real property, we need to override the magic
<tt class="docutils literal">__isset</tt> method to return <tt class="docutils literal">true</tt> if a property is evaluated dynamically.
With this last piece of the puzzle, we are able to construct a robust
descriptable class, completing our PHP descriptor support:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// The base descriptor interface.</span>
    <span class="k">interface</span> <span class="nx">Descriptor</span>
    <span class="p">{</span>
    <span class="p">}</span>

    <span class="c">// The descriptor interface for &quot;get&quot; access.</span>
    <span class="k">interface</span> <span class="nx">DescriptorGet</span> <span class="k">extends</span> <span class="nx">Descriptor</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Returns the value of the descriptor when accessed.</span>
<span class="sd">         *</span>
<span class="sd">         * @param object $instance The object instance on which the descriptor was</span>
<span class="sd">         *                         accessed. &#39;null&#39; will be passed if accessed the</span>
<span class="sd">         *                         descriptor was accessed statically.</span>
<span class="sd">         * @param string $owner    The name of the class which owns the descriptor.</span>
<span class="sd">         *</span>
<span class="sd">         * @return mixed</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">getDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">,</span> <span class="nv">$owner</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c">// The descriptor interface for &quot;set&quot; access.</span>
    <span class="k">interface</span> <span class="nx">DescriptorSet</span> <span class="k">extends</span> <span class="nx">Descriptor</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Sets the value of the descriptor.</span>
<span class="sd">         *</span>
<span class="sd">         * @param object $instance The object instance on which the descriptor was</span>
<span class="sd">         *                         accessed.</span>
<span class="sd">         * @param mixed  $value    The value that was given to the descriptor.</span>
<span class="sd">         *</span>
<span class="sd">         * @return null</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">setDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">,</span> <span class="nv">$value</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c">// The descriptor interface for &quot;unset&quot; access.</span>
    <span class="k">interface</span> <span class="nx">DescriptorUnset</span> <span class="k">extends</span> <span class="nx">Descriptor</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Unsets the descriptor&#39;s value.</span>
<span class="sd">         *</span>
<span class="sd">         * @param object $instance The object instance on which the descriptor was</span>
<span class="sd">         *                         accessed.</span>
<span class="sd">         *</span>
<span class="sd">         * @return null</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">unsetDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c">// The base class for all descriptor-bearing subclasses.</span>
    <span class="k">abstract</span> <span class="k">class</span> <span class="nc">Descriptable</span>
    <span class="p">{</span>
        <span class="sd">/**</span>
<span class="sd">         * Finds and returns a descriptor instance for the class.</span>
<span class="sd">         *</span>
<span class="sd">         * This method will return null if either a descriptor was not found in</span>
<span class="sd">         * the class property with the specified name, or if a descriptor was</span>
<span class="sd">         * found but does not implement the requested interface. Passing the</span>
<span class="sd">         * default interface name &#39;Descriptor&#39; will return any type of descriptor</span>
<span class="sd">         * as long as it is stored in the correct class property.</span>
<span class="sd">         *</span>
<span class="sd">         * @param string $name  The name of the descriptor property being accessed.</span>
<span class="sd">         * @param string $iface The name of the descriptor interface which must</span>
<span class="sd">         *                      be supported by the descriptor instance.</span>
<span class="sd">         *</span>
<span class="sd">         * @return mixed</span>
<span class="sd">         */</span>
        <span class="k">protected</span> <span class="k">function</span> <span class="nf">_descriptorInstance</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span> <span class="nv">$iface</span><span class="o">=</span><span class="s1">&#39;Descriptor&#39;</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c">// Get an introspection reflection for the class.</span>
            <span class="nv">$class</span> <span class="o">=</span> <span class="nb">get_class</span><span class="p">(</span><span class="nv">$this</span><span class="p">);</span>
            <span class="nv">$class_ref</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReflectionClass</span><span class="p">(</span><span class="nv">$class</span><span class="p">);</span>

            <span class="c">// Get the static class property with the given name.</span>
            <span class="nv">$attr</span> <span class="o">=</span> <span class="nv">$class_ref</span><span class="o">-&gt;</span><span class="na">getStaticPropertyValue</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span> <span class="k">null</span><span class="p">);</span>

            <span class="c">// Check to see if the property is a descriptor instance.</span>
            <span class="k">if</span> <span class="p">(</span><span class="nb">is_object</span><span class="p">(</span><span class="nv">$attr</span><span class="p">))</span> <span class="p">{</span>

                <span class="c">// Get an introspection reflection of the property.</span>
                <span class="nv">$attr_ref</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ReflectionClass</span><span class="p">(</span><span class="nb">get_class</span><span class="p">(</span><span class="nv">$attr</span><span class="p">));</span>

                <span class="c">// Check to see if the static property has the right interface.</span>
                <span class="k">if</span> <span class="p">(</span><span class="nv">$attr_ref</span><span class="o">-&gt;</span><span class="na">implementsInterface</span><span class="p">(</span><span class="nv">$iface</span><span class="p">))</span> <span class="p">{</span>

                    <span class="c">// Return the found descriptor.</span>
                    <span class="k">return</span> <span class="nv">$attr</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="c">// Return null since we didn&#39;t find a matching descriptor.</span>
            <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="sd">/**</span>
<span class="sd">         * Finds and runs a descriptor method for the class.</span>
<span class="sd">         *</span>
<span class="sd">         * This method will run the specified descriptor method of the descriptor</span>
<span class="sd">         * with the provided name if it exists. The method may be one of &#39;get&#39;,</span>
<span class="sd">         * &#39;set&#39;, or &#39;unset&#39;. Any arguments provided in the &#39;$args&#39; array parameter</span>
<span class="sd">         * will be passed to the descriptor method. This method will return &#39;null&#39;</span>
<span class="sd">         * if no matching descriptor instance was found.</span>
<span class="sd">         *</span>
<span class="sd">         * @param string $method The name of the descriptor method to run.</span>
<span class="sd">         * @param string $name   The descriptor property name.</span>
<span class="sd">         * @param array  $args   An array of arguments to pass to the descriptor</span>
<span class="sd">         *                       method or &#39;null&#39;.</span>
<span class="sd">         *</span>
<span class="sd">         * @return mixed</span>
<span class="sd">         */</span>
        <span class="k">protected</span> <span class="k">function</span> <span class="nf">_descriptorAccess</span><span class="p">(</span><span class="nv">$method</span><span class="p">,</span> <span class="nv">$name</span><span class="p">,</span> <span class="nv">$args</span><span class="o">=</span><span class="k">null</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c">// Initialize descriptor method arguments.</span>
            <span class="k">if</span> <span class="p">(</span><span class="nb">is_null</span><span class="p">(</span><span class="nv">$args</span><span class="p">))</span> <span class="p">{</span>
                <span class="nv">$args</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="c">// Get the name of the required descriptor interface.</span>
            <span class="nv">$iface</span> <span class="o">=</span> <span class="s1">&#39;Descriptor&#39;</span> <span class="o">.</span> <span class="nb">ucfirst</span><span class="p">(</span><span class="nv">$method</span><span class="p">);</span>

            <span class="c">// Retrieve the descriptor instance matching the name and interface.</span>
            <span class="nv">$attr</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_descriptorInstance</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span> <span class="nv">$iface</span><span class="p">);</span>

            <span class="c">// Check for a valid descriptor instance.</span>
            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">is_null</span><span class="p">(</span><span class="nv">$attr</span><span class="p">))</span> <span class="p">{</span>

                <span class="c">// Call the descriptor instance method with the passed arguments.</span>
                <span class="k">return</span> <span class="nb">call_user_func_array</span><span class="p">(</span>
                    <span class="k">array</span><span class="p">(</span><span class="nv">$attr</span><span class="p">,</span> <span class="nv">$method</span> <span class="o">.</span> <span class="s1">&#39;Descriptor&#39;</span><span class="p">),</span>
                    <span class="nv">$args</span>
                <span class="p">);</span>
            <span class="p">}</span>

            <span class="c">// Trigger an error if the appropriate descriptor wasn&#39;t found.</span>
            <span class="nb">trigger_error</span><span class="p">(</span><span class="nb">sprintf</span><span class="p">(</span><span class="s1">&#39;Undefined property: %s::$%s&#39;</span><span class="p">,</span> <span class="nv">$class</span><span class="p">,</span> <span class="nv">$name</span><span class="p">));</span>
            <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="sd">/**</span>
<span class="sd">         * Gets a dynamic instance property&#39;s value.</span>
<span class="sd">         *</span>
<span class="sd">         * @param string $name The name of the instance property being accessed.</span>
<span class="sd">         *</span>
<span class="sd">         * @return mixed</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__get</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nv">$args</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="nv">$this</span><span class="p">,</span> <span class="nb">get_class</span><span class="p">(</span><span class="nv">$this</span><span class="p">));</span>
            <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_descriptorAccess</span><span class="p">(</span><span class="s1">&#39;get&#39;</span><span class="p">,</span> <span class="nv">$name</span><span class="p">,</span> <span class="nv">$args</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="sd">/**</span>
<span class="sd">         * Sets a dynamic instance property&#39;s value.</span>
<span class="sd">         *</span>
<span class="sd">         * @param string $name  The name of the instance property being set.</span>
<span class="sd">         * @param string $value The new value to use for the property.</span>
<span class="sd">         *</span>
<span class="sd">         * @return null</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__set</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span> <span class="nv">$value</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nv">$args</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="nv">$this</span><span class="p">,</span> <span class="nv">$value</span><span class="p">);</span>
            <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_descriptorAccess</span><span class="p">(</span><span class="s1">&#39;set&#39;</span><span class="p">,</span> <span class="nv">$name</span><span class="p">,</span> <span class="nv">$args</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="sd">/**</span>
<span class="sd">         * Unsets a dynamic instance property.</span>
<span class="sd">         *</span>
<span class="sd">         * @param string $name The name of the instance property being unset.</span>
<span class="sd">         *</span>
<span class="sd">         * @return null</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__unset</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="nv">$args</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="nv">$this</span><span class="p">);</span>
            <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_descriptorAccess</span><span class="p">(</span><span class="s1">&#39;unset&#39;</span><span class="p">,</span> <span class="nv">$name</span><span class="p">,</span> <span class="nv">$args</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="sd">/**</span>
<span class="sd">         * Returns true if a descriptor instance exists in the named class property.</span>
<span class="sd">         *</span>
<span class="sd">         * @param string $name The name of the instance property being accessed.</span>
<span class="sd">         *</span>
<span class="sd">         * @return boolean</span>
<span class="sd">         */</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">__isset</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c">// Simply test to see if we have any descriptor with that name.</span>
            <span class="k">return</span> <span class="nb">is_null</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_descriptorInstance</span><span class="p">(</span><span class="nv">$name</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>
</pre></div>
</div>
<div class="section" id="the-gotchas">
<h2>The Gotchas</h2>
<p>There are some tiny differences between our PHP descriptors and those in
Python, aside from the static access restriction discussed previously. Python&#8217;s
property resolution automatically searches for a class property if an instance
does not contain property with the requested name. PHP does not do this, and
treats static properties very differently. This is unlikely to become an issue
in practice, and is a very specific edge case. Another similar detail is that
Python will short circuit its property resolution if a property defines a
&quot;setting&quot; access method, always using the descriptor even if the instance
defines it&#8217;s own property with the same name. Descriptors which only handle
&quot;getting&quot; of properties will not be used by default. This is very confusing
and really just an intricacy of Python that doesn&#8217;t relate to PHP since we
don&#8217;t have the concept of an object&#8217;s &quot;data dict.&quot; In all except the wildest
of edge cases, these differences may be ignored.</p>
<p>Another small gotcha can pop up when developers erroneously re-include a
source file containing a class definition. Since descriptor instances must
be assigned to a class property and PHP <a class="reference external" href="http://www.php.net/manual/en/language.oop5.properties.php">doesn&#8217;t allow</a> evaluated variables
as property values in the class definition, we&#8217;re forced to add the descriptor
instance to the class <em>after</em> it has been defined. This isn&#8217;t something that
should be feared and it&#8217;s certainly not &quot;wrong&quot; according to how PHP works,
but it is a little off putting to some developers. Regardless, it&#8217;s important
to <em>not</em> re-include a file if it adds a descriptor to a class. Otherwise, a
new instance of the descriptor may suddenly appear in all of the existing
instances of that class. The fix is simple: use <tt class="docutils literal">require_once</tt> like you
should be doing in the first place when importing class definitions.</p>
</div>
<div class="section" id="the-payoff">
<h2>The Payoff</h2>
<p>At this point you&#8217;re probably very angry (and crosseyed) after having read
this exhausting tome, and yet we still haven&#8217;t explored <em>why</em> descriptors
are useful in the first place. Let&#8217;s go over a few benefits descriptors have
over other types of dynamic properties.</p>
<div class="section" id="easy-class-wide-caching">
<h3>Easy (Class-wide) Caching</h3>
<p>A classic use case for dynamic properties is value caching. If the value
of a property is expensive to calculate, it&#8217;s trivial to set up a dynamic
property that calculates the value once, store it locally in the object
instance and return the calculated value upon subsequent access. The down
side to simple caching (probably using the <tt class="docutils literal">__get</tt> method) is that the
cache is local to the object. That means in naive implementations the
value is calculated <em>and</em> stored each and every time you create an object
instance and try to access the dynamic property. This is unneccessary
when the calculated value will not differ between object instances. Of course
this problem only gets larger the more instances you actually create.</p>
<p>Descriptors open up the possibility of per-class caching, rather than
per-object caching since descriptor instances are defined on a class. Even
when accessed on different object instances, a single descriptor property
instance is handling all of the requests. This allows the descriptor to
be used as a class-wide cache with very little effort. Using a descriptor
in this way can be a lifesaver for intensive operations like database
access:</p>
<div class="highlight"><pre><span class="cp">&lt;?php</span>
    <span class="c">// Define a descriptor to manage retrieving the high score.</span>
    <span class="k">class</span> <span class="nc">HighScoreDescriptor</span>
    <span class="k">implements</span> <span class="nx">DescriptorGet</span><span class="p">,</span> <span class="nx">DescriptorSet</span>
    <span class="p">{</span>
        <span class="k">protected</span> <span class="nv">$_high_score</span><span class="p">;</span>

        <span class="c">// Returns the current high score.</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">getDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">,</span> <span class="nv">$owner</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c">// Check to see if we&#39;ve already retreived the high score.</span>
            <span class="k">if</span> <span class="p">(</span><span class="nb">is_null</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_high_score</span><span class="p">))</span> <span class="p">{</span>

                <span class="c">// Fetch and cache the high score from the db.</span>
                <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_high_score</span> <span class="o">=</span> <span class="nx">get_from_db</span><span class="p">(</span><span class="s1">&#39;high_score&#39;</span><span class="p">);</span>
            <span class="p">}</span>

            <span class="c">// Return the cached high score.</span>
            <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_high_score</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c">// Sets the current high score.</span>
        <span class="k">public</span> <span class="k">function</span> <span class="nf">setDescriptor</span><span class="p">(</span><span class="nv">$instance</span><span class="p">,</span> <span class="nv">$value</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="c">// Update the cached high score.</span>
            <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_high_score</span> <span class="o">=</span> <span class="nv">$value</span><span class="p">;</span>

            <span class="c">// Save the new high score to the db.</span>
            <span class="nx">update_db</span><span class="p">(</span><span class="s1">&#39;high_score&#39;</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_high_score</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c">// Define a scored game class.</span>
    <span class="k">class</span> <span class="nc">ScoredGame</span> <span class="k">extends</span> <span class="nx">Descriptable</span>
    <span class="p">{</span>
        <span class="c">// Define a class property to hold the high score descriptor.</span>
        <span class="k">public</span> <span class="k">static</span> <span class="nv">$high_score</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c">// Add a high score descriptor to the class.</span>
    <span class="nx">ScoredGame</span><span class="o">::</span><span class="nv">$high_score</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">HighScoreDescriptor</span><span class="p">();</span>

    <span class="c">// Create a game instance.</span>
    <span class="nv">$game</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ScoredGame</span><span class="p">();</span>

    <span class="c">// Retrieve the high score for the game (db queried).</span>
    <span class="k">echo</span> <span class="s1">&#39;High Score: &#39;</span> <span class="o">.</span> <span class="nv">$game</span><span class="o">-&gt;</span><span class="na">high_score</span><span class="p">;</span>

    <span class="c">// Set the high score, cheater.</span>
    <span class="nv">$game</span><span class="o">-&gt;</span><span class="na">high_score</span> <span class="o">=</span> <span class="m">31337</span><span class="p">;</span>

    <span class="c">// Retreive the high score again (db *not* queried).</span>
    <span class="k">echo</span> <span class="s1">&#39;New High Score: &#39;</span> <span class="o">.</span> <span class="nv">$game</span><span class="o">-&gt;</span><span class="na">high_score</span><span class="p">;</span>

    <span class="c">// Create another game instance.</span>
    <span class="nv">$another_game</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ScoredGame</span><span class="p">();</span>

    <span class="c">// Retrieve the high score for the new game (db *still* not queried).</span>
    <span class="k">echo</span> <span class="s1">&#39;Same High Score: &#39;</span> <span class="o">.</span> <span class="nv">$another_game</span><span class="o">-&gt;</span><span class="na">high_score</span><span class="p">;</span>
</pre></div>
</div>
<div class="section" id="memory-footprint">
<h3>Memory Footprint</h3>
<p>It&#8217;s obvious that caching values with descriptors saves you from executing
expensive operations multiple times, but what about the amount of memory
used? When you cache a variable locally per-instance, you&#8217;re storing that
information once for each instance that requests it. Descriptors use <em>far</em>
less memory in this case by only storing one copy. This idea can be extended
into realms other than simple caching, and will always reap the rewards of
leaner memory usage.</p>
</div>
<div class="section" id="cleaner-than-the-alternatives">
<h3>Cleaner Than the Alternatives</h3>
<p>Implementing a dynamic property with the <tt class="docutils literal">__get</tt> magic method requires
checking the name of the requested property to determine whether or not
it should be dynamically handled. Once that is determined, the <tt class="docutils literal">__get</tt>
method must then figure out what to actually do to calculate the dynamic
value. Innumerable approaches to this problem exist in the wild, and that&#8217;s
a problem in itself. There exists no standard way of determining what
should be called, when, and in which context with traditional dynamic
variables. Descriptors provide a standardized interface to these concepts,
and don&#8217;t require hacky switch statements with string checking, or a gazillion
<tt class="docutils literal">setSomething</tt> and <tt class="docutils literal">getSomething</tt> stop-gap methods. Descriptions are
cleaner and easier.</p>
</div>
<div class="section" id="true-object-oriented-design">
<h3>True Object Oriented Design</h3>
<p>Whether or not you&#8217;re an OOP lover (or even a hater) doesn&#8217;t matter. You have
to agree that this confusing middle ground in which PHP has been living
sucks. Descriptors promote the concept of PHP classes as objects themselves,
which is what they really are. This is a good move for the language if it
is even going to try to compete with the Pythons and Rubys of tomorrow. If
functionality is class-wide, move it up to the class logic level where it
belongs!</p>
</div>
</div>
<div class="section" id="conclusion">
<h2>Conclusion</h2>
<p>Descriptors are fun, they&#8217;re cool, and they&#8217;re just an all around good tool
to have in your toolbox. They&#8217;re the ideal solution to some very complex,
real world problems that have plagued developers for years. I long for the
day when PHP releases support for dynamic class properties as well so we
can get our hands on a drop-in replacement for Python&#8217;s descriptors. Until
then, the descriptor interfaces and descriptable object class we&#8217;ve just
designed will fit the bill just fine.</p>
</div>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2010/01/15/mimicking-python-descriptors-in-php/feed/rss2/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Leverage Twitter&#039;s Distraction Value to Stay Focused</title>
		<link>http://xdissent.com/2009/07/03/leverage-twitters-distraction-value-to-stay-focused/</link>
		<comments>http://xdissent.com/2009/07/03/leverage-twitters-distraction-value-to-stay-focused/#comments</comments>
		<pubDate>Fri, 03 Jul 2009 23:14:50 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[Productivity]]></category>
		<category><![CDATA[GTD]]></category>
		<category><![CDATA[Twitter]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=56</guid>
		<description><![CDATA[Twitter by nature is a stream of distractions. I personally never keep Twitter open and visible while I&#8217;m working because I know I&#8217;ll be too easily derailed from whatever I&#8217;m doing. If Life Hacker reported that a single email arriving in your inbox can cost you over a minute of mental recovery time, Twitter&#8217;s rapid-fire [...]]]></description>
			<content:encoded><![CDATA[<p><a class="reference external" href="http://twitter.com">Twitter</a> by nature is a stream of distractions. I personally never keep Twitter open and visible while I&#8217;m working because I know I&#8217;ll be too easily derailed from whatever I&#8217;m doing. If <a class="reference external" href="http://lifehacker.com">Life Hacker</a> <a class="reference external" href="http://lifehacker.com/5048463/single-email-interruption-recovery-time-over-a-minute">reported</a> that a single email arriving in your inbox can cost you over a minute of mental recovery time, Twitter&#8217;s rapid-fire updates could prove to be literally stupefying. <span id="more-56"></span> With that in mind, it&#8217;s probably a good idea to set aside a particular time of day to catch up on your tweets to prevent wasting too much time.</p>
<p>Once you&#8217;re committed to the idea that Twitter is a serious time waster, you can then use it to save a few more <a class="reference external" href="http://www.preciousmoments.com/">precious moments</a> of your day that would otherwise be lost to internet distractions. For example, instead of subscribing to other distracting feeds and sites in your normal feed reader, downgrade them to twitter feeds and conglomerate your slacking. Most such sites mirror their feed on Twitter anyway, so catch up on all your time-wasters all at once &#8211; when you have the time to waste. Your reader can then be used for more serious items that might actually require your attention. Plus you&#8217;ll have the added bonus of <em>missing</em> a few posts during the day due to the sheer traffic on Twitter, which means less time wasted period. And really, you wouldn&#8217;t really <em>miss</em> another lolcat, would you?</p>
<p>Want to try it out? Follow <a class="reference external" href="http://godbitesman.com">&#64;godbitesman</a>, <a class="reference external" href="http://thisisphotobomb.com">&#64;thisisphotobomb</a>, <a class="reference external" href="http://itemnotasdescribed.com">&#64;notasdescribed</a>, and of course, <a class="reference external" href="http://failblog.org">&#64;failblog</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2009/07/03/leverage-twitters-distraction-value-to-stay-focused/feed/rss2/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>uTidyLib Fix</title>
		<link>http://xdissent.com/2009/03/15/utidylib-fix/</link>
		<comments>http://xdissent.com/2009/03/15/utidylib-fix/#comments</comments>
		<pubDate>Sun, 15 Mar 2009 19:41:41 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[OSX]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=35</guid>
		<description><![CDATA[uTidyLib is a Python wrapper for the HTML Tidy Library. It&#8217;s a pretty handy library and is dead simple to use, but unfortunately it does not compile on Leopard out of the box. I wrote a quick patch to fix it, and will maintain a vendor branch on GitHub since development seems to have been [...]]]></description>
			<content:encoded><![CDATA[<p><a class="reference external" href="http://utidylib.berlios.de/">uTidyLib</a> is a Python wrapper for the <a class="reference external" href="http://tidy.sf.net/">HTML
Tidy Library</a>. It&#8217;s a pretty handy library and is dead
simple to use, but unfortunately it does not compile on Leopard out of the
box. I wrote a quick patch to fix it, and will maintain a vendor branch on <a class="reference external" href="http://github.com/xdissent/utidylib/">GitHub</a> since
development seems to have been abandoned years ago.</p>
<span id="more-35"></span>
<p>The other day I wrote a tiny Django middleware that alerts me if a response
contains markup errors (debug only of course). uTidyLib looked promising, but
I couldn&#8217;t get pip to successfully install the damn thing. My deployment
environment needs to be able to use pip so I had to patch <tt class="docutils literal">setup.py</tt> to be
setuptools friendly.</p>
<div class="highlight"><pre><span class="gp">xdissent@stewart:~$</span> mkvirtualenv tidy_fix
<span class="go">New python executable in tidy_fix/bin/python</span>
<span class="go">Installing setuptools............done.</span>
<span class="gp">(tidy_fix)xdissent@stewart:~$</span> easy_install pip
<span class="go">Searching for pip</span>
<span class="go">Reading http://pypi.python.org/simple/pip/</span>
<span class="go">[..]</span>
<span class="go">Finished processing dependencies for pip</span>
<span class="gp">(tidy_fix)xdissent@stewart:~$</span> pip install http://download.berlios.de/utidylib/uTidylib-0.2.zip
<span class="go">Downloading/unpacking http://download.berlios.de/utidylib/uTidylib-0.2.zip</span>
<span class="go">  Downloading uTidylib-0.2.zip</span>
<span class="go">  Running setup.py egg_info for package from http://download.berlios.de/utidylib/uTidylib-0.2.zip</span>
<span class="go">Installing collected packages: uTidylib</span>
<span class="go">  Running setup.py install for uTidylib</span>
<span class="go">    usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]</span>
<span class="go">       or: -c --help [cmd1 cmd2 ...]</span>
<span class="go">       or: -c --help-commands</span>
<span class="go">       or: -c cmd --help</span>

<span class="go">    error: option --single-version-externally-managed not recognized</span>
<span class="go">    Complete output from command /Users/xdissent/.virtualenvs/tidy_fix/bin/python -c &quot;import setuptools; __file__=&#39;/var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/pip-1c9Slw-build/setup.py&#39;; execfile(&#39;/var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/pip-1c9Slw-build/setup.py&#39;)&quot; install --single-version-externally-managed --record /var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/pip-SEIOXm-record/install-record.txt --install-headers /var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/lib/include:</span>
<span class="go">    usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]</span>

<span class="go">   or: -c --help [cmd1 cmd2 ...]</span>

<span class="go">   or: -c --help-commands</span>

<span class="go">   or: -c cmd --help</span>



<span class="go">error: option --single-version-externally-managed not recognized</span>

<span class="go">----------------------------------------</span>
<span class="go">Command /Users/xdissent/.virtualenvs/tidy_fix/bin/python -c &quot;import setuptools; __file__=&#39;/var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/pip-1c9Slw-build/setup.py&#39;; execfile(&#39;/var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/pip-1c9Slw-build/setup.py&#39;)&quot; install --single-version-externally-managed --record /var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/pip-SEIOXm-record/install-record.txt --install-headers /var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/lib/include failed with error code 1</span>
<span class="go">Storing complete log in ./pip-log.txt</span>
</pre></div>
<p>The message <tt class="docutils literal">error: option <span class="pre">--single-version-externally-managed</span> not recognized</tt>
means <tt class="docutils literal">setup.py</tt> is subclassing <tt class="docutils literal">distutils.command.install.install</tt>, which
sucks because we want to use <tt class="docutils literal">setuptools</tt>. The fix is easy though: just
change the import statement in <tt class="docutils literal">setup.py</tt> to
<tt class="docutils literal">from setuptools.command.install import install</tt>. I got it all patched up and
into my vendor SVN repository, but ran across another problem when trying to
use pip.</p>
<div class="highlight"><pre><span class="gp">(tidy_fix)xdissent@stewart:~$</span> pip install http://code.hartzogcreative.com/svn/vendor/utidylib/current
<span class="go">Downloading/unpacking http://code.hartzogcreative.com/svn/vendor/utidylib/current</span>
<span class="go">  Downloading current</span>
<span class="go">  Checking out svn repository http://code.hartzogcreative.com/svn/vendor/utidylib/current to /var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/pip-mOglX5-build</span>
<span class="go">  Running setup.py egg_info for package from http://code.hartzogcreative.com/svn/vendor/utidylib/current</span>
<span class="go">Installing collected packages: uTidylib</span>
<span class="go">  Running setup.py install for uTidylib</span>
<span class="go">    *** This library requires that you have two libraries ***</span>
<span class="go">    ***           installed: ctypes and libtidy.          ***</span>
<span class="go">    ***   Please make sure they are installed correctly   ***</span>
<span class="go">    ***              before reporting a bug.              ***</span>
<span class="go">    *** See:                                              ***</span>
<span class="go">    ***  http://starship.python.net/crew/theller/ctypes/  ***</span>
<span class="go">    ***         and http://tidy.sourceforge.net           ***</span>
<span class="go">    *** (or consult your vendor documentation for binary  ***</span>
<span class="go">    ***                    packages.)                     ***</span>
<span class="go">Successfully installed uTidylib</span>
</pre></div>
<p>Things look normal here, but uTidyLib thinks differently.</p>
<div class="highlight"><pre><span class="go">Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13)</span>
<span class="go">[GCC 4.0.1 (Apple Inc. build 5465)] on darwin</span>
<span class="go">Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">tidy</span>
<span class="gt">Traceback (most recent call last):</span>
  File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n-Identifier">&lt;module&gt;</span>
  File <span class="nb">&quot;/Users/xdissent/.virtualenvs/tidy_fix/lib/python2.5/site-packages/tidy/__init__.py&quot;</span>, line <span class="m">43</span>, in <span class="n-Identifier">&lt;module&gt;</span>
    <span class="kn">from</span> <span class="nn">tidy.lib</span> <span class="kn">import</span> <span class="n">parse</span><span class="p">,</span> <span class="n">parseString</span>
  File <span class="nb">&quot;/Users/xdissent/.virtualenvs/tidy_fix/lib/python2.5/site-packages/tidy/lib.py&quot;</span>, line <span class="m">33</span>, in <span class="n-Identifier">&lt;module&gt;</span>
    <span class="k">raise</span> <span class="ne">OSError</span><span class="p">(</span><span class="s">&quot;Couldn&#39;t find libtidy, please make sure it is installed.&quot;</span><span class="p">)</span>
<span class="nc">OSError</span>: <span class="n-Identifier">Couldn&#39;t find libtidy, please make sure it is installed.</span>
<span class="gp">&gt;&gt;&gt; </span><span class="o">^</span><span class="n">D</span>
</pre></div>
<p>uTidyLib looks for libtidy in a few predefined locations, and Leopard&#8217;s
libtidy is <em>not</em> in one of those places. The author doesn&#8217;t take into
consideration the possibility of a library having the extension <tt class="docutils literal">dylib</tt>
so I patched my vendor repository to correctly find libtidy. The source
is available at
<a class="reference external" href="http://github.com/xdissent/utidylib/">http://github.com/xdissent/utidylib/</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2009/03/15/utidylib-fix/feed/rss2/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>TextMate reStructuredText Bundle</title>
		<link>http://xdissent.com/2009/02/28/textmate-restructuredtext-bundle/</link>
		<comments>http://xdissent.com/2009/02/28/textmate-restructuredtext-bundle/#comments</comments>
		<pubDate>Sun, 01 Mar 2009 05:43:51 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[reStructuredText]]></category>
		<category><![CDATA[TextMate]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=90</guid>
		<description><![CDATA[TextMate is great for general editing of most text formats. By default, reStructuredText is not one of them. This document describes how to fix TextMate so that it is a better fit for the tools I work with. We want reStructuredText rendered previews and HTML export functions in TextMate. Update Monday July 6, 2009: TextMate&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>TextMate is great for general editing of most text formats. By default,
<a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> is not one
of them. This document describes how to fix TextMate so that it is a better
fit for the tools I work with. We want reStructuredText rendered previews
and HTML export functions in TextMate.</p>
<span id="more-90"></span>
<p><strong>Update</strong> Monday July 6, 2009: TextMate&#8217;s bundle repository has moved. This
article has been updated to reflect this change.</p>
<p>Requires:
<a class="reference external" href="http://subversion.tigris.org/">Subversion</a>
<a class="reference external" href="http://pypi.python.org/pypi/virtualenv">virtualenv</a>
<a class="reference external" href="http://www.doughellmann.com/projects/virtualenvwrapper/">virtualenvwrapper</a>
<a class="reference external" href="http://macromates.com/">TextMate</a></p>
<div class="section" id="create-a-virtualenv-for-textmate">
<h2>Create a virtualenv for TextMate</h2>
<p>We&#8217;re gonna need to install a few Python packages in order to make this work.
That means it&#8217;s time to fire up virtualenv and make an isolated Python
environment for our fix.</p>
<div class="highlight"><pre><span class="gp">stewart:~ xdissent$</span> mkvirtualenv TextMate
<span class="go">New python executable in TextMate/bin/python</span>
<span class="go">Installing setuptools............done.</span>
</pre></div>
</div>
<div class="section" id="install-pip">
<h2>Install pip</h2>
<p>Virtualenv&#8217;s come pre-loaded with <tt class="docutils literal">easy_install</tt>, but we&#8217;re going to be
changing a couple of files in some packages so we should use <a class="reference external" href="http://pypi.python.org/pypi/pip">pip</a> to make that easier on ourselves.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> easy_install pip
<span class="go">Searching for pip</span>
<span class="go">[...]</span>
<span class="go">Installed /Users/xdissent/.virtualenvs/TextMate/lib/python2.5/site-packages/pip-0.3.1-py2.5.egg</span>
<span class="go">Processing dependencies for pip</span>
<span class="go">Finished processing dependencies for pip</span>
</pre></div>
</div>
<div class="section" id="install-docutils">
<h2>Install Docutils</h2>
<p><a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> is the package that provides
the reStructuredText parsers, so we&#8217;ll definitely be needing <em>that</em>.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> pip install docutils
<span class="go">Downloading/unpacking docutils</span>
<span class="go">  Downloading docutils-0.5.tar.gz (1.3Mb): 1.3Mb downloaded</span>
<span class="go">[...]</span>
<span class="go">Successfully installed docutils</span>
</pre></div>
</div>
<div class="section" id="install-restructuredtext-textmate-bundle">
<h2>Install reStructuredText TextMate Bundle</h2>
<p>TextMate has a bundle repository at
<tt class="docutils literal"><span class="pre">http://svn.textmate.org/trunk/Bundles</span></tt> with a reStructuredText
bundle already built. We need to download that to a place where TextMate will
actually find the bundle and load it. Typically, thats
<tt class="docutils literal">~/Library/Application Support/TextMate/Bundles</tt>.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> mkdir -p ~/Library/Application<span class="se">\ </span>Support/TextMate/Bundles
<span class="gp">(TextMate)stewart:~ xdissent$</span> <span class="nb">cd</span> ~/Library/Application<span class="se">\ </span>Support/TextMate/Bundles
<span class="gp">(TextMate)stewart:Bundles xdissent$</span> svn co http://svn.textmate.org/trunk/Bundles/reStructuredText.tmbundle
<span class="go">A    reStructuredText.tmbundle/Commands</span>
<span class="go">[...]</span>
<span class="go">A    reStructuredText.tmbundle/Syntaxes/reStructuredText.plist</span>
<span class="go">Checked out revision 10740.</span>
</pre></div>
</div>
<div class="section" id="make-restructuredtext-bundle-use-virtualenv">
<h2>Make reStructuredText Bundle use virtualenv</h2>
<p>Now that we have the bundle, we need to let it know that it needs to be
called from inside our virtualenv. The Preview command uses <tt class="docutils literal">rst2html.py</tt>
to generate the preview, and luckily provides a way to customize the path
to <tt class="docutils literal">rst2html.py</tt>. All we need to do is define <tt class="docutils literal">TM_RST2HTML</tt> variable
with the correct path in our <tt class="docutils literal">.bash_profile</tt> and TextMate will find the
right one.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> <span class="nb">echo export </span><span class="nv">TM_RST2HTML</span><span class="o">=</span><span class="sb">`</span>which rst2html.py<span class="sb">`</span> &gt;&gt; ~/.bash_profile
<span class="gp">(TextMate)stewart:~ xdissent$</span> <span class="nb">source</span> ~/.bash_profile
</pre></div>
</div>
<div class="section" id="reload-textmate-bundles">
<h2>Reload TextMate Bundles</h2>
<p>If you&#8217;re running TextMate at this point, you can either use the &quot;Bundles&quot;
menu or the following command to reload the bundles and install our new
reStructuredText bundle.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> osascript -e <span class="s1">&#39;tell app &quot;TextMate&quot; to reload bundles&#39;</span>
</pre></div>
</div>
<div class="section" id="bask-in-the-glory">
<h2>Bask in the glory</h2>
<p>We&#8217;re done. Now we can preview or export reStructuredText easily. Open a
reStructuredText file and go wild. It&#8217;s incredibly convenient to preview
documents as you go, and saving them to HTML makes it simple to blog from
TextMate.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> mate ~/Documents/TextMate<span class="se">\ </span>Fixes.rst
</pre></div>
</div>
<div class="section" id="colour-my-world">
<h2>Colour My World</h2>
<p>If you&#8217;re like me, you tend to include source code or console sessions in
your articles. <a class="reference external" href="http://pygments.org/">Pygments</a> is a Python package that
can add syntax highlighting to these snippets, which comes in handy for
previewing in TextMate. Installation is slightly more advanced, but well
worth setting up.</p>
</div>
<div class="section" id="install-mercurial">
<h2>Install Mercurial</h2>
<p>We want to hack on our Pygments package, so we&#8217;re going to want to install
it in editable mode. Plus, the latest stable release doesn&#8217;t have the
<tt class="docutils literal">BashSessionLexer</tt> that I use quite a bit. Pygments uses <a class="reference external" href="http://www.selenic.com/mercurial/">Mercurial</a> for source control, but I don&#8217;t have
a system-wide copy of <tt class="docutils literal">hg</tt>, so I needed to grab one real fast.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> pip install mercurial
<span class="go">Downloading/unpacking mercurial</span>
<span class="go">  Downloading mercurial-1.1.2.tar.gz (952Kb): 952Kb downloaded</span>
<span class="go">[...]</span>
<span class="go">Successfully installed mercurial</span>
</pre></div>
</div>
<div class="section" id="enable-the-fetch-command">
<h2>Enable the <tt class="docutils literal">fetch</tt> command</h2>
<p>When pip installs an editable package from a Mercurial repository, it uses
the <tt class="docutils literal">fetch</tt> command with <tt class="docutils literal">hg</tt>. We need to enable it in our <tt class="docutils literal"><span class="pre">~/.hgrc</span></tt>
file because it&#8217;s disabled by default.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> cat &gt;&gt; ~/.hgrc <span class="s">&lt;&lt;EOF</span>
<span class="gp">&gt;</span><span class="s"> [extensions]</span>
<span class="gp">&gt;</span><span class="s"> fetch =</span>
<span class="gp">&gt;</span><span class="s"> EOF</span>
</pre></div>
</div>
<div class="section" id="get-pygments">
<h2>Get Pygments</h2>
<p>Now we can install Pygments from Mercurial. Pip will download the source and
unpack it into <tt class="docutils literal">$VIRTUAL_ENV/src</tt> before finally creating an <tt class="docutils literal"><span class="pre">.egg-link</span></tt>
file to point our interpreter to the source code.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> pip install -e hg+http://dev.pocoo.org/hg/pygments-main@799#egg<span class="o">=</span>Pygments
<span class="go">Checking out Pygments from hg+http://dev.pocoo.org/hg/pygments-main@799#egg=Pygments checkout from hg+http://dev.pocoo.org/hg/pygments-main@799#egg=Pygments</span>
<span class="go">[...]</span>
<span class="go">    Installed /Users/xdissent/.virtualenvs/TextMate/src/pygments</span>
<span class="go">Successfully installed Pygments</span>
</pre></div>
</div>
<div class="section" id="install-the-restructuredtext-sourcecode-directive">
<h2>Install the reStructuredText <tt class="docutils literal">sourcecode</tt> directive</h2>
<p>Activating syntax highlighting in a document requires the <tt class="docutils literal">sourcecode</tt>
directive which highlights the following block of code. We can switch out
different Pygments lexers using an option after the directive:</p>
<pre class="literal-block">
.. sourcecode:: python

    from hartzog import VERSION
    print 'Running Hartzog %%s' %% VERSION
</pre>
<p>The rendered version looks something like this:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">hartzog</span> <span class="kn">import</span> <span class="n">VERSION</span>
<span class="k">print</span> <span class="s">&#39;Running Hartzog </span><span class="si">%%s</span><span class="s">&#39;</span> <span class="o">%%</span> <span class="n">VERSION</span>
</pre></div>
<p>The <tt class="docutils literal">sourcecode</tt> directive isn&#8217;t installed automatically, so we&#8217;ll need to
add it ourselves. Directives are simply python functions that must be
registered with docutils before any parsing is done. That means we can&#8217;t use
<tt class="docutils literal">rst2html.py</tt> with highlighting yet because the directive won&#8217;t be loaded.
Pygments does come with the directive already in its source tree, but we&#8217;re
going to have to let virtualenv know where it is, and then modify <tt class="docutils literal">rst2html.py</tt>
to register the directive before it begins parsing.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> add2virtualenv <span class="nv">$VIRTUAL_ENV</span>/src/pygments/external
<span class="gp">(TextMate)stewart:~ xdissent$</span> sed -i <span class="s1">&#39;&#39;</span> <span class="s1">&#39;/^publish_cmdline/i\</span>
<span class="gp">&gt;</span><span class="s1"> __import__(&quot;rst-directive&quot;)</span>
<span class="gp">&gt;</span><span class="s1"> &#39;</span> <span class="sb">`</span>which rst2html.py<span class="sb">`</span>
</pre></div>
</div>
<div class="section" id="colorize-the-preview-command">
<h2>Colorize the Preview command</h2>
<p>What good is all this work if we can&#8217;t see the damn colors? As it stands now,
pygments is marking up our code to be colorized, but the CSS to actually make
the colors show up isn&#8217;t being loaded into our preview window. We have to
enhance the Preview command in the reStructuredText bundle to include Pygments
stylesheets. You can control which Pygments theme will be used by defining
a <tt class="docutils literal">TM_PYGMENTIZE_STYLE</tt> environment variable. I&#8217;ve created a patch that we
can apply to update our bundle.</p>
<div class="highlight"><pre><span class="gh">Index: Commands/Preview.plist</span>
<span class="gh">===================================================================</span>
<span class="gd">--- Commands/Preview.plist  (revision 10740)</span>
<span class="gi">+++ Commands/Preview.plist  (working copy)</span>
<span class="gu">@@ -25,6 +25,13 @@</span>
            border: 1px #888 solid;
            padding: 0 1em;
    }&#39; &amp;gt; $css
<span class="gi">+    if [[ -f &quot;$TM_PYGMENTIZE&quot; ]]</span>
<span class="gi">+    then</span>
<span class="gi">+        if [[ &quot;$TM_PYGMENTIZE_STYLE&quot; = &quot;&quot; ]]</span>
<span class="gi">+            then TM_PYGMENTIZE_STYLE=default</span>
<span class="gi">+        fi</span>
<span class="gi">+        $TM_PYGMENTIZE -S $TM_PYGMENTIZE_STYLE -f html &amp;gt;&amp;gt; $css</span>
<span class="gi">+    fi</span>
    stylesheet=$css
    tmpCreated=&quot;yes&quot;
 fi
</pre></div>
<p>We want to apply the patch into the root of the bundle with <tt class="docutils literal">patch</tt>.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> patch -d ~/Library/Application<span class="se">\ </span>Support/TextMate/Bundles/reStructuredText.tmbundle -p0 -i rst_bundle.diff
<span class="go">patching file Commands/Preview.plist</span>
</pre></div>
</div>
<div class="section" id="tell-textmate-how-to-use-pygmentize">
<h2>Tell TextMate how to use <tt class="docutils literal">pygmentize</tt></h2>
<p>The patch for the Preview command requires an environment variable called
<tt class="docutils literal">TM_PYGMENTIZE</tt> that contains the path to the <tt class="docutils literal">pygmentize</tt> script. We
need to add that to our <tt class="docutils literal">.bash_profile</tt> along with any custom style we want
to use for highlighting.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> <span class="nb">echo export </span><span class="nv">TM_PYGMENTIZE</span><span class="o">=</span><span class="nv">$VIRTUAL_ENV</span>/bin/pygmentize &gt;&gt; ~/.bash_profile
<span class="gp">(TextMate)stewart:~ xdissent$</span> <span class="nb">echo export </span><span class="nv">TM_PYGMENTIZE_STYLE</span><span class="o">=</span>fruity &gt;&gt; ~/.bash_profile
<span class="gp">(TextMate)stewart:~ xdissent$</span> <span class="nb">source</span> ~/.bash_profile
</pre></div>
</div>
<div class="section" id="inform-textmate-of-the-updated-preview-command">
<h2>Inform TextMate of the updated Preview command</h2>
<p>Just like before, we want to nudge TextMate so we can check out the new
preview command without restarting.</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> osascript -e <span class="s1">&#39;tell app &quot;TextMate&quot; to reload bundles&#39;</span>
</pre></div>
</div>
<div class="section" id="patch-pygments-for-virtualenv">
<h2>Patch Pygments for virtualenv</h2>
<p>Everything should be working perfectly now in full color, but I went one step
further in my setup to tweak the <tt class="docutils literal">BashSessionLexer</tt> in Pygments. Because I
use virtualenv all the time, my bash prompt usually has a virtual env name
at the beginning, surrounded by parentheses. If I were to use the <tt class="docutils literal">console</tt>
option for <tt class="docutils literal">sourcecode</tt> with a virtualenv active in the session, the lexer
wouldn&#8217;t recognize my prompt and it wouldn&#8217;t get highlighted. I put together
a quick patch for Pygments that correctly highlights virtualenvwrapper style
prompts.</p>
<div class="highlight"><pre><span class="gh">diff -r 9fe544c7818b pygments/lexers/other.py</span>
<span class="gd">--- a/pygments/lexers/other.py      Sat Feb 14 13:27:41 2009 +0100</span>
<span class="gi">+++ b/pygments/lexers/other.py      Sat Feb 28 22:33:39 2009 -0600</span>
<span class="gu">@@ -416,7 +416,7 @@</span>

         for match in line_re.finditer(text):
             line = match.group()
<span class="gd">-            m = re.match(r&#39;^((?:|sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)?|\[\S+[@:]&#39;</span>
<span class="gi">+            m = re.match(r&#39;^((?:\([A-Za-z_0-9-]+\))?(?:|sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)?|\[\S+[@:]&#39;</span>
                          r&#39;[^\n]+\].+)[$#%%])(.*\n?)&#39;, line)
             if m:
                 # To support output lexers (say diff output), the output
</pre></div>
<p>Mercurial patches are weird, so be sure to use the <tt class="docutils literal"><span class="pre">-p1</span></tt> option when applying:</p>
<div class="highlight"><pre><span class="gp">(TextMate)stewart:~ xdissent$</span> patch -d <span class="nv">$VIRTUAL_ENV</span>/src/pygments -p1 -i pygments.diff
<span class="go">patching file pygments/lexers/other.py</span>
</pre></div>
</div>
<div class="section" id="chump-and-a-hoagie">
<h2>Chump and a Hoagie</h2>
<p>That&#8217;s it. Go <a class="reference external" href="http://www.youtube.com/watch?v=4UW2jW1-avA">buck wild</a> on
some reStructuredText in TextMate dawg. You&#8217;ve earned it.</p>
</div>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2009/02/28/textmate-restructuredtext-bundle/feed/rss2/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Safari 4 Beta and Coda Tabs Joy</title>
		<link>http://xdissent.com/2009/02/28/safari-4-beta-and-coda-tabs-joy/</link>
		<comments>http://xdissent.com/2009/02/28/safari-4-beta-and-coda-tabs-joy/#comments</comments>
		<pubDate>Sat, 28 Feb 2009 18:32:50 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[Productivity]]></category>
		<category><![CDATA[Coda]]></category>
		<category><![CDATA[OSX]]></category>
		<category><![CDATA[Safari]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=26</guid>
		<description><![CDATA[So the Safari beta 4 has landed and opinions are all over the place about the new features. The most consistent target for criticism has been the changes to the tab interface. Although the differences were a little disconcerting at first, I&#8217;ve come to love the new tabs and I even took a cue from [...]]]></description>
			<content:encoded><![CDATA[<p>So the <a class="reference external" href="http://www.apple.com/safari/">Safari</a> beta 4 has <a class="reference external" href="http://www.apple.com/safari/download/">landed</a> and opinions are all <strong>over</strong> the place
about the new features. The most consistent target for criticism has
been the changes to the tab interface. Although the differences were a
little disconcerting at first, I&#8217;ve come to love the new tabs and I even
took a cue from Safari and decided to improve one of my most precious
tools: <a class="reference external" href="http://panic.com/">Panic</a>&#8216;s <a class="reference external" href="http://panic.com/coda/">Coda</a>.</p>
<span id="more-26"></span>
<div class="section" id="beta-blues">
<h2>Beta Blues</h2>
<p>I&#8217;ve been kind of bummed reading all the negative responses to Safari&#8217;s
update because I&#8217;m so impressed by the damn thing. It seems like most of
the gripers are claiming Apple ripped off <a class="reference external" href="http://google.com/chrome/">Chrome</a>&#8216;s &quot;Tabs-on-Top&quot; layout
and &quot;Top Sites&quot; feature or that Safari 4 isn&#8217;t as many times faster than
every competitor as Apple claims. So&#8230; when Apple notices good design
they&#8217;re not supposed to act on improving their product? Should Apple have
included tabs <em>at all</em> since they certainly were not the first to develop
the idea?. Nevermind the fact that Chrome wouldn&#8217;t even <strong>exist</strong> if
Apple hadn&#8217;t shared WebKit with the world, since Chrome uses it exclusively
for rendering. So what&#8217;s the problem with a few &quot;good as new&quot; bad-ass
features, dudes? And if the absence of a handful of outdated Safari 3
behaviors is driving you nuts, you can <a class="reference external" href="http://www.macosxhints.com/article.php?story=20090225065338303">easily bring them back</a>.</p>
</div>
<div class="section" id="silver-bindings">
<h2>Silver Bindings</h2>
<p>The one cool thing that really put a smile on my face when trying out
Safari 4 was discovering the new tab-switching keyboard shortcuts. It seems
like every app in the world has <a class="reference external" href="http://homepage.mac.com/bradster/iarchitect/tabs.htm">tabs these days</a>, and moving around between
them is the most common (yet also most application-specific) task you will
perform. Since the dawn of tab, developers haven&#8217;t been able to agree on a
global shortcut to switch tabs and my mouse has suffered great wear as the
battle rages on. I would guess this problem is a <a class="reference external" href="http://en.wikipedia.org/wiki/Not_Invented_Here">NIH</a> related artifact from
a time when tabs were the killer new feature to have, and no application had
yet proven to be the ubiquitous tab-interaction authority. I&#8217;ve always found
Safari 3&#8242;s built-in tab navigation key combo about as awkward as this
sentence. Firefox certainly paved the way with some very intuitive shortcuts,
but I only use Firefox for UI testing with Firebug, so rarely do I find myself
treading water in a sea of tabs. Also, Safari 4&#8242;s new developer tools have
pretty much guaranteed a serious decline in my Firebug time anyway.</p>
<p>With Safari 4, a great tab usage barrier has been shattered as the Firefox
<tt class="docutils literal"><span class="pre">control-tab</span></tt> shortcut replaces Safari&#8217;s original loathsome key combo.
I was so inspired by this move towards consistency (or additional Apple
rip-off attempt as one could claim) that I decided to add it to another
tab-defying application that I love so dearly: Coda.</p>
</div>
<div class="section" id="the-leopard-way">
<h2>The Leopard Way</h2>
<p>Mac OS X provides a System Preferences pane that allows you to add, edit and
remove global and application-specific keyboard shortcuts. It&#8217;s a handy
little tool found under the &quot;Keyboard &amp; Mouse&quot; preferences that somehow I&#8217;ve
only used once&#8230; to change tab behaviors. A while back, I actually had the
(impressively moronic) idea to change Coda&#8217;s tab shortcuts to <tt class="docutils literal"><span class="pre">command-tab</span></tt>
so switching between tabs would be &quot;as easy as switching applications.&quot; Now
<em>that&#8217;s</em> taking Coda&#8217;s &quot;one window development&quot; tagline to the eXtR3m#.</p>
<p>Luckily for me, Leopard doesn&#8217;t allow you to use the <tt class="docutils literal">tab</tt> key when
defining new shortcuts, so I settled for <tt class="docutils literal"><span class="pre">command-~</span></tt>, which is the standard
&quot;switch windows within application&quot; key command. &quot;Mostly one window
development&quot; turned out to be a real drag though, because I frequently work
on two or three Coda Sites at once, and found myself with no way to switch between
Sites. Bummer.</p>
<p>It was looking like Apple was going to deny me my tab nirvana.</p>
</div>
<div class="section" id="the-working-way">
<h2>The Working Way</h2>
<p>After poking around on Google, I discovered System Preferences stores
application shortcuts in your preferences <tt class="docutils literal">plist</tt>. It uses a dictionary
value named <tt class="docutils literal">NSUserKeyEquivalents</tt> to map the commands to the shortcuts
and can be changed easily with <tt class="docutils literal">defaults write</tt>. I also found that you
can use the <tt class="docutils literal">^</tt> character to represent the <tt class="docutils literal">control</tt> key when defining
your shortcuts, but what about <tt class="docutils literal">tab</tt>? Brute force experimentation landed me
in tab heaven on my first try. Here is how to make <tt class="docutils literal"><span class="pre">control-tab</span></tt> and
<tt class="docutils literal"><span class="pre">control-shift-tab</span></tt> map to &quot;Select Next Tab&quot; and &quot;Select Previous Tab&quot;
respectively in Coda:</p>
<div class="highlight"><pre><span class="gp">stewart:~ xdissent$</span> defaults write com.panic.Coda NSUserKeyEquivalents -dict-add <span class="s2">&quot;Select Next Tab&quot;</span> <span class="s1">&#39;^\t&#39;</span>
<span class="gp">stewart:~ xdissent$</span> defaults write com.panic.Coda NSUserKeyEquivalents -dict-add <span class="s2">&quot;Select Previous Tab&quot;</span> <span class="s1">&#39;^$\t&#39;</span>
</pre></div>
</div>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2009/02/28/safari-4-beta-and-coda-tabs-joy/feed/rss2/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Fixing Paver</title>
		<link>http://xdissent.com/2009/02/24/fixing-paver/</link>
		<comments>http://xdissent.com/2009/02/24/fixing-paver/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 17:23:04 +0000</pubDate>
		<dc:creator>xdissent</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Paver]]></category>

		<guid isPermaLink="false">http://xdissent.com/?p=1</guid>
		<description><![CDATA[I&#8217;ve been a fan of Paver since the first time I read about it. It gives me all of the control I want with just enough of an implied structure to keep things sane and easy. The problem was, I couldn&#8217;t get the latest version, which had many shiny new features. You know how I [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been a fan of <a class="reference external" href="http://www.blueskyonmars.com/projects/paver/">Paver</a> since the first time I read about it. It gives
me all of the control I want with just enough of an implied structure to keep
things sane and easy. The problem was, I couldn&#8217;t get the latest version,
which had many shiny new features. You know how I feel about <em>shiny</em>, so
I obviously needed to find a way to run Paver&#8217;s trunk, lest I invalidate
my work by targeting the almost-irrelevant current version.</p>
<span id="more-8"></span>
<p>Paver is such a flexible tool, it can present a chicken-and-egg packaging
dilemma by relying heavily on <em>itself</em> to build&#8230; <strong>itself</strong>. The packaged
1.0a1 was having trouble installing via <tt class="docutils literal">easy_install</tt> and <tt class="docutils literal">pip</tt> so I
had to figure out a way to build my own package. Unfortunately Paver&#8217;s
trunk has neither <tt class="docutils literal"><span class="pre">paver-minilib.zip</span></tt> nor <tt class="docutils literal">setup.py</tt> which means to
get things going, you have to use the current version of Paver. Of course,
the new alpha is so different, that the current release can&#8217;t handle it
and the build fails. I finally figured out a way to get a clean trunk
checkout installed and set out to squash the bug that got <a class="reference external" href="http://www.blueskyonmars.com/2009/01/29/paver-10a1-recalled/">1.0a1 recalled</a>
in the first place.</p>
<p>After poking around for a while, I believe paver&#8217;s option parsing methods
are confusing <tt class="docutils literal">distutils</tt> commands. It has to do with the way that options
are translated from the command line to Paver&#8217;s <tt class="docutils literal">options</tt>. I updated
<a class="reference external" href="http://code.google.com/p/paver/issues/detail?id=14">a relevant issue</a> on <a class="reference external" href="http://code.google.com/p/paver/">Paver&#8217;s project page</a> and attached a patch that fixes the
problem and allows Paver to build itself cleanly.</p>
<div class="section" id="the-fix">
<h2>The Fix</h2>
<p>Using <a class="reference external" href="http://pypi.python.org/pypi/virtualenv">virtualenv</a>, <a class="reference external" href="http://pypi.python.org/pypi/virtualenvwrapper">virtualenvwrapper</a>, and Paver SVN <a class="reference external" href="http://code.google.com/p/paver/source/detail?r=37">r37</a>, I set up a clean
build environment for paver like so:</p>
<div class="highlight"><pre><span class="gp">stewart:~ xdissent$</span> <span class="nb">cd</span> ~/Code
<span class="gp">stewart:Code xdissent$</span> svn co http://paver.googlecode.com/svn/trunk/ paver-trunk
<span class="go">A    paver-trunk/.bzrignore</span>
<span class="go">A    paver-trunk/LICENSE.txt</span>
<span class="go">[...]</span>
<span class="go">A    paver-trunk/paver/doctools.py</span>
<span class="go">A    paver-trunk/paver/virtual.py</span>
<span class="go">U   paver-trunk</span>
<span class="go">Checked out revision 37.</span>
<span class="gp">stewart:Code xdissent$</span> <span class="nb">cd </span>paver-trunk
<span class="gp">stewart:paver-trunk xdissent$</span> mkvirtualenv paver-tmp
<span class="go">New python executable in paver-tmp/bin/python</span>
<span class="go">Installing setuptools............done.</span>
<span class="gp">(paver-tmp)stewart:paver-trunk xdissent$</span> add2virtualenv <span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>
<span class="gp">(paver-tmp)stewart:paver-trunk xdissent$</span> python distutils_scripts/paver minilib generate_setup
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; paver.misctasks.minilib</span>
<span class="go">Generate paver-minilib.zip</span>
<span class="go">---&gt; paver.misctasks.generate_setup</span>
<span class="go">Write setup.py</span>
<span class="gp">(paver-tmp)stewart:paver-trunk xdissent$</span> mkvirtualenv paver-trunk
<span class="go">New python executable in paver-trunk/bin/python</span>
<span class="go">Installing setuptools............done.</span>
<span class="gp">(paver-trunk)stewart:paver-trunk xdissent$</span> rmvirtualenv paver-tmp
<span class="gp">(paver-trunk)stewart:paver-trunk xdissent$</span> python setup.py develop
<span class="go">python setup.py develop</span>
<span class="go">Paver 1.0a1</span>
<span class="go">warning: no files found matching &#39;*&#39; under directory &#39;paver/docs&#39;</span>
</pre></div>
<p>At this point I have Paver 1.0a1 installed in <em>development</em> mode, so the
new <tt class="docutils literal">paver</tt> binary is on my path <em>AND</em> changes I make to <tt class="docutils literal"><span class="pre">~/Code/paver-trunk</span></tt>
are effective immediately.</p>
<p>To demonstrate the problem, I created <tt class="docutils literal">options_test.py</tt>. Here&#8217;s a simple
task with some options:</p>
<div class="highlight"><pre><span class="kn">from</span> <span class="nn">paver.easy</span> <span class="kn">import</span> <span class="o">*</span>

<span class="n">options</span><span class="p">(</span>
   <span class="n">show_opts</span><span class="o">=</span><span class="n">Bunch</span><span class="p">(</span>
       <span class="n">foo_bar</span><span class="o">=</span><span class="s">&#39;default foobar&#39;</span><span class="p">,</span>
       <span class="n">no_foo</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
   <span class="p">),</span>
<span class="p">)</span>

<span class="nd">@task</span>
<span class="nd">@cmdopts</span><span class="p">([(</span><span class="s">&quot;foo-bar=&quot;</span><span class="p">,</span> <span class="s">&quot;f&quot;</span><span class="p">,</span> <span class="s">&quot;Foo bar value&quot;</span><span class="p">),</span>
         <span class="p">(</span><span class="s">&quot;no-foo&quot;</span><span class="p">,</span> <span class="s">&quot;n&quot;</span><span class="p">,</span> <span class="s">&quot;Foo negation&quot;</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">show_opts</span><span class="p">(</span><span class="n">options</span><span class="p">):</span>
   <span class="k">print</span> <span class="n">options</span><span class="o">.</span><span class="n">show_opts</span>
</pre></div>
<p>Since we have Paver 1.0a1, we can run the task with the <tt class="docutils literal"><span class="pre">-f</span></tt> option.
(Somebody said <em>somethin</em> about shiny new features.)</p>
<div class="highlight"><pre><span class="gp">(paver-trunk)stewart:tmp xdissent$</span> paver -f options_test.py show_opts
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; pavement.show_opts</span>
<span class="go">Bunch(foo_bar=&#39;default foobar&#39;, no_foo=False)</span>
<span class="gp">(paver-trunk)stewart:tmp xdissent$</span> paver -f options_test.py show_opts --foo-bar<span class="o">=</span><span class="s1">&#39;new foobar&#39;</span> --no-foo
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; pavement.show_opts</span>
<span class="go">Bunch(foo-bar=&#39;new foobar&#39;, foo_bar=&#39;default foobar&#39;, no-foo=True, no_foo=False)</span>
</pre></div>
<p>It becomes clear that <tt class="docutils literal">cmdopts</tt> definitions will never override default
options for this task. We can&#8217;t use <tt class="docutils literal"><span class="pre">foo-bar</span></tt> as an option name in our
Bunch, nor could we access the runtime value using
<tt class="docutils literal"><span class="pre">options.show_opts.foo-bar</span></tt>. It turns out distutils and setuptools
already noticed this problem and convert hyphens to underscores when
determining the storage variable&#8217;s name for an option. I&#8217;ve attached a
small patch for Paver SVN r37 that automatically makes this conversion.</p>
<div class="highlight"><pre><span class="gp">(paver-trunk)stewart:tmp xdissent$</span> patch -d ~/Code/paver-trunk -p0 -i options_fix.diff
<span class="go">patching file paver/setuputils.py</span>
<span class="go">patching file paver/tasks.py</span>
<span class="gp">(paver-trunk)stewart:tmp xdissent$</span> paver -f options_test.py show_opts
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; pavement.show_opts</span>
<span class="go">Bunch(foo_bar=&#39;default foobar&#39;, no_foo=False)</span>
<span class="gp">(paver-trunk)stewart:tmp xdissent$</span> paver -f options_test.py show_opts --foo-bar<span class="o">=</span><span class="s1">&#39;new foobar&#39;</span> --no-foo
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; pavement.show_opts</span>
<span class="go">Bunch(foo_bar=&#39;new foobar&#39;, no_foo=True)</span>
</pre></div>
<p>That&#8217;s more like it. I further tested the patch to confirm that distutil
commands receive their options correctly and that the easy_install problem
has been solved.</p>
<div class="highlight"><pre><span class="gp">(paver-trunk)stewart:paver-trunk xdissent$</span> paver minilib
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; paver.misctasks.minilib</span>
<span class="go">Generate paver-minilib.zip</span>
<span class="gp">(paver-trunk)stewart:paver-trunk xdissent$</span> mkvirtualenv paver-tmp
<span class="go">New python executable in paver-tmp/bin/python</span>
<span class="go">Installing setuptools............done.</span>
<span class="gp">(paver-tmp)stewart:paver-trunk xdissent$</span> python setup.py sdist
<span class="go">Paver 1.0a1</span>
<span class="go">warning: no files found matching &#39;*&#39; under directory &#39;paver/docs&#39;</span>
<span class="gp">(paver-tmp)stewart:paver-trunk xdissent$</span> easy_install dist/Paver-1.0a1.tar.gz
<span class="go">Processing Paver-1.0a1.tar.gz</span>
<span class="go">Running Paver-1.0a1/setup.py -q bdist_egg --dist-dir /var/folders/LU/LUPONriBGYepwd3i9FaBB++++TI/-Tmp-/easy_install-GnYR4C/Paver-1.0a1/egg-dist-tmp-iTfLMZ</span>
<span class="go">Paver 1.0a1</span>
<span class="go">running bdist_egg</span>
<span class="go">running egg_info</span>
<span class="go">[...]</span>
<span class="go">Adding Paver 1.0a1 to easy-install.pth file</span>
<span class="go">Installing paver script to /Users/xdissent/.virtualenvs/paver-tmp/bin</span>

<span class="go">Installed /Users/xdissent/.virtualenvs/paver-tmp/lib/python2.5/site-packages/Paver-1.0a1-py2.5.egg</span>
<span class="go">Processing dependencies for Paver==1.0a1</span>
<span class="go">Finished processing dependencies for Paver==1.0a1</span>
<span class="gp">(paver-tmp)stewart:paver-trunk xdissent$</span> which paver
<span class="go">/Users/xdissent/.virtualenvs/paver-tmp/bin/paver</span>
<span class="gp">(paver-tmp)stewart:paver-trunk xdissent$</span> <span class="nb">cd </span>tmp
<span class="gp">(paver-tmp)stewart:tmp xdissent$</span> paver -f options_test.py show_opts
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; pavement.show_opts</span>
<span class="go">Bunch(foo_bar=&#39;default foobar&#39;, no_foo=False)</span>
<span class="gp">(paver-tmp)stewart:tmp xdissent$</span> paver -f options_test.py show_opts --foo-bar<span class="o">=</span><span class="s1">&#39;it works&#39;</span> --no-foo
<span class="go">Paver 1.0a1</span>
<span class="go">---&gt; pavement.show_opts</span>
<span class="go">Bunch(foo_bar=&#39;it works&#39;, no_foo=True)</span>
</pre></div>
<p>Hopefully 1.0 will come out soon, but until then you can <a class="reference external" href="http://paver.googlecode.com/issues/attachment?aid=-3172846000840253404&amp;name=options_fix.diff">download the patch</a>
and apply it from within <tt class="docutils literal">trunk</tt>:</p>
<div class="highlight"><pre><span class="gp">$</span> patch -p0 -i options_fix.diff
</pre></div>
</div>]]></content:encoded>
			<wfw:commentRss>http://xdissent.com/2009/02/24/fixing-paver/feed/rss2/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

