<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en" xml:base="http://nefariousdesigns.co.uk/wp-atom.php">
	<title type="text">Nefarious Designs</title>
	<subtitle type="text">Nefarious Designs is the web development agency and blog of Tim Huegdon, a web developer with over 5 years experience in the industry.</subtitle>

	<updated>2009-10-25T21:04:00Z</updated>
	<generator uri="http://wordpress.org/" version="2.7.1">WordPress</generator>

	<link rel="alternate" type="text/html" href="http://nefariousdesigns.co.uk" />
	<id>http://nefariousdesigns.co.uk/feed/atom/</id>
	

			<logo>http://nefariousdesigns.co.uk/favicon.ico</logo><link rel="self" href="http://feeds.feedburner.com/nefariousdesigns" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[Virtualised Development Environments on a Mac]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/bPBSMoLXK-E/" />
		<id>http://nefariousdesigns.co.uk/?p=258</id>
		<updated>2009-09-06T14:06:42Z</updated>
		<published>2009-09-06T13:23:37Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="Linux" /><category scheme="http://nefariousdesigns.co.uk" term="Mac OS X" /><category scheme="http://nefariousdesigns.co.uk" term="Server" /><category scheme="http://nefariousdesigns.co.uk" term="Site" /><category scheme="http://nefariousdesigns.co.uk" term="Testing" /><category scheme="http://nefariousdesigns.co.uk" term="Tutorials" /><category scheme="http://nefariousdesigns.co.uk" term="UNIX" /><category scheme="http://nefariousdesigns.co.uk" term="Virtualisation" /><category scheme="http://nefariousdesigns.co.uk" term="Web" />		<summary type="html"><![CDATA[Recently, I bought myself a brand new MacBook Pro, which gave me the perfect opportunity to clean up my development environment. Since I&#8217;ve started doing all of my development on virtual machines, I began thinking about my development workflow: In theory, I should be able to model the perfect server environment virtually.
I wasn&#8217;t sure what [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2009/09/virtualised-development-environments-on-a-mac/"><![CDATA[<p>Recently, I bought myself a brand new MacBook Pro, which gave me the perfect opportunity to clean up my development environment. Since I&#8217;ve started doing all of my development on virtual machines, I began thinking about my development workflow: In theory, I should be able to model the perfect server environment virtually.</p>
<p>I wasn&#8217;t sure what environment I wanted to end up with, but I had a good idea what basic virtual machines I wanted as a starting layout.</p>
<p><span id="more-258"></span></p>
<h2>The Proposed Environment</h2>
<p>A basic, well structured development environment should include:</p>
<ul>
<li>
<p><strong>Development Server</strong> &#8212; Where all development takes place. This will probably have more detailed logs, development dependencies, and a high likelihood of being broken at any given moment in time. Bugs that occur here are likely to be frequent.</p>
</li>
<li>
<p><strong>Staging Server</strong> &#8212; A duplicate of the production server that allows testing of build scripts, packages etc. before they are pushed to production. Bugs that occur here are likely to be less frequent and flagged by specific testing.</p>
</li>
<li>
<p><strong>Production Server</strong> &#8212; The live environment. By the time the code reaches this server, it should have gone through development testing <em>and</em> staging testing. This means bugs should be at a bare minimum.</p>
</li>
</ul>
<p>If bugs arise on either the staging server or the production server, the fixes should be made on the development server and pushed to staging and then production. No code changes should happen on staging or production.</p>
<p>We could, of course, add more stages between development and live (a quality assurance (QA) server, for instance), and we could even branch the environment to allow for a continuous integration (CI) server (the product of an automated build, and including automated testing; see <a href="http://en.wikipedia.org/wiki/Continuous_integration">Wikipedia&#8217;s Continuous Integration page</a> for more information).</p>
<p>In my specific case, the environment should be structured thus:</p>
<ul>
<li>
<p><strong>Development Server</strong> &#8212; The development virtual machine I&#8217;ve been using for the past few months. This VM shares key folders with Mac OS X so that I get the pleasure of using Mac based tools for development (see: TextMate).</p>
</li>
<li>
<p><strong>Staging Server</strong> &#8212; A duplicate of the production server (or as close to as possible) in VM format. This should be the target for my build scripts, and where I run integration tests.</p>
</li>
<li>
<p><strong>Production Server</strong> &#8212; My live web server. Only functioning, tested code should ever be pushed here.</p>
</li>
</ul>
<p>So, rather than developing with just the <em>one</em> virtual machine, it&#8217;s clear I should be moving forward with <em>two</em> virtual machines (at least) &#8212; one as the <em>development server</em>, and one as the <em>staging server</em>.</p>
<h2>Linked Clones</h2>
<p>Once I&#8217;ve built a good master virtual machine as a source &#8212; and set it all up as a basic server, including all the dependencies and packages I want &#8212; I could create duplicates for each server. The drawback to that method is the considerable amount of disk space the independent VMs would take up.</p>
<p>The second option would be to branch snapshots on the master, setting up one branch as the development server, and another as the staging server. However, this would mean that I would have no way of running the two servers simultaneously, which would be a terrible trudge when attempting to test a build.</p>
<p>The best solution would be to build <em>linked clones</em>. A linked clone is a copy of a virtual machine that shares virtual disks with the parent virtual machine in an ongoing manner. This conserves disk space, and allows multiple virtual machines to use the same software installation. It also means we&#8217;ll be able to run the two VMs simultaneously. However, VMWare Fusion (the Mac version of VMWare) doesn&#8217;t actually allow this functionality through the interface; so it has to be done manually.</p>
<p>When you take a snapshot of a VM, the original virtual disk becomes read-only and a new <a href="http://en.wikipedia.org/wiki/Copy-on-write">Copy-On-Write (COW)</a> disk is created. To create a linked clone manually, we&#8217;re going to set up a virtual machine, then create new ones using that as the base disk. We then snapshot  the new VMs so they never try to modify the base disk.</p>
<p><strong>Note:</strong> I pretty much followed the &#8220;<a href="http://communities.vmware.com/docs/DOC-5611">HOWTO: Manual Linked Cloning in VMWare Fusion</a>&#8221; article on the VMWare Communites site. I&#8217;ve recreated the steps below to allow illustration of my particular implementation, and to hopefully save you flicking backwards and forwards between articles.</p>
<h2>Building the environment</h2>
<p>The first thing we need to do is create a master VM. This will be our base disk, and the root of all evil… Uh… I mean… The root of all awesome.</p>
<h3>Create the master VM</h3>
<p>In my particular instance I&#8217;m creating web servers, so I&#8217;m going to build an Ubuntu VM and base the clones on that. To do this, I&#8217;d recommend following <a href="http://intranation.com/entries/2009/03/development-virtual-machines-os-x-using-vmware-and/">Brad Wright&#8217;s excellent Ubuntu VM build tutorial</a>, right up to the point where you&#8217;ve finished installing VMWare Tools &#8212; we don&#8217;t want to start sharing folders on our base build because we&#8217;ll do that on the development server VM only.</p>
<p>The next step is to set up all the common packages, dependencies, configurations, and applications on your base VM so that you have the minimum to set up on your clones (and so they&#8217;re as close to one another as possible at the outset; after all, that&#8217;s part of the reason we&#8217;re doing this).</p>
<p>In my case, I set up the following:</p>
<ul>
<li>Apache</li>
<li>PHP</li>
<li>MySQL</li>
<li>Python</li>
<li>Django</li>
<li>CouchDB</li>
<li>Twisted</li>
</ul>
<p>I also made sure I&#8217;d configured them all as I wanted them &#8212; including default site layout for Apache &#8212; and included interdependencies like MySQL-Python.</p>
<p>Once this is complete, we&#8217;ve got a suitable master VM for our linked clones.</p>
<p>Finally, make sure you back up the <samp>.vmwarevm</samp> package for your master VM. We&#8217;re going to be editing the package contents, and deleting a bunch of stuff; plus if anything gets hosed in the future, you&#8217;ll be able to restore the master and start over. I&#8217;d recommend creating an archive of the <samp>.vmware</samp> package and storing it somewhere safe.</p>
<h3>Prepare the master VM for cloning</h3>
<p>The only thing we want to keep from the master is the virtual disk, since the rest of the VM is inconsequential to our linked clones. So, once you&#8217;ve definitely made a back-up of the full VM, let&#8217;s change directory to where our VMs are stored, copy out the <samp>.vmdk</samp> files (virtual machine disk), and delete the master bundle:</p>
<pre><samp>$ <kbd>cd /Users/timbo/vms</kbd>
$ <kbd>ls</kbd>
master.vmwarevm
$ <kbd>mkdir master</kbd>
$ <kbd>ls</kbd>
master  master.vmwarevm
$ <kbd>mv master.vmwarevm/*.vmdk master/</kbd>
$ <kbd>rm -f master.vmwarevm</kbd>
$ <kbd>ls master</kbd>
master-s001.vmdk  master-s002.vmdk  master-s003.vmdk  master-s004.vmdk  master-s005.vmdk  master-s006.vmdk  master.vmdk</samp></pre>
<p>Next we need to update the the virtual disk to reflect our changed location and to remove it&#8217;s UUID (Universally Unique Identifier). To do that, we need to edit the root <samp>.vmdk</samp> file:</p>
<pre><samp>$ <kbd>cd master</kbd>
$ <kbd>mate master.vmdk</kbd></samp></pre>
<p>I&#8217;m using TextMate as my editor of choice, but you could easily use vi, vim, nano, BBEdit etc.</p>
<p>You should see a file that looks something like this:</p>
<pre><samp># Disk DescriptorFile
version=1
encoding="UTF-8"
CID=96100eeb
parentCID=ffffffff
createType="twoGbMaxExtentSparse"

# Extent description
RW 4192256 SPARSE "master-s001.vmdk"
RW 4192256 SPARSE "master-s002.vmdk"
RW 4192256 SPARSE "master-s003.vmdk"
RW 4192256 SPARSE "master-s004.vmdk"
RW 4192256 SPARSE "master-s005.vmdk"
RW 10240 SPARSE "master-s006.vmdk"

# The Disk Data Base
#DDB

ddb.uuid = "60 00 C2 99 ab ce aa 92-d3 3e 55 a9 30 e7 c6 dd"
ddb.toolsVersion = "7462"
ddb.adapterType = "lsilogic"
ddb.geometry.sectors = "63"
ddb.geometry.heads = "255"
ddb.geometry.cylinders = "1305"
ddb.virtualHWVersion = "7"</samp></pre>
<p>We need to fix the paths of our <samp>.vmdk</samp> files so that they will work from other locations. Obviously, for the best results, we should probably use absolute paths so that they work from anywhere. I updated mine like this:</p>
<pre><samp># Extent description
RW 4192256 SPARSE "/Users/timbo/vms/master/master-s001.vmdk"
RW 4192256 SPARSE "/Users/timbo/vms/master/master-s002.vmdk"
RW 4192256 SPARSE "/Users/timbo/vms/master/master-s003.vmdk"
RW 4192256 SPARSE "/Users/timbo/vms/master/master-s004.vmdk"
RW 4192256 SPARSE "/Users/timbo/vms/master/master-s005.vmdk"
RW 10240 SPARSE "/Users/timbo/vms/master/master-s006.vmdk"</samp></pre>
<p>The next thing you should do is remove the UUID line, either by commenting it with a #, or by deleting it entirely:</p>
<pre><samp>ddb.uuid = "60 00 C2 99 ab ce aa 92-d3 3e 55 a9 30 e7 c6 dd"</samp></pre>
<p>Once these changes have been made, save the file and exit back to the command line.</p>
<p>The final thing we should do is write protect these files &#8212; make sure you update your path appropriately:</p>
<pre><samp>chmod a-w /Users/timbo/vms/master/*.vmdk</samp></pre>
<p>Our master is now totally prepared for cloning, and can be used repeatedly. In my instance, this means I can now clone it twice; once for the development VM, and once for the staging VM (since it&#8217;s no longer a full VM by itself).</p>
<h3>Create a clone</h3>
<p>To create a clone, we need to create the VM in VMWare Fusion. Once you&#8217;re back in the VMWare GUI, do the following:</p>
<ol>
<li>
<p>Push cmd + N, or select File > New from the menu, to create a new virtual machine.</p>
</li>
<li>
<p>Click &#8220;Continue without disk&#8221;.</p>
</li>
<li>
<p>Select &#8220;Create a custom virtual machine&#8221; and click &#8220;Continue&#8221;.</p>
</li>
<li>
<p>Select the appropriate guest OS type (in my case, Linux/Ubuntu) and click &#8220;Continue&#8221;.</p>
</li>
<li>
<p>Click &#8220;Customize Settings&#8221;.</p>
</li>
<li>
<p>Choose a name and location for your clone VM. I chose to call mine &#8220;child&#8221; and saved it in my <samp>~/vms</samp> directory.</p>
</li>
<li>
<p>You should now be looking at the settings of your new VM. Set it up with the same basic settings as you did for the master (256MB RAM, printers disabled), but leave the HDD alone because…</p>
</li>
<li>
<p>Edit the HDD settings and delete the disk device by clicking the &#8220;-&#8221; when the device is selected in the list.</p>
</li>
<li>
<p>Go back to your terminal and delete the <samp>.vmdk</samp> files in the new VM package:</p>
<pre><samp>$ <kbd>cd /Users/timbo/vms/child.vmwarevm</kbd>
$ <kbd>rm -f *.vmdk</kbd></samp></pre>
</li>
<li>
<p>Now copy the root <samp>.vmdk</samp> file from your master (the one we edited the paths in earlier) to your clone package:</p>
<pre><samp>$ <kbd>cd /Users/timbo/vms</kbd>
$ <kbd>cp master/master.vmdk child.vmwarevm/</kbd></samp></pre>
<p>We need a copy because VMWare will create a <samp>.lck</samp> file at the same level as this file. With no copy, all the clones would be attempting to use the same <samp>.lck</samp> file simultaneously, which would prevent them running in parallel.</li>
<li>
<p>Back in the Hard Disk settings pane, press the + button to add a new HDD device.</p>
</li>
<li>
<p>Select &#8220;Choose existing disk&#8230;&#8221; in the &#8220;File name&#8221; drop-down, and choose the copied metadata file (the one in <samp>child.vmwarevm</samp>, not the one in the <samp>master</samp> folder) and uncheck the checkbox &#8212; we&#8217;re happy with the file where it is, we don&#8217;t want to copy or move it since we&#8217;ve already copied it manually (VMWare copies <em>all</em> the <samp>.vmdk</samp> files over, and that wouldn&#8217;t save us any disk space at all).</p>
</li>
<li>
<p>Finally, create a snapshot of your new VM. This tells Fusion not to try to write to the original disk. I named mine &#8220;Base&#8221; with the comment &#8220;Don&#8217;t delete!&#8221;. Even if you run without the snapshot, because of the read-only permissions on the <samp>.vmdk</samp> files you shouldn&#8217;t be able to change the original virtual disk, but the clone virtual machine (i.e. not the master) will not be happy.</p>
</li>
<li>
<p>Run the clone.</p>
</li>
<li>
<p>Since this is a clone of your master, log in using the same user(s).</p>
</li>
<li>
<p>Run the interface configurator:</p>
<pre><samp>$ <kbd>ifconfig</kbd></samp></pre>
<p>If the <samp>eth0</samp> interface is showing, skip ahead to step 19, otherwise…</p>
</li>
<li>
<p>To fix this problem, we just need to truncate the udev persistent network rules file:</p>
<pre><samp>$ <kbd>cat /dev/null > /etc/udev/rules.d/70-persistent-net.rules</kbd></samp></pre>
</li>
<li>
<p>Reboot the VM to regenerate the rules:</p>
<pre><samp>$ <kbd>sudo reboot</kbd></samp></pre>
<p>Now wait for your VM to reboot and log back in again.</p>
</li>
<li>
<p>Run the interface configurator:</p>
<pre><samp>$ <kbd>ifconfig</kbd></samp></pre>
<p>Make a note of the IP address of the <samp>eth0</samp> interface, alongside <samp>inet addr:</samp> since this is the new address for your clone.</p>
</li>
<li>
<p>Finally, update the hostname of your clone VM by editing and saving the <samp>/etc/hostname</samp> file:</p>
<pre><samp>$ <kbd>sudo vim /etc/hostname</kbd></samp></pre>
</li>
<li>
<p>Restart the hostname service:</p>
<pre><samp>$ <kbd>sudo /etc/init.d/hostname.sh start</kbd></samp></pre>
</li>
<li>
<p>Use your clone VM as normal.</p>
</li>
</ol>
<p>To create another clone VM for your staging environment, simply repeat the steps above (in the &#8220;Create a clone&#8221; sub-section).</p>
<h3>Playing nice</h3>
<p>You might like to run your VMs in &#8220;Headless Mode&#8221; (see &#8220;<a href="http://communities.vmware.com/docs/DOC-1201">A Power User&#8217;s Guide to VMWare Fusion</a>&#8220;), which will prevent you having to open VMWare every time you want to run your VMs. You can activate this option in VMWare itself by running the following command at the terminal of your Mac:</p>
<pre><samp>$ <kbd>defaults write com.vmware.fusion fluxCapacitor -bool YES</kbd></samp></pre>
<p>Then you&#8217;ll be able to select &#8220;Enter Headless&#8221; under the View menu of your VM in VMWare. Once the VM is running in Headless mode, you can quit VMWare and the VM itself won&#8217;t shut down (until your reboot your Mac).</p>
<p>If you <em>do</em> reboot your Mac for any reason, you can boot up the VM using the following command:</p>
<pre><samp>$ <kbd>/Library/Application\ Support/VMware\ Fusion/vmrun start /Users/timbo/vms/child.vmwarevm/child.vmx nogui</kbd></samp></pre>
<p>If you&#8217;re going to do lots of VM stuff from the command line, I&#8217;d recommend updating your <samp>$PATH</samp> variable to include the VMWare Fusion directory by adding the following line to your <samp>.bash_profile</samp>:</p>
<pre><samp>export PATH="$PATH:/Library/Application Support/VMware Fusion"</samp></pre>
<p>Now you&#8217;ll be able to run <samp>vmrun</samp> without the directory prefix:</p>
<pre><samp>$ <kbd>vmrun start /Users/timbo/vms/child.vmwarevm/child.vmx nogui</kbd></samp></pre>
<h3>Shared folders</h3>
<p>Now that we have a fully functioning development VM, you can continue to set up shared folders between the Ubuntu VM and Mac OS.</p>
<p>I&#8217;m not going to walk through this again; it&#8217;s covered (including a common read/write issue that can occur) in <a href="http://intranation.com/entries/2009/03/development-virtual-machines-os-x-using-vmware-and/">Brad Wright&#8217;s excellent Ubuntu VM build tutorial</a> which I previously mentioned.</p>
<p>Once your shared folders are set up and functioning correctly, you can do all your development with your favourite Mac-based tools, but running it on our Ubuntu virtualised development server.</p>
<p>Incidentally, you can also add new shared folders using <samp>vmrun</samp>:</p>
<pre><samp>$ <kbd>vmrun addSharedFolder /Users/timbo/vms/child.vmwarevm/child.vmx myfolder /Users/timbo/projects/myfolder</kbd></samp></pre>
<p>I love <samp>vmrun</samp>.</p>
<h2>Summary</h2>
<p>I now have the environment I set out to achieve; and with the aid of Git, and my own build scripts, I can easily develop, test, and release code in much the same way I would with a live server environment. What&#8217;s more, I have infinitely more control over my environments than I ever had with MAMP, or MacPorts installed instances of Apache, PHP, MySQL etc.</p>
<p>I guess it&#8217;s safe to say that there&#8217;s really no going back from here.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2009/09/virtualised-development-environments-on-a-mac/#comments" thr:count="5" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2009/09/virtualised-development-environments-on-a-mac/feed/atom/" thr:count="5" />
		<thr:total>5</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2009/09/virtualised-development-environments-on-a-mac/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[CouchDB&#8217;s RESTful API]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/3xDpa5B4CZM/" />
		<id>http://nefariousdesigns.co.uk/?p=241</id>
		<updated>2009-08-05T07:41:26Z</updated>
		<published>2009-08-01T10:06:43Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="API" /><category scheme="http://nefariousdesigns.co.uk" term="Apache" /><category scheme="http://nefariousdesigns.co.uk" term="CouchDB" /><category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="HTTP" /><category scheme="http://nefariousdesigns.co.uk" term="Linux" /><category scheme="http://nefariousdesigns.co.uk" term="REST" /><category scheme="http://nefariousdesigns.co.uk" term="UNIX" />		<summary type="html"><![CDATA[I&#8217;ve recently been playing with Apache CouchDB; a distributed, fault-tolerant and schema-free document-oriented database. CouchDB supplies a RESTful API for the manipulation of data within it. This is a great demonstration of REST at work, so I thought I&#8217;d illustrate its usage in a quick post.
Let&#8217;s get the technical details out of the way first: [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2009/08/couchdbs-restful-api/"><![CDATA[<p>I&#8217;ve recently been playing with Apache CouchDB; a distributed, fault-tolerant and schema-free document-oriented database. CouchDB supplies a RESTful API for the manipulation of data within it. This is a great demonstration of REST at work, so I thought I&#8217;d illustrate its usage in a quick post.</p>
<p>Let&#8217;s get the technical details out of the way first: CouchDB makes use of HTTP verbs &#8212; GET, PUT, POST, and DELETE &#8212; and handles requests and responses in JSON only. This means it is entirely possible to utilise CouchDB using JavaScript alone. For the purpose of these examples, however, I&#8217;ll be using cURL, which is easier to follow along with via the command line.</p>
<p><span id="more-241"></span></p>
<p>My CouchDB server is running on my dev Ubuntu VM, on the default port of 5984. I&#8217;ve also set up a &#8220;couchhost&#8221; alias to the VM in my hosts file, to make my requests more readable.</p>
<h2>Creating databases</h2>
<p>The first thing we should do is create a database within our CouchDB server. To do that, we use the PUT verb:</p>
<pre><samp>$ <kbd>curl -X PUT http://couchhost:5984/fu</kbd>
{"ok":true}</samp></pre>
<p>In the command above, we specified the database name in the resource URI &#8212; which is the correct usage of PUT &#8212; and received a JSON object response. As you can see, that JSON object contains a single field; the boolean &#8220;ok&#8221;, with a value of true.</p>
<p>We can obtain a list of all the databases on our CouchDB server, by sending it a GET request to the _all_dbs resource:</p>
<pre><samp>$ <kbd>curl -X GET http://couchhost:5984/_all_dbs</kbd>
["fu"]</samp></pre>
<p>Here we receive an array response containing all our database ids. In this case, we only have our &#8220;fu&#8221; database.</p>
<p>Let&#8217;s try adding another:</p>
<pre><samp>$ <kbd>curl -X PUT http://couchhost:5984/bar</kbd>
{"ok":true}</samp></pre>
<p>Since we&#8217;ve received an ok response, let&#8217;s list all the databases again:</p>
<pre><samp>$ <kbd>curl -X GET http://couchhost:5984/_all_dbs</kbd>
["fu","bar"]</samp></pre>
<p>Now we can see multiple values in the array response.</p>
<p>We can obtain information about a specific database by sending a GET request to its URI:</p>
<pre><samp>$ <kbd>curl -X GET http://couchhost:5984/fu</kbd>
{"db_name":"fu","doc_count":0,"doc_del_count":0,"update_seq":0,"compact_running":false,"disk_size":4096}</samp></pre>
<p>Here we can see, amongst other things, the number of documents in the database &#8212; none &#8212; and how much diskspace the database is taking up &#8212; 4k.</p>
<h2>CouchDB documents</h2>
<p>A CouchDB document is simply a JSON object with some associated metadata. This is an example document (lifted from the CouchDB documentation):</p>
<pre><code>{
    "_id":"discussion_tables",
    "_rev":"D1C946B7",
    "Sunrise":true,
    "Sunset":false,
    "FullHours":[1,2,3,4,5,6,7,8,9,10],
    "Activities":[
        {
            "Name":"Football",
            "Duration":2,
            "DurationUnit":"Hours"
        },
        {
            "Name":"Breakfast",
            "Duration":40,
            "DurationUnit":"Minutes",
            "Attendees":[
                "Jan",
                "Damien",
                "Laura",
                "Gwendolyn",
                "Roseanna"
            ]
        }
    ]
}</code></pre>
<p>As you can see, a CouchDB document can handle multiple types of data, including arrays and nested objects. By default the structure is flat; in this particular case, the &#8220;Activities&#8221; attribute is structure imposed by the user.</p>
<p>It&#8217;s also worth noting that any top-level fields with a name that starts with an underscore prefix are reserved.</p>
<h3>Adding documents</h3>
<p>So let&#8217;s define a document for one of our databases:</p>
<pre><code>{
    "FirstName":"Isabella",
    "FamilyName":"Huegdon",
    "Age":2,
    "Likes":[
        "noise",
        "animals",
        "chocolate",
        "kicking Milly",
        "throwing tantrums"
    ]
}</code></pre>
<p>This is a fairly straight forward document that includes several different types of data. To add it to our database, let&#8217;s use an HTTP POST request:</p>
<pre><samp>$ <kbd>curl -X POST http://couchhost:5984/fu \
-H 'Content-Type: application/json' \
-d '{"FirstName":"Isabella","FamilyName":"Huegdon","Age":2,"Likes":["noise","animals","chocolate","kicking Milly","throwing tantrums"]}'</kbd>
{"ok":true,"id":"30d53965f5aec18391c258faa66921c1","rev":"2348720638"}</samp></pre>
<p>Because we&#8217;ve used POST, CouchDB has automagically assigned our document an identifier. You can see this in the id attribute of the response object. But what if we want to specify our own id? Simple; we use HTTP PUT instead.</p>
<p>Let&#8217;s define another document:</p>
<pre><code>{
    "FirstName":"Amélie",
    "FamilyName":"Huegdon",
    "Age":2,
    "Likes":[
        "noise",
        "animals",
        "chocolate",
        "hitting Izzy",
        "running and screaming"
    ]
}</code></pre>
<p>And now let&#8217;s add that document using HTTP PUT:</p>
<pre><samp>$ <kbd>curl -X PUT http://couchhost:5984/fu/milly-huegdon \
-H 'Content-Type: application/json' \
-d '{"FirstName":"Amélie","FamilyName":"Huegdon","Age":2,"Likes":["noise","animals","chocolate","hitting Izzy","running and screaming"]}'</kbd>
{"ok":true,"id":"milly-huegdon","rev":"3414255320"}</samp></pre>
<p>This time, we can see our specified id in the id attribute of the response object.</p>
<p>Now let&#8217;s have a look what documents we&#8217;ve got in the database, using the _all_docs resource, beneath our database:</p>
<pre><samp>$ <kbd>curl -X GET http://couchhost:5984/fu/_all_docs</kbd>
{
    "total_rows":2,
    "offset":0,
    "rows":[
        {
            "id":"30d53965f5aec18391c258faa66921c1",
            "key":"30d53965f5aec18391c258faa66921c1",
            "value":{"rev":"2348720638"}
        },
        {
            "id":"milly-huegdon",
            "key":"milly-huegdon",
            "value":{"rev":"3414255320"}
        }
    ]
}</samp></pre>
<p>Note: I&#8217;ve styled the response here for ease of reading; your cURL output will lack indenting but will contain the same information.</p>
<p>Here we can see, from the total_rows attribute, that there are 2 documents in our database; and we can see the metadata for those documents in the rows attribute array.</p>
<p>To obtain the actual document, we just need to send an HTTP GET request to the document&#8217;s URI:</p>
<pre><samp>$ <kbd>curl -X GET http://couchhost:5984/fu/milly-huegdon</kbd>
{
    "_id":"milly-huegdon",
    "_rev":"3414255320",
    "FirstName":"Am\u00e9lie",
    "FamilyName":"Huegdon",
    "Age":2,
    "Likes":[
        "noise",
        "animals",
        "chocolate",
        "hitting Izzy",
        "running and screaming"
    ]
}</samp></pre>
<p>Once again, I&#8217;ve styled the response somewhat for readability. In this instance, you can also see that CouchDB can handle unicode characters (since JSON should always be encoded as UTF-8) from the FirstName attribute.</p>
<h2>Deleting items</h2>
<p>Deletion of documents and databases from our CouchDB server couldn&#8217;t be simpler. All we need to do is send an HTTP DELETE request.</p>
<p>To remove the bar database we created earlier, we send the following request:</p>
<pre><samp>$ <kbd>curl -X DELETE http://couchhost:5984/bar</kbd>
{"ok":true}</samp></pre>
<h2>Summary</h2>
<p>You are now able to create and delete items from your CouchDB database. This is all you&#8217;re going to need if you just want to use CouchDB as a persistent document store; however, if you want to query the data in your databases, you&#8217;re going to need to make use of CouchDB Views. I&#8217;ll cover those in a later blog post.</p>
<p>I&#8217;m going to continue to play with CouchDB over the next week or so and will continue to blog about it (with the hope of using the blog posts as the basis for a TechTalk at Yahoo! Europe &#8212; slides possibly to follow on SlideShare). For now, you should be able to see that CouchDB is a wonderfully easy thing to use, thanks to its entirely RESTful API and its use of JSON as a request and response format.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2009/08/couchdbs-restful-api/#comments" thr:count="1" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2009/08/couchdbs-restful-api/feed/atom/" thr:count="1" />
		<thr:total>1</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2009/08/couchdbs-restful-api/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[CouchDB on an Ubuntu development VM]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/HBTeSpbwRtI/" />
		<id>http://nefariousdesigns.co.uk/?p=231</id>
		<updated>2009-07-29T22:02:47Z</updated>
		<published>2009-07-29T16:41:41Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Apache" /><category scheme="http://nefariousdesigns.co.uk" term="CouchDB" /><category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="HTTP" /><category scheme="http://nefariousdesigns.co.uk" term="Linux" /><category scheme="http://nefariousdesigns.co.uk" term="REST" /><category scheme="http://nefariousdesigns.co.uk" term="Server" /><category scheme="http://nefariousdesigns.co.uk" term="Tutorials" /><category scheme="http://nefariousdesigns.co.uk" term="UNIX" /><category scheme="http://nefariousdesigns.co.uk" term="Virtualisation" />		<summary type="html"><![CDATA[I&#8217;ve recently set-up CouchDB on the Ubuntu 8.10 (Intrepid Ibex) virtual machine I use for development. This was a relatively pain-free experience and I thought I&#8217;d outline what I did here for the benefit of others (and my future self).

Firstly, I decided to install using the aptitude package, rather than from source. If you&#8217;re interested [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2009/07/couchdb-on-an-ubuntu-development-vm/"><![CDATA[<p>I&#8217;ve recently set-up <a href="http://couchdb.apache.org/">CouchDB</a> on the <a href="http://nefariousdesigns.co.uk/archive/2009/04/a-better-os-x-development-environment/">Ubuntu 8.10 (Intrepid Ibex) virtual machine I use for development</a>. This was a relatively pain-free experience and I thought I&#8217;d outline what I did here for the benefit of others (and my future self).</p>
<p><span id="more-231"></span></p>
<p>Firstly, I decided to install using the aptitude package, rather than from source. If you&#8217;re interested in building the most recent version, I&#8217;d recommend taking a look at <a href="http://isolani.co.uk/blog/web/InstallingCouchdbOnJeos804">Isofarro&#8217;s handy &#8220;Installing CouchDB on JeOS 8.04&#8243; post</a>.</p>
<p>To begin, install the package:</p>
<pre><samp>$ <kbd>sudo apt-get install couchdb</kbd></samp></pre>
<p>Once the package has downloaded and installed, you should check the status to see if it&#8217;s running correctly:</p>
<pre><samp>$ <kbd>sudo /etc/init.d/couchdb status</kbd></samp></pre>
<p>If you see the following output, then CouchDB is running successfully:</p>
<pre><samp>Apache CouchDB is running as process 32651. Time to relax.</samp></pre>
<p>If you don&#8217;t see that output, try starting CouchDB manually. I needed to do this:</p>
<pre><samp>$ <kbd>sudo /etc/init.d/couchdb start</kbd></samp></pre>
<p>And then check the status again:</p>
<pre><samp>$ <kbd>sudo /etc/init.d/couchdb status</kbd>
Apache CouchDB is running as process 32651. Time to relax.</samp></pre>
<p>If you&#8217;re still not getting the correct status, you&#8217;ll need to refer to the <a href="http://couchdb.apache.org/docs/">CouchDB documentation</a>.</p>
<p>Next, you should check that you can access CouchDB over HTTP on the default port of 5984. To do that, I used curl:</p>
<pre><samp>$ <kbd>curl http://localhost:5984/</kbd></samp></pre>
<p>Which should return the following response:</p>
<pre><samp>{"couchdb":"Welcome","version":"0.8.0-incubating"}</samp></pre>
<p>The final step, to make sure you can access CouchDB externally to your dev VM, is to make sure the CouchDB configuration isn&#8217;t bound to the loopback address (127.0.0.1) &#8212; which it is by default. To do that, you&#8217;ll need to edit the couch.ini file:</p>
<pre><samp>$ <kbd>sudo vim /etc/couchdb/couch.ini</kbd></samp></pre>
<p>And comment out the following line:</p>
<pre><samp>BindAddress=127.0.0.1</samp></pre>
<p>You can also change the port that CouchDB is running on in this configuration file if you so desire.</p>
<p>Finally, you&#8217;ll need to restart CouchDB:</p>
<pre><samp>$ <kbd>sudo /etc/init.d/couchdb stop</kbd>
$ <kbd>sudo /etc/init.d/couchdb start</kbd></samp></pre>
<p>Now you should be able to log into Futon, the browser-based CouchDB admin system, on the following address:</p>
<pre><samp>http://mydevbox:5984/_utils/</samp></pre>
<p>Huzzah!</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2009/07/couchdb-on-an-ubuntu-development-vm/#comments" thr:count="1" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2009/07/couchdb-on-an-ubuntu-development-vm/feed/atom/" thr:count="1" />
		<thr:total>1</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2009/07/couchdb-on-an-ubuntu-development-vm/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[Unit Testing: The Basics]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/WNDmFQ48frI/" />
		<id>http://nefariousdesigns.co.uk/?p=187</id>
		<updated>2009-05-21T21:43:47Z</updated>
		<published>2009-05-03T11:36:52Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="Python" /><category scheme="http://nefariousdesigns.co.uk" term="Testing" /><category scheme="http://nefariousdesigns.co.uk" term="Tools" /><category scheme="http://nefariousdesigns.co.uk" term="Tutorials" /><category scheme="http://nefariousdesigns.co.uk" term="Web" />		<summary type="html"><![CDATA[As a web development manager, it&#8217;s part of my job to be involved in defining best practices for my team. This means defining a set of standard practices that will benefit the whole team throughout the entire development process. A good example of one such best practice would be code testing.
Testing, for web developers, is [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2009/05/unit-testing-the-basics/"><![CDATA[<p>As a web development manager, it&#8217;s part of my job to be involved in defining best practices for my team. This means defining a set of standard practices that will benefit the whole team throughout the entire development process. A good example of one such best practice would be code testing.</p>
<p>Testing, for web developers, is usually a fairly disorganised affair. Most of the time, we test &#8220;on the job&#8221;, i.e., we code something and then we run it; if it breaks, we fix it. In fact, often, the thought of actually structuring our testing process can seem somewhat of an over complication. This is certainly not the case.</p>
<p>The benefits of a structured testing process are legion, as any software engineer will attest. At the very least, it provides confidence in our code &#8212; something which can only be of incredible benefit when code is shared across a wider team. At its best, the tests we write can influence the way we develop the code being tested; and save effort, time, and money.</p>
<p>In this post I&#8217;m going to look at &#8220;unit testing&#8221;, and how it can be of benefit to web developers in general.</p>
<p><span id="more-187"></span></p>
<h2>What <em>is</em> unit testing?</h2>
<p>Unit testing is the process of validating and verifying the individual &#8220;units&#8221; of code in a system. A unit is the smallest testable part of a system or application. In the majority of cases within web development, these units are usually individual functions or methods of our object-oriented back-end and front-end code.</p>
<p>At this point, it&#8217;s probably worth noting that unit testing is really only applicable to functional code, such as PHP, JavaScript, Python, Perl, Ruby etc., because it breaks down into units nicely. Testing mark-up and CSS is a slightly different process and one that I may look at in a future blog post.</p>
<h2>A note on language</h2>
<p>The concept of unit testing is entirely language agnostic, therefore the examples in the following tutorials can be ported to PHP, Perl, Ruby, JavaScript, or any other functional or object-oriented language you are using.</p>
<p>I&#8217;ll use Python as the language for all examples in this post as it&#8217;s my current weapon of choice (and the interactive interpreter is very handy for trying out ideas); if you&#8217;ve never seen Python syntax, don&#8217;t worry, it&#8217;s easily understandable (if you want more information, take a look at <a href="http://docs.python.org/tutorial/">the Python tutorial</a>).</p>
<h2>Writing a simple test</h2>
<p>To begin with, let&#8217;s imagine we have a function for calculating the area of a rectangle:</p>
<pre><code>def get_area_of_rect( width, height ):
    return width * height</code></pre>
<p>Let&#8217;s paste this function into the interactive interpreter, so that we can play with it:</p>
<pre><samp>>>> <kbd>def get_area_of_rect( width, height ):</kbd>
... <kbd>    return width * height</kbd>
...
>>> </samp></pre>
<p>Now if we call it, we&#8217;ll receive the area of our rectangle like so:</p>
<pre><samp>>>> <kbd>get_area_of_rect( 2, 3 )</kbd>
6
>>></samp></pre>
<p>Now if we wanted to test this function, we could call it with a some defined parameters and test the output against our expectations:</p>
<pre><code>def test_get_area_of_rect():
    width = 2
    height = 3
    expected_result = 6

    result = get_area_of_rect( width, height )

    if result == expected_result:
        print "Test passed: Received expected result!"</code></pre>
<p>Now if we run our test, we should see the following:</p>
<pre><samp>>>> <kbd>test_get_area_of_rect()</kbd>
Test passed: Received expected result!
>>></samp></pre>
<p>Brilliant; our code passed the test because the <em>expected</em> result was the same as the <em>actual</em> result &#8212; however, that&#8217;s actually not very useful to us. It&#8217;s far more important that our tests can trap <em>failing</em> code than code that works.</p>
<p>Imagine our <code>get_area_of_rect</code> function had been updated as follows:</p>
<pre><code>def get_area_of_rect( width, height ):
    return width / height</code></pre>
<p>Anyone with rudimentary maths skills can tell that this will no longer return the correct area of a rectangle. In fact, if we rerun our test, we&#8217;ll get no output:</p>
<pre><samp>>>> <kbd>test_get_area_of_rect()</kbd>
>>></samp></pre>
<p>A fat lot of good <em>that</em> is. If our code is failing for some reason, we want to know about it. After all, that&#8217;s the whole reason for testing it.</p>
<p>Let&#8217;s update the <code>if</code> statement in our <code>test_get_area_of_rect</code> function:</p>
<pre><code>if not result == expected_result:
    raise AssertionError
else:
    print "Test passed: Received expected result!"</code></pre>
<p>In fact, we could probably dump the <code>else</code> clause since it&#8217;s of little real importance except for logging purposes:</p>
<pre><code>if not result == expected_result:
    raise AssertionError</code></pre>
<p>That&#8217;s nicer, but Python provides us a shortcut to this <code>if not &hellip; raise AssertionError</code> syntax with the <code>assert</code> statement:</p>
<pre><code>assert result == expected_result</code></pre>
<p>With that in mind, the <code>test_get_area_of_rect</code> function should now look like this:</p>
<pre><code>def test_get_area_of_rect():
    width           = 2
    height          = 3
    expected_result = 6

    result = get_area_of_rect( width, height )

    assert result == expected_result</code></pre>
<p>If we run our test again, we should receive more constructive output, considering our <code>get_area_of_rect</code> function is still dividing:</p>
<pre><samp>>>> <kbd>test_get_area_of_rect()</kbd>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in test_get_area_of_rect
AssertionError
>>></samp></pre>
<p>We now have a decent automated test, but we&#8217;re only checking a single expected result. If, for instance, we passed in a width of 1 and a height of 1, both multiplication and division would return the same result: 1. This means that we should pass multiple sets of width and height parameters and test against their expected results. Since these parameters will be constant, let&#8217;s define them as tuples and loop through them, testing each in turn:</p>
<pre><code>def test_get_area_of_rect():
    known_values = ( ( 0, 0, 0 ),
                     ( 1, 1, 1 ),
                     ( 2, 2, 4 ),
                     ( 2, 3, 6 ),
                     ( 3, 0, 0 ) )

    for width, height, expected_result in known_values:
        result = get_area_of_rect( width, height )
        assert result == expected_result</code></pre>
<p>As you can see, we&#8217;re now passing several sets of parameters. Each set of parameters is valid, and as such, we should expect a successful outcome from our <code>get_area_of_rect()</code> call.</p>
<p>Now if we run our test, we should get something like the following:</p>
<pre><samp>>>> <kbd>test_get_area_of_rect()</kbd>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in test_get_area_of_rect
  File "<stdin>", line 2, in get_area_of_rect
ZeroDivisionError: integer division or modulo by zero
>>></samp></pre>
<p>Ah ha! By passing multiple sets of parameters we&#8217;ve uncovered more significant information regarding the <code>AssertionError</code> that was raised earlier; we have a division by zero error on line 2 of <code>get_area_of_rect</code>. This has supplied us with all the information we need to fix our error, so let&#8217;s take a look at that function again:</p>
<pre><code>def get_area_of_rect( width, height ):  # Line 1
    return width / height               # Line 2</code></pre>
<p>Change the division back to multiplication, like so:</p>
<pre><code>def get_area_of_rect( width, height ):  # Line 1
    return width * height               # Line 2</code></pre>
<p>And then rerun our test:</p>
<pre><samp>>>> <kbd>test_get_area_of_rect()</kbd>
>>> </samp></pre>
<p>We get no output at all because the test passed successfully. As the old proverb goes, &#8220;no news is good news&#8221;.</p>
<p>Now if we add to our original code in the future, we&#8217;ll still be able to run this testing code to see what has been affected. What&#8217;s more, we can add to it by writing <em>more</em> tests, should the complexity grow. In fact, creating an extensive suite of tests is generally the aim of unit testing, but to do so, it&#8217;s a good idea to utilise a testing framework, which I&#8217;ll look at in the next post of this series.</p>
<h2>Summary</h2>
<p>Writing tests for code enables more efficient debugging, faster bug fixing, and an overall confidence in the quality of your code. Furthermore, the rest of your team (or whomsoever you&#8217;re sharing said code with) will equally have confidence thanks to the tests included.</p>
<p>In brief, here&#8217;s what we&#8217;ve learnt about unit testing:</p>
<ul>
<li>It&#8217;s most important that your tests catch failure, not success.</li>
<li>Test the smallest amount of code, or &#8220;unit&#8221;, possible each time.</li>
<li>Test as many different parameters, inputs, and outputs as possible.</li>
<li>Run the tests often.</li>
</ul>
<h2>The next post</h2>
<p>In the next post, I&#8217;ll be expanding on unit testing by looking at test frameworks, test architecture, code coverage, and other more advanced techniques that can improve your testing environment.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2009/05/unit-testing-the-basics/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2009/05/unit-testing-the-basics/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2009/05/unit-testing-the-basics/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[Housekeeping]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/7SfTHVhMbng/" />
		<id>http://nefariousdesigns.co.uk/?p=183</id>
		<updated>2009-04-13T10:14:55Z</updated>
		<published>2009-04-13T10:14:55Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="Site" /><category scheme="http://nefariousdesigns.co.uk" term="Syndication" /><category scheme="http://nefariousdesigns.co.uk" term="Tools" /><category scheme="http://nefariousdesigns.co.uk" term="Web" />		<summary type="html"><![CDATA[I&#8217;ve just completed some quick housekeeping on the site &#8212; mostly on my feed.
I noticed last night that Google had acquired Feedburner, and that I needed to port my feed over to my Google account. At the same time, I swapped the feed to use Atom instead of RSS. Having completed that, I decided to [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2009/04/housekeeping/"><![CDATA[<p>I&#8217;ve just completed some quick housekeeping on the site &#8212; mostly on my feed.</p>
<p>I noticed last night that <a href="http://www.feedburner.com/google">Google had acquired Feedburner</a>, and that I needed to port my feed over to my Google account. At the same time, I swapped the feed to use Atom instead of RSS. Having completed that, I decided to address a problem with my feed that&#8217;s been bugging me for some time:</p>
<p>My feed has always published only an excerpt of each post, instead of the full post. This is because I use the &#8220;more&#8221; break functionality in Wordpress for the introduction snippets in the bespoke home page theme files, instead of writing an entirely separate excerpt. On previous versions of Wordpress, this also limited the RSS feed regardless of whether you had specifically chosen the &#8220;display full article&#8221; option in the syndication settings. I&#8217;ve had a couple of complaints about this, since most developers seem to prefer reading articles in their feed readers &#8212; and so do I, for that matter.</p>
<p>Last time I needed to fix this problem, I had a hell of a time trying to find the offending code in the horrific Wordpress PHP. Thankfully, since then, the bug has been fixed so all I needed to do was update Wordpress. Having completed that update, my feed should now be working as intended originally.</p>
<p>The next step, it seems, will be getting off Wordpress to something home-grown.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2009/04/housekeeping/#comments" thr:count="1" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2009/04/housekeeping/feed/atom/" thr:count="1" />
		<thr:total>1</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2009/04/housekeeping/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[A better OS X development environment]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/b3kdjz5EWJ8/" />
		<id>http://nefariousdesigns.co.uk/archive/2009/04/a-better-os-x-development-environment/</id>
		<updated>2009-04-13T09:52:48Z</updated>
		<published>2009-04-12T21:41:44Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Apache" /><category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="Django" /><category scheme="http://nefariousdesigns.co.uk" term="Linux" /><category scheme="http://nefariousdesigns.co.uk" term="Mac OS X" /><category scheme="http://nefariousdesigns.co.uk" term="MySQL" /><category scheme="http://nefariousdesigns.co.uk" term="PHP" /><category scheme="http://nefariousdesigns.co.uk" term="Python" /><category scheme="http://nefariousdesigns.co.uk" term="Server" /><category scheme="http://nefariousdesigns.co.uk" term="Tutorials" /><category scheme="http://nefariousdesigns.co.uk" term="UNIX" /><category scheme="http://nefariousdesigns.co.uk" term="Virtualisation" /><category scheme="http://nefariousdesigns.co.uk" term="Web" />		<summary type="html"><![CDATA[When I first began developing using a Mac I made use of the bundled versions of Apache and PHP that were included in OS X Tiger. This meant I only needed to install MySQL to be able to develop fairly complex websites.
However, as my requirements evolved, I discovered that recompiling PHP to include support for [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2009/04/a-better-os-x-development-environment/"><![CDATA[<p>When I first began developing using a Mac I made use of the bundled versions of Apache and PHP that were included in OS X Tiger. This meant I only needed to install MySQL to be able to develop fairly complex websites.</p>
<p>However, as my requirements evolved, I discovered that recompiling PHP to include support for features such as HTML Tidy was going to be a pain in the proverbials. So, following advice from friends, I opted to install another version of Apache, PHP5, and MySQL using MacPorts package management. This required minimal configuration and allowed me to easily activate or deactivate features on install &#8212; and it also allowed me to add to my environment as I learnt new skills; including Python and Django.</p>
<p>Recently, however, this became an issue again, as I attempted to install mod_wsgi in the MacPorts Apache. No matter how hard I tried, I just couldn&#8217;t get it working with more complex Python scripts. At this point, I sought more help from my friends and, after a couple of attempts to rectify the problem, was suggested the movement of my entire development environment to a dedicated virtual machine.</p>
<p>So, thanks to <a href="http://dannyamey.com/">Danny Amey</a> and <a href="http://intranation.com">Brad Wright</a>, I&#8217;m now running a proper Ubuntu server as my development environment and have just as much control over it as I do on my live web server. In fact, the two are pretty much identical; and, best of all, I have access to all the files on my dev server using VMWare shared folders, which means I can use all my favourite OS X tools to create and edit files. I really can&#8217;t advocate this method enough.</p>
<p>For more information on creating such an environment, I recommend reading Brad&#8217;s post, &#8220;<a href="http://intranation.com/entries/2009/03/development-virtual-machines-os-x-using-vmware-and/">development virtual machines on OS X using VMWare and Ubuntu</a>&#8220;.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2009/04/a-better-os-x-development-environment/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2009/04/a-better-os-x-development-environment/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2009/04/a-better-os-x-development-environment/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[Web Servers and Email: Postfix on Ubuntu]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/1F2WS8SX19Y/" />
		<id>http://nefariousdesigns.co.uk/archive/2009/04/web-servers-and-email-postfix-on-ubuntu/</id>
		<updated>2009-04-11T16:16:39Z</updated>
		<published>2009-04-11T16:16:39Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="Email" /><category scheme="http://nefariousdesigns.co.uk" term="Linux" /><category scheme="http://nefariousdesigns.co.uk" term="Server" /><category scheme="http://nefariousdesigns.co.uk" term="UNIX" /><category scheme="http://nefariousdesigns.co.uk" term="Web" />		<summary type="html"><![CDATA[Setting up web servers is an oft-covered subject in the web development blogosphere. However, when I needed to set up a shared web and email server, I discovered that such documentation was somewhat more dispersed. With that in mind, and following the development of an email newsletter at work (in which I demonstrated several email [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2009/04/web-servers-and-email-postfix-on-ubuntu/"><![CDATA[<p>Setting up web servers is an oft-covered subject in the web development blogosphere. However, when I needed to set up a shared web and <em>email</em> server, I discovered that such documentation was somewhat more dispersed. With that in mind, and following the development of an email newsletter at work (in which I demonstrated several email testing techniques that were of interest to some of the other developers), I began drafting a blog post that would hopefully bring some of that information together. Sadly, I never finished.</p>
<p>Due to an ever more stressful workplace and the arrival of another child in the household, I took a somewhat forced break from blogging. This was entirely unintended and I&#8217;ve decided to get back on the wagon again by finishing the aforementioned post.</p>
<p>So here it is; the finished article on setting up Postfix on a shared mail and web server. Please remember that I&#8217;m technically a mail server numpty and probably won&#8217;t have all the answers to questions should you have them. However, feel free to leave them in the comments and hopefully someone out there (or someone I can poke for comment elsewhere) can help you out.</p>
<p><span id="more-180"></span></p>
<h2>A bit of background</h2>
<p>I made the jump from supported, pre-built hosting to entirely self-managed hosting last year. As a result, I soon discovered the need to handle email received by the various hosted domains on my server.</p>
<p>One solution to this problem would be to use <a href="http://www.google.com/apps/intl/en/business/index.html">Google Apps for Business</a>. Too easy, drill sergeant; and also $50 more than I was willing to spend a year. Another solution would be to install my own email server and manage it myself. Despite this idea giving me The Fear&trade;, it also meant I&#8217;d be learning something new &#8212; a buzz I find difficult to ignore at the best of times.</p>
<p>Given those options, I&#8217;m sure it&#8217;s not too hard to figure out which route I took, and the following blog post is a small amount of what I learnt in the process.</p>
<h2>Hostname</h2>
<p>Often, when building a web server, the server hostname is overlooked as unimportant. This is generally because the DNS system provides all the functionality needed when serving files over HTTP. However, when the server is required to handle email, it is important to set a hostname because it is used by the various email protocols as a source and destination identifier; ergo a key component in the identification of spam.</p>
<p>Postfix will let you send and receive mail just fine between accounts on a machine with a fantasy name. However, you cannot use a fantasy hostname in your email address when sending email via the Internet, because no-one would be able to reply to your mail. In fact, more and more sites refuse mail addresses with non-existent domain names.</p>
<p>Further to this, you may want to specify a <em>fully-qualified domain name</em> hostname. A hostname is considered to be a fully qualified domain name (FQDN) if all the labels up to and including the top-level domain name (TLD) are specified. The hostname &#8220;mail.nerfabulousdesigns.co.uk&#8221; terminates with the top-level domain &#8220;co.uk&#8221; and is thus fully-qualified. Depending on the system, an unqualified hostname such as &#8220;server1&#8243; or &#8220;test-server&#8221; may be combined with default domain names in order to determine the fully qualified domain name. However, in most cases, a web server or mail server won&#8217;t be running in that sort of environment.</p>
<p>With all that in mind, it&#8217;s best to use a domain name you can access the DNS records for, as you&#8217;ll want to forward email to it using the MX record. An accepted standard, when working with mail, is to use &#8220;mail.mydomain.com&#8221;, however, in this case, we&#8217;re sharing email and web serving duties on the same box. A better solution would be to come up with any name that you can use to uniquely identify the server in your DNS settings. For the purpose of my examples, I&#8217;m going to work with the hostname &#8220;spartacus.nerfabulousdesigns.co.uk&#8221;.</p>
<h3>DNS for your hostname</h3>
<p>Before I start wading into the installing and configuring the server, it&#8217;s important to get the DNS set up in your DNS manager. So, if the IP address of our server is 123.456.789.123 we&#8217;ll want the following DNS records:</p>
<pre><samp>A Records:
spartacus.nerfabulousdesigns.co.uk -> 123.456.789.123
mail.nerfabulousdesigns.co.uk -> 123.456.789.123

MX Record:
mail.nerfabulousdesigns.co.uk</samp></pre>
<p>Any other domains you want to set up for email only need the following rules:</p>
<pre><samp>A Records:
mail.yourdomain.com -> 123.456.789.123

MX Record:
mail.yourdomain.com</samp></pre>
<p>Once you&#8217;ve saved those changes, and you&#8217;re awaiting DNS propagation, it&#8217;s time to get back to our server.</p>
<h3>Server settings for your hostname</h3>
<p>To find out the current hostname of your server (which is defined during installation), type the following:</p>
<pre><samp>$ <kbd>hostname</kbd></samp></pre>
<p>The <code>hostname</code> command will output the current hostname if you run it without any parameters. If you want to see the fully qualified hostname, type the following:</p>
<pre><samp>$ <kbd>hostname -f</kbd></samp></pre>
<p>To change your hostname temporarily, you can simply enter the new hostname as the first parameter of the hostname command. So, assuming I want to identify my server as &#8220;spartacus&#8221;, I&#8217;d enter the following:</p>
<pre><samp>$ <kbd>hostname spartacus.nerfabulousdesigns.co.uk</kbd></samp></pre>
<p>Now we can check our change:</p>
<pre><samp>$ <kbd>hostname</kbd>
spartacus.nerfabulousdesigns.co.uk</samp></pre>
<p>Huzzah!</p>
<p>Unfortunately, that change will be reset should the machine be rebooted for any reason.</p>
<p>Not-huzzah!</p>
<p>To make the change permanent, you&#8217;ll need to edit the single-line <code>/etc/hostname</code> file:</p>
<pre><samp>$ <kbd>sudo vim /etc/hostname</kbd></samp></pre>
<p>Update it to contain the following:</p>
<pre><samp>spartacus.nerfabulousdesigns.co.uk</samp></pre>
<p>Now you can trigger your change by restarting the hostname daemon:</p>
<pre><samp>$ <kbd>/etc/init.d/hostname.sh start</kbd></samp></pre>
<p>However, you don&#8217;t need to do that just yet since you&#8217;ll probably want to restart your server later on in the process.</p>
<h3>The hosts file</h3>
<p>Next we&#8217;ll need to update the hosts file on the server so that it recognises the new hostname as a referer to the loopback IP 127.0.0.1. To do that, edit the hosts file with the following command:</p>
<pre><samp>$ <kbd>sudo vi /etc/hosts</kbd></samp></pre>
<p>And then update it so that it contains a line similar to this:</p>
<pre><samp>127.0.0.1        spartacus.nerfabulousdesigns.co.uk</samp></pre>
<p>Once this file has been saved, we can do that reboot we were going to do earlier:</p>
<pre><samp>$ <kbd>sudo reboot</kbd></samp></pre>
<p>Once your server is back up, the hostname change is complete. Your server will now use the loopback address when hostname references are used, instead of wasting valuable time checking DNS records.</p>
<h2>Install Postfix</h2>
<p>Now that the server is primed for email, it&#8217;s time to install postfix &#8212; and while we&#8217;re at it, let&#8217;s install mailx, an improved version of the mail email user agent program, and let&#8217;s also confirm we have telnet installed as we&#8217;re going to use it for testing later on:</p>
<pre><samp>$ <kbd>sudo aptitude install postfix mailx telnet</kbd></samp></pre>
<p>As Postfix installs, you&#8217;ll be asked a number of configuration questions:</p>
<p>For &#8220;general type of mail configuration&#8221;, choose:</p>
<pre><samp>Internet Site</samp></pre>
<p>You will then be asked the fully qualified domain name of your server, which in our case is:</p>
<pre><samp>spartacus.nerfabulousdesigns.co.uk</samp></pre>
<p>Once you&#8217;ve done that, the installation is complete. Your web server is now capable of sending email; and to prove it, let&#8217;s just test it from the command line.</p>
<h2>Testing</h2>
<p>Let&#8217;s send an email to a real email address (remember to replace <samp>somebody@somewhere.com</samp> with the real email address of your choice):</p>
<pre><samp>$ <kbd>mail somebody@somewhere.com</kbd></samp></pre>
<p>The mail program will now ask you for a subject. Once you press return, mail will now be accepting the body of your email (albeit without a prompt). To tell the program that you&#8217;ve reached the end of your email body, you&#8217;ll need to place a single full-stop character, &#8220;.&#8221;, on a line by itself (since the return character is perfectly valid in the body of your mail). Finally, the mail program will then ask you for any CC addresses. </p>
<p>Here&#8217;s an example of the complete screen output:</p>
<pre><samp>$ <kbd>mail somebody@somewhere.com</kbd>
Subject: <kbd>Testing my all new email server!</kbd>
<kbd>Red leader, this is mail server. Are you receiving, over?
.</kbd>
Cc:
$</samp></pre>
<p>You won&#8217;t receive any confirmation that the email has been sent &#8212; you should just return to the command prompt &#8212; however, as long as everything was set up correctly, you should have received the email.</p>
<p>The next step is to configure our mail server so that it knows what to do with received email.</p>
<h2>Configuration</h2>
<p>Configuration of a mail server is where all the pain and torment begins; but in this case, I only want to set up mail forwarding &#8212; which takes all the pain and torment away and leaves us with a fairly straight forward, unburdened mail server that&#8217;s using very little disk space. I could usually do this at the DNS level, but I chose not to in case I decided to work with real mail boxes in the future &#8212; and also because I was still relishing the learning experience. If you want to set up something different, I&#8217;m afraid you&#8217;re going to have to look elsewhere for a tutorial because I haven&#8217;t the time (or the patience) to go through the multitude of different set-ups that are possible.</p>
<p>The first step in setting up mail forwarding in Postfix is to create a virtual hash. To do that, we need to create the following file:</p>
<pre><samp>$ <kbd>sudo vim /etc/postfix/virtual</kbd></samp></pre>
<p>And inside that file, we need to define some values. Here&#8217;s how we&#8217;d set up nerfabulousdesigns.co.uk as a mail forwarding domain:</p>
<pre><samp>john@nerfabulousdesigns.co.uk    john.smith@yahoo.com
joe@nerfabulousdesigns.co.uk     joe.bloggs@yahoo.com

# For a catch-all, do the following
@nerfabulousdesigns.co.uk        example@yahoo.com</samp></pre>
<p>Once you&#8217;ve saved that file, run the following command to create the postmap:</p>
<pre><samp>$ <kbd>postmap /etc/postfix/virtual</kbd></samp></pre>
<p>Next you&#8217;ll also need to update this file:</p>
<pre><samp>$ <kbd>sudo vim /etc/postfix/main.cf</kbd></samp></pre>
<p>Add your domain to <code>virtual_alias_domains</code> and your virtual hash to <code>virtual_alias_maps</code> like so:</p>
<pre><samp>virtual_alias_domains = nerfabulousdesigns.co.uk ...other hosted domains...
virtual_alias_maps = hash:/etc/postfix/virtual</samp></pre>
<p>Finally, you&#8217;ll need to do the following to restart Postfix:</p>
<pre><samp>$ <kbd>postfix reload</kbd></samp></pre>
<p>Any emails to the domains or addresses you added to the virtual hash should now be forwarded as expected.</p>
<h2>Telnet</h2>
<p>Finally, let&#8217;s test the email server is working using Telnet. This is a handy little technique for checking everything is as expected, since it will also display responses at every stage of the mail sending process.</p>
<p>You can telnet from any machine that has it installed &#8212; and since we installed it on our server earlier, we can even telnet the server to itself, which might be useful when trying to track down problems.</p>
<p>To begin with, let&#8217;s Telnet to our server over the default <abbr title="Simple Mail Transfer Protocol">SMTP</abbr> port 25:</p>
<pre><samp>$ <kbd>telnet mail.nerfabulousdesigns.co.uk 25</kbd></samp></pre>
<p>Which should trigger a response similar to this:</p>
<pre><samp>Trying 209.20.86.249...
Connected to mail.nerfabulousdesigns.co.uk.
Escape character is '^]'.
220 spartacus.nerfabulousdesigns.co.uk ESMTP Postfix (Ubuntu)</samp></pre>
<p>If the connection was a success, you should receive a 220 <abbr title="Simple Mail Transfer Protocol">SMTP</abbr> reply code. For more information, here&#8217;s <a href="http://www.greenend.org.uk/rjk/2000/05/21/SMTP-replies.html">a handy reference on <abbr title="Simple Mail Transfer Protocol">SMTP</abbr> reply codes</a>.</p>
<p>The next thing we need to do, in the essence of being polite, is greet the mail server. We can do that one of two ways. We can either use the <abbr title="Simple Mail Transfer Protocol">SMTP</abbr> command, <code>HELO</code>:</p>
<pre><samp><kbd>HELO my.local.domain</kbd>
250 spartacus.nerfabulousdesigns.co.uk</samp></pre>
<p>Or we can use the more recent <code>EHLO</code> command, which will return more detailed information about the server:</p>
<pre><samp><kbd>EHLO my.local.domain</kbd>
250-spartacus.nerfabulousdesigns.co.uk
â€¦ More detailed info. hereâ€¦</samp></pre>
<p>Great, so now that we&#8217;ve identified ourselves, we&#8217;re reading to start defining the envelope for our message. First, we need to tell the mail server who the sender is:</p>
<pre><samp><kbd>MAIL FROM:<telnet@nerfabulousdesigns.co.uk></kbd>
250 2.1.0 Ok</samp></pre>
<p>Second, we need to tell the mail server who we&#8217;d like the email delivered to:</p>
<pre><samp><kbd>RCPT TO:joe@nerfabulousdesigns.co.uk</kbd>
250 2.1.5 Ok</samp></pre>
<p>That&#8217;s it; the envelope is now complete. We can now move onto filling our message with data. To do that, we simply use the command <code>DATA</code>, which will then allow us to enter the subject and main content for our message:</p>
<pre><samp><kbd>DATA</kbd>
354 End data with <CR><LF>.<CR><LF>
<kbd>Subject: Test Email
This is a test email.
.</kbd>
250 2.0.0 Ok: queued as 40F438D023</samp></pre>
<p>Our message is complete, and has been queued by the mail server for redistribution to the recipient. We&#8217;re done here, so let&#8217;s quite out of the telnet session:</p>
<pre><samp><kbd>QUIT</kbd>
221 2.0.0 Bye
Connection closed by foreign host.
$ </samp></pre>
<p>And we&#8217;re now back at our original command prompt. If you check the mailbox of your recipient, you should have successfully received the email.</p>
<h2>Summary</h2>
<p>Despite the fact that I could probably have handled email in more efficient ways, I thoroughly enjoyed learning about Postfix and the black magic therein. I&#8217;m sure that some of my peers will have plenty to correct me on, and I sincerely hope they do; as previously stated, this was intended as a learning experience, and learning is what I have done.</p>
<p>Hopefully, some &#8212; if not all &#8212; the techniques used in this article will be of some use to you. Certainly, they have aided me in my understanding of core internet technologies.</p>
<p>Please feel free to comment, question, or to point out flaws with my methods. I hope the discussion is fruitful.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2009/04/web-servers-and-email-postfix-on-ubuntu/#comments" thr:count="2" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2009/04/web-servers-and-email-postfix-on-ubuntu/feed/atom/" thr:count="2" />
		<thr:total>2</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2009/04/web-servers-and-email-postfix-on-ubuntu/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[Practical Django Projects and Django 1.0]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/l91aOe0_SiU/" />
		<id>http://nefariousdesigns.co.uk/archive/2008/09/practical-django-projects-and-django-10/</id>
		<updated>2008-10-24T10:04:32Z</updated>
		<published>2008-09-25T09:41:46Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Books" /><category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="Django" /><category scheme="http://nefariousdesigns.co.uk" term="Python" /><category scheme="http://nefariousdesigns.co.uk" term="Web" />		<summary type="html"><![CDATA[I recently purchased a copy of &#8220;Practical Django Projects&#8220;, by James Bennett, with the intention of diving straight in and learning Django. I&#8217;d prepared for this daring feat of code-ninjutsu [yes, that's the correct spelling] with a crash course in Python via &#8220;Dive Into Python&#8220;, by Mark Pilgrim.
This appears to have been the correct choice, [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2008/09/practical-django-projects-and-django-10/"><![CDATA[<p>I recently purchased a copy of &#8220;<a href="http://www.amazon.co.uk/gp/product/1590599969?ie=UTF8&#038;tag=gamersite-21&#038;linkCode=as2&#038;camp=1634&#038;creative=6738&#038;creativeASIN=1590599969">Practical Django Projects</a>&#8220;, by James Bennett, with the intention of diving straight in and learning Django. I&#8217;d prepared for this daring feat of code-ninjutsu [yes, that's the correct spelling] with a crash course in Python via &#8220;<a href="http://www.amazon.co.uk/gp/product/1590593561?ie=UTF8&#038;tag=gamersite-21&#038;linkCode=as2&#038;camp=1634&#038;creative=6738&#038;creativeASIN=1590593561">Dive Into Python</a>&#8220;, by Mark Pilgrim.</p>
<p>This appears to have been the correct choice, as I&#8217;ve already acquired a confident grasp of the concepts and techniques of Django in less than a few days of playing with it (granted; with the odd reference look-up on <a href="http://www.djangobook.com/">http://www.djangobook.com/</a> and <a href="http://docs.djangoproject.com/en/dev/">http://docs.djangoproject.com/en/dev/</a>).</p>
<p>However, last night it all went a bit wrong when I upgraded my local version of Django from 0.96 to 1.0&hellip;</p>
<p><span id="more-179"></span></p>
<p>Most of my issues were due to the fact that some of the code in the examples, included in <a href="http://www.amazon.co.uk/gp/product/1590599969?ie=UTF8&#038;tag=gamersite-21&#038;linkCode=as2&#038;camp=1634&#038;creative=6738&#038;creativeASIN=1590599969">Practical Django Projects</a>, was now out of date. However, I had expected this (thanks to warnings from <a href="http://cyril.doussin.name/">Cyril Doussin</a> and <a href="http://dynamicflash.com/">Steve Webster</a>) and went about fixing the problems using the informative <a href="http://docs.djangoproject.com/en/dev/releases/1.0-porting-guide/">Porting Guide on the Django documentation site</a>.</p>
<p>Following my extensive fixes, I was still experiencing issues when decoupling the Admin class from its relevant model. In most cases, this was a relatively easy process, but in one particular situation, where I had used <code>edit_inline</code> and needed to update the method to use the new InlineModelAdmin objects, I had a slightly confusing issue: I needed to add the InlineModelAdmin object to one of the django.contrib applications â€” FlatPage â€” to get the SearchKeyword application to work.</p>
<p>After a short period googling, I found an interesting, well-written blog post by <a href="http://jasonism.org/">Jason Broyles</a>, &#8220;<a href="http://jasonism.org/weblog/2008/aug/04/adding-inlinemodeladmin-djangocontrib-application/">Add InlineModelAdmin to a django.contrib app</a>&#8220;. Unsurprisingly, it seemed others had had exactly the same issue.</p>
<p>Jason&#8217;s final model.py and admin.py files were as follows:</p>
<pre><code># models.py

from django.db import models
from django.contrib.flatpages.models import FlatPage

class SearchKeyword(models.Model):
    keyword = models.CharField(max_length=50)
    page = models.ForeignKey(FlatPage)

    def __unicode__(self):
        return self.keyword</code></pre>
<pre><code># admin.py

from django.contrib import admin
from cms.search.models import SearchKeyword
from django.contrib.flatpages.models import FlatPage
from django.utils.translation import ugettext_lazy as _

class SearchKeywordInline(admin.StackedInline):
    model = SearchKeyword
    extra = 3
    max_num = 6

class FlatPageAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {'fields': ('url', 'title', 'content', 'sites')}),
        (_('Advanced options'), {'classes': ('collapse',), 'fields': ('enable_comments', 'registration_required', 'template_name')}),
    )
    list_display = ('url', 'title')
    list_filter = ('sites', 'enable_comments', 'registration_required')
    search_fields = ('url', 'title')
    inlines = [SearchKeywordInline,]

admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageAdmin)</code></pre>
<p>Jason followed these examples with the following explanation:</p>
<blockquote cite="http://jasonism.org/weblog/2008/aug/04/adding-inlinemodeladmin-djangocontrib-application/"><p>The above admin.py file will make it so the SearchKeyword model is accessible in the FlatPages admin interface. It does this by first creating the SearchKeywordInline class and adding it to a new FlatPage ModelAdmin function. I also didn&#8217;t want to loose out on any of the extra options from the default django.contrib.flatpages ModelAdmin so I copied lines 6 and 14-20 from it and added the code to my file. Line 23 then unregisters the default FlatPage ModelAdmin and line 24 registers this new one with my inlines included.</p></blockquote>
<p>This solution fixed my problems, however I was concerned that copying and pasting code from <code>django.contrib.flatpages ModelAdmin</code> to <code>admin.py</code> was a massively inelegant solution. With that in mind, I came up with the following solution using inheritance:</p>
<pre><code># admin.py

from django.contrib import admin
from cms.search.models import SearchKeyword
from django.contrib.flatpages.models import FlatPage
from django.contrib.flatpages.admin import FlatPageAdmin
from django.utils.translation import ugettext_lazy as _

class SearchKeyword_Inline(admin.TabularInline):
	model = SearchKeyword
	extra = 3
	max_num = 6

class FlatPageAdmin(FlatPageAdmin):
 	inlines = [SearchKeyword_Inline]

admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageAdmin)</code></pre>
<p>This worked perfectly.</p>
<p>As a final note, I&#8217;d just like to thank Jason for posting his solution; without that much needed information, I probably would have spent a lot more time trying to fix the issue than was really required. It just goes to show that developers that blog are a huge benefit to confused developers everywhere.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2008/09/practical-django-projects-and-django-10/#comments" thr:count="4" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2008/09/practical-django-projects-and-django-10/feed/atom/" thr:count="4" />
		<thr:total>4</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2008/09/practical-django-projects-and-django-10/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[The Joy of UNIX]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/7108kRNsRYg/" />
		<id>http://nefariousdesigns.co.uk/archive/2008/09/the-joy-of-unix/</id>
		<updated>2008-09-23T12:33:20Z</updated>
		<published>2008-09-23T12:33:20Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Development" /><category scheme="http://nefariousdesigns.co.uk" term="Linux" /><category scheme="http://nefariousdesigns.co.uk" term="Mac OS X" /><category scheme="http://nefariousdesigns.co.uk" term="Reference" /><category scheme="http://nefariousdesigns.co.uk" term="Server" /><category scheme="http://nefariousdesigns.co.uk" term="UNIX" /><category scheme="http://nefariousdesigns.co.uk" term="Web" />		<summary type="html"><![CDATA[One of the best things about developing from a Mac is the fact that it is a UNIX-based operating system. This means that my development environment is a far closer match to my production hosting environment (Linux) than it was when I was developing under Windows.
The net result of this shift in paradigm is a [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2008/09/the-joy-of-unix/"><![CDATA[<p>One of the best things about developing from a Mac is the fact that it is a UNIX-based operating system. This means that my development environment is a far closer match to my production hosting environment (Linux) than it was when I was developing under Windows.</p>
<p>The net result of this shift in paradigm is a rapidly expanding knowledge of the command-set and OS structure. The exposure to UNIX has done me the world of good; particularly in regard to web development and server management. Sadly, the relative shelter I was accustomed to at Rentokil Initial, thanks to the inclusion of immensely talented and helpful server admins within our team, had hindered my development as a web developer, and provoked ridicule at the hands of my fellow Y! EU developers (in the nicest possible way, of course).</p>
<p>With all that in mind, here&#8217;s a bunch of stuff I&#8217;ve learnt that might be a useful reference resource&hellip;</p>
<p><span id="more-176"></span></p>
<h2>Books</h2>
<p><a href="http://www.amazon.co.uk/gp/product/013937681X?ie=UTF8&#038;tag=gamersite-21&#038;linkCode=as2&#038;camp=1634&#038;creative=6738&#038;creativeASIN=013937681X"><img src='http://nefariousdesigns.co.uk/blog/images/41qm4speaal_sl160_.jpg' alt='' class='pos1 framed' /></a> The first book I&#8217;d recommend reading for a good grounding in UNIX is &#8220;<a href="http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&#038;location=http%3A%2F%2Fwww.amazon.co.uk%2FUnix-Programming-Environment-Prentice-Hall-Software%2Fdp%2F013937681X%3Fie%3DUTF8%26s%3Dbooks%26qid%3D1222162055%26sr%3D8-1&#038;tag=gamersite-21&#038;linkCode=ur2&#038;camp=1634&#038;creative=6738"><strong>The UNIX Programming Environment</strong></a>&#8220;, by Brian W. Kernighan and Rob Pike. Although this is book is over 20 years old, it&#8217;s still highly relevant; Kernighan and Pike both worked at Bell Labs during the development of UNIX and contributed greatly to the OS and its school of thought. The book covers a huge expanse of subjects, from the basics through to full program development.</p>
<p><a href="http://www.amazon.co.uk/gp/product/0596100299?ie=UTF8&#038;tag=gamersite-21&#038;linkCode=as2&#038;camp=1634&#038;creative=6738&#038;creativeASIN=0596100299"><img src="http://nefariousdesigns.co.uk/blog/images/51w740pbx7l_sl160_.thumbnail.jpg" alt="" class="pos2 framed" /></a>The second book, as is so often the case, is published by O&#8217;Reilly. &#8220;<a href="http://www.amazon.co.uk/gp/product/0596100299?ie=UTF8&#038;tag=gamersite-21&#038;linkCode=as2&#038;camp=1634&#038;creative=6738&#038;creativeASIN=0596100299"><strong>UNIX in a Nutshell</strong></a>&#8220;, by Arnold Robbins, is a great, up-to-date examination of the operating system, including a vast array of information perfect for beginners and experienced users alike.</p>
<p>Both of these books should provide enough knowledge for you to feel safe getting down and dirty with UNIX; so let&#8217;s dive in with a look at some useful keyboard and command line shortcuts:</p>
<h2>Keyboard and Command Line Shortcuts</h2>
<p>The following are common keyboard and command line shortcuts for use in UNIX-based systems. Please note that all UNIX variants differ and some of these shortcuts may not be available to you, or may require different syntax or keystrokes.</p>
<h3>Keyboard Shortcuts</h3>
<p>To begin with, here&#8217;s a list of useful keyboard shortcuts:</p>
<ul>
<li><kbd>CTRL + A</kbd> â€” Moves the cursor to the beginning of the line.</li>
<li><kbd>CTRL + E</kbd> â€” Moves the cursor to the end of the line.</li>
<li><kbd>CTRL + U</kbd> â€” Erases the complete line.</li>
<li><kbd>CTRL + B</kbd> â€” Moves the cursor backward one character.</li>
<li><kbd>CTRL + F</kbd> â€” Moves the cursor forward one character.</li>
<li><kbd>CTRL + H</kbd> â€” Erase one character. Similar to pressing backspace.</li>
<li><kbd>CTRL + W</kbd> â€” Deletes the last word typed.</li>
<li><kbd>CTRL + C</kbd> â€” Cancels the currently running command.</li>
<li><kbd>CTRL + D</kbd> â€” Logs out of the current session.</li>
</ul>
<p>Most of those are self-explanatory, but you&#8217;ll wonder how you lived without them soon enough.</p>
<h3>Command Line Shortcuts</h3>
<p>Next, let&#8217;s take a look at some useful command line shortcuts, starting with <em>directory navigation</em>:</p>
<ul>
<li><code>~</code> â€” References the current user&#8217;s home directory.</li>
<li><code>.</code> â€” References the current directory.</li>
<li><code>..</code> â€” References the parent of the current directory.</li>
</ul>
<p>Here&#8217;s a quick demonstration to hopefully make their use a little clearer (the names have been changed to protect the innocent). Note, this example uses several UNIX commands; <code>cd</code> = change directory, <code>pwd</code> = print working directory, <code>ls</code> = list contents of directory, and <code>cp</code> = copy files:</p>
<pre><code>$ pwd
/Users/timbo
$ cd /var/www/nefarious/
$ pwd
/var/www/nefarious
$ cd ..
$ pwd
/var/www
$ ls
a-website   another-website   nefarious
$ cd /var/www2
$ pwd
/var/www2
$ ls
$ cp /var/www/* ./
$ ls
a-website   another-website   nefarious
$ cd ~
$ pwd
/Users/timbo</code></pre>
<p>Now that we can effectively navigate the directory structure, let&#8217;s have a look at the incredibly useful history utility and its command line shortcuts:</p>
<ul>
<li><code>!!</code> â€” Re-run your last command.</li>
<li><code>!10</code> â€” Re-run line number 10 in the history.</li>
<li><code>!-n</code> â€” Refer to the current command line minus n.</li>
<li><code>!?str?</code> â€” Refer to the most recent command containing str (string).</li>
<li><code>!str</code> â€” Re-run the last command ran that started with str (string).</li>
<li><code>!$</code> â€” Re-use the parameters from the previous command.</li>
</ul>
<p>I won&#8217;t go into detail on each one of those, but here&#8217;s an example of the last one â€” arguably the most useful â€” in action:</p>
<pre><code>$ pwd
/Users/timbo
$ ls projects/django/
cms    coltrane    davis
$ cd !$
cd projects/django/
$ pwd
/Users/timbo/projects/django</code></pre>
<h2>Summary</h2>
<p>This is just a quick reference to some useful shortcuts; I could have delved deeper into setting up aliases in your profiles and making your prompt look pretty, but I thought I&#8217;d save that for a possible follow up post.</p>
<p>I hope somebody out there finds this useful; and feel free to add your own tips and tricks in the comments.</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2008/09/the-joy-of-unix/#comments" thr:count="8" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2008/09/the-joy-of-unix/feed/atom/" thr:count="8" />
		<thr:total>8</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2008/09/the-joy-of-unix/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>Tim</name>
						<uri>http://nefariousdesigns.co.uk/</uri>
					</author>
		<title type="html"><![CDATA[Back on the horse]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/nefariousdesigns/~3/gvr-8V9aXY0/" />
		<id>http://nefariousdesigns.co.uk/archive/2008/08/back-on-the-horse/</id>
		<updated>2008-08-10T11:07:05Z</updated>
		<published>2008-08-10T11:03:01Z</published>
		<category scheme="http://nefariousdesigns.co.uk" term="Personal" />		<summary type="html"><![CDATA[Right then. After almost a years hiatus, it&#8217;s about time I got back into this blogging lark. Unfortunately, I have a plethora of subjects I&#8217;d like to cover and no idea where to start!
I think I&#8217;ll get the personal stuff out of the way first and then I can think about planning the web development [...]]]></summary>
		<content type="html" xml:base="http://nefariousdesigns.co.uk/archive/2008/08/back-on-the-horse/"><![CDATA[<p>Right then. After almost a years hiatus, it&#8217;s about time I got back into this blogging lark. Unfortunately, I have a plethora of subjects I&#8217;d like to cover and no idea where to start!</p>
<p>I think I&#8217;ll get the personal stuff out of the way first and then I can think about planning the web development stuff&hellip;</p>
<p><span id="more-175"></span></p>
<p>The biggest news is that <a href="http://flickr.com/photos/nefarioustim/2587002110/">I&#8217;m going to be a father again</a>, but thankfully this time it&#8217;s just the one baby. Further to that, my twin girls are now 14 months old and <em>extremely</em> mobile (in horizontal <em>and</em> vertical directions &#8212; we&#8217;ve had to rearrange the furniture to discourage climbing).</p>
<p>I&#8217;m still working at <a href="http://www.yahoo.co.uk">Yahoo!</a> in London and, despite the odd crisis of morale, am still enjoying working with what is arguably the best corporate web development team in the world. It&#8217;s a shame my commute is 2 hours each way otherwise I&#8217;d also be enjoying the more social aspects of the team as well (but since most evenings are spent on <a href="http://www.xbox.com">Xbox Live</a> with my colleagues, I&#8217;m not missing <em>too</em> much).</p>
<p>I also found out that I have <a href="http://en.wikipedia.org/wiki/Coeliac_disease">Coeliac Disease</a>; a harsh name for what is, effectively, an allergy to gluten (found in wheat, barley, oats, and rye). This hasn&#8217;t been all that difficult to adapt to since my sister has been diagnosed since she was 3 years old &#8212 ;and my dad worked out, shortly after she was diagnosed, that he is too. This basically means that I&#8217;ve been used to shopping, cooking, and dining-out with Coeliacs since a very early age. Want to know the worst part of the allergy? I can no longer drink beer. Sweet, sweet beer. I really miss a tasty pint of <a href="http://www.guinness.com">Guinness</a>.</p>
<p>Finally, I&#8217;ve also undertaken a few personal projects: a new rock band, a (possible) book on web development, a cooking blog (no link because it&#8217;s not up yet), and various other things.</p>
<p>So that&#8217;s the past year in a nutshell. I&#8217;m sure there&#8217;s lots more I could&#8217;ve written but it&#8217;s easier to just condense. Now I can get started on the web development related blog posts&hellip;</p>
<p>&hellip; although I <em>still</em> have no idea where to start!</p>
]]></content>
		<link rel="replies" type="text/html" href="http://nefariousdesigns.co.uk/archive/2008/08/back-on-the-horse/#comments" thr:count="4" />
		<link rel="replies" type="application/atom+xml" href="http://nefariousdesigns.co.uk/archive/2008/08/back-on-the-horse/feed/atom/" thr:count="4" />
		<thr:total>4</thr:total>
	<feedburner:origLink>http://nefariousdesigns.co.uk/archive/2008/08/back-on-the-horse/</feedburner:origLink></entry>
	</feed>
