<?xml version='1.0' encoding='UTF-8'?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel><title>The Journals of Tarn Barford</title><link>https://tarnbarford.net/atom</link><description>Infrequent blog posts</description><atom:link href="https://tarnbarford.net/atom" rel="self"/><docs>http://www.rssboard.org/rss-specification</docs><generator>python-feedgen</generator><language>en</language><lastBuildDate>Fri, 01 Mar 2024 17:38:06 +0000</lastBuildDate><item><title>debugProxy</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        debugProxy
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 02, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;debugProxy was a project I worked on with &lt;a href="https://twitter.com/D_J_Spencer"&gt;David&lt;/a&gt; in 2017, I hosted and
supported the service at &lt;a href="https://web.archive.org/web/20171210050450/https://debugproxy.com/"&gt;debugproxy.com&lt;/a&gt; for several years. I
did a "Show HN" &lt;a href="https://news.ycombinator.com/item?id=15885703"&gt;thread&lt;/a&gt; when we released it which was on the front
page for a few hours, this gave us plenty of initial users. Despite it working
quite well once setup, configuring devices to use it and trust root
certificates was always fiddly and very difficult if a certificate pinning
technique was used. I made all the &lt;a href="https://github.com/tarnacious/debugproxy/commit/69ed9e439301163e55026ccabe209b5eb410295d"&gt;code&lt;/a&gt; public in 2019. By 2021 usage
was sporadic and I shut it down.&lt;/p&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="500" src="https://www.youtube.com/embed/RxlvvuJv1sg?si=50uupyRUNemvc_Vh" title="YouTube video player" width="900"&gt;&lt;/iframe&gt;
&lt;p&gt;Despite the project not being wildly successful, I enjoyed working on it and am
proud of what we built.&lt;/p&gt;
&lt;p&gt;Because I've apparently learned nothing, I decided to see if it would still
work. It turns out I can't even install the packages to build the frontend code
anymore (React 15.6, Webpack 4), but I could find a copy of the compiled
JavaScript, which I've &lt;a href="https://github.com/tarnacious/debugproxy/commit/986f1b1f6f276371b02a094f1d18153a0edfd9bd"&gt;added&lt;/a&gt; to the repository. To run the backend
code, I had to &lt;a href="https://github.com/tarnacious/server-configuration/commit/cfdf4e12155a53426ab13fe038e16328c5dc0ff0"&gt;build&lt;/a&gt; Python 3.7 and &lt;a href="https://github.com/tarnacious/debugproxy/commit/69ed9e439301163e55026ccabe209b5eb410295d"&gt;pin&lt;/a&gt; a few dependency
versions.&lt;/p&gt;
&lt;p&gt;I regret to say that I have DebugProxy fully working at
&lt;a href="https://debugproxy.tarnbarford.net/"&gt;debugproxy.tarnbarford.net&lt;/a&gt;. I'm definitely not going to provide
support for getting specific devices to use the proxy, but if there are issues
with the proxy or the website, I'd be interested to hear about it.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/debugproxy</guid><pubDate>Thu, 02 Nov 2023 12:00:00 +0000</pubDate></item><item><title>Creating Debian virtual machines</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Creating Debian virtual machines
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 27, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Creating virtual machines is one of the few things in &lt;a href="https://github.com/tarnacious/server-configuration"&gt;my personal computer
infrastructure&lt;/a&gt; I don't configure directly with Ansible.
However, I have tried to make the process as fast and simple as possible. For
generic Debian based virtual machines, I base new virtual machines off a &lt;a href="/journal/building-a-debian-12-base-image"&gt;base
disk image&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I shell into the hypervisor to copy the base image.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@hypervisor:~# &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/images/&lt;span class="o"&gt;{&lt;/span&gt;debian-12-base.qcow2,demo.qcow2&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I then create a new virtual machine with the new disk image.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~# &lt;/span&gt;virt-install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;demo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--network&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bridge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;virbr0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--network&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bridge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;br0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--disk&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/images/demo.qcow2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--vcpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--memory&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--os-variant&lt;span class="w"&gt; &lt;/span&gt;debian11&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--console&lt;span class="w"&gt; &lt;/span&gt;pty,target_type&lt;span class="o"&gt;=&lt;/span&gt;virtio&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--serial&lt;span class="w"&gt; &lt;/span&gt;pty&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--graphics&lt;span class="w"&gt; &lt;/span&gt;none&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--noautoconsole&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--import
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create a headless virtual machine with 4 vCPUs and 4GB RAM. On my
host a standard virtual machine gets two network interfaces. The &lt;code&gt;virbr0&lt;/code&gt;
interface is a virtual IPv4 bridge with DHCP support, this network will be
allocated an IP address on an internal network and have a route to the Internet
when the machine starts. The &lt;code&gt;br0&lt;/code&gt; inferface is a IPv6 bridge provided by the
host system, the network doesn't have DHCP and will need to be configured. The
&lt;code&gt;--serial&lt;/code&gt; and &lt;code&gt;--console&lt;/code&gt; options allow access to the machine via a serial
console, the &lt;code&gt;--noautoconsole&lt;/code&gt; prevents &lt;code&gt;virt-install&lt;/code&gt; automatically connecting
to serial console after creating the virtual machine.&lt;/p&gt;
&lt;p&gt;The next step is to work out what IP address was assigned to the NIC in the
virtual machine. This can be found by looking the DHCP leases, but its made a
bit more complicated because every new virtual machine will initially request a
lease with the hostname &lt;code&gt;debian-12-base&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~# &lt;/span&gt;virsh&lt;span class="w"&gt; &lt;/span&gt;net-dhcp-leases&lt;span class="w"&gt; &lt;/span&gt;default
&lt;span class="go"&gt; Expiry Time           MAC address         Protocol   IP address          Hostname         Client ID or DUID&lt;/span&gt;
&lt;span class="go"&gt;-----------------------------------------------------------------------------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="go"&gt; 2023-10-22 22:26:12   52:54:00:16:b6:7e   ipv4       192.168.122.34/24   debian-12-base   ff:00:16:b6:7e:00:01:00:01:2c:c1:6b:25:52:54:00:16:b6:7e&lt;/span&gt;
&lt;span class="go"&gt; 2023-10-22 22:33:44   52:54:00:b6:9d:8a   ipv4       192.168.122.66/24   debian-12-base   ff:00:b6:9d:8a:00:01:00:01:2c:c1:6b:25:52:54:00:16:b6:7e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To workout which is the correct &lt;code&gt;IP address&lt;/code&gt;, the MAC addresses assigned to the
new virtual machine networks can be used.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~# &lt;/span&gt;virsh&lt;span class="w"&gt; &lt;/span&gt;domiflist&lt;span class="w"&gt; &lt;/span&gt;demo
&lt;span class="go"&gt; Interface   Type     Source   Model    MAC&lt;/span&gt;
&lt;span class="go"&gt;-----------------------------------------------------------&lt;/span&gt;
&lt;span class="go"&gt; vnet85      bridge   virbr0   virtio   52:54:00:b6:9d:8a&lt;/span&gt;
&lt;span class="go"&gt; vnet86      bridge   br0      virtio   52:54:00:07:91:8f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This IP isn't is not directly accessable from over the Internet and I don't
have a VPN setup with the hypervisor, however I do have an &lt;code&gt;sshd&lt;/code&gt; daemon on it
and I can use that as jump-host.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~# &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-J&lt;span class="w"&gt; &lt;/span&gt;hypervisor.tarnbarford.net&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192&lt;/span&gt;.168.122.66
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I wouldn't usually SSH directly in at this point, but rather have Ansible use
SSH to configure the system from here, including the &lt;a href="https://github.com/tarnacious/server-configuration/blob/8b1c16da21241d26ac778403643ff63ed606748a/roles/vm-networking/templates/interfaces#L1-L16"&gt;networking&lt;/a&gt;
where I configure static IPv4 and IPv6 addresses. This means on the first run I
use the DHCP assigned address as the &lt;code&gt;ansible_host&lt;/code&gt; subsequently the staticly
configured address.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;hosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;ansible_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;tarn&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;ansible_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;192.168.122.66&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;ansible_ssh_common_args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"-o&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ProxyCommand='ssh&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-q&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-W&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;%h:%p&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tarn@hypervisor.tarnbarford.net'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The virtual machine can then be accessed from the Internet via its static IPv6
address. To make it accessable via IPv4 I use &lt;code&gt;iptables&lt;/code&gt; rules to route a
specific port to a specific virtual machine address. All HTTP/HTTPS traffic is
routed to load balancer running HAProxy which uses the &lt;code&gt;Host&lt;/code&gt; header or Server
Name Indication (SNI) to route traffic to a specific VMs.&lt;/p&gt;
&lt;p&gt;The process is pretty quick but it is still significantly more fiddly than
starting a virtual machine with a cloud provider.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/creating-debian-virtual-machines</guid><pubDate>Fri, 27 Oct 2023 12:00:00 +0000</pubDate></item><item><title>Building a Debian 12 base image</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;script src="splide-4.1.3/dist/js/splide.min.js"&gt;&lt;/script&gt;
&lt;link rel="stylesheet" href="splide-4.1.3/dist/css/splide.min.css"&gt;



    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Building a Debian 12 base image
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 27, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I need a new Debian-based image to create new virtual machines since the old
one is now two major versions out of date. I previously created the base
images using &lt;a href="https://github.com/tarnacious/server-configuration/blob/8b1c16da21241d26ac778403643ff63ed606748a/roles/libvirt/server/scripts/templates/build_vm#L1"&gt;a script&lt;/a&gt; that creates a disk image, mounts it as a
block device and installs Debian with debootstrap. This process is somewhat
tricky because it requires privileges and kernel modules to access system
network block devices. It also takes several minutes to install the system, for
creating a new virtual machine it's much quicker and simpler to &lt;a href="https://github.com/tarnacious/server-configuration/blob/8b1c16da21241d26ac778403643ff63ed606748a/roles/libvirt/server/scripts/templates/create_domain#L3"&gt;copy a base
image&lt;/a&gt;. Rather than get the script working, this time I'm just
going to create a new virtual machine and use the graphical Debian installer to
manually build a new image.&lt;/p&gt;
&lt;p&gt;The installation media is downloaded on the host system.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@hypervisor:~/# &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/installers/debian-12.1.0-amd64-netinst.iso&lt;span class="w"&gt; &lt;/span&gt;https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.1.0-amd64-netinst.iso
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A &lt;code&gt;qcow2&lt;/code&gt; disk image is created on the server.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@hypervisor:~/# &lt;/span&gt;qemu-img&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;qcow2&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/images/debian-12-base.qcow2&lt;span class="w"&gt; &lt;/span&gt;20G
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As I've prevously configured &lt;a href="https://tarnbarford.net/journal/using-libvirtd-remotely/"&gt;remote libvirt connections with
TLS&lt;/a&gt; I can connect to a remote host with &lt;code&gt;virt-install&lt;/code&gt;,
&lt;code&gt;virt-viewer&lt;/code&gt; and &lt;code&gt;virsh&lt;/code&gt; by exporting an environment variable.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~/# &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LIBVIRT_DEFAULT_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qemu+tls://hypervisor.tarnbarford.net:16514/system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I use &lt;code&gt;virt-install&lt;/code&gt; to create a new virtual machine.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~/# &lt;/span&gt;virt-install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;debian-12-base&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--connect&lt;span class="w"&gt; &lt;/span&gt;qemu+tls://hypervisor.tarnbarford.net:16514/system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--network&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bridge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;virbr0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--disk&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/images/debian-12-base.qcow2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--vcpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--memory&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--os-variant&lt;span class="w"&gt; &lt;/span&gt;debian11&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--graphics&lt;span class="w"&gt; &lt;/span&gt;vnc&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--console&lt;span class="w"&gt; &lt;/span&gt;pty,target_type&lt;span class="o"&gt;=&lt;/span&gt;virtio&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--serial&lt;span class="w"&gt; &lt;/span&gt;pty&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--cdrom&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/installers/debian-12.1.0-amd64-netinst.iso
&lt;span class="go"&gt;Starting install...&lt;/span&gt;
&lt;span class="go"&gt;Creating domain...&lt;/span&gt;
&lt;span class="go"&gt;Running graphical console command: virt-viewer --connect qemu+tls://hypervisor.tarnbarford.net:16514/system --wait debian-12-base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The virtual machine will use a network bridge on the host machine called
&lt;code&gt;virbr0&lt;/code&gt; the DHCP service will allocate an IP address on an internal network
and the bridge provides a route to the Internet. Both the installation media
and the disk image are specified. I'll need graphics for the installation, I'll
use VNC which I've previously configured to run remote over a secure TLS
connection. The &lt;code&gt;--console&lt;/code&gt; and &lt;code&gt;--serial&lt;/code&gt; options will allow me to make a
serial console connection once it's enabled on the virtual machine.&lt;/p&gt;
&lt;p&gt;This brings up a virt-viewer window on which I can run through the standard
Debian graphic install process. I've taken some screenshot of the process.
After completing the installation the system will reboot and the &lt;code&gt;virt-viewer&lt;/code&gt;
window will close.&lt;/p&gt;
&lt;section aria-label="Debian installation" class="splide"&gt;
&lt;div class="splide__track"&gt;
&lt;ul class="splide__list"&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="1.png" src="images/1.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="2.png" src="images/2.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="3.png" src="images/3.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="4.png" src="images/4.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="5.png" src="images/5.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="6.png" src="images/6.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="7.png" src="images/7.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="8.png" src="images/8.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="9.png" src="images/9.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="10.png" src="images/10.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="11.png" src="images/11.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="12.png" src="images/12.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="13.png" src="images/13.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="14.png" src="images/14.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="15.png" src="images/15.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="16.png" src="images/16.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="17.png" src="images/17.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="18.png" src="images/18.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="19.png" src="images/19.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="20.png" src="images/20.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="21.png" src="images/21.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="22.png" src="images/22.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="23.png" src="images/23.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="24.png" src="images/24.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="25.png" src="images/25.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="26.png" src="images/26.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="27.png" src="images/27.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="28.png" src="images/28.png" width="100%"/&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;For the rest of the setup I prefer to use a serial console rather than VNC,
mostly because copy and paste works and I don't have to deal with a window the
hijacks my mouse cursor, but this won't work just yet.&lt;/p&gt;
&lt;p&gt;First, I need a new VNC connection. I can either use &lt;code&gt;virt-manager&lt;/code&gt; or &lt;code&gt;virt-viewer&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~/# &lt;/span&gt;virt-viewer&lt;span class="w"&gt; &lt;/span&gt;debain-12-base
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the VNC window I just want to configure the system so it supports a serial
console. Enabling the &lt;code&gt;serial-getty@ttyS0&lt;/code&gt; serice &lt;em&gt;should&lt;/em&gt; be enough, but for
some reason this &lt;em&gt;didn't&lt;/em&gt; work after I cloned the base image.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@debian-12-base:~/# &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;serial-getty@ttyS0.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's also possible to configure this in &lt;code&gt;grub&lt;/code&gt; which worked reliably for me.
First edit &lt;code&gt;/etc/default/grub&lt;/code&gt; and ensure the following to values are set.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,115200n8"
GRUB_TERMINAL=console
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once that's done, &lt;code&gt;grub&lt;/code&gt; needs to be updated.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@debian-12-base:~/# &lt;/span&gt;update-grub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now it should be possible to get a serial console with &lt;code&gt;libvirt&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~/# &lt;/span&gt;virsh&lt;span class="w"&gt; &lt;/span&gt;console&lt;span class="w"&gt; &lt;/span&gt;debian-12-base
&lt;span class="go"&gt;Connected to domain 'debian-12-base'&lt;/span&gt;
&lt;span class="go"&gt;Escape character is ^] (Ctrl + ])&lt;/span&gt;

&lt;span class="go"&gt;debian-12-base login: root&lt;/span&gt;
&lt;span class="go"&gt;Password:&lt;/span&gt;
&lt;span class="go"&gt;Linux debian-12-base 6.1.0-13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.55-1 (2023-09-29) x86_64&lt;/span&gt;
&lt;span class="gp"&gt;root@debian-12-base:~#&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;From this console we do the remaining configuration. Firstly install a couple of packages:&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@debian-12-base:~# &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;curl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add the reguarly user &lt;code&gt;tarn&lt;/code&gt; created during the install process to the &lt;code&gt;sudo&lt;/code&gt;
group.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@debian-12-base:~# &lt;/span&gt;usermod&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;-G&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;tarn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add my SSH key so I can SSH into the server as the &lt;code&gt;tarn&lt;/code&gt; user.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@debian-12-base:~# &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/home/tarn/.ssh
&lt;span class="gp"&gt;root@debian-12-base:~# &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;/home/tarn/.ssh/authorized_keys&lt;span class="w"&gt; &lt;/span&gt;https://github.com/tarnacious.keys
&lt;span class="gp"&gt;root@debian-12-base:~# &lt;/span&gt;chown&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;tarn:tarn&lt;span class="w"&gt; &lt;/span&gt;/home/tarn/.ssh
&lt;span class="gp"&gt;root@debian-12-base:~# &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/home/tarn/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Clear the history&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@debian-12-base:~# &lt;/span&gt;&lt;span class="nb"&gt;history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-w
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The close the bash shell with &lt;code&gt;ctrl d&lt;/code&gt; and then the serial console with &lt;code&gt;ctrl
]&lt;/code&gt;. The base image is now configured so we can shut down the VM and delete it's
&lt;code&gt;libvirt&lt;/code&gt; configuration, the disk is the only thing we care about.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@thinkpad:~/# &lt;/span&gt;virsh&lt;span class="w"&gt; &lt;/span&gt;destroy&lt;span class="w"&gt; &lt;/span&gt;debian-12-base
&lt;span class="gp"&gt;tarn@thinkpad:~/# &lt;/span&gt;virsh&lt;span class="w"&gt; &lt;/span&gt;undefined&lt;span class="w"&gt; &lt;/span&gt;debian-12-base
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The file &lt;code&gt;/var/kvm/images/debian-12-base.qcow2&lt;/code&gt; on the host is now a bootable
Debian image that I can copy and create new virtual machines from.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script&gt;
   new Splide( '.splide' ).mount();
&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/building-a-debian-12-base-image</guid><pubDate>Fri, 27 Oct 2023 11:00:00 +0000</pubDate></item><item><title>Building NixOS qcow2 images with flakes</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Building NixOS qcow2 images with flakes
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 25, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've been thinking again about using NixOS to build bootable disk images I can
run on a remote qemu/KVM host. I worked out &lt;a href="https://gist.github.com/tarnacious/f9674436fff0efeb4bb6585c79a3b9ff"&gt;how to build bootable &lt;code&gt;qcow2&lt;/code&gt; disk
images&lt;/a&gt; a few years back, which still seems to work, but I
wanted something that worked with &lt;a href="https://nixos.wiki/wiki/Flakes"&gt;&lt;em&gt;flakes&lt;/em&gt;&lt;/a&gt; which I've since adopted.&lt;/p&gt;
&lt;p&gt;I initially found a way using &lt;a href="https://github.com/nix-community/nixos-generators"&gt;nixos-generators&lt;/a&gt; which worked
pretty well until I wanted to &lt;a href="https://github.com/nix-community/nixos-generators/pull/195"&gt;change the diskSize&lt;/a&gt;. This wasn't
a big issue, but I realised I didn't really need nixos-generators either.&lt;/p&gt;
&lt;p&gt;This is a pretty standard looking &lt;code&gt;configuration.nix&lt;/code&gt; module that describes an
example system to build.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; config&lt;span class="p"&gt;,&lt;/span&gt; lib&lt;span class="p"&gt;,&lt;/span&gt; pkgs&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  boot&lt;span class="o"&gt;.&lt;/span&gt;kernelPackages &lt;span class="o"&gt;=&lt;/span&gt; pkgs&lt;span class="o"&gt;.&lt;/span&gt;linuxPackages_5_15&lt;span class="p"&gt;;&lt;/span&gt;

  users&lt;span class="o"&gt;.&lt;/span&gt;users &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    tarn &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      isNormalUser &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      extraGroups &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"wheel"&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
      password &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  environment&lt;span class="o"&gt;.&lt;/span&gt;systemPackages &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; pkgs&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    python310
  &lt;span class="p"&gt;];&lt;/span&gt;

  system&lt;span class="o"&gt;.&lt;/span&gt;stateVersion &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"23.05"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The configuration for building &lt;code&gt;qcow2&lt;/code&gt; disk images image can be separated into
it's own module called &lt;code&gt;qcow.nix&lt;/code&gt;. The attribute &lt;code&gt;system.build.qcow2&lt;/code&gt; is set to use the NixOS
&lt;a href="https://github.com/NixOS/nixpkgs/blob/master/nixos/lib/make-disk-image.nix"&gt;&lt;code&gt;make-disk-image.nix&lt;/code&gt;&lt;/a&gt; magic.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; config&lt;span class="p"&gt;,&lt;/span&gt; lib&lt;span class="p"&gt;,&lt;/span&gt; pkgs&lt;span class="p"&gt;,&lt;/span&gt; modulesPath&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  imports &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;toString&lt;/span&gt; modulesPath&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/profiles/qemu-guest.nix"&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  fileSystems&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    device &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/dev/disk/by-label/nixos"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    autoResize &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    fsType &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ext4"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  boot&lt;span class="o"&gt;.&lt;/span&gt;kernelParams &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"console=ttyS0"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  boot&lt;span class="o"&gt;.&lt;/span&gt;loader&lt;span class="o"&gt;.&lt;/span&gt;grub&lt;span class="o"&gt;.&lt;/span&gt;device &lt;span class="o"&gt;=&lt;/span&gt; lib&lt;span class="o"&gt;.&lt;/span&gt;mkDefault &lt;span class="s2"&gt;"/dev/vda"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  system&lt;span class="o"&gt;.&lt;/span&gt;build&lt;span class="o"&gt;.&lt;/span&gt;qcow2 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;modulesPath&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/../lib/make-disk-image.nix"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;inherit&lt;/span&gt; lib config pkgs&lt;span class="p"&gt;;&lt;/span&gt;
    diskSize &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10240&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    format &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"qcow2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    partitionTableType &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hybrid"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To tie it all together we can create a &lt;code&gt;flake.nix&lt;/code&gt; file which describes an
input and uses the modules above to build an output.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Example Virtual Machine Configuration"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  inputs &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
    nixpkgs&lt;span class="o"&gt;.&lt;/span&gt;url &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github:NixOS/nixpkgs/nixos-23.05"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  outputs &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; self&lt;span class="p"&gt;,&lt;/span&gt; nixpkgs &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    nixosConfigurations &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;# configuration for builidng qcow2 images&lt;/span&gt;
      build-qcow2 &lt;span class="o"&gt;=&lt;/span&gt; nixpkgs&lt;span class="o"&gt;.&lt;/span&gt;lib&lt;span class="o"&gt;.&lt;/span&gt;nixosSystem &lt;span class="p"&gt;{&lt;/span&gt;
        system &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"x86_64-linux"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        modules &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="l"&gt;/configuration.nix&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="l"&gt;/qcow.nix&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To run a flake all the configuration needs to be added to a git repository.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;init
&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To build the &lt;code&gt;qcow2&lt;/code&gt; images &lt;code&gt;nix build&lt;/code&gt; is used. Here we use our &lt;code&gt;build_qcow2&lt;/code&gt;
configuration and the &lt;code&gt;system.build.qcow2&lt;/code&gt; attribute from earlier which is now
somehow available.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;nix&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;.#nixosConfigurations.build-qcow2.config.system.build.qcow2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This builds a qcow2 disk image in the nix store and creates a &lt;code&gt;result&lt;/code&gt; symlink
to the the containing directory in the store. To try out the disk image, we'll
need a writable copy that can be mounted as a writable disk by &lt;code&gt;qemu&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;result/nixos.qcow2&lt;span class="w"&gt; &lt;/span&gt;root.qcow2
&lt;span class="gp"&gt;$ &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;644&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;root.qcow2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The image can now be used to boot a qemu virtual machine&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;qemu-kvm&lt;span class="w"&gt; &lt;/span&gt;-name&lt;span class="w"&gt; &lt;/span&gt;nixos&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;4G&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;-smp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;-drive&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;writeback,file&lt;span class="o"&gt;=&lt;/span&gt;root.qcow2,id&lt;span class="o"&gt;=&lt;/span&gt;drive1,if&lt;span class="o"&gt;=&lt;/span&gt;none,index&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,werror&lt;span class="o"&gt;=&lt;/span&gt;report&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;-device&lt;span class="w"&gt; &lt;/span&gt;virtio-blk-pci,bootindex&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,drive&lt;span class="o"&gt;=&lt;/span&gt;drive1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;-nographic
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;&amp;lt;&amp;lt; Welcome to NixOS 23.05.20230924.261abe8 (x86_64) - ttyS0 &amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class="go"&gt;Run 'nixos-help' for the NixOS manual.&lt;/span&gt;

&lt;span class="go"&gt;nixos login: tarn&lt;/span&gt;
&lt;span class="go"&gt;Password:&lt;/span&gt;

&lt;span class="gp"&gt;[tarn@nixos:~]$ &lt;/span&gt;uname&lt;span class="w"&gt; &lt;/span&gt;-a
&lt;span class="go"&gt;Linux nixos 5.15.133 #1-NixOS SMP Sat Sep 23 09:10:03 UTC 2023 x86_64 GNU/Linux&lt;/span&gt;

&lt;span class="gp"&gt;[tarn@nixos:~]$ &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;span class="go"&gt;Python 3.10.12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To kill the VM shut it down with the &lt;code&gt;poweroff&lt;/code&gt; command or use &lt;code&gt;ctrl + a&lt;/code&gt;
followed by &lt;code&gt;x&lt;/code&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/building-nixos-qcow2-images-with-flakes</guid><pubDate>Mon, 25 Sep 2023 12:00:00 +0000</pubDate></item><item><title>Using libvirtd VNC Remotely</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Using libvirtd VNC Remotely
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 20, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Continuing on this foolish path to &lt;a href="/journal/using-libvirtd-remotely"&gt;manage some virtual machines on a remote
host with TLS and peer authentication&lt;/a&gt;, here I'm going
to configure VNC.&lt;/p&gt;
&lt;p&gt;The trick here is we're really configuring &lt;code&gt;qemu&lt;/code&gt; not &lt;code&gt;libvirtd&lt;/code&gt; as &lt;code&gt;qemu&lt;/code&gt;
itself provides the VNC server. On my Debian system the &lt;em&gt;default&lt;/em&gt; configuration
file is &lt;a href="etc/libvirt/qemu.conf"&gt;&lt;code&gt;/etc/libvirt/qemu.conf&lt;/code&gt;&lt;/a&gt;. By default, TLS for VNC is not
enabled.&lt;/p&gt;
&lt;p&gt;To &lt;a href="https://github.com/tarnacious/server-configuration/blob/ffd8e76b27519332656f2c94082f0c5c494c56d0/roles/libvirt/server/tls/templates/qemu.conf#L72"&gt;listen on all interfaces&lt;/a&gt;, &lt;a href="https://github.com/tarnacious/server-configuration/blob/ffd8e76b27519332656f2c94082f0c5c494c56d0/roles/libvirt/server/tls/templates/qemu.conf#L93"&gt;use TLS&lt;/a&gt; and &lt;a href="https://github.com/tarnacious/server-configuration/blob/ffd8e76b27519332656f2c94082f0c5c494c56d0/roles/libvirt/server/tls/templates/qemu.conf#L126"&gt;do peer authentication&lt;/a&gt; the following
options need to be &lt;a href="https://github.com/tarnacious/server-configuration/blob/ffd8e76b27519332656f2c94082f0c5c494c56d0/roles/libvirt/server/tls/tasks/main.yml#L32-L36"&gt;set&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vnc_listen = "0.0.0.0"
vnc_tls = 1
vnc_tls_x509_verify = 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The default certificates directories are either the default &lt;code&gt;qemu&lt;/code&gt; certificate
directory.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/etc/pki/qemu/ca-cert.pem
/etc/pki/qemu/server-cert.pem
/etc/pki/qemu/server-key.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or the &lt;code&gt;qemu&lt;/code&gt; VNC certificate directory.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/etc/pki/libvirt-vnc/ca-cert.pem
/etc/pki/libvirt-vnc/server-cert.pem
/etc/pki/libvirt-vnc/server-key.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I chose to &lt;a href="https://github.com/tarnacious/server-configuration/blob/ffd8e76b27519332656f2c94082f0c5c494c56d0/roles/libvirt/server/tls/tasks/main.yml#L38-L54"&gt;copy them to the latter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;VNC ports are allocated by &lt;code&gt;qemu&lt;/code&gt; starting a port 5900 by default. I'm going to
assume these are reused when either a domain is destroyed or deleted and
&lt;a href="https://github.com/tarnacious/server-configuration/blob/ffd8e76b27519332656f2c94082f0c5c494c56d0/roles/hypervisor/tasks/main.yml#L70-L74"&gt;allowed the port range 5900:5910 through the firewall&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I could only find limited &lt;a href="https://wiki.libvirt.org/VNCTLSSetup.html"&gt;documentation&lt;/a&gt; about where
&lt;code&gt;virt-manager&lt;/code&gt; or &lt;code&gt;virt-viewer&lt;/code&gt; expects the certificates to be installed for
making VNC connections. I ended up using &lt;code&gt;strace&lt;/code&gt; to see where it was trying to
read them from.&lt;/p&gt;
&lt;p&gt;I was a bit surprised to find they actually expect the client certificates in
different paths. Here is where &lt;code&gt;virt-manager&lt;/code&gt; reads certificates.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;~/.pki/CA/cacert.pem
~/.pki/libvirt-vnc/clientcert.pem
~/.pki/libvirt-vnc/private/clientkey.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And &lt;code&gt;virt-viewer&lt;/code&gt; here, which &lt;em&gt;partially&lt;/em&gt; overlaps where libvirt reads its
certificates.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;~/.pki/CA/cacert.pem
~/.pki/libvirt/clientcert.pem
~/.pki/libvirt/private/clientkey.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After &lt;a href="https://github.com/tarnacious/server-configuration/blob/ffd8e76b27519332656f2c94082f0c5c494c56d0/roles/libvirt/client/certificates/tasks/main.yml#L27-L49"&gt;copying the missing certificates&lt;/a&gt; we should be
ready to go.&lt;/p&gt;
&lt;p&gt;To create a new virtual machines with a graphical installer, which is what I
wanted to do, I still need to download the installer ISO disk image on the
server.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;https://channels.nixos.org/nixos-23.05/latest-nixos-gnome-x86_64-linux.iso&lt;span class="w"&gt; &lt;/span&gt;-P&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/installers/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's now possible to create a new virtual machine with &lt;code&gt;virt-manager&lt;/code&gt; and then
use a graphical installer to install an operating system through a VNC display.
&lt;a href="images/virt-manager-new-vm-1.png"&gt;1&lt;/a&gt; &lt;a href="images/virt-manager-new-vm-2.png"&gt;2&lt;/a&gt; &lt;a href="images/virt-manager-new-vm-3.png"&gt;3&lt;/a&gt; &lt;a href="images/virt-manager-new-vm-4.png"&gt;4&lt;/a&gt; &lt;a href="images/virt-manager-new-vm-5.png"&gt;5&lt;/a&gt; &lt;a href="images/virt-manager-new-vm-6.png"&gt;6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I can also script the virtual machine creation with &lt;code&gt;virt-install&lt;/code&gt; which will
launch the &lt;code&gt;virt-viewer&lt;/code&gt; VNC client. A list of possible &lt;code&gt;--os-variant&lt;/code&gt; values
can be found run running &lt;code&gt;virt-install --osinfo list&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;virt-install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--connect&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'qemu+tls://hypervisor.tarnbarford.net:16514/system'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;demo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--memory&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2048&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--vcpus&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--disk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--cdrom&lt;span class="w"&gt; &lt;/span&gt;/var/kvm/installers/latest-nixos-gnome-x86_64-linux.iso&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--boot&lt;span class="w"&gt; &lt;/span&gt;cdrom,hd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--os-variant&lt;span class="w"&gt; &lt;/span&gt;nixos-22.05&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--graphics&lt;span class="w"&gt; &lt;/span&gt;vnc

&lt;span class="go"&gt;Starting install...&lt;/span&gt;
&lt;span class="go"&gt;Allocating 'demo.qcow2'                                                                                                    | 3.1 MB  00:00:03 ...&lt;/span&gt;
&lt;span class="go"&gt;Creating domain...                                                                                                         |    0 B  00:00:00&lt;/span&gt;
&lt;span class="go"&gt;Running graphical console command: virt-viewer --connect qemu+tls://hypervisor.tarnbarford.net:16514/system --wait demo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Bam! A &lt;code&gt;virt-viewer&lt;/code&gt; window appears displying the install media boot screen.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/virt-install.png" style="width: 100%"/&gt;&lt;/p&gt;
&lt;p&gt;This is pretty great, I can now quickly and easily create new virtual machines
on the server and immediately connect to their display and input devices using
VPN client.&lt;/p&gt;
&lt;p&gt;I'm still not fully satisfied, when I create virtual machines on my laptop I
always prefer &lt;a href="https://www.spice-space.org/"&gt;SPICE&lt;/a&gt; over VPN. The main reason is that SPICE has a
feature "Redirect USB device" which works &lt;em&gt;really well&lt;/em&gt; for many of my
workflows that rely on &lt;a href="https://shop.nitrokey.com/shop/product/nkpr2-nitrokey-pro-2-3"&gt;my USB security key&lt;/a&gt;. The reason I didn't
setup SPICE initially is that it doesn't support TLS peer authentication (!). I
have an idea how I could make this work which I'd like to try, but I'll leave
that for another time.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/using-libvirtd-vnc-remotely</guid><pubDate>Wed, 20 Sep 2023 12:00:00 +0000</pubDate></item><item><title>Using libvirtd Remotely</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Using libvirtd Remotely
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 18, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I have a headless server running &lt;a href="https://libvirt.org/"&gt;&lt;code&gt;libvirtd&lt;/code&gt;&lt;/a&gt; with a
&lt;a href="https://www.linux-kvm.org/page/Main_Page"&gt;&lt;code&gt;KVM&lt;/code&gt;&lt;/a&gt;/&lt;a href="https://www.qemu.org/"&gt;&lt;code&gt;qemu&lt;/code&gt;&lt;/a&gt; backend and I wanted to securely connect to it with
&lt;a href="https://libvirt.org/manpages/virsh.html#description"&gt;&lt;code&gt;virsh&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://virt-manager.org/"&gt;&lt;code&gt;virt-manager&lt;/code&gt;&lt;/a&gt; on my laptop. There are
&lt;a href="https://libvirt.org/remote.html"&gt;several ways to do this&lt;/a&gt;, I decided to try &lt;a href="https://en.wikipedia.org/wiki/Transport_Layer_Security"&gt;TLS&lt;/a&gt; with a
&lt;a href="/journal/simple-private-public-key-infrastructure/"&gt;private PKI&lt;/a&gt; for transport encryption and peer authentication.&lt;/p&gt;
&lt;p&gt;I wrote &lt;a href="https://github.com/tarnacious/server-configuration/blob/43c2d99bfabf22078415536da9ed2eb655bee543/scripts/gen-libvirt-certificates"&gt;a script&lt;/a&gt; to generate a Certificate Authority (CA)
certificate, then generate certificates for the client and the server that are
both signed by the CA certificate.&lt;/p&gt;
&lt;p&gt;The server runnning libvirtd needs to be configured to support TLS connections.
On Debian systems the libvirtd configuration is in
&lt;a href="etc/libvirt/libvirtd.conf"&gt;&lt;code&gt;/etc/libvirt/libvirtd.conf&lt;/code&gt;&lt;/a&gt;. The relevant default
configuration values were good and I didn't make any changes to this file.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;listen_tls = 1
tls_port = "16514"
key_file = "/etc/pki/libvirt/private/serverkey.pem"
cert_file = "/etc/pki/libvirt/servercert.pem"
ca_file = "/etc/pki/CA/cacert.pem"
tls_no_verify_certificate = 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code&gt;libvirtd&lt;/code&gt; itself, I only needed to &lt;a href="https://github.com/tarnacious/server-configuration/blob/43c2d99bfabf22078415536da9ed2eb655bee543/roles/libvirt/server/tls/tasks/main.yml#L10-L29"&gt;copy the server certificates&lt;/a&gt; into the
locations specified in the configuration files and then &lt;a href="https://github.com/tarnacious/server-configuration/blob/43c2d99bfabf22078415536da9ed2eb655bee543/roles/libvirt/server/tls/handlers/main.yml#L1"&gt;restart the service&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the server, I needed to &lt;a href="https://github.com/tarnacious/server-configuration/blob/43c2d99bfabf22078415536da9ed2eb655bee543/roles/hypervisor/tasks/main.yml#L65"&gt;enable access to the port 16514&lt;/a&gt;
through the filewall.&lt;/p&gt;
&lt;p&gt;Debian has systemd unit for libvirt socket activation via the TLS port
&lt;a href="lib/systemd/system/libvirtd-tls.socket"&gt;/lib/systemd/system/libvirtd-tls.socket&lt;/a&gt;. This means
&lt;code&gt;libvirtd&lt;/code&gt; will start when a connection to port 16514 is made.&lt;/p&gt;
&lt;p&gt;For the client I needed to &lt;a href="https://github.com/tarnacious/server-configuration/blob/43c2d99bfabf22078415536da9ed2eb655bee543/roles/libvirt/client/certificates/tasks/main.yml#L10C1-L23"&gt;copy my client certificates&lt;/a&gt; to the locations to &lt;a href="https://libvirt.org/kbase/tlscerts.html"&gt;where libvirt expects them&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'm now able to connect to my server with &lt;code&gt;virsh&lt;/code&gt; on my laptop.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ virsh -c qemu+tls://hypervisor.tarnbarford.net:16514/system list
 Id   Name           State
------------------------------
 1    bacula         running
 2    loadbalancer   running
 3    mail-server    running
 4    tarnbarford    running
 5    monitoring     running
 7    bab-website    running
 8    owncloud       running
 9    icinga         running
 10   dns            running
 11   ns1            running
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I can connect to the serial console of the virtual machines, which is pretty
useful when working with headless servers.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ virsh -c qemu+tls://hypervisor.tarnbarford.net:16514/system console tarnbarford
Connected to domain 'tarnbarford'
Escape character is ^] (Ctrl + ])

tarnbarford login:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And I can connect &lt;code&gt;virt-manager&lt;/code&gt; to work with &lt;code&gt;libvirtd&lt;/code&gt; on my server.&lt;/p&gt;
&lt;p&gt;&lt;img src="virt-manager.png" style="width: 100%"/&gt;&lt;/p&gt;
&lt;p&gt;What isn't yet working is using &lt;code&gt;virt-viewer&lt;/code&gt; with &lt;a href="https://en.wikipedia.org/wiki/Virtual_Network_Computing"&gt;VNC&lt;/a&gt; or &lt;a href="https://www.spice-space.org/"&gt;SPICE&lt;/a&gt;
to conect to the displays of the virtual machines.&lt;/p&gt;
&lt;p&gt;&lt;img src="virt-viewer.png" style="width: 100%"/&gt;&lt;/p&gt;
&lt;p&gt;The VNC and SPICE connections are directly from virtual
machine emulators, not via &lt;code&gt;libvirtd&lt;/code&gt; so this needs to be setup separately. I'd
really like to get this working smoothly, so I expect to post something about
this soon.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/using-libvirtd-remotely</guid><pubDate>Mon, 18 Sep 2023 12:00:00 +0000</pubDate></item><item><title>Securing TCP Sockets with TLS</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Securing TCP Sockets with TLS
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 14, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Using a &lt;a href="/journal/simple-private-public-key-infrastructure/"&gt;simple private Public Key Infrastructure&lt;/a&gt; (PKI), plain TCP
sockets can be secured with TLS. This can be demonstrated with trivial examples
that run locally on a loopback device.&lt;/p&gt;
&lt;p&gt;A simple echo TCP socket server that listens on port 7000 can be created with
&lt;code&gt;socat&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ socat PIPE TCP-LISTEN:7000,fork
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When data is sent to the socket, it will echo it back. The &lt;code&gt;ncat&lt;/code&gt; tool from
&lt;code&gt;nmap&lt;/code&gt; is used because it has TLS features that will be needed later.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ echo "hello" | ncat localhost 7000
hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The socket listening on port 7000 won't actually be secured, sorry, instead a
new socket listening on port 6000 will provide a secure TLS tunnel/proxy.&lt;/p&gt;
&lt;p&gt;The tunnel is created with &lt;code&gt;stunnel&lt;/code&gt;. It's configured with local file called
&lt;a href="static/stunnel.conf"&gt;&lt;code&gt;stunnel.conf&lt;/code&gt;&lt;/a&gt; which contains a section for the tunnel.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;[service1]
accept = 6000
connect = 7000
CAfile = ca.crt
cert = service1.crt
key = service1.key
verify = 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The tunnel is called &lt;code&gt;service1&lt;/code&gt;, it listens on port 6000 and creates a tunnel
to port 7000. The CA and &lt;code&gt;service1&lt;/code&gt; certificates were created by the PKI. The
&lt;code&gt;verify&lt;/code&gt; option is set to 0 meaning that peer (client) certificates will not be
required or verified.&lt;/p&gt;
&lt;p&gt;The tunnel is started by running &lt;code&gt;stunnel&lt;/code&gt; with the path to the configuration
file.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ stunnel service1.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Trying to connect port 6000 like before won't work.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ echo "hello" | ncat localhost 6000
Ncat: Connection reset by peer.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The server is expecting the client to start a TLS negotiation. Instead, the
characters "hello" are being sent, so the server disconnects. The error
&lt;a href="static/not_ssl.log"&gt;logged&lt;/a&gt; by &lt;code&gt;stunnel&lt;/code&gt; is "SSL routines::wrong version number" which
isn't particularly helpful.&lt;/p&gt;
&lt;p&gt;Fortunately &lt;code&gt;ncat&lt;/code&gt; supports TLS/SSL and it be used with the &lt;code&gt;--ssl&lt;/code&gt; option.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ echo "hello" | ncat --ssl localhost 6000
hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A response is now returned through an encrypted TLS connection.&lt;/p&gt;
&lt;p&gt;If this connection was made over an insecure network, there is no certainty
that the server being connected to is really &lt;code&gt;service1&lt;/code&gt;. With TLS, certificates
used by 'service1' can be verified to have been signed by a specific CA and
belonging to service1.&lt;/p&gt;
&lt;p&gt;To do this with &lt;code&gt;netcat&lt;/code&gt; the &lt;code&gt;--ssl-verify&lt;/code&gt; option tells &lt;code&gt;ncat&lt;/code&gt; to check the
server certificate. The certificate should be signed by a specific CA, so
&lt;code&gt;ca.crt&lt;/code&gt; needs to be specified with the &lt;code&gt;--ssl-trustfile&lt;/code&gt; option. The name in
the certificate will also be validated, so &lt;code&gt;service1&lt;/code&gt; needs to specified with
the &lt;code&gt;--ssl-servername&lt;/code&gt; option.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ echo "hello" | ncat --ssl --ssl-verify --ssl-servername service1 --ssl-trustfile ca_public_key.pem  localhost 6000
hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Expecting a different server name should result in a certificate error.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;echo "hello" | ncat --ssl --ssl-verify --ssl-servername service2 --ssl-trustfile ca_public_key.pem  localhost 6000
Ncat: Certificate verification error. QUITTING.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A really useful tool when debugging TLS connections is &lt;a href="https://www.openssl.org/docs/man3.1/man1/s_client.html"&gt;&lt;code&gt;s_client&lt;/code&gt;&lt;/a&gt;
the &lt;code&gt;openssl&lt;/code&gt; test client.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl s_client -servername service1 -connect localhost:6000 -CAfile ca.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will attempt to negotiate a connection and &lt;a href="static/s_client.log"&gt;output&lt;/a&gt; a heaps
of useful information about the certificate, the signatures, the alorithms
used, the validation results and more.&lt;/p&gt;
&lt;p&gt;TLS also supports a "peer authentication" mode in which the server checks the
client certificates. Servers, or tunnels, can be configured to only allow
clients with certificates signed by a specific CA to connect.&lt;/p&gt;
&lt;p&gt;A secure tunnel configuration that uses strict peer authentication can be
added to the &lt;a href="static/stunnel.conf"&gt;&lt;code&gt;stunnel.conf&lt;/code&gt;&lt;/a&gt; file.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;[service1-secure]
accept = 5000
connect = 7000
CAfile = ca.crt
cert = service1.crt
key = service1.key
verify = 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This tunnel is configuration is similar to the previous one except it'll listen
on a different port (5000) and &lt;code&gt;verify&lt;/code&gt; is set to 2, meaning it will require
client certificates and will validate they are signed by the CA (&lt;a href="static/ca.crt"&gt;&lt;code&gt;ca.crt&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;After restarting the &lt;code&gt;stunnel&lt;/code&gt; process with the updated configuration,
connecting as before to the new tunnel on port 5000 does not work.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ echo "hello" | ncat --ssl --ssl-verify --ssl-servername service1 --ssl-trustfile ca.crt  localhost 5000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This time there is no response, this is because no client certificates are
being sent. The error &lt;a href="static/no_peer_certificate.log"&gt;logged&lt;/a&gt; by &lt;code&gt;stunnel&lt;/code&gt; is "peer did
not return a certificate".&lt;/p&gt;
&lt;p&gt;To connect to this service, a client certificate pair signed by the CA is
required. The process to generate a certificate pair for the client is the same
process used to create the &lt;code&gt;service1&lt;/code&gt; certificate pair, but this certificate
pair will be called &lt;code&gt;client1&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;SUBJECT="/C=DE/ST=Berlin/L=Berlin/O=tarnbarford.net/OU=tarnbarford.net/CN=client1"

openssl ecparam -genkey -name prime256v1 -noout -out client1.key
openssl req -new -key client1.key -subj $SUBJECT -out client1.csr
openssl x509 -req -days 365 -sha256 -in client1.csr -CA ca.crt -CAkey ca.key -set_serial 1 -out client1.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The will generate a private key &lt;a href="static/client1.key"&gt;&lt;code&gt;client1.key&lt;/code&gt;&lt;/a&gt; and a public key
&lt;a href="static/client1.crt"&gt;&lt;code&gt;client1.crt&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To configure &lt;code&gt;ncat&lt;/code&gt; to use the client certificates,
&lt;a href="static/client1.key"&gt;&lt;code&gt;client1.key&lt;/code&gt;&lt;/a&gt; is provided with the  &lt;code&gt;--ssl-key&lt;/code&gt; option and
&lt;a href="static/client1.crt"&gt;&lt;code&gt;client1.crt&lt;/code&gt;&lt;/a&gt; is provided with the &lt;code&gt;--ssl-cert&lt;/code&gt; option.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ echo "hello" | ncat --ssl --ssl-verify --ssl-servername service1 --ssl-trustfile ca.crt --ssl-cert client1.crt --ssl-key client1.key  localhost 5000
hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A response is now returned, but only if the client and server certificates are
signed by the CA.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/securing-sockets-with-tls</guid><pubDate>Thu, 14 Sep 2023 19:54:00 +0000</pubDate></item><item><title>Simple Private Public Key Infrastructure</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Simple Private Public Key Infrastructure
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 14, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;In this private Public Key Infrastructure (PKI) a secret key and a self signed
certificate pair is the Certificate Authority (CA) root. The CA can sign
certificates containing identity information with its secret key. Anyone can
verify a certificate was signed by the CA using the CA root certificate.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;openssl&lt;/code&gt; tool is used for all PKI operations including generating the CA
certificate pair. Elliptic Curve (EC) keys are being generated and the
&lt;code&gt;prime256v1&lt;/code&gt; curve (a.k.a &lt;code&gt;secp256r1&lt;/code&gt;) has been chosen. The CA certificate is
valid for 365 days. The &lt;code&gt;Subject&lt;/code&gt; field contains the identity attributes in the
certificate, for this CA they're not important and some example values have
been chosen.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;SUBJECT="/C=DE/ST=Berlin/L=Berlin/O=tarnbarford.net/OU=tarnbarford.net/CN=ca"

openssl ecparam -genkey -name prime256v1 -noout -out ca.key
openssl req -new -x509 -sha256 -days 365 -key ca.key -subj $SUBJECT -out ca.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A private key &lt;a href="/journal/simple-private-public-key-infrastructure/static/ca.key"&gt;&lt;code&gt;ca.key&lt;/code&gt;&lt;/a&gt; and a public certificate &lt;a href="/journal/simple-private-public-key-infrastructure/static/ca.crt"&gt;&lt;code&gt;ca.crt&lt;/code&gt;&lt;/a&gt;
are generated.&lt;/p&gt;
&lt;p&gt;A certificate pair for an example service &lt;code&gt;service1&lt;/code&gt; can be generated and then
signed with the CA created above. This has an intermediate step where a
certificate signing request (CSR) is created, typically this would be sent to
the CA who would verify it then return a signed public certificate. For these
certificates the Common Name (CN) attribute in the &lt;code&gt;subject&lt;/code&gt; field is
important, as this is the part of the identity that will be verified later.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;SUBJECT="/C=DE/ST=Berlin/L=Berlin/O=tarnbarford.net/OU=tarnbarford.net/CN=service1"

openssl ecparam -genkey -name prime256v1 -noout -out service1.key
openssl req -new -key service1.key -subj $SUBJECT -out service1.csr
openssl x509 -req -days 365 -sha256 -in service1.csr -CA ca.crt -CAkey ca.key -set_serial 1 -out service1.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A private key &lt;a href="/journal/simple-private-public-key-infrastructure/static/service1.key"&gt;&lt;code&gt;service1.key&lt;/code&gt;&lt;/a&gt; and a public key
&lt;a href="/journal/simple-private-public-key-infrastructure/static/service1.crt"&gt;&lt;code&gt;service1.crt&lt;/code&gt;&lt;/a&gt; are generated. The CSR certificate
&lt;a href="/journal/simple-private-public-key-infrastructure/static/service1.csr"&gt;&lt;code&gt;service1.csr&lt;/code&gt;&lt;/a&gt; can be discarded.&lt;/p&gt;
&lt;p&gt;The identity information can be extracted from a certificate&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ openssl x509 -noout -subject -in service1.crt
subject=C = DE, ST = Berlin, L = Berlin, O = tarnbarford.net, OU = tarnbarford.net, CN = service1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The public key can be verified to have been signed by the CA.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ openssl verify -verbose -CAfile ca.crt service1.crt
service1.crt: OK
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The keys generated can also be used for signing and verifying signatures. For
this the public key needs to be extracted from the certificate which also
contains an identity and a signature from a CA.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;openssl x509 -pubkey -noout -in service1.crt -out service1.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The extracted public key is called &lt;a href="/journal/simple-private-public-key-infrastructure/static/service1.pem"&gt;&lt;code&gt;service1.pem&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The private key can be used to create a signature &lt;a href="/journal/simple-private-public-key-infrastructure/static/data.sig"&gt;&lt;code&gt;data.sig&lt;/code&gt;&lt;/a&gt; of
some data, then the public key that was extracted from the public cerificate
can be used to verify the signature of this exact data was made with the
private key.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ echo "hello world" &amp;gt; data.txt
$ openssl pkeyutl -sign -in data.txt -inkey service1.key -out data.sig
$ openssl pkeyutl -verify -pubin -inkey service1.pem -sigfile data.sig -in data.txt
Signature Verified Successfully
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Encrypting data with an EC public key is a lot more involved than with an RSA
public key and will not be explored here. What's probably more interesting is
how to use the PKI to secure connections with TLS. This is covered in a
follow-up post, &lt;a href="/journal/securing-sockets-with-tls/"&gt;securing sockets with TLS&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/simple-private-public-key-infrastructure</guid><pubDate>Thu, 14 Sep 2023 12:40:00 +0000</pubDate></item><item><title>Linux One-Liner for Monitoring File Changes</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Linux One-Liner for Monitoring File Changes
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 10, 2023
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;A Linux one-liner to run a command when a file in a directory tree is created,
modified or deleted. Uses &lt;code&gt;inotifywait&lt;/code&gt; from the &lt;code&gt;inotify-tools&lt;/code&gt; package.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;while inotifywait  -e modify,create,delete -r .; do echo "file changed"; done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is easy but annoying to look up. When I asked an LLM, it omitted the
recursive flag (&lt;code&gt;-r&lt;/code&gt;) unless I explicitly asked for it.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/linux-one-liner-for-monitoring-file-changes</guid><pubDate>Sun, 10 Sep 2023 12:00:00 +0000</pubDate></item><item><title>Static virtual bridge IP addresses with DHCP</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Static virtual bridge IP addresses with DHCP
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 27, 2018
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;It's sometimes desirable to be able to specify a static IP address for a
virtual machine that is using a virtual network bridge. For example, this can
be useful when you have additional iptables rules for a specific virtual
machines on the host.&lt;/p&gt;
&lt;p&gt;With libvirt networks, one way to do this is by configuring the DHCP server to
allocate IP addresses to virtual machines based on their MAC address. This
doesn't require any special configuration in the virtual machine as it obtains
its IP via DHCP.&lt;/p&gt;
&lt;p&gt;Configuring this requires the MAC address of the virtual machines network
interface, when using libvirt this can be found in the virtual machines
configuration.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh dumpxml guest-1
&amp;lt;domain type='kvm' id='1'&amp;gt;
..
  &amp;lt;devices&amp;gt;
    ..
    &amp;lt;interface type='bridge'&amp;gt;
      &amp;lt;mac address='52:54:00:b3:e8:b7'/&amp;gt;
      &amp;lt;source bridge='virbr0'/&amp;gt;
      &amp;lt;target dev='vnet0'/&amp;gt;
      &amp;lt;model type='virtio'/&amp;gt;
      &amp;lt;alias name='net0'/&amp;gt;
      &amp;lt;address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/&amp;gt;
    &amp;lt;/interface&amp;gt;
    ..
  &amp;lt;/devices&amp;gt;
..
&amp;lt;/domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This shows the MAC address of this virtual machines interface is
&lt;code&gt;52:54:00:b3:e8:b7&lt;/code&gt;. As this virtual machine had previously obtained an IP
address from the DHCP server it will appear in the &lt;code&gt;net-dhcp-leases&lt;/code&gt; for the
network.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-dhcp-leases  default
Expiry Time          MAC address        Protocol  IP address                Hostname        Client ID or DUID
-------------------------------------------------------------------------------------------------------------------
2018-11-27 12:04:27  52:54:00:b3:e8:b7  ipv4      192.168.122.14/24         guest-1         -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Currently the virtual machine has been allocated &lt;code&gt;192.168.122.14&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Within this &lt;code&gt;/24&lt;/code&gt; subnetwork there are 254 available IP addresses, the range
192.168.122.2 to 192.168.122.254 is available to allocate to virtual machines.&lt;/p&gt;
&lt;p&gt;To allow both dynamically allocated and static IP addresses, the available
range for dynamically allocated IP addresses can be limited to a subset so that
the rest are can be allocated to specific MAC addresses.&lt;/p&gt;
&lt;p&gt;To do this the default network need to be modified and the dynamic IP range
updated. To edit the default network the &lt;code&gt;virsh net-edit default&lt;/code&gt; command is
used, this will open the configuration in the users default editor.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;lt;network&amp;gt;
  &amp;lt;name&amp;gt;default&amp;lt;/name&amp;gt;
  &amp;lt;uuid&amp;gt;4690aefc-dfa3-4a0a-bd4c-e4297126a845&amp;lt;/uuid&amp;gt;
  &amp;lt;forward mode='nat'&amp;gt;
    &amp;lt;nat&amp;gt;
      &amp;lt;port start='1024' end='65535'/&amp;gt;
    &amp;lt;/nat&amp;gt;
  &amp;lt;/forward&amp;gt;
  &amp;lt;bridge name='virbr0' stp='on' delay='0'/&amp;gt;
  &amp;lt;mac address='52:54:00:95:92:6b'/&amp;gt;
  &amp;lt;ip address='192.168.122.1' netmask='255.255.255.0'&amp;gt;
    &amp;lt;dhcp&amp;gt;
      &amp;lt;range start='192.168.122.2' end='192.168.122.128'/&amp;gt;
    &amp;lt;/dhcp&amp;gt;
  &amp;lt;/ip&amp;gt;
&amp;lt;/network&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the above configuration the upper DHCP range has been limited from
192.168.122.254 to 192.168.122.128. &lt;/p&gt;
&lt;p&gt;The next task is to assign a specific IP outside this range to a specific MAC
address. This done by adding a host element containing the MAC address found
earlier, the desired IP address in this example 192.168.122.129 and the name of
the virtual machine.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;lt;network&amp;gt;
  &amp;lt;name&amp;gt;default&amp;lt;/name&amp;gt;
  &amp;lt;uuid&amp;gt;4690aefc-dfa3-4a0a-bd4c-e4297126a845&amp;lt;/uuid&amp;gt;
  &amp;lt;forward mode='nat'&amp;gt;
    &amp;lt;nat&amp;gt;
      &amp;lt;port start='1024' end='65535'/&amp;gt;
    &amp;lt;/nat&amp;gt;
  &amp;lt;/forward&amp;gt;
  &amp;lt;bridge name='virbr0' stp='on' delay='0'/&amp;gt;
  &amp;lt;mac address='52:54:00:95:92:6b'/&amp;gt;
  &amp;lt;ip address='192.168.122.1' netmask='255.255.255.0'&amp;gt;
    &amp;lt;dhcp&amp;gt;
      &amp;lt;range start='192.168.122.2' end='192.168.122.128'/&amp;gt;
      &amp;lt;host mac='52:54:00:b3:e8:b7' name='guest-1' ip='192.168.122.129'/&amp;gt;
    &amp;lt;/dhcp&amp;gt;
  &amp;lt;/ip&amp;gt;
&amp;lt;/network&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Modifying this network configuration has no effect until the network is
restarted. To do this the network is destroyed and started again, in
virtualisation terminology destroying means stopping. This will cause virtual
machines using the network to lose network connectivity.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-destroy default
Network default destroyed

root@demo ~ # virsh net-start default
Network default started
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even after the network is restarted, running virtual machines using the network
will not be able to use the new network, they need to be destroyed and
restarted for the interface to work again.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh destroy guest-1
Domain guest-1 destroyed

root@demo ~ # virsh start guest-1
Domain guest-1 started
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Looking at the leases again, the virtual machine guest-1 has now been allocated
192.168.122.129.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-dhcp-leases default
Expiry Time          MAC address        Protocol  IP address                Hostname        Client ID or DUID
-------------------------------------------------------------------------------------------------------------------
 2018-11-27 13:49:09  52:54:00:b3:e8:b7  ipv4      192.168.122.129/24        guest-1        -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The VM has the IP address we specified, but taking down the entire network is a
real problem, especially if there are other virtual machines using the network. &lt;/p&gt;
&lt;p&gt;It is also possible to make some live configurations to the network such as the
IP address assigned to a virtual machine, this change is immediate, the
interface in the virtual machine doesn't even need to be restarted.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-update default modify ip-dhcp-host \
      '&amp;lt;host mac="52:54:00:b3:e8:b7" name="guest-1" ip="192.168.122.130"/&amp;gt;' \
      --live --config
Updated network default persistent config and live state
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Host elements can also be deleted. This won't automatically allocate a new IP
in the virtual machine, in fact it will keep using the previously allocated
one. When the interface is restarted it will be allocated a new address.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-update default delete ip-dhcp-host \
      '&amp;lt;host mac="52:54:00:b3:e8:b7" name="guest-1" ip="192.168.122.130"/&amp;gt;' \
      --live --config
Updated network default persistent config and live state
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Host elements can also be added, the interface in the virtual machine will need
to be restarted to obtain the new IP address.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-update default add ip-dhcp-host \
      '&amp;lt;host mac="52:54:00:b3:e8:b7" name="guest-1" ip="192.168.122.131"/&amp;gt;' \
      --live --config
Updated network default persistent config and live state
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Modifying the networks with commands is a bit unwieldy but may be preferable
than taking down the entire virtual network. It's also possible to specify IPv6
addresses that will be allocated to virtual machines, but I intend to use a
physical bridge network for IPv6 networking, so I'm not going to write about
it. For more information on that, check out the &lt;a href="https://wiki.libvirt.org/page/Networking"&gt;libvirt networking
documentation&lt;/a&gt; or this great &lt;a href="https://jamielinux.com/docs/libvirt-networking-handbook/appendix/dhcp-host-entries.html"&gt;libvirt tutial&lt;/a&gt; by &lt;a href="https://jamielinux.com"&gt;Jamie Nguyen&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/static-virtual-bridge-ip-with-dhcp</guid><pubDate>Tue, 27 Nov 2018 19:54:00 +0000</pubDate></item><item><title>Virtual bridge networking with libvirt</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Virtual bridge networking with libvirt
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 26, 2018
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;The virtual bridged networking provided by libvirt is the easiest way to enable
networking on KVM virtual machines. In most cases it should be as simple as
starting the default network and specifying the network when defining new
virtual machines.&lt;/p&gt;
&lt;p&gt;The default libvirt network creates a virtual bridge called &lt;code&gt;virbr0&lt;/code&gt; and it has
its own IP address &lt;code&gt;192.168.122.1/24&lt;/code&gt;. When a virtual machine is configured to
use this bridge, a new interface is created called &lt;code&gt;vbox1&lt;/code&gt;, &lt;code&gt;vbox2&lt;/code&gt; etc. The
interface is attached to the virtual bridge and shared with the virtual
machine.&lt;/p&gt;
&lt;p&gt;The virtual bridge network supports DHCP so that virtual machines can enable
DHCP to obtain an IP address on the bridged network. Some iptables rules are
added by libvirt so that packets to addresses outside the host are correctly
routed.&lt;/p&gt;
&lt;p&gt;The default libvirt network can be started with the &lt;code&gt;virsh&lt;/code&gt; command.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-start default
Network default started
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The configuration of the network looks like this:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh net-dumpxml default
&amp;lt;network&amp;gt;
  &amp;lt;name&amp;gt;default&amp;lt;/name&amp;gt;
  &amp;lt;uuid&amp;gt;4690aefc-dfa3-4a0a-bd4c-e4297126a845&amp;lt;/uuid&amp;gt;
  &amp;lt;forward mode='nat'&amp;gt;
    &amp;lt;nat&amp;gt;
      &amp;lt;port start='1024' end='65535'/&amp;gt;
    &amp;lt;/nat&amp;gt;
  &amp;lt;/forward&amp;gt;
  &amp;lt;bridge name='virbr0' stp='on' delay='0'/&amp;gt;
  &amp;lt;mac address='52:54:00:95:92:6b'/&amp;gt;
  &amp;lt;ip address='192.168.122.1' netmask='255.255.255.0'&amp;gt;
    &amp;lt;dhcp&amp;gt;
      &amp;lt;range start='192.168.122.2' end='192.168.122.254'/&amp;gt;
    &amp;lt;/dhcp&amp;gt;
  &amp;lt;/ip&amp;gt;
&amp;lt;/network&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On the host it has created two new interfaces, the &lt;code&gt;virbr0&lt;/code&gt; interface and a
&lt;code&gt;virbr0-nic&lt;/code&gt; interface. The later isn't so important, it's added to the bridge
by default so that the &lt;code&gt;virbr0&lt;/code&gt; has a MAC address.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # ip addr
..
3: virbr0: &amp;lt;NO-CARRIER,BROADCAST,MULTICAST,UP&amp;gt; mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 52:54:00:95:92:6b brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
4: virbr0-nic: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000
    link/ether 52:54:00:95:92:6b brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When &lt;code&gt;--network bridge=virbr0&lt;/code&gt; is passed to &lt;code&gt;virt-install&lt;/code&gt;, an interface will
be added as a PCI device.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@demo ~ # virsh dumpxml guest-1
&amp;lt;domain type='kvm' id='1'&amp;gt;
..
  &amp;lt;devices&amp;gt;
    ..
    &amp;lt;interface type='bridge'&amp;gt;
      &amp;lt;mac address='52:54:00:8c:7f:94'/&amp;gt;
      &amp;lt;source bridge='virbr0'/&amp;gt;
      &amp;lt;target dev='vnet0'/&amp;gt;
      &amp;lt;model type='virtio'/&amp;gt;
      &amp;lt;alias name='net0'/&amp;gt;
      &amp;lt;address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/&amp;gt;
    &amp;lt;/interface&amp;gt;
    ..
  &amp;lt;/devices&amp;gt;
..
&amp;lt;/domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the virtual machine disk image doesn't configure a network interface, for
example if &lt;code&gt;/etc/network/interfaces&lt;/code&gt; only contains a loopback configuration
like this:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;auto lo
iface lo inet loopback
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then when we access the machine via its console, we'll find an interface called
&lt;code&gt;ens2&lt;/code&gt; that is down. &lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@guest-1:~# ip addr
..
2: ens2: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 52:54:00:b3:e8:b7 brd ff:ff:ff:ff:ff:ff
root@guest-nonw:~# ping tarnbarford.net
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we can't access the internet.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@guest-1:~# ping tarnbarford.net
ping: tarnbarford.net: Temporary failure in name resolution
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As the network supports DHCP, we just need to run &lt;code&gt;dhclient&lt;/code&gt; on the interface
to obtain an IP address and bring the interface up.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@guest-1:~# dhclient
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the interface is up and we have been allocated an IP address, &lt;code&gt;192.168.122.14&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@guest-1:~# ip addr
..
2: ens2: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:b3:e8:b7 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.14/24 brd 192.168.122.255 scope global ens2
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:feb3:e8b7/64 scope link
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Access to the internet is possible.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root@guest-1:~# ping -c 1 tarnbarford.net
PING tarnbarford.net (178.63.44.71) 56(84) bytes of data.
64 bytes from mail01.tarnbarford.net (178.63.44.71): icmp_seq=1 ttl=59 time=0.401 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To make this more permanent, it can be configured in &lt;code&gt;/etc/network/interfaces&lt;/code&gt;
by appending the following lines.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;auto ens2
iface ens2 inet dhcp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After a reboot, the network should be up.&lt;/p&gt;
&lt;p&gt;The virtual machine can now access the internet as well as other virtual
machines running on the host that are using the same virtual bridge. However,
it's still inaccessible from the internet.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/default-virtual-networking-with-libvirt</guid><pubDate>Mon, 26 Nov 2018 19:54:00 +0000</pubDate></item><item><title>Cheap root servers from Hetzner auctions</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;script src="splide-4.1.3/dist/js/splide.min.js"&gt;&lt;/script&gt;
&lt;link rel="stylesheet" href="splide-4.1.3/dist/css/splide.min.css"&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Cheap root servers from Hetzner auctions
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 25, 2018
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I rent a couple of root servers from &lt;a href="https://www.hetzner.com/"&gt;Hetzner&lt;/a&gt; that run various
services I host. You can rent standard and custom configurations but these have
an initial setup cost and setup time. For impatient hobbyists like myself,
Hetzner also allows you to rent unused servers from their &lt;a href="https://www.hetzner.com/sb"&gt;server
auctions&lt;/a&gt;. These servers can be rented with a one month notice period,
have no up front costs, and are available to use almost immediately.&lt;/p&gt;
&lt;p&gt;I'm experimenting with IPv6 bridge setups, for this I've found a cheap server
from the auctions and am trying to document the process.&lt;/p&gt;
&lt;p&gt;When you order a root server from &lt;a href="https://www.hetzner.com/"&gt;Hetzner&lt;/a&gt; you will be given an IPv4
address and an IPv6 subnet for the server. When you SSH to the IP address you
will reach the Hetzner &lt;a href="https://wiki.hetzner.de/index.php/Installimage/en"&gt;rescue system&lt;/a&gt;, a shell environment where you
can format, mount and install an operating system on the disks connected to
your server.&lt;/p&gt;
&lt;p&gt;Note: when SSHing into the rescue system you may want to disable "strict host
checking" as the rescue system host key will be different than that of your
host.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@laptop $ &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;server&lt;span class="w"&gt; &lt;/span&gt;IP&lt;span class="w"&gt; &lt;/span&gt;address&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's possible to mount the disks connected to the server yourself and manually
install whatever you want. For convenience Hetzner also provides an
&lt;code&gt;installimage&lt;/code&gt; script that starts an ncurses interface that leads you through
the setup of various distributions and will handle partitioning, grub setup,
software RAID, prompt for root password, network configuration, install sshd
and add your SSH key as an authorized key.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@rescue ~ # &lt;/span&gt;installimage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I chose Debian 9 and took all the default options except the hostname, which I
changed to demo. This isn't very interesting, but I took some screen shots of
the process anyway. &lt;a href="install_image1.png"&gt;1&lt;/a&gt; &lt;a href="install_image2.png"&gt;2&lt;/a&gt; &lt;a href="install_image3.png"&gt;3&lt;/a&gt; &lt;a href="install_image4.png"&gt;4&lt;/a&gt; &lt;a href="install_image5.png"&gt;5&lt;/a&gt; &lt;a href="install_image6.png"&gt;6&lt;/a&gt; &lt;a href="install_image7.png"&gt;7&lt;/a&gt;&lt;/p&gt;
&lt;section aria-label="Debian installation" class="splide"&gt;
&lt;div class="splide__track"&gt;
&lt;ul class="splide__list"&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="1.png" src="install_image1.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="2.png" src="install_image2.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="3.png" src="install_image3.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="4.png" src="install_image4.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="5.png" src="install_image5.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="6.png" src="install_image6.png" width="100%"/&gt;&lt;/li&gt;
&lt;li class="splide__slide"&gt;&lt;img alt="7.png" src="install_image7.png" width="100%"/&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;p&gt;After installing the image you can reboot from the rescue system and your
server will boot up.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;root@rescue ~ # &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next time you SSH to your IP address you'll be connecting to your own
server. This time you want strict host checking so when we connect via SSH
going forward you can be confident you're connecting to your server.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;tarn@laptop $ &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;server&lt;span class="w"&gt; &lt;/span&gt;IP&lt;span class="w"&gt; &lt;/span&gt;address&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;root@demo ~ #&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all goes well you should have an ssh shell on your new physical server!&lt;/p&gt;
&lt;p&gt;If you ever find yourself in a situation where you can't SSH into your own
server because the network is misconfigured or the server doesn't boot, you can
re-enable the rescue system from the Hetzner web interface and either mount the
disks and fix the configuration or install a new operating system.&lt;/p&gt;
&lt;p&gt;If you're having problems with the boot process you can also request VNC access
to the servers video and input devices. This is a manual process that requires
raising a ticket, the few times I've made a request it's taken less than half
an hour to get setup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script&gt;
   new Splide( '.splide' ).mount();
&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/cheap-root-servers-from-hetzner-auctions</guid><pubDate>Sun, 25 Nov 2018 19:54:00 +0000</pubDate></item><item><title>Techbikers 2016 and World Bicyle Relief</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;style&gt;
&lt;/style&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Techbikers 2016 and World Bicyle Relief
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 13, 2016
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;On July 1st, I will join &lt;a href="http://www.techbikers.de/"&gt;Techbikers Germany&lt;/a&gt; for a charity bike ride from
Poznan to Berlin to raise money for &lt;a href="http://www.worldbicyclerelief.org/"&gt;World Bicycle Relief&lt;/a&gt;. The ride relies
on donations, so I hope to explain why the ride is important to me, that the
charity is not only a good cause but also effective, and hopefully convince you
to &lt;a href="https://altruja.de/tarn-barford?lang=en"&gt;donate&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="static/angeber.png" style="float: left; margin-right: 20px; margin-top: 5px"/&gt;&lt;/p&gt;
&lt;p&gt;As most of you will know, I am pretty passionate about tech and love cycling,
&lt;a href="https://twitter.com/tarnacious/status/682567065828474880"&gt;despite an accident or two&lt;/a&gt;. Some of you will have even donated in
previous years, for which I am eternally grateful, we made it and I am really
excited to be doing it all again.&lt;/p&gt;
&lt;p&gt;The Techbikers initiative started in London in 2012, the idea was to organize a
ride from London to Paris every year and have participants raise money for
charity. The ride successfully raised over £170,000 for &lt;a href="http://www.roomtoread.org/"&gt;Room to Read&lt;/a&gt; over
the first 3 years.&lt;/p&gt;
&lt;p&gt;In 2013, &lt;a href="http://www.leabauer.com/"&gt;Lea&lt;/a&gt; and &lt;a href="https://janbechler.de/"&gt;Jan&lt;/a&gt; who participated in the inaugural ride
decided to bring the idea to Berlin. Techbikers Germany's first ride was from
Prague to Berlin. I joined the group a week before that ride and later &lt;a href="/journal/prague-berlin"&gt;wrote
about it&lt;/a&gt;. It's been a pleasure to ride together in 2014 from Copenhagen to
Berlin and in 2015 from Hamburg to Berlin.&lt;/p&gt;
&lt;p&gt;Since the 2014 ride, we have been raising money for &lt;a href="http://www.worldbicyclerelief.org/"&gt;World Bicycle Relief&lt;/a&gt;,
a charity whose work nicely aligns with our common interest, cycling.  World
Bicycle Relief &lt;a href="http://www.worldbicyclerelief.org/the-bike"&gt;designs and manufactures rugged bicycle parts engineered
specifically for rural terrain and load requirements&lt;/a&gt; and &lt;a href="http://www.worldbicyclerelief.org/our-work/field-mechanics"&gt;provides training
and tools to assemble and maintain the bikes locally&lt;/a&gt;. The bikes are mostly
distributed to &lt;a href="http://www.worldbicyclerelief.org/our-work/education"&gt;students&lt;/a&gt; and &lt;a href="http://www.worldbicyclerelief.org/our-work/healthcare"&gt;healthcare workers&lt;/a&gt;, but also
&lt;a href="http://www.worldbicyclerelief.org/our-work/microfinance"&gt;entrepreneurs and family businesses&lt;/a&gt; &lt;a href="http://www.worldbicyclerelief.org/storage/documents/wbroverview.pdf"&gt;in a number of African countries, but
also in Thailand and Colombia&lt;/a&gt;. I am really impressed by how efficient they
are: they distribute a high quality bike &lt;a href="http://www.de.worldbicyclerelief.org/spenden"&gt;for €134 raised&lt;/a&gt;. &lt;a href="http://www.worldbicyclerelief.org/our-story/governance"&gt;64% of their
income comes from individual contributions&lt;/a&gt;, so rides such as this one are
important to them. And they are passionate about what they do!&lt;/p&gt;
&lt;p&gt;Cycling is my primary form of transport, even though I live in a city with a
world class public transport system. It's also pretty flat which helps, but
nevertheless I personally really believe in the power of bicycles. I am happy
to be supporting a program that gets quality bikes to people for whom this
really makes life a lot easier and who could otherwise not afford them.&lt;/p&gt;
&lt;p&gt;This year, 34 of us will cycle 400km from Poznan to Berlin. It will be a little
different for me as I &lt;a href="https://twitter.com/tarnacious/status/682567065828474880"&gt;had to take a break from cycling this year&lt;/a&gt; and I'm
leaving my much trusted Koga Miyata at home and riding my fixed gear bike (&lt;a href="https://8bar-bikes.com/"&gt;hey
8bar-bikes, it would be great if you could donate too&lt;/a&gt;!). It should be fun.&lt;/p&gt;
&lt;p&gt;&lt;img src="static/wbr.png" style="float: left; margin-right: 20px"/&gt;&lt;/p&gt;
&lt;p&gt;This year we are targeting €25,000. My personal goal is €750 and every donation
no matter how large or small will help reach this goal. To the companies I work
with, a percentage of this is small relative to the value of our relationships
and I will thank you in a follow up post. To friends and family who donate, I
will thank you in person or if that's not possible by phone. For my Australian
friends an extra hug when I'm back later this year!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://altruja.de/tarn-barford?lang=en"&gt;Click here to visit my fundraising page directly&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update 15/6: The link should be in english now.&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-decoration: line-through;"&gt;&lt;i&gt;(The above link is in German, you can visit the Techbikers group page, &lt;a href="https://altruja.de/new-donation-event-95"&gt;here&lt;/a&gt;,
and change the language in the top right corner then scroll down to find my
page, don't donate to the other people, they are already mostly ahead of me)&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Thanks&lt;/p&gt;
&lt;p&gt;Tarn&lt;/p&gt;
&lt;!--
&lt;div style="height: 100px"&gt;
&lt;img style="float: left; margin-right: 20px; hight: 100px" src="static/techbikers.png"&gt;
&lt;/div&gt;
--&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/techbikers-2016</guid><pubDate>Mon, 13 Jun 2016 00:27:00 +0000</pubDate></item><item><title>Cross-compiling with Rust</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Cross-compiling with Rust
    &lt;/h1&gt;
    &lt;p&gt;
    Dec 29, 2015
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I found a &lt;a href="https://github.com/japaric/ruststrap/blob/master/1-how-to-cross-compile.md"&gt;guide&lt;/a&gt; for cross-compiling &lt;a href="https://www.rust-lang.org/"&gt;Rust&lt;/a&gt; which basically involves
configuring it with a &lt;code&gt;--target&lt;/code&gt; option. The target needs to include the
cross-compile target and the host target.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;https://static.rust-lang.org/dist/rustc-1.5.0-src.tar.gz
&lt;span class="gp"&gt;$ &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;xf&lt;span class="w"&gt; &lt;/span&gt;rustc-1.5.0-src.tar.gz
&lt;span class="gp"&gt;$ &lt;/span&gt;./configure&lt;span class="w"&gt; &lt;/span&gt;--target&lt;span class="o"&gt;=&lt;/span&gt;arm-unknown-linux-gnueabi,x86_64-unknown-linux-gnu&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;PWD&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/../bin
&lt;span class="gp"&gt;$ &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="k"&gt;$(&lt;/span&gt;nproc&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This didn't work with my target &lt;code&gt;arm-unknown-linux-gneabi&lt;/code&gt; as at some point in
the build it started trying to use &lt;code&gt;arm-linux-gneabi-gcc&lt;/code&gt; and
&lt;code&gt;arm-linux-gneabi-ar&lt;/code&gt;, which didn't work as they are called
&lt;code&gt;arm-unknown-linux-gneabi-gcc&lt;/code&gt; and &lt;code&gt;arm-unknown-linux-gneabi-gcc&lt;/code&gt;. I guess this
is a problem with the build script, but I couldn't easily work out a fix, so I
just created some symlinks with the names it was expecting. This worked well.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/projects/arm-rust-cross-compiler/bin/lib/
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:~/projects/arm-rust-cross-compiler/bin/bin/
&lt;span class="gp"&gt;$ &lt;/span&gt;bin/bin/rustc&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;span class="go"&gt;rustc 1.5.0-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/rust-lang/carg://github.com/rust-lang/cargo"&gt;Cargo&lt;/a&gt; can also be built from source. I don't think Cargo needs to be
cross-compiled as it supports compiling for different targets and I don't
actually want to run Cargo on the target machine.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/rust-lang/cargo
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cargo
&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;submodule&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;--init
&lt;span class="gp"&gt;$ &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-B&lt;span class="w"&gt; &lt;/span&gt;src/etc/install-deps.py
&lt;span class="gp"&gt;$ &lt;/span&gt;./configure&lt;span class="w"&gt; &lt;/span&gt;--local-rust-root&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/../bin&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;PWD&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/../bin
&lt;span class="gp"&gt;$ &lt;/span&gt;make
&lt;span class="gp"&gt;$ &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To cross-compile with Cargo, a linker needs to be specified for the target. In my case:&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/.cargo/config&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;EOF
&lt;span class="go"&gt;[target.arm-unknown-linux-gnueabi]&lt;/span&gt;
&lt;span class="go"&gt;linker = "arm-unknown-linux-gnueabi-gcc-5.2.0"&lt;/span&gt;
&lt;span class="go"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now Cargo can be used to create a new project and build a binary for an ARM target.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cargo&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;--bin&lt;span class="w"&gt; &lt;/span&gt;hello-rust
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;hello
&lt;span class="gp"&gt;$ &lt;/span&gt;cargo&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;--target&lt;span class="o"&gt;=&lt;/span&gt;arm-unknown-linux-gnueabi
&lt;span class="gp"&gt;$ &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;target/arm-unknown-linux-gnueabi/debug/hello-rust
&lt;span class="go"&gt;target/arm-unknown-linux-gnueabi/debug/hello: ELF 32-bit LSB shared object,&lt;/span&gt;
&lt;span class="go"&gt;ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter&lt;/span&gt;
&lt;span class="go"&gt;/lib/ld-linux.so.3, for GNU/Linux 4.3.0, not stripped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I was pretty confident at this point, until I tried to run it in &lt;a href="/journal/arm-qemu-linux-busybox"&gt;my
emulator&lt;/a&gt; and the program immediately terminated with the message
"Illegal instruction". So far, &lt;a href="/journal/arm-unknown-linux-gnueabi"&gt;my cross-compiler toolchain&lt;/a&gt; and
emulator had worked well. Why is now generating illegal instructions?&lt;/p&gt;
&lt;p&gt;I eventually found that I wasn't specifying a CPU type and &lt;code&gt;qemu&lt;/code&gt; was using the
default ARM926 processor on the Versatile board. This can be fixed by adding
&lt;code&gt;--cpu arm1176&lt;/code&gt; (which is the CPU on the first generation Raspberry Pi which I
am trying to emulate). However, now the Kernel doesn't boot as it was built for
an ARM926 processor as specified by &lt;code&gt;versatile_defconfig&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Linux ARM Versatile config restricts the possible CPU targets to CPUs that
can actually fit in the board. Fortunately I found &lt;a href="static/versatile.patch"&gt;this patch&lt;/a&gt; which removes
this restriction. Now &lt;code&gt;make ARCH=arm versatile_defconfig &amp;amp;&amp;amp; make ARCH=arm
menuconfig&lt;/code&gt; allows the &lt;code&gt;System Type -&amp;gt; Support ARM V6 processor&lt;/code&gt; option to be
selected.&lt;/p&gt;
&lt;p&gt;Finally, after rebuilding the Kernel and adding the Rust built &lt;code&gt;hello&lt;/code&gt; binary to
the filesystem, the program can run in the emulator.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;qemu-system-arm&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;versatilepb&lt;span class="w"&gt; &lt;/span&gt;-cpu&lt;span class="w"&gt; &lt;/span&gt;arm1176&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;128M&lt;span class="w"&gt; &lt;/span&gt;-kernel&lt;span class="w"&gt; &lt;/span&gt;zImage&lt;span class="w"&gt; &lt;/span&gt;-initrd&lt;span class="w"&gt; &lt;/span&gt;./rootfs.img.gz&lt;span class="w"&gt; &lt;/span&gt;-append&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"root=/dev/ram rdinit=/sbin/init"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="hello rust screenshot" src="static/hello-rust.png"/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/arm-rust-cross-compiler</guid><pubDate>Tue, 29 Dec 2015 19:55:00 +0000</pubDate></item><item><title>Building Linux and BusyBox for ARM and emulating it on QEMU</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Building Linux and BusyBox for ARM and emulating it on QEMU
    &lt;/h1&gt;
    &lt;p&gt;
    Dec 27, 2015
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;Cross compiling a minimal Linux sytem for the ARM architecture and emulating it
in QEMU.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Almost all the information from this and &lt;a href="/journal/arm-unknown-linux-gnueabi"&gt;my previous
post&lt;/a&gt; can be found in &lt;a href="https://briolidz.wordpress.com/2012/02/07/building-embedded-arm-systems-with-crosstool-ng/"&gt;this nice tutorial&lt;/a&gt;
by Kamel Messaoudi, which also includes debugging user programs. I'm posting my
notes as I go through them again and which will use as reference points for
future posts.&lt;/p&gt;
&lt;p&gt;A Linux kernel that can be emulated by &lt;a href="http://wiki.qemu.org"&gt;QEMU&lt;/a&gt; can be built using the
&lt;code&gt;versatile_defconfig&lt;/code&gt; and a &lt;a href="/journal/arm-unknown-linux-gnueabi"&gt;working cross-compiler&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.3.3.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.3.3.tar.sign

gpg --recv 6092693E
unxz linux-4.3.3.tar.xz
gpg --verify linux-4.3.3.tar.sign

tar xf linux-4.3.3.tar

cd linux-4.3.3
make ARCH=arm versatile_defconfig
# to further configure the build
#make ARCH=arm menuconfig
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- all

# this builds a kernal image to `./arch/arm/boot/zImage`
cp linux-4.3.3/arch/arm/boot/zImage .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://busybox.net/"&gt;Busybox&lt;/a&gt; is a single binary combining many Unix utilities including a
shell.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;wget https://busybox.net/downloads/busybox-1.24.1.tar.bz2
wget https://busybox.net/downloads/busybox-1.24.1.tar.bz2.sign

gpg --recv ACC9965B
gpg --verify busybox-1.24.1.tar.bz2.sign

tar xf busybox-1.24.1.tar.bz2
cd xf busybox-1.24.1
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- defconfig
make ARCH=arm CROSS_COMPILE=arm-unknown-linux-gnueabi- install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This builds a minimal filesystem in &lt;code&gt;./_install&lt;/code&gt;. Some additional directories
are required.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cd _install
mkdir -p lib proc sys dev etc etc/init.d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some shared libraries are required by the BusyBox binary. The exact libraries
can be found using the &lt;code&gt;arm-unknown-linux-gnueabi-readelf&lt;/code&gt; program. I'm just
going to copy all the libraries from &lt;a href="/journal/arm-unknown-linux-gnueabi"&gt;my toolchain&lt;/a&gt;
to the target.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp ~/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot/lib/* lib
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next we add an init script at &lt;code&gt;/etc/init.d/rcS&lt;/code&gt; which will be executed by the
BusyBox init script.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cat &amp;gt; ./etc/init.d/rcS &amp;lt;&amp;lt; EOF
#!/bin/sh

# Mount the /proc and /sys filesystems
mount -t proc none /proc
mount -t sysfs none /sys

# Populate /dev
/sbin/mdev -s

# Enable the localhost interface
ifconfig lo up
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It needs to be executable.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;chmod +x etc/init.d/rcS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A compressed disk image of this filesystem which can be read by &lt;code&gt;qemu&lt;/code&gt; can be
made using the &lt;code&gt;cpio&lt;/code&gt; program.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;find . | cpio -o --format=newc | gzip &amp;gt; ../../rootfs.img.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I use my system qemu package, but it is not hard to build.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;qemu-system-arm -M versatilepb -m 128M -kernel ./zImage -initrd ./rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="qemu screenshot" src="static/arm-qemu-busybox.png"/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/arm-qemu-linux-busybox</guid><pubDate>Sun, 27 Dec 2015 19:55:00 +0000</pubDate></item><item><title>Compiling an arm-unkown-linux-gnueabi toolchain with crosstool-ng</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Compiling an arm-unkown-linux-gnueabi toolchain with crosstool-ng
    &lt;/h1&gt;
    &lt;p&gt;
    Dec 27, 2015
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;Building an &lt;code&gt;arm-unkown-linux-gnueabi&lt;/code&gt; GNU cross-compiler toolchain using &lt;a href="http://crosstool-ng.org/"&gt;crosstool-ng&lt;/a&gt; in
order to compile code from an x86_64 GNU/Linux system to an ARM GNU/Linux
system.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Building cross-compiler toolchains &lt;a href="http://wiki.osdev.org/GCC_Cross-Compiler"&gt;is&lt;/a&gt; &lt;a href="http://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/"&gt;complicated&lt;/a&gt; and in some cases
using a prebuilt one might be preferable. For example, Ubuntu has a &lt;a href="http://packages.ubuntu.com/precise/devel/gcc-arm-linux-gnueabi"&gt;package&lt;/a&gt;
for an &lt;code&gt;arm-unkown-linux-gnueabi&lt;/code&gt; toolchain. However, crosstool-ng makes
compiling toolchains quite straight forward and makes customization possible if
required.&lt;/p&gt;
&lt;p&gt;First specify the crosstools-ng version and some build directories. The final
toolchain is built to &lt;code&gt;~/x-tools&lt;/code&gt; by default.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;CROSSTOOL_VERSION=1.22.0
BASEDIR=~/arm-tool-chain
CROSS_TOOL_HOME=$BASDIR/crosstool/source/
CROSS_TOOL_PREFIX=$BASDIR/crosstool/bin/
TOOLCHAIN_HOME=$BASDIR/toolchain/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make the directories used to build the cross-compiler.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir -p $CROSS_TOOL_HOME
mkdir -p $CROSS_TOOL_PREFIX
mkdir -p $TOOLCHAIN_HOME
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Downloading, verifying and building &lt;code&gt;crosstools-ng&lt;/code&gt; should be no problem in
sane build environment. I have found that I sometimes need to apply a minor
patch to get the entire toolchain to build. So far these have not been too
difficult to work out or find a patch for.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cd $CROSS_TOOL_HOME
wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-$CROSSTOOL_VERSION.tar.bz2
tar xjf crosstool-ng-$CROSSTOOL_VERSION.tar.bz2

# The package signed with Bryan Hundven's gpg key, this can be verified by
# downloading the key are verifying the signature.
wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-$CROSSTOOL_VERSION.tar.bz2.sig
gpg --recv-keys 35B871D1
gpg --verify crosstool-ng-$CROSSTOOL_VERSION.tar.bz3.sig


cd crosstool-ng

# I had to apply this patch to get the native gdb to compile,
# https://github.com/crosstool-ng/crosstool-ng/pull/273/files

./configure --prefix=$CROSS_TOOL_PREFIX
make
make install
PATH="${PATH}:$CROSS_TOOL_PREFIX/bin"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case configuring the toolchain is as simple as selecting a predefined
sample. In other cases &lt;code&gt;crosstool-ng&lt;/code&gt; has a Linux kernel like &lt;code&gt;menuconfig&lt;/code&gt;
command to browse and update settings.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cd $TOOLCHAIN_HOME
ct-ng arm-unknown-linux-gnueabi

# If you want to further configure the build, I didn't make any changes here,
# but if your target has hardware floating point support for example you might
# want to enable it here.
#   ct-ng menuconfig

ct-ng build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If all works out well, the tools and libraries should have been built to
&lt;code&gt;~/x-tools/arm-unknown-linux-gnueabi/&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ ls -1a ~/x-tools/arm-unknown-linux-gnueabi/bin/
.
..
arm-unknown-linux-gnueabi-addr2line
arm-unknown-linux-gnueabi-ar
arm-unknown-linux-gnueabi-as
arm-unknown-linux-gnueabi-c++
arm-unknown-linux-gnueabi-cc
arm-unknown-linux-gnueabi-c++filt
arm-unknown-linux-gnueabi-cpp
arm-unknown-linux-gnueabi-ct-ng.config
arm-unknown-linux-gnueabi-dwp
arm-unknown-linux-gnueabi-elfedit
arm-unknown-linux-gnueabi-g++
arm-unknown-linux-gnueabi-gcc
arm-unknown-linux-gnueabi-gcc-5.2.0
arm-unknown-linux-gnueabi-gcc-ar
arm-unknown-linux-gnueabi-gcc-nm
arm-unknown-linux-gnueabi-gcc-ranlib
arm-unknown-linux-gnueabi-gcov
arm-unknown-linux-gnueabi-gcov-tool
arm-unknown-linux-gnueabi-gdb
arm-unknown-linux-gnueabi-gprof
arm-unknown-linux-gnueabi-ld
arm-unknown-linux-gnueabi-ld.bfd
arm-unknown-linux-gnueabi-ldd
arm-unknown-linux-gnueabi-ld.gold
arm-unknown-linux-gnueabi-nm
arm-unknown-linux-gnueabi-objcopy
arm-unknown-linux-gnueabi-objdump
arm-unknown-linux-gnueabi-populate
arm-unknown-linux-gnueabi-ranlib
arm-unknown-linux-gnueabi-readelf
arm-unknown-linux-gnueabi-size
arm-unknown-linux-gnueabi-strings
arm-unknown-linux-gnueabi-strip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We should add this to our path if we want to use them:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;PATH=$PATH:~/x-tools/arm-unknown-linux-gnueabi/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; option will tell us a little about the toolchain:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ arm-unknown-linux-gnueabi-gcc -v
Using built-in specs.
COLLECT_GCC=arm-unknown-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/home/dev/x-tools/arm-unknown-linux-gnueabi/libexec/gcc/arm-unknown-linux-gnueabi/5.2.0/lto-wrapper
Target: arm-unknown-linux-gnueabi
Configured with:
/home/dev/projects/arm-tool-chain/build/bin/.build/src/gcc-5.2.0/configure
--build=x86_64-build_pc-linux-gnu --host=x86_64-build_pc-linux-gnu
--target=arm-unknown-linux-gnueabi
--prefix=/home/dev/x-tools/arm-unknown-linux-gnueabi
--with-sysroot=/home/dev/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot
--enable-languages=c,c++ --with-float=soft --with-pkgversion='crosstool-NG crosstool-ng-1.22.0' --disable-sjlj-exceptions --enable-__cxa_atexit
--disable-libmudflap --disable-libgomp --disable-libssp
--disable-libquadmath --disable-libquadmath-support --disable-libsanitizer
--with-gmp=/home/dev/projects/arm-tool-chain/build/bin/.build/arm-unknown-linux-gnueabi/buildtools
--with-mpfr=/home/dev/projects/arm-tool-chain/build/bin/.build/arm-unknown-linux-gnueabi/buildtools
--with-mpc=/home/dev/projects/arm-tool-chain/build/bin/.build/arm-unknown-linux-gnueabi/buildtools
--with-isl=/home/dev/projects/arm-tool-chain/build/bin/.build/arm-unknown-linux-gnueabi/buildtools
--with-cloog=/home/dev/projects/arm-tool-chain/build/bin/.build/arm-unknown-linux-gnueabi/buildtools
--with-libelf=/home/dev/projects/arm-tool-chain/build/bin/.build/arm-unknown-linux-gnueabi/buildtools
--enable-lto --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++,-Bdynamic -lm' --enable-threads=posix
--enable-target-optspace --enable-plugin --enable-gold --disable-nls
--disable-multilib
--with-local-prefix=/home/dev/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot
--enable-long-long
Thread model: posix
gcc version 5.2.0 (crosstool-NG crosstool-ng-1.22.0)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can also try using it to build something simple:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ cd /tmp
$ cat &amp;gt; ./etc/init.d/rcS &amp;lt;&amp;lt; EOF
#include &amp;lt;stdio.h&amp;gt;

int main (void) {
    printf ("Hello, ARM!\n");
    return 0;
}
EOF
$ arm-unknown-linux-gnueabi-gcc hello.c -o hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Obviously we can't run it on the host as it is compiled for an ARM system:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ ./hello
bash: ./hello: cannot execute binary file: Exec format error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can get a bit of information about the binary using the &lt;code&gt;file&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 4.3.0, not stripped
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can get a lot more information using the &lt;code&gt;readelf&lt;/code&gt; (some of the output removed for verbosity):&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ arm-unknown-linux-gnueabi-readelf -a hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x102c8
  Start of program headers:          52 (bytes into file)
  Start of section headers:          4676 (bytes into file)
  Flags:                             0x5000202, has entry point, Version5 EABI, soft-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         8
  Size of section headers:           40 (bytes)
  Number of section headers:         29
  Section header string table index: 26

..

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x0004a4 0x000104a4 0x000104a4 0x00008 0x00008 R   0x4
  PHDR           0x000034 0x00010034 0x00010034 0x00100 0x00100 R E 0x4
  INTERP         0x000134 0x00010134 0x00010134 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.3]
  LOAD           0x000000 0x00010000 0x00010000 0x004b0 0x004b0 R E 0x10000
  LOAD           0x0004b0 0x000204b0 0x000204b0 0x0011c 0x00120 RW  0x10000
  DYNAMIC        0x0004bc 0x000204bc 0x000204bc 0x000e8 0x000e8 RW  0x4
  NOTE           0x000148 0x00010148 0x00010148 0x00020 0x00020 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10

..

Dynamic section at offset 0x4bc contains 24 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Amongst the information here we can see that it links to the
&lt;code&gt;/lib/ld-linux.so.3&lt;/code&gt; and &lt;code&gt;libc.so.6&lt;/code&gt; and shared libraries. These libraries were
built with the cross-compiler and will need to be present in the &lt;code&gt;/lib&lt;/code&gt;
directory on the target system.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ ls -1a ~/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot/lib
.
..
ld-2.22.so
ld-linux.so.3
libanl-2.22.so
libanl.so.1
libatomic.a
libatomic.so
libatomic.so.1
libatomic.so.1.1.0
libBrokenLocale-2.22.so
libBrokenLocale.so.1
libc-2.22.so
libcrypt-2.22.so
libcrypt.so.1
libc.so.6
libdl-2.22.so
libdl.so.2
libgcc_s.so
libgcc_s.so.1
libitm.a
libitm.so
libitm.so.1
libitm.so.1.0.0
libitm.spec
libm-2.22.so
libmemusage.so
libm.so.6
libnsl-2.22.so
libnsl.so.1
libnss_compat-2.22.so
libnss_compat.so.2
libnss_db-2.22.so
libnss_db.so.2
libnss_dns-2.22.so
libnss_dns.so.2
libnss_files-2.22.so
libnss_files.so.2
libnss_hesiod-2.22.so
libnss_hesiod.so.2
libnss_nis-2.22.so
libnss_nisplus-2.22.so
libnss_nisplus.so.2
libnss_nis.so.2
libpcprofile.so
libpthread-2.22.so
libpthread.so.0
libresolv-2.22.so
libresolv.so.2
librt-2.22.so
librt.so.1
libSegFault.so
libstdc++.a
libstdc++.so
libstdc++.so.6
libstdc++.so.6.0.21
libstdc++.so.6.0.21-gdb.py
libsupc++.a
libthread_db-1.0.so
libthread_db.so.1
libutil-2.22.so
libutil.so.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In a follow up post I write about using this cross compiler toolchain to
compile a Linux kernel and Busybox and then run them in the &lt;code&gt;qemu&lt;/code&gt; emulator.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/arm-unknown-linux-gnueabi</guid><pubDate>Sun, 27 Dec 2015 19:54:00 +0000</pubDate></item><item><title>Securing my Raspberry Pi backup server</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Securing my Raspberry Pi backup server
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 30, 2015
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;I run my own email server, ownCloud server, this blog and git server on a VM
running on a physical machine we rent from Hetzner. I depend on not losing data
from these servers so I setup my Raspberry Pi as a backup server. Here are some
thoughts on how I have tried to secure everything, perhaps it is interesting
and I &lt;a href="https://twitter.com/ExchServPro/status/605914061960642560"&gt;appreciate&lt;/a&gt; &lt;a href="http://www.reddit.com/r/sysadmin/comments/387rck/how_i_sent_a_million_spam_emails_and_what_to_do/crt1okj"&gt;feedback&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ideally the backup server shouldn't accept connections from the Internet at
all, thereby reducing its surface area for attack. For this my Raspberry Pi
securely protected from in-bound connections by my home router! Actually this
isn't very secure at all as I don't trust the router or the other devices
connected to it, although it is likely to see less random attacks then on the
open Internet. As I run the server headless I need &lt;code&gt;sshd&lt;/code&gt; running so I added
some basic security; disallowed password and root SSH login and installed
&lt;code&gt;fail2ban&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The SSH keys that are used to access the server need to be stored on the
Raspberry PI, this means someone could just take the SD card and copy the keys
off it. To prevent this I &lt;a href="http://deadunicornz.org/blog/2013/12/20/raspberrypi-root-partition-encryption/"&gt;encrypted the root partition and set it up so it
will try and read the secret key from a USB stick during the boot&lt;/a&gt;. This way
if I remove the USB stick after it has booted and the Raspberry Pi and SD card
were stolen, the thief would still need to break the disk encryption to get the
SSH keys. Of course this also means it won't restart after a power outage
unless the USB stick is inserted.&lt;/p&gt;
&lt;p&gt;It is tempting to allow the backup server root access on the target machine as
it makes it easier to backup everything, but this is not ideal. If the backup
server is somehow compromised then root access to the target machine is also
given. To limit this risk I created an unprivileged user the backup server
could connect to the as. I granted this user permissions to dump databases I
wanted to backup and added secondary groups to the user for &lt;code&gt;www-data&lt;/code&gt; and the
private groups of the users home directories I wanted it to backup. This way
the backup server has read access to all the data it needs much not much more. &lt;/p&gt;
&lt;p&gt;I sync everything I want to backup from the server to a local encrypted disk
connected to the Raspberry Pi. As the root partition of the Raspberry PI is
itself encrypted it is reasonably safe to keep the key for the local disk on
the root partition. &lt;/p&gt;
&lt;p&gt;I don't really trust my local disk (disk failure, apartment disaster!), so I
also transfer my backups to Amazon S3 who I trust not to lose data but not
really for privacy. To protect my data privacy on Amazon I encrypt it with &lt;a href="https://tarnbarford.net/static/pgp.txt"&gt;my
GPG key&lt;/a&gt;. This is secure enough that it would cost more than I would bet anyone
would pay to crack my backups.&lt;/p&gt;
&lt;p&gt;Below is the script I run as a daily cron job. It backs up several databases
and directories. It snapshots the backup directory every day to the local disk
and every Sunday it pushes a PGP encrypted snapshot to Amazon S3. When it
finishes, successfully or not, it emails me with the result and the logs. It
also prevents multiple backups running simultaneously and cleans up old
snapshots on the local disk. I don't bother removing old snapshot from S3 yet,
but might consider it when I get an Amazon storage bill of more than $2.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#!/bin/bash
set -e

ssh_host=someone@somehost.com
ssh_port=22
email=someone@somehost
s3_bucket=some.bucket
log_path=/mnt/backup/logs
backup_path=/mnt/backup/current
snapshot_path=/mnt/backup/snapshots
snapshot_name=`date +"%Y%m%d_%H%M%S"`.tar.gz
snapshot_file=$snapshot_path/$snapshot_name
scriptname=$(basename $0)
pidfile="/tmp/${scriptname}"

function log {
    echo "[$(date)]: $*"
}

initialize_directories() {
    log "Initializing directories"
    mkdir -p $backup_path
    mkdir -p $snapshot_path
    mkdir -p $log_path
}

backup_databases() {
    cat databases | while read database; do
        log "Backing up database: $database"
        ssh -p $ssh_port $ssh_host "pg_dump --data-only $database" &amp;gt; $backup_path/$database.sql
    done
}

backup_files() {
    cat locations | while read remote_path local_path; do 
        log "Backing up $remote_path to $local_path"

        log "Ignoring files:"
        ssh -n -p $ssh_port $ssh_host "find $remote_path -type d -a ! -executable -prune -o ! -readable -a ! -type l" | 
            cut -c ${#remote_path}- &amp;gt; /tmp/unreadable
        cat /tmp/unreadable

        log "Start rsync"
        rsync --delete -rltzuv --exclude-from="/tmp/unreadable" -e "ssh -p $ssh_port" $ssh_host:$remote_path $backup_path/$local_path
    done
}

snapshot() {
    log "Taking snapshot $snapshot_name"
    tar -czf $snapshot_file $backup_path
}

remove_old_snapshots() {
        $snapshot_path -name "*.tar.gz" | sort -r | tail -n +10 | xargs rm
}

should_push_offsite() {
    # Returns successfully if the backups should be pushed offsite.
    # Currently it returns successfully on Sundays, but it would be better
    # if it checked how long since the last successful offsite push.
    day_of_week=$(date +%u)
    sunday=7
    if [ $day_of_week = $sunday ]; then
        return 0;
    else
        return 1;
    fi
}


push_offsite() {
    log "Encrypting snapshot"
    gpg --encrypt -o $snapshot_file.gpg --recipient $email $snapshot_file 
    log "Send encrypting snapshot"
    s3cmd put $snapshot_file.gpg s3://$s3_bucket/$snapshot_name.gpg
}

get_lock() {
    log "Attempting to aquire lock"
    exec 200&amp;gt;$pidfile
    if flock -n 200; then 
        pid=$$
        log "Lock aquired for process $pid"
        echo $pid 1&amp;gt;&amp;amp;200
        return 0;
    else 
        log "Failed to aquire lock"
        return 1;
    fi
}

do_backup() {
    get_lock
    initialize_directories
    backup_databases
    backup_files
    snapshot
    if should_push_offsite; then
        push_offsite
        remove_old_snapshots
    else
        log "Skipping offsite storage"
    fi
    log "Completed backup."
}

main() {
    do_backup | tee $log_path/$snapshot_name.log

    if [ ${PIPESTATUS[0]} -eq 0 ]; then
        log "Sending report to $email."
        gpg -ea -r $email -o $log_path/$snapshot_name.log.asc $log_path/$snapshot_name.log
        echo "Results attached" | mail -s "Buckup Succeeded" -a $snapshot_file.log.asc $email
    else
        log "Backup failed, sending report to $email."
        gpg -ea -r $email -o $log_path/$snapshot_name.log.asc $log_path/$snapshot_name.log
        echo "Results attached" | mail -s "Buckup Failed" -a $snapshot_file.log.asc $email
    fi
    log "Completed."
}

main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now I think my backup system is relatively secure from attackers and unlikely
to lose data, my main risk of data loss is probably me losing a key or
un-plugging the Raspberry Pi.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/securing-my-raspberry-pi-backup-server</guid><pubDate>Thu, 30 Jul 2015 19:00:00 +0000</pubDate></item><item><title>How I delivered a million spam emails</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        How I delivered a million spam emails
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 02, 2015
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've been running my own personal mail server for almost two years. On Friday
May 15, I tried to send an email and immediately received a undeliverable mail
response from my mail server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;This is the mail system at host tarnbarford.net.

I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.

For further assistance, please send mail to postmaster.

If you do so, please include this problem report. You can
delete your own text from the attached returned message.

                   The mail system

&amp;lt;xxxxxxxx@xxxxxxxx&amp;gt;: host gmail-smtp-in.l.google.com[74.125.136.26] said:
    550-5.7.1 [144.76.187.44      12] Our system has detected that this message
    is 550-5.7.1 likely unsolicited mail. To reduce the amount of spam sent to
    Gmail, 550-5.7.1 this message has been blocked. Please visit 550-5.7.1
    http://support.google.com/mail/bin/answer.py?hl=en&amp;amp;answer=188131 for 550
    5.7.1 more information. y7si5709303wjw.181 - gsmtp (in reply to end of DATA
    command)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A week earlier a friend told me that one of my mails had been marked as spam by
GMail, which was concerning, but I figured maybe I could take some steps to
ensure my mail server looked more legitimate, like setting up &lt;a href="http://en.wikipedia.org/wiki/Reverse_DNS_lookup"&gt;reverse DNS
lookup&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Sender_Policy_Framework"&gt;SPF&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/DomainKeys_Identified_Mail"&gt;DKIM&lt;/a&gt; correctly. This seemed much more concerning.
I took a break outside to think about and what I should do next and why Google was
picking on me all of a sudden? Slowly it dawned on me that maybe my server had
been compromised.&lt;/p&gt;
&lt;p&gt;I rushed back to my computer, shelled into the server and ran &lt;code&gt;tail -f&lt;/code&gt; on the
mail log. I was immediately presented a continuous stream of log messages.
&lt;em&gt;Fuck&lt;/em&gt;. I stopped the tail to see what was happening, it didn't look good
"delivery temporarily suspended", "ERROR: Mail Refused", "Retrying will NOT
succeed.". I stopped postfix and re-ran the tail command, the tail of log
appeared but thankfully new log messages were not appearing.&lt;/p&gt;
&lt;p&gt;"Table tennis?" a colleague had approached me from behind and shocked me. "Not
now, I'm busy" I snapped back, shocking him and me. I broke my trance like
stare from the shell on my screen, turned around and explained that my mail
server had been compromised and appeared to by trying to send spam.&lt;/p&gt;
&lt;p&gt;I turned back to my computer to see a message from Michal, we share a physical
server which we run virtual machines on using KVM. "Hey", this cannot have been
unrelated so I beat him to the punch, "my server was spamming :'(".  He'd
received an abuse email from our hosting provider.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;15:41:48 piwoni: hey
15:41:54 tarnacious: yes?
15:42:05 tarnacious: my server was spamming :'(
15:42:09 piwoni: yes
15:42:14 tarnacious: I don't know how
15:42:16 piwoni: wanted to tell you
15:42:19 tarnacious: thanks
15:42:22 tarnacious: too late
15:42:44 tarnacious: you get email from hetzner?
15:42:45 tarnacious: :D
15:42:49 piwoni: yes
15:42:53 tarnacious: today?
15:42:55 piwoni: yes
15:42:58 tarnacious: ok.
15:43:05 tarnacious: has been 4 days.
15:43:11 tarnacious: ip address is ruined :(
15:43:33 piwoni: how did it happen?
15:43:42 tarnacious: I don't know yet.
15:43:45 piwoni: ok
15:44:05 piwoni: do you have anyting on your
15:44:08 piwoni: site?
15:44:13 piwoni: form?
15:44:36 tarnacious: nope.
15:44:37 piwoni: is the smtp with auth
15:44:45 tarnacious: yep.
15:44:56 tarnacious: maybe brute force the auth
15:45:10 tarnacious: I do not know yet.
15:45:49 tarnacious: queue was pretty massive, but most mail servers have stopped talking to me.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;What happened&lt;/h3&gt;
&lt;p&gt;I still hadn't worked out how my server had been compromised. I've been running
it for almost two years and I was pretty confident it wasn't an open relay. I
looked through the logs and found a message from &lt;code&gt;android@tarnbarford.net&lt;/code&gt;. I
ran &lt;code&gt;grep from= /var/log/mail.log&lt;/code&gt;, this account was trying to send a lot of
mail.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;15:51:43 tarnacious: ok, i see what has happened :(
15:51:49 piwoni: ?
15:52:39 tarnacious: in my mail logs...
15:52:47 tarnacious: sasl_username=android connects.
15:52:51 tarnacious: and me.
15:53:06 piwoni: android?
15:53:11 piwoni: username
15:53:11 piwoni: :P
15:53:17 tarnacious: android is an account I created to build android.
15:53:21 piwoni: heh
15:53:25 piwoni: and pass ?
15:53:28 tarnacious: I guess I must have given him a week password.
15:53:29 piwoni: empty ?
15:53:53 piwoni: how do you store them ?
15:54:57 tarnacious: unix password
15:55:13 piwoni: change it
15:56:44 tarnacious: no shit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use this machine for many other things including; a web server, a VPN server,
a git server, for connecting to IRC servers, for &lt;a href="/journal/pair-programming"&gt;pair programming&lt;/a&gt;, and for
running arbitrary long running processes. I try to manage the system like I
think a Unix system administer would; I create user accounts for everything, I
care about file permissions and try to run a much as possible as non-privileged
users.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;android&lt;/code&gt; account was created for building a custom &lt;a href="http://www.fail2ban.org/wiki/index.php/Main_Page"&gt;CyanogenMod&lt;/a&gt;.
Aside from taking ages to build, it also requires significantly more disk space
than I can free on my Macbook Air. As the user was not in the &lt;code&gt;sudoers&lt;/code&gt; file
and password SSH connections are not allowed, I hadn't thought the password was
very important and presumably created an extremely weak one.&lt;/p&gt;
&lt;p&gt;What I had not thought of was that the password could be used to authenticate
with the SMTP server, this is because I have postfix set-up to use Linux
accounts as users. It appeared that someone had managed to guess a
username/password combination on the SMTP server. A friend later joked that the
attacker may have been surprised to get access so early in their dictionary
attack.&lt;/p&gt;
&lt;p&gt;I changed the &lt;code&gt;android&lt;/code&gt; users password, cleared the entire postfix mail queue,
restarted postfix and watched the logs. There were a few intermittent failed
login attempts, but the server didn't seem to be trying to send any more mail.&lt;/p&gt;
&lt;h3&gt;What was sent&lt;/h3&gt;
&lt;p&gt;Feeling like I had done enough to temporarily safely start running the server
again while still keeping an eye on the logs I moved onto working out what had
happened. For this I had copied all the logs to a working directory and
unzipped them. I had logs from April 19th to May 17th, 12GB in total, which
itself was pretty ominous.&lt;/p&gt;
&lt;p&gt;I initially attempted to analyse the logs myself with some &lt;code&gt;awk&lt;/code&gt; scripts but
found the postfix logs quite difficult to extract useful summaries from. The
problem is there can be many log entries for a single message and useful
information is spread across them. There is some sort of ID in each log message
and I initially thought that the ID would be unique per message and I could
group on that and work out what happened for each ID, but it turns out it is a
little more complicated than that as messages can have multiple recipients.
Probably worth looking around, surely someone has solved the problem.&lt;/p&gt;
&lt;p&gt;After a quick search I found a script, &lt;a href="http://jimsun.linxnet.com/postfix_contrib.html"&gt;pflogsumm.pl&lt;/a&gt;, that seemed to
summarise the type of information I was looking for. I'm not sure how accurate
it is or what these numbers mean, but it is very clear that my little mail
server had been quite busy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Grand Totals
------------
messages

 493404   received
   1543k  delivered
      0   forwarded
 311685   deferred  (33608k deferrals)
 448708   bounced
    486   rejected (0%)
      0   reject warnings
      0   held
      0   discarded (0%)

   2406m  bytes received
   5826m  bytes delivered
    672   senders
   1166   sending hosts/domains
   1053k  recipients
 105817   recipient hosts/domains
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The per day traffic summary was quite interesting to me as I shows that the
account had been compromised a few weeks earlier than I thought. I also find
the usage patterns quite interesting, it looks like it was compromised on April
26 and a few thousand emails were sent, possibly to test if the server could be
used to send spam. Then it is used in bursts, with no email at been sent
between May 7 and May 9.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Per-Day Traffic Summary
-----------------------
    date          received  delivered   deferred    bounced     rejected
    --------------------------------------------------------------------
    Apr 19 2015        21         21          0          0         12
    Apr 20 2015        35         35          0          0         30
    Apr 21 2015        32         32          0          0         14
    Apr 22 2015        30         30          0          0         44
    Apr 23 2015        34         34          0          0          8
    Apr 24 2015        28         28          0          0         13
    Apr 25 2015        22         22          0          0         10
    Apr 26 2015       110       2030          0       2039          9
    Apr 27 2015        61         60          0          0         18
    Apr 28 2015     67547     193033        565k     52301         15
    Apr 29 2015       149      11711     453347      10425         21
    Apr 30 2015    136912     460833       1244k     90682         17
    May  1 2015     11679     194702       4523k     26787         15
    May  2 2015       113      10189       3798k      2113         10
    May  3 2015        69       7538       3601k       850          9
    May  4 2015        44       5902       3344k      6094         10
    May  5 2015       109      19998       3064k        64         14
    May  6 2015        63       7587     187980          0          9
    May  7 2015        52         54         43          0         18
    May  8 2015        65         64         21          0         17
    May  9 2015        50         50         21          0         27
    May 10 2015     35576      74756     335577      22229         14
    May 11 2015     48658     125790        668k     68149         22
    May 12 2015     88920     203938       3024k    115446         26
    May 13 2015        73       5227       3056k     11819         13
    May 14 2015        76       3157       2885k      6634         13
    May 15 2015    102763     253975       2876k     33070         12
    May 16 2015        48         48          0          3         30
    May 17 2015        65         65          0          3         16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The top domains is pretty uninteresting, except that I can clearly see why
GMail decided to start blocking emails from me.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host/Domain Summary: Message Delivery
--------------------------------------
 sent cnt  bytes   defers   avg dly max dly host/domain
 -------- -------  -------  ------- ------- -----------
 281109     1116m    1540k    4.8 h   87.7 h  hotmail.com
 247760      705m   15277k   13.1 h  121.2 h  yahoo.com
 152088     1129m       0     1.2 m    4.9 h  tarnbarford.net
 118590   371368k  132586     5.3 h  120.9 h  gmail.com
  40608   130322k  359555     2.5 h   37.5 h  aol.com
  35961   132388k       1     2.8 h   15.5 h  telenet.be
  23607    73362k      12     7.3 h   17.3 h  163.com
  21060    77012k  206635    11.9 h  120.8 h  skynet.be
  18418    73872k  481938    25.0 h   76.2 h  hanmail.net
  17039    61274k   88959     4.4 h   82.1 h  msn.com
  15737    57300k   62322     5.4 h   15.5 h  live.nl
  13636    42315k       9     6.2 h   17.3 h  126.com
  13020    34618k   58867     6.9 h   21.1 h  ameritech.net
  11876    36937k   95879    10.5 h   77.1 h  vip.sina.com
  11828    30012k    1176k    8.0 h  121.1 h  adelphia.net
  11168    40958k   38749     5.1 h   31.7 h  hotmail.fr
  10203    33512k  267776    15.7 h  120.9 h  yahoo.fr
   9005    29039k       0     6.8 h   17.3 h  sina.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are the top two recipients, the next ten are genuine and then a huge list
of addresses used three or less times.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Recipients by message count
---------------------------
 150742   android@tarnbarford.net
  34696   ppway2323@hotmail.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have no idea why &lt;code&gt;ppway2323@hotmail.com&lt;/code&gt; was sent so many emails and I also
don't know where the 150742 emails to &lt;code&gt;android@tarnbarford.net&lt;/code&gt; are. There are
only 87 emails in the &lt;code&gt;Maildir&lt;/code&gt; of the user, they are all delivery status
notifications. Usually the delivery status emails contain the original email,
so we get some information on what was sent. They are not all the same, but
they are all spam, this one is quite common.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Subject: Re: Part time job opportunity
To: Recipients &amp;lt;android@tarnbarford.net&amp;gt;
From: android@tarnbarford.net
Date: Thu, 14 May 2015 15:49:24 -0700
Reply-To: tinagerardi9470@gmail.com

Our Ref: Bny/23/9swd/34
Your Ref: ABWN/NYT/87E3

Position: Retail Product Research Coordinator.

We are a reputable Survey Company handling products and services survey
evaluation for most Fortune 500 companies in the United States of America. We
are seeking Dedicated Part time staff for the position of Retail Product
Research Coordinator. You will work as a team to test products pricing and
product display in your Geographical Zone.It is probationary assignments that
will last 18 months.Your basic pay per assignment will be $900.00. You will be
entitled to a pay review after 90 days. You only need 1hr in a day to carry out
your assignment. If interested in this position please send your reply to
tinagerardi9470@gmail.com. Include your full name, Address and a Day time
Telephone number. We will send you a package with your first Assignment as soon
as we we hear from you.

Sincerely;  Tina Gerardi.
Survey  Coordinator.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Adding security and anti-spam measures&lt;/h3&gt;
&lt;p&gt;My initial thoughts were to build a new mail server on its own machine, I had
wanted to do this anyway as the mail server was was both relatively important
to me and relatively difficult to set up. If not automate the set up, I wanted
to at least document it. I decided against building it now as it appeared the
server itself wasn't compromised, I didn't have the time and I would probably
have to deal with my host name being on email blacklists even if I changed IP
address. So I decided to see if I could harden this server and get myself off
the blacklists.&lt;/p&gt;
&lt;p&gt;I immediately added &lt;a href="http://www.postfix.org/postconf.5.html#smtpd_sender_restrictions"&gt;&lt;code&gt;smtpd_sender_restrictions&lt;/code&gt;&lt;/a&gt; to only allow mail to be
sent by specified users. Although I will probably create users with &lt;code&gt;--disabled-password&lt;/code&gt; in the future, there are potentially other accounts on the
system with weak passwords. It was quite easy to verify this was working, I
just removed myself from the list, restarted postfix and tried to send a mail.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat mail.eml | msmtp -a tarn tarn.barford@retresco.de
msmtp: recipient address tarn.barford@retresco.de not accepted by the server
msmtp: server message: 554 5.7.1 &amp;lt;tarn@tarnbarford.net&amp;gt;: Sender address rejected: Access denied
msmtp: could not send mail (account tarn from /Users/tarn/.msmtprc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I enabled &lt;a href="http://www.fail2ban.org/wiki/index.php/Main_Page"&gt;&lt;code&gt;fail2ban&lt;/code&gt;&lt;/a&gt; jails for &lt;code&gt;postfix&lt;/code&gt; and &lt;code&gt;sasl&lt;/code&gt; which means any IP
address that fails to authenticate 3 times will be banned for an hour. This was
just a matter of &lt;a href="https://scottlinux.com/2011/05/26/prevent-postfix-brute-force/"&gt;enabling some sections&lt;/a&gt; in the &lt;code&gt;fail2ban&lt;/code&gt; configuration as
I already had it running for &lt;code&gt;ssh&lt;/code&gt; connections. I found out this works the hard
way by accidentally banning myself while testing other SMTP logins failed.&lt;/p&gt;
&lt;p&gt;I used &lt;a href="http://wiki.policyd.org/"&gt;&lt;code&gt;policyd&lt;/code&gt;&lt;/a&gt; to setup per-address rate limiting for sending and
receiving mail.  This was a bit more tricky, but I found &lt;a href="http://uname.pingveno.net/blog/index.php/post/2015/03/11/Configure-sender-rate-limits-to-prevent-spam,-using-cluebringer-%28policyd%29-with-Postfix"&gt;a guide&lt;/a&gt; that
helped me get it set-up. I tested this works by setting the quota really low
and sending a few messages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat mail.eml | msmtp -a tarn tarn.barford@retresco.de
$ cat mail.eml | msmtp -a tarn tarn.barford@retresco.de
$ cat mail.eml | msmtp -a tarn tarn.barford@retresco.de
$ cat mail.eml | msmtp -a tarn tarn.barford@retresco.de
msmtp: recipient address tarn.barford@retresco.de not accepted by the server
msmtp: server message: 554 5.7.1 &amp;lt;tarn@tarnbarford.net&amp;gt;: Sender address rejected: Sender quota exceed.
msmtp: could not send mail (account tarn from /Users/tarn/.msmtprc)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting up a &lt;a href="http://en.wikipedia.org/wiki/DomainKeys_Identified_Mail"&gt;Sender Policy Framework (SPF)&lt;/a&gt; is just a matter setting a
&lt;code&gt;TXT&lt;/code&gt; record on the domain name.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dig +short -t txt tarnbarford.net
"v=spf1 mx a -all"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my case all mail from my domain should come from the IP that is in the
&lt;a href="http://en.wikipedia.org/wiki/MX_record"&gt;&lt;code&gt;MX&lt;/code&gt;&lt;/a&gt; record, and if not then it should be rejected.&lt;/p&gt;
&lt;p&gt;To send &lt;a href="http://en.wikipedia.org/wiki/DomainKeys_Identified_Mail"&gt;DomainKeys Identified Mail (DKIM)&lt;/a&gt; you need to create a key-pair,
publish the public key as a domain record and add a header each email with a
signature of the contents. There is a Debian package for &lt;a href="http://www.opendkim.org/"&gt;&lt;code&gt;OpenDKIM&lt;/code&gt;&lt;/a&gt; that
includes a daemon that can sign mails and is &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-dkim-with-postfix-on-debian-wheezy"&gt;easy to integrate&lt;/a&gt; with
Postfix. Below is an example of a DKIM signed message I sent to myself.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarnbarford.net;
    s=mail; t=1432809266;
    bh=YocFhtTgjr2exNi9eb3SUWb8spcRxFH1Tqh/0OcRcfA=;
    h=Date:From:To:Subject:From;
    b=PAy0LzlmVsAQaQ9NzI4d+RSLMpz8Fg/eqNrVYU9rFlc7WHnIkBwBpFol2NQy8T+yg
    Dxuw3OAjDh0kjV7W9LF0Y2rnjVfVJ5RLQRVe0HJiVOkLyS9cESoZZ63Ki4uUb/oB8N
    hhvWPjrKw6hukPZYGSCOhGg8Zz8da1EviMHB6oP4=
Date: Thu, 28 May 2015 12:34:24 +0200
From: Tarn Barford &amp;lt;tarn@tarnbarford.net&amp;gt;
To: tarn@tarnbarford.net
Subject: DKIM
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
User-Agent: Mutt/1.5.23 (2014-03-12)

hello, DKIM.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My DKIM public key for this message can be found based on the from header and
the &lt;code&gt;s&lt;/code&gt; field in the DKIM signature.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dig +short -t txt mail._domainkey.tarnbarford.net
"v=DKIM1\; k=rsa\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0LNf8wdRCk2eXt1+EAIAdDSfq4aLMR/a6hCdPzJdAZ5OB1Z2LvOJlUGtx81MAttOG2lztjMmdrEq4mmGe0LUXOmOTqMcY8/woNspqvj9N4zPUEZXFP6yrYlgVGuVcLYWV6huCviwlt49KciaB9al+E2PogZJxDda5/cYffrI4PQIDAQAB"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are plenty of tools to create and validate DKIM mail signatures including
&lt;a href="https://launchpad.net/dkimpy"&gt;a Python library&lt;/a&gt;. This validated that my DKIM signature was valid for the
above mail.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ virtualenv .
$ ./bin/pip install dkimpy
$ ./bin/pip install dnspython
$ ./bin/dkimverify.py &amp;lt; test.eml
signature ok
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While this verifies the signature is ok, I wasn't entirely satisfied and wanted
to see what was going on. The &lt;code&gt;DKIM-Signature&lt;/code&gt; header has a body hash field
&lt;code&gt;bh&lt;/code&gt; which is specified in the header as being an &lt;code&gt;rsa-sha256&lt;/code&gt; hash. Once this
is verified the signature itself can be verified on the headers alone as the
headers contain a hash of the body contents.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ printf "hello, DKIM.\r\n" | openssl dgst -binary -sha256 | openssl base64
YocFhtTgjr2exNi9eb3SUWb8spcRxFH1Tqh/0OcRcfA=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This matches the body hash in the signature, so we can continue. To verify the
authenticity of this mails headers a signature, a public key and the exact
headers that were signed with the private key are required.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;b&lt;/code&gt; field in the &lt;code&gt;DKIM-Signature&lt;/code&gt; header is the signature. It is &lt;code&gt;base64&lt;/code&gt;
encoded and &lt;code&gt;openssl&lt;/code&gt; requires the binary form, so it needs to be decoded and
saved.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ printf "PAy0LzlmVsAQaQ9NzI4d+RSLMpz8Fg/eqNrVYU9rFlc7WHnIkBwBpFol2NQy8T+ygDxuw3OAjDh0kjV7W9LF0Y2rnjVfVJ5RLQRVe0HJiVOkLyS9cESoZZ63Ki4uUb/oB8NhhvWPjrKw6hukPZYGSCOhGg8Zz8da1EviMHB6oP4=" | openssl enc -base64 -d -A &amp;gt; signature
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;p&lt;/code&gt; field in the &lt;code&gt;mail._domainkey&lt;/code&gt; is a base64 encoded, &lt;a href="http://en.wikipedia.org/wiki/X.690#DER_encoding"&gt;DER&lt;/a&gt; encoded
public key.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ printf "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0LNf8wdRCk2eXt1+EAIAdDSfq4aLMR/a6hCdPzJdAZ5OB1Z2LvOJlUGtx81MAttOG2lztjMmdrEq4mmGe0LUXOmOTqMcY8/woNspqvj9N4zPUEZXFP6yrYlgVGuVcLYWV6huCviwlt49KciaB9al+E2PogZJxDda5/cYffrI4PQIDAQAB" | openssl enc -base64 -d -A &amp;gt; key.der
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be verified this by converting it back into a familiar looking public
key and saving it as &lt;code&gt;key.pub&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ openssl rsa  -pubin -inform DER &amp;lt; key.derwriting RSA key | tee key.pub
writing RSA key
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0LNf8wdRCk2eXt1+EAIAdDSfq
4aLMR/a6hCdPzJdAZ5OB1Z2LvOJlUGtx81MAttOG2lztjMmdrEq4mmGe0LUXOmOT
qMcY8/woNspqvj9N4zPUEZXFP6yrYlgVGuVcLYWV6huCviwlt49KciaB9al+E2Po
gZJxDda5/cYffrI4PQIDAQAB
-----END PUBLIC KEY-----
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The modulus and exponent of this public key can also be checked and should be
rejected if it isn't long enough.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ openssl rsa -noout -text -pubin &amp;lt; key.pub
Modulus (1024 bit):
    00:b4:2c:d7:fc:c1:d4:42:93:67:97:b7:5f:84:00:
    80:1d:0d:27:ea:e1:a2:cc:47:f6:ba:84:27:4f:cc:
    97:40:67:93:81:d5:9d:8b:bc:e2:65:50:6b:71:f3:
    53:00:b6:d3:86:da:5c:ed:8c:c9:9d:ac:4a:b8:9a:
    61:9e:d0:b5:17:3a:63:93:a8:c7:18:f3:fc:28:36:
    ca:6a:be:3f:4d:e3:33:d4:11:95:c5:3f:ac:ab:62:
    58:15:1a:e5:5c:2d:85:95:ea:1b:82:be:2c:25:b7:
    8f:4a:72:26:81:f5:a9:7e:13:63:e8:81:92:71:0d:
    d6:b9:fd:c6:1f:7e:b2:38:3d
Exponent: 65537 (0x10001)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;DKIM-Signature&lt;/code&gt; also specifies a list of headers in the &lt;code&gt;h&lt;/code&gt; field. These
are the headers, followed by the &lt;code&gt;DKIM-Signature&lt;/code&gt; header itself minus the
signature field &lt;code&gt;b&lt;/code&gt; that are hashed and then signed. There are some rules about
formatting these headers before they are signed; line wrapping must be removed,
the header key and value are separated by a single colon without spaces and
headers are separated with a &lt;code&gt;Carriage Return&lt;/code&gt; and &lt;code&gt;Line Feed&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ printf "date:Thu, 28 May 2015 12:34:24 +0200\r\n" &amp;gt; headers
$ printf "from:Tarn Barford &amp;lt;tarn@tarnbarford.net&amp;gt;\r\n" &amp;gt;&amp;gt; headers
$ printf "to:tarn@tarnbarford.net\r\n" &amp;gt;&amp;gt; headers
$ printf "subject:DKIM\r\ndkim-signature:v=1; a=rsa-sha256; c=relaxed/simple; d=tarnbarford.net; s=mail; t=1432809266; bh=YocFhtTgjr2exNi9eb3SUWb8spcRxFH1Tqh/0OcRcfA=; h=Date:From:To:Subject:From; b=" &amp;gt;&amp;gt; headers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now &lt;code&gt;openssl&lt;/code&gt; can again be used, this time to verify the key, the signature and
the headers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ openssl dgst -sha256 -verify key.der -keyform DER -signature signature headers
Verified OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, it verifies as expected. There is obviously more detail in the
&lt;a href="http://dkim.org/#sign"&gt;RFCs&lt;/a&gt; or by looking at an open source implementation, but I found it
interesting to see how it worked for one email.&lt;/p&gt;
&lt;p&gt;A &lt;a href="http://en.wikipedia.org/wiki/DMARC"&gt;Domain-based Message Authentication, Reporting and Conformance (DMARC)&lt;/a&gt;
policy is simply another DNS record that allows a domain to specify what action
other mail servers should take with mail purportedly from this domain based on
the combined results of SPF and DKIM checks (allow, reject, hold, etc). It also
provides an email address for participating mail servers to send reports.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dig +short -t txt _dmarc.tarnbarford.net
"v=DMARC1\;p=reject\;pct=100\;rua=mailto:postmaster@tarnbarford.net"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My DMARC policy says to reject 100% of mails that fail either SPF or DKIM
checks and mail conformance information to &lt;code&gt;postmaster@tarnbarford.net&lt;/code&gt;. I now
get digest DMARC reports from participating mail servers, here is what Google
sent me.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;
&amp;lt;feedback&amp;gt;
  &amp;lt;report_metadata&amp;gt;
    &amp;lt;org_name&amp;gt;google.com&amp;lt;/org_name&amp;gt;
    &amp;lt;email&amp;gt;noreply-dmarc-support@google.com&amp;lt;/email&amp;gt;
    &amp;lt;extra_contact_info&amp;gt;https://support.google.com/a/answer/2466580&amp;lt;/extra_contact_info&amp;gt;
    &amp;lt;report_id&amp;gt;12005498305772839030&amp;lt;/report_id&amp;gt;
    &amp;lt;date_range&amp;gt;
      &amp;lt;begin&amp;gt;1432684800&amp;lt;/begin&amp;gt;
      &amp;lt;end&amp;gt;1432771199&amp;lt;/end&amp;gt;
    &amp;lt;/date_range&amp;gt;
  &amp;lt;/report_metadata&amp;gt;
  &amp;lt;policy_published&amp;gt;
    &amp;lt;domain&amp;gt;tarnbarford.net&amp;lt;/domain&amp;gt;
    &amp;lt;adkim&amp;gt;r&amp;lt;/adkim&amp;gt;
    &amp;lt;aspf&amp;gt;r&amp;lt;/aspf&amp;gt;
    &amp;lt;p&amp;gt;reject&amp;lt;/p&amp;gt;
    &amp;lt;sp&amp;gt;reject&amp;lt;/sp&amp;gt;
    &amp;lt;pct&amp;gt;100&amp;lt;/pct&amp;gt;
  &amp;lt;/policy_published&amp;gt;
  &amp;lt;record&amp;gt;
    &amp;lt;row&amp;gt;
      &amp;lt;source_ip&amp;gt;144.76.187.44&amp;lt;/source_ip&amp;gt;
      &amp;lt;count&amp;gt;1&amp;lt;/count&amp;gt;
      &amp;lt;policy_evaluated&amp;gt;
        &amp;lt;disposition&amp;gt;none&amp;lt;/disposition&amp;gt;
        &amp;lt;dkim&amp;gt;pass&amp;lt;/dkim&amp;gt;
        &amp;lt;spf&amp;gt;pass&amp;lt;/spf&amp;gt;
      &amp;lt;/policy_evaluated&amp;gt;
    &amp;lt;/row&amp;gt;
    &amp;lt;identifiers&amp;gt;
      &amp;lt;header_from&amp;gt;tarnbarford.net&amp;lt;/header_from&amp;gt;
    &amp;lt;/identifiers&amp;gt;
    &amp;lt;auth_results&amp;gt;
      &amp;lt;spf&amp;gt;
        &amp;lt;domain&amp;gt;tarnbarford.net&amp;lt;/domain&amp;gt;
        &amp;lt;result&amp;gt;pass&amp;lt;/result&amp;gt;
      &amp;lt;/spf&amp;gt;
    &amp;lt;/auth_results&amp;gt;
  &amp;lt;/record&amp;gt;
&amp;lt;/feedback&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Blacklists&lt;/h3&gt;
&lt;p&gt;I found &lt;a href="http://mxtoolbox.com/SuperTool.aspx?action=blacklist%3atarnbarford.net&amp;amp;run=toolpage"&gt;MXToolbox&lt;/a&gt; which checks a domain or IP address against quite a few
email blacklists. It is quite useful as it also provides some information and
links to get your IP off each list. My IP was listed in eight blacklists, I was
able to get it off six almost immediately by following the delisting
procedures. I believe I was on the other two remaining lists already as our
other IP addresses in the network are also on the list, and are not even mail
servers.&lt;/p&gt;
&lt;p&gt;Google has been more difficult, &lt;a href="http://www.rackaid.com/blog/gmail-blacklist-removal/"&gt;"How to Remove Your IP from the Gmail
Blacklist"&lt;/a&gt; goes into some detail and links to a &lt;a href="https://support.google.com/mail/contact/msgdelivery"&gt;Google Support page&lt;/a&gt;
where you can submit delivery issues. The post says it will take 3-7 days to
process provided you fixed the reason it was blocked in the first place. I
filled the form out a few weeks ago after stopping the spamming and adding the
sending restrictions. I thought I had done enough at that stage to be
unblocked, but apparently Google didn't think so as I am still blocked. I have
since added SPF, DKIM, DMARC and rate limiting and I will fill the form out
again after I post this story.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This story shouldn't be seen as a reason not to run your own mail server, if
you want to you should, it's fun! This was a pretty embarrassing security
failure, but the exploit was crude and relatively harmless. In working out what
happened and putting systems in place to prevent it occurring again I have
learned a lot and added a range of anti-spam and security features to my little
mail server.&lt;/p&gt;
&lt;p&gt;Hopefully soon I will again be able to mail Google addresses, I will update
this post when this happens (or doesn't).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update June 19, 2014: I never actually re-sent that request to Google to unblock me, but today it seems I am able to send mail to GMail accounts again. Thanks GMail!&lt;/em&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/how-i-delivered-a-million-spam-emails</guid><pubDate>Tue, 02 Jun 2015 19:54:00 +0000</pubDate></item><item><title>Packaging a Flask Application</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Packaging a Flask Application
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 09, 2014
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Packages are not very exciting but the only sane way to deploy. I've tried to
&lt;code&gt;git clone&lt;/code&gt; and &lt;code&gt;make install&lt;/code&gt; on the servers with &lt;a href="http://puppetlabs.com/"&gt;Puppet&lt;/a&gt;, that was
silly. It's about time I learnt how to build packages.&lt;/p&gt;
&lt;p&gt;I run a few &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; applications (including this site) that I would
like to package for Debian linux, so packaging a minimal Flask application
seemed a good place to start.&lt;/p&gt;
&lt;p&gt;I created a very simple Flask application called &lt;a href="https://github.com/tarnacious/demosite"&gt;demosite&lt;/a&gt;. The
basic requirement of the site is that &lt;code&gt;python setup.py install&lt;/code&gt; works and that
it provides some mechanism changing the configuration file and logging paths.&lt;/p&gt;
&lt;p&gt;The package should install the application, all its dependencies, add the
configuration files to &lt;code&gt;/etc/demosite&lt;/code&gt;, set up the logging to log to
&lt;code&gt;/var/log/demosite&lt;/code&gt;, create a un-privileged user and install an init script in
&lt;code&gt;/etc/init.d/demosite&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;I found this post: &lt;a href="http://www.plankandwhittle.com/packaging-a-flask-app-in-a-debian-package/"&gt;Packaging a Flask app in a Debian package&lt;/a&gt;
very helpful getting me started.&lt;/p&gt;
&lt;p&gt;The package is built and packaged on a Debian machine provisioned by &lt;a href="https://www.vagrantup.com/"&gt;Vagrant&lt;/a&gt; and run in &lt;a href="https://www.virtualbox.org/"&gt;Virtualbox&lt;/a&gt;. The application itself is
installed inside a &lt;a href="https://virtualenv.pypa.io/en/latest/"&gt;virtualenv&lt;/a&gt; environment which is then packaged
by &lt;a href="https://github.com/jordansissel/fpm"&gt;fpm&lt;/a&gt;. Below are all the steps needed to create a package and install
it. &lt;/p&gt;
&lt;p&gt;Create a vagrant box and ssh into it:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ vagrant init chef/debian-7.4
$ vagrant up &amp;amp;&amp;amp; vagrant ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Switch to the root user, update the system and install build dependencies:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ sudo su
vagrant:~$ apt-get update
vagrant:~$ apt-get install ruby1.8 rubygems git python-dev python-setuptools python-virtualenv ruby-dev curl
vagrant:~$ gem install fpm pleaserun
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Installing the application into a &lt;code&gt;virtualenv&lt;/code&gt; is quite straight forward.
Download the source, create a &lt;code&gt;virtualenv&lt;/code&gt; and install the application in it, a
lot problems with generated paths can be avoided by creating the &lt;code&gt;virtualenv&lt;/code&gt;
in the desired install path.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ git clone https://github.com/tarnacious/demosite.git demosite-src
vagrant:~$ virtualenv /usr/share/demosite
vagrant:~$ cd /home/vagrant/demosite-src
vagrant:~/demosite-src$ /usr/share/demosite/bin/python setup.py install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I used &lt;a href="https://github.com/jordansissel/pleaserun"&gt;pleaserun&lt;/a&gt; to create the init file. The gem requires the
ruby-dev package to be installed.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ pleaserun --user demosite --install --name demosite /usr/share/demosite/bin/gunicorn demosite:app
No platform selected. Autodetecting... {:platform=&amp;gt;"sysv", :version=&amp;gt;"lsb-3.1", :level=&amp;gt;:warn}
Writing file {:destination=&amp;gt;"/etc/init.d/demosite"}
Writing file {:destination=&amp;gt;"/etc/default/demosite"}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The file &lt;code&gt;/etc/default&lt;/code&gt; is sourced using the dot operator in the &lt;a href="http://ss64.com/bash/source.html"&gt;init script&lt;/a&gt;. Environment variables can be added here, for the demosite
application these are the locations of the config file and the logging config
file.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ cat &amp;gt; /etc/default/demosite &amp;lt;&amp;lt; EOF
export DEMOSITE_CONFIG_PATH=/etc/demosite/config.cfg
export DEMOSITE_LOGGING_CONFIG_PATH=/etc/demosite/logging.conf
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create the &lt;code&gt;config.cfg&lt;/code&gt; expected by the application.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ mkdir /etc/demosite
vagrant:~$ cat &amp;gt; /etc/demosite/config.cfg &amp;lt;&amp;lt; EOF
TAGLINE="this is all so far"
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create the &lt;code&gt;logging.conf&lt;/code&gt; expected by the application.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ cat &amp;gt; /etc/demosite/logging.conf &amp;lt;&amp;lt; EOF
[loggers]
keys = root, demosite

[handlers]
keys = root, demosite

[formatters]
keys = default

[formatter_default]
format = [%(asctime)s] - %(name)s - %(levelname)s - %(message)s
class = logging.Formatter

[logger_root]
level = DEBUG
qualname = root
handlers = root

[handler_root]
class = logging.handlers.RotatingFileHandler
formatter = default
args = ("/var/log/demosite/root.log",)

[logger_demosite]
level = DEBUG
qualname = demosite
handlers = demosite

[handler_demosite]
class = logging.handlers.RotatingFileHandler
formatter = default
args = ("/var/log/demosite/demosite.log",)
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Debian packages have hooks for a &lt;code&gt;postinst&lt;/code&gt;, &lt;code&gt;preinst&lt;/code&gt; and &lt;code&gt;prerm&lt;/code&gt;. This
&lt;code&gt;postinst&lt;/code&gt; script creates a user &lt;code&gt;demosite&lt;/code&gt;, a directory &lt;code&gt;/var/log/demosite&lt;/code&gt;
and changes ownership over to the &lt;code&gt;demosite&lt;/code&gt; user. &lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ cat &amp;gt; postconfig &amp;lt;&amp;lt; EOF
#!/bin/sh -e

action="\$1"
oldversion="\$2"

. /usr/share/debconf/confmodule
db_version 2.0

if [ "\$action" != configure ]; then
    exit 0
fi

if ! getent passwd demosite &amp;gt;/dev/null; then
    adduser --quiet --system --no-create-home --home /home/vagrant/demosite --shell /usr/sbin/nologin demosite
fi

mkdir -p /var/log/demosite
chown demosite /var/log/demosite
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this package the &lt;code&gt;preinst&lt;/code&gt; and &lt;code&gt;prerm&lt;/code&gt; hooks are the same for now: stop the
service if the init script is there.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ cat &amp;gt;stopservice &amp;lt;&amp;lt; EOF
#!/bin/sh
set -e

if [ -f /etc/init.d/demosite ]; then
    /etc/init.d/demosite stop
fi
EOF
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Package the application into a &lt;code&gt;deb&lt;/code&gt; package with &lt;code&gt;fpm&lt;/code&gt;. &lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ fpm -s dir \
    -t deb \
    -n demosite \
    -v 0.1 \
    --deb-init /etc/init.d/demosite \
    --before-install stopservice \
    --after-install postconfig \
    --before-remove stopservice \
    -d "python" \
    /etc/demosite/=/etc/demosite \
    /etc/default/demosite=/etc/default/ \
    /usr/share/demosite/=/usr/share/demosite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now copy the package to the host machine, to a &lt;code&gt;build&lt;/code&gt; directory:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ mkdir -p /vagrant/build
vagrant:~$ cp demosite_0.1_amd64.deb /vagrant/build/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unless there was any problems with the build we can exit this box and even
destroy it.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ exit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ok, let's try it out in a new vagrant box!&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ cd build
$ vagrant init chef/debian-7.4
$ vagrant up &amp;amp;&amp;amp; vagrant ssh
vagrant:~$ sudo dpkg -i /vagrant/demosite_0.1_amd64.deb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Does it work? Let's try start it and find out.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ sudo /etc/init.d/demosite start
demosite started.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Looks good, let's see if it works.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vagrant:~$ curl localhost:8000 
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Woohoo!&lt;/p&gt;
&lt;p&gt;To automate all this one could use the &lt;a href="https://docs.vagrantup.com/v2/provisioning/shell.html"&gt;Vagrant Shell Provisioner&lt;/a&gt; or something like &lt;a href="http://www.fabfile.org/"&gt;Fabric&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Currently the server is not binding to an external network interface and would
not be able to bind a privileged port as it is not running as a privileged
user. This is OK as I intend to proxy it though &lt;a href="http://nginx.org/"&gt;nginx&lt;/a&gt;, this is quite
simple to configure but it would be nice to manage this configuration via
&lt;a href="http://puppetlabs.com/"&gt;Puppet&lt;/a&gt;, which I hope to write about in my next post.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/packaging-a-flask-app</guid><pubDate>Tue, 09 Sep 2014 19:54:00 +0000</pubDate></item><item><title>Running of the Bulls</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Running of the Bulls
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 17, 2014
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I'm going to &lt;a href="http://scoopcamp.de"&gt;Scoopcamp&lt;/a&gt; for the &lt;a href="http://scoopcamp.de/hackathon/"&gt;hackathon&lt;/a&gt; again this
year as I had a lot of fun last year. I'm joining the Henri-Nannen-Schule team
who to want to make some news games.  To help ensure I can help with their
ideas and not get bogged down in boring technical details, I decided to have a
look at what tools, resources, libraries and frameworks exist these days that
might be useful.&lt;/p&gt;
&lt;p&gt;This morning I decided I would build a game with the &lt;a href="http://phaser.io/"&gt;Phaser HTML5 game
framework&lt;/a&gt;, &lt;a href="http://www.mapeditor.org/"&gt;Tiled map editor&lt;/a&gt; and &lt;a href="https://www.codeandweb.com/texturepacker"&gt;Texture Packer&lt;/a&gt; (trail version) and post it on this blog. I decided the game
would be about bull fighting as I was trying to animate a bull earlier and
because I knew Inés wouldn't approve.&lt;/p&gt;
&lt;div id="wrapper"&gt;
&lt;div id="game"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I um, used, some peoples art. I hope this is ok, here is where I found it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The awesome Batman style collisions from &lt;a href="https://dribbble.com/Morkki"&gt;Morkii&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;I found the &lt;a href="http://universomario.foros.ws/t1500/las-aventuras-de-toad/"&gt;Vega character spites in a forum&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;The &lt;a href="http://opengameart.org/content/orthographic-outdoor-tiles"&gt;map tiles&lt;/a&gt; are by &lt;a href="http://opengameart.org/users/buch"&gt;Buch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The background is from the &lt;a href="https://github.com/photonstorm/phaser"&gt;Phaser Examples&lt;/a&gt; project&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's a bit of a mess, but was fun. I think I'm better prepared and now looking
forward to the hackathon.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="static/js/phaser.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="static/js/underscore-min.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="static/js/simple.js" type="text/javascript"&gt;&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/running-of-the-bulls</guid><pubDate>Sun, 17 Aug 2014 19:54:00 +0000</pubDate></item><item><title>Chess</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;html&gt;
    &lt;head&gt;
        &lt;link rel="stylesheet" type="text/css" href="static/chessboardjs/css/chessboard-0.3.0.min.css"&gt;&lt;/link&gt;
        &lt;link rel="stylesheet" type="text/css" href="static/main.css"&gt;&lt;/link&gt;
    &lt;/head&gt;
&lt;body&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Chess
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 20, 2014
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This week's post is a playable chess engine that runs in modern browsers that
have support for websockets. The engine is &lt;a href="https://github.com/tarnacious/chess-at-nite"&gt;my fork&lt;/a&gt; of the
&lt;a href="https://code.google.com/p/chess-at-nite/"&gt;chess-at-nite&lt;/a&gt; engine which is written in C++. The engine is
compiled with &lt;a href="http://clang.llvm.org/"&gt;clang&lt;/a&gt; to &lt;a href="http://llvm.org/"&gt;LLVM byte code&lt;/a&gt; and then to &lt;a href="http://asmjs.org/"&gt;asmjs&lt;/a&gt; with &lt;a href="https://github.com/kripken/emscripten"&gt;Emscripten&lt;/a&gt;. The engine runs in a web worker and the
UI is built with a minimal amount of Javascript that makes use of the excellent
&lt;a href="https://github.com/jhlywa/chess.js"&gt;chess.js&lt;/a&gt; and &lt;a href="http://chessboardjs.com/"&gt;chessboard.js&lt;/a&gt; libraries.&lt;/p&gt;
&lt;div id="wrapper"&gt;
&lt;div id="game"&gt;
&lt;div id="gameover"&gt;
&lt;h3&gt;Game is Drawn&lt;/h3&gt;
&lt;div&gt;
&lt;button&gt;New Game&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="options"&gt;
&lt;h3&gt;New Game&lt;/h3&gt;
&lt;div&gt;&lt;span&gt;Black Pieces&lt;/span&gt;
&lt;select class="black"&gt;
&lt;option value="human"&gt;Human&lt;/option&gt;
&lt;option selected="" value="computer"&gt;Computer&lt;/option&gt;
&lt;option value="random"&gt;Random&lt;/option&gt;
&lt;/select&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;White Pieces&lt;/span&gt;
&lt;select class="white"&gt;
&lt;option selected="" value="human"&gt;Human&lt;/option&gt;
&lt;option value="computer"&gt;Computer&lt;/option&gt;
&lt;option value="random"&gt;Random&lt;/option&gt;
&lt;/select&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;Position&lt;/span&gt;
&lt;select&gt;
&lt;option value="start"&gt;Start&lt;/option&gt;
&lt;/select&gt;
&lt;/div&gt;
&lt;div class="start"&gt;
&lt;button&gt;Start Game&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="board" style="width: 500px"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To get started I compiled &lt;a href="https://github.com/kripken/emscripten"&gt;Emscripten&lt;/a&gt;, and the Emscripten forks
of &lt;a href="https://github.com/kripken/emscripten-fastcomp"&gt;LLVM&lt;/a&gt; and &lt;a href="https://github.com/kripken/emscripten-fastcomp-clang"&gt;clang&lt;/a&gt; as Enscripten currently prefers to use
the &lt;a href="https://github.com/kripken/emscripten/wiki/LLVM-Backend"&gt;"fastcomp" LLVM backend&lt;/a&gt; which is not the version I had
previously installed with &lt;a href="https://github.com/Homebrew/homebrew/blob/master/Library/Formula/llvm.rb"&gt;brew&lt;/a&gt;. After configuring my Emscripten to
use the new clang I could run &lt;code&gt;emcc&lt;/code&gt; which is pretty much a drop-in replacement
for &lt;code&gt;gcc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I found the &lt;a href="https://code.google.com/p/chess-at-nite/"&gt;chess-at-nite&lt;/a&gt; randomly by googling for chess engine
source code. I would have liked to have used the &lt;a href="http://www.gnu.org/software/chess/"&gt;GNU Chess&lt;/a&gt; engine,
but I have looked at the source previously and did not think it was a great
codebase to adapt. Fortunately the chess-at-nite source compiles with &lt;code&gt;gcc&lt;/code&gt; and
looked like a pretty nice codebase to hack on.&lt;/p&gt;
&lt;p&gt;When I initially tried to compile the code with Enscripten I got a heap of
&lt;code&gt;'error: reference to 'move' is ambiguous'&lt;/code&gt; errors. Unfortunately many of the
source files use &lt;code&gt;'using namespace std;'&lt;/code&gt; and apparently the Emscripten &lt;code&gt;std&lt;/code&gt;
namespace defines &lt;code&gt;move&lt;/code&gt;. After updating all the usages to only include the
referenced parts of the &lt;code&gt;std&lt;/code&gt; namespace, it compiled and even printed the menu
in the Emscripten generated web page! And then crashed my browser.&lt;/p&gt;
&lt;p&gt;At this point I thought it would be awesome if I could compile it as a worker
and have it automagically use &lt;code&gt;postMessage&lt;/code&gt; and &lt;code&gt;onmessage&lt;/code&gt; for
&lt;code&gt;stdin&lt;/code&gt;/&lt;code&gt;stdout&lt;/code&gt; and &lt;code&gt;console.error&lt;/code&gt; for &lt;code&gt;stderr&lt;/code&gt;. I was disappointed, but this is
a compiler option I would consider working on!&lt;/p&gt;
&lt;p&gt;I also couldn't get all of the &lt;a href="https://github.com/kripken/emscripten/blob/master/system/include/emscripten/emscripten.h#L376"&gt;Worker API&lt;/a&gt; working for some reason
I have not investigated yet. However, I could get enough of it working to hack
together this post. I created a new entry point in the chess-at-nite code that
exports a C style function suitable to be called using the Emscripten Worker
API, this partly worked. I was unable to get the Worker API calling the worker,
but noticed I could create and call the worker with normal Javascript, provided
I posted and handled the internal message formats used by the Worker API.&lt;/p&gt;
&lt;p&gt;For this post the worker takes a &lt;a href="http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation"&gt;FEN&lt;/a&gt; game state and returns a new FEN
string with an updated game state. Luckily &lt;a href="https://github.com/jhlywa/chess.js"&gt;chess.js&lt;/a&gt; and
&lt;a href="http://chessboardjs.com/"&gt;chessboard.js&lt;/a&gt; support FEN strings which made wiring up the UI
quite straight forward. As FEN strings do not provide enough information about
previous moves to detect &lt;a href="http://en.wikipedia.org/wiki/Threefold_repetition"&gt;threefold repetitions&lt;/a&gt;, the engine is quite
susceptible to threefold repetition draws, as can be seen when it plays itself.&lt;/p&gt;
&lt;p&gt;Ideally I would have liked to have the Chess engine running in the worker,
comunicating with the &lt;a href="http://www.gnu.org/software/xboard/engine-intf.html"&gt;xboard protocol&lt;/a&gt; using "standard" &lt;code&gt;stdio&lt;/code&gt; over
the web worker &lt;code&gt;onmessage&lt;/code&gt;/&lt;code&gt;postMessage&lt;/code&gt; interface. However I am pretty happy
with the results and may have found a project I want to contribute too.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script type="text/javascript" src="static/jquery-2.1.0.min.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="static/chess/chess.min.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="static/chessboardjs/js/chessboard-0.3.0.js"&gt;&lt;/script&gt;
&lt;script async type="text/javascript" src="chess-at-nite.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="static/main.js"&gt;&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/chess</guid><pubDate>Sun, 20 Apr 2014 19:54:00 +0000</pubDate></item><item><title>Haste Web Workers</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;style&gt;
    #swipe-canvas {
        position: relative;
        width: 900px;
        height: 300px;
    }

    #swipe-results {
        font-size: 30px;
        padding-left: 50px;
        padding-left: 50px;
    }

    #swipe-results ul {
        margin: 0px;
        padding: 0px;
    }

    #swipe-results li {
        float: left;
        background-color: #DDDDDD;
        list-style-type: none;
        padding: 10px;
        margin: 5px; 
        border-radius: 5px;
    }
    #swipe {
        position: relative;
    }

    #swipe-loading {
        position: absolute;
        height: 50px;
        width: 300px;
        top: 85px;
        left: 300px;
        background-color: darkgray;
        border-radius: 10px;
        text-align: center;
        padding-top: 20px;
        border: black;
        border-width: 5px;
    }

&lt;/style&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Haste Web Workers
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 13, 2014
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This week I learnt of &lt;a href="http://haste-lang.org/"&gt;Haste&lt;/a&gt;, a Haskell to Javascript compiler
intended to be used to build web applications.  I have been trying to learn
Haskell as a side project for a while and &lt;a href="/journal/uhc-haskell-to-javascript"&gt;have previously looked into options
for compiling it to Javascript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was very happy that after a little playing around I was able to get an
&lt;a href="http://www.haskell.org/haskellwiki/Learning_Haskell_with_Chess"&gt;incomplete Haskell chess implementation&lt;/a&gt; compiling to Javascript and
playing itself in a browser.&lt;/p&gt;
&lt;pre id="output"&gt;&lt;/pre&gt;
&lt;p&gt;Getting it running was just a matter of including the modules, generating some
moves and writing the state to an element. However as it does some computation
calculating moves it also blocked the page. This can be fixed by running the
code in a &lt;a href="http://en.wikipedia.org/wiki/Web_worker"&gt;web worker&lt;/a&gt;, I'd used web workers with ClojureScript
last week to do the calculations for my &lt;a href="/journal/swipe"&gt;Swipe Keyboard&lt;/a&gt; and figured I
could probably use them for this too.&lt;/p&gt;
&lt;p&gt;There does not appear to be any support for web workers in Haste, but it does
support a &lt;a href="http://en.wikipedia.org/wiki/Foreign_function_interface"&gt;foreign function interface&lt;/a&gt; which allows support to be added.&lt;/p&gt;
&lt;p&gt;To support creating and using &lt;code&gt;Worker&lt;/code&gt; objects I created three functions in
Javascript. One to create a &lt;code&gt;Worker&lt;/code&gt; object, one to bind an &lt;code&gt;onmessage&lt;/code&gt;
function and one to call the workers &lt;code&gt;postMessage&lt;/code&gt; function. The &lt;code&gt;A&lt;/code&gt; function
is provided by Haste to call callbacks and provide arguments.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;make_worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Haskell I create a worker, send it a message, wait for messages from
the worker and write them to an element on the page.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;{-# LANGUAGE ForeignFunctionInterface #-}&lt;/span&gt;

&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Haste&lt;/span&gt;

&lt;span class="kr"&gt;newtype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSAny&lt;/span&gt;

&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ccall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;make_worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Worker&lt;/span&gt;
&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ccall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSFun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ccall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mkCallback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;

&lt;span class="nf"&gt;handle_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Elem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;handle_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;setProp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fromJSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;elemById&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;make_worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toJSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"worker.js"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;handle_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toJSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello worker!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Worker scripts run in an environment where an &lt;code&gt;onmessage&lt;/code&gt; event can be bound
and a &lt;code&gt;postMessage&lt;/code&gt; function is provided. I created some more wrapper functions
to use these in Haskell.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The Haskell web worker script sends a message once it has loaded and adds a
prefix to every message it receives and sends the message back.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;{-# LANGUAGE ForeignFunctionInterface #-}&lt;/span&gt;

&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Haste&lt;/span&gt;

&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ccall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSFun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;foreign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ccall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mkCallback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;

&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;JSString&lt;/span&gt;
&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toJSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Worker recieved, "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s'&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fromJSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toJSString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Worker: Hello"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I didn't need all this for the Chess worker as it just posts updated game
stages and doesn't receive any messages.&lt;/p&gt;
&lt;p&gt;I still don't know Haskell well enough to really know what I'm doing, but
hopefully Haste will encourage me to engage with more Haskell code.&lt;/p&gt;
&lt;p&gt;I've pushed all the &lt;a href="https://github.com/tarnacious/haste-websockets"&gt;code for this post to Github&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="main.js" type="text/javascript"&gt;&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/haste-webworker</guid><pubDate>Sun, 13 Apr 2014 19:54:00 +0000</pubDate></item><item><title>Swipe Keyboard</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;style&gt;
    #swipe-canvas {
        position: relative;
        width: 900px;
        height: 300px;
    }

    #swipe-results {
        font-size: 30px;
        padding-left: 50px;
        padding-left: 50px;
    }

    #swipe-results ul {
        margin: 0px;
        padding: 0px;
    }

    #swipe-results li {
        float: left;
        background-color: #DDDDDD;
        list-style-type: none;
        padding: 10px;
        margin: 5px;
        border-radius: 5px;
    }
    #swipe {
        position: relative;
    }

    #swipe-loading {
        position: absolute;
        height: 50px;
        width: 300px;
        top: 85px;
        left: 300px;
        background-color: darkgray;
        border-radius: 10px;
        text-align: center;
        padding-top: 20px;
        border: black;
        border-width: 5px;
    }

&lt;/style&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Swipe Keyboard
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 06, 2014
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;When I first tried a &lt;a href="http://www.swype.com/"&gt;Swype&lt;/a&gt; keyboard I was impressed how effective it
was. Even though I don't use the feature on my phone I was interested in how it
could be built, so I &lt;a href="https://github.com/tarnacious/swipe-keyboard"&gt;implemented this otherwise useless swipe-able keyboard&lt;/a&gt; below. It probably doesn't work on mobile devices, but works on modern
browsers with mouse pointers (although I've only really tried Chrome and
Firefox).&lt;/p&gt;
&lt;div id="swipe"&gt;
&lt;canvas height="300px" id="swipe-canvas" width="900px"&gt;&lt;/canvas&gt;
&lt;div id="swipe-results"&gt;&lt;/div&gt;
&lt;div style="clear: both"&gt;&lt;/div&gt;
&lt;h2 id="swipe-loading"&gt;Loading&lt;noscript&gt;Javascript is Required&lt;/noscript&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;I initially tried to solve this using the technique Peter Norvig famously uses
in his &lt;a href="http://norvig.com/spell-correct.html]"&gt;spell checker&lt;/a&gt;. He takes a sequence of characters and
generates a set of word candidates by adding, removing and swapping characters
in the original sequence, the generated candidates are removed if they are not
found a dictionary. This can work but to be effective too many combinations
need to be generated.&lt;/p&gt;
&lt;p&gt;If the dictionary is indexed into a &lt;a href="http://en.wikipedia.org/wiki/Trie"&gt;trie&lt;/a&gt; the number of combinations
generated can be reduced significantly by traversing the trie and only
generating valid letter combinations. This is a pretty bare implementation of
that, it requires: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first and last characters of the initial sequence are used &lt;/li&gt;
&lt;li&gt;Intermediate characters in the initial sequence can be repeated or discarded &lt;/li&gt;
&lt;li&gt;No characters are added or swapped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basically, if you swipe through all the characters in a word in order, then the
word will be found if it is in the index regardless how many characters are
swiped in between. It is surprisingly quick and effective.&lt;/p&gt;
&lt;p&gt;This implementation uses &lt;a href="https://raw.github.com/first20hours/google-10000-english/master/google-10000-english.txt"&gt;these 10000 words&lt;/a&gt;, I intended to use digital
books but never got around to it as these words demonstrate the concept well
enough.&lt;/p&gt;
&lt;p&gt;This is the first thing I've written in &lt;a href="https://github.com/clojure/clojurescript"&gt;ClojureScript&lt;/a&gt; or
&lt;a href="https://github.com/clojure/clojurescript"&gt;Clojure&lt;/a&gt;, so my code my vary from non-idiomatic to shamblolic. I
initially used a &lt;a href="http://clojuredocs.org/clojure_core/clojure.zip/zipper"&gt;zipper&lt;/a&gt; to build the trie with immutable data
structures, but found the indexing took to long with my zipper implementation
so I &lt;a href="https://github.com/tarnacious/swipe-keyboard/commit/6edd7b26e78121fbe8586b3f0ef54ca8277d9e32"&gt;switched to using native Javascript maps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I found that &lt;a href="https://github.com/clojure/core.async"&gt;core.async&lt;/a&gt; library is really awesome, the &lt;a href="http://docs.closure-library.googlecode.com/git/index.html"&gt;Google
closure library&lt;/a&gt; and &lt;a href="https://developers.google.com/closure/compiler/"&gt;compiler&lt;/a&gt; integration with &lt;a href="http://leiningen.org/"&gt;Leiningen&lt;/a&gt; the &lt;a href="https://github.com/emezeske/lein-cljsbuild"&gt;cljsbuild plug-in&lt;/a&gt; to be impressive. My main pains
were the slow JVM start-up time, the advanced closure compiler build of the web
worker script fails silently when run (but the main script works fine when
compiled with the advanced compiler), and at times I felt some compile time
type checking would be nice.&lt;/p&gt;
&lt;p&gt;I would like to extend this experiment to index the word occurrence counts and
proceeding word counts in original text and rank the found words as most
likely. Support casing, umlauts, special characters, spelling correction and
compound words in the indexing and lookup. I think a live lookup while swiping
would also be possible.&lt;/p&gt;
&lt;p&gt;Overall this was fun, turned out OK I think, and was a great learning
experience.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="swipe.js" type="text/javascript"&gt;&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/swipe</guid><pubDate>Sun, 06 Apr 2014 19:54:00 +0000</pubDate></item><item><title>Mozilla Persona</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Mozilla Persona
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 10, 2013
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I added the ability to sign into this site to comment with &lt;a href="http://www.mozilla.org/en-US/persona/"&gt;Mozilla
Persona&lt;/a&gt;. Not that I'm expecting people to use it, I've only had a
handful of genuine comments since I started the site some years ago. I was just
interested in trying it out, which is why I have this site.&lt;/p&gt;
&lt;p&gt;It was pretty straight forward as I already only support the concept of signing
in with 3rd party authentication providers such at OpenID, Google and Twitter.
Adding Persona was just adding another provider, I didn't need to change
how the authentication system works. Here is the &lt;a href="https://bitbucket.org/tarnacious/author/commits/c71a9525fd22643371875316257704760e16063a"&gt;commit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A difference from the other providers is that I had to use Javascript. It's not
that I don't like Javascript, &lt;a href="/journal/cube"&gt;many&lt;/a&gt; &lt;a href="/journal/tile-land"&gt;posts&lt;/a&gt; &lt;a href="/journal/asteroids"&gt;here&lt;/a&gt;
&lt;a href="/journal/transforms"&gt;have&lt;/a&gt; &lt;a href="/journal/old-school-for-fun"&gt;running&lt;/a&gt; &lt;a href="/journal/rxjs-zip"&gt;Javascript&lt;/a&gt; &lt;a href="/journal/rxjs-capture"&gt;in&lt;/a&gt; &lt;a href="/journal/rxjs-merge"&gt;the&lt;/a&gt; &lt;a href="/journal/revisiting-dragging-and-inertia-with-rxjs"&gt;content&lt;/a&gt;.
It's just over the several re-writes of this site in different languages I've
included less Javascript in the site itself. There was none on this site, I
liked it that way.&lt;/p&gt;
&lt;p&gt;Another thing I liked is OpenID. I understand it had usability and adoption
issues. With OpenID though, I could choose who manages my identity or I could
&lt;a href="https://github.com/openid/python-openid"&gt;manage it myself&lt;/a&gt;. Apparently an email provider (in my case, me)
can implement the Persona (BrowswerID) protocol and not depend on
&lt;a href="https://login.persona.org/"&gt;login.persona.org&lt;/a&gt;. Perhaps that is something I will look into
after posting this.&lt;/p&gt;
&lt;p&gt;Finally a note on adoption; I was having dinner with Özlem last night and tried
to explain Persona, but was unable to convince her to even try it to sign into
this site. To be fair it's only a sample size of one and I think Özlem was
hoping dinner conversation wouldn't be about online identity management.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/persona</guid><pubDate>Sat, 10 Aug 2013 19:54:00 +0000</pubDate></item><item><title>Prague to Berlin</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Prague to Berlin
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 08, 2013
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;On July 26-28 I joined 32 other entrepreneurs, VCs and engineers to cycle from
Prague to Berlin and raise money for &lt;a href="http://www.dothiv.org/dothiv/idea-hiv-domain-2/"&gt;dotHIV&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="koga.png" style="float:left;padding-right: 20px"/&gt;&lt;/p&gt;
&lt;p&gt;It all started on Thursday a week before; our communications manager, Agnes,
emailed me a link to the &lt;a href="http://techbikers.de/"&gt;techbikers.de&lt;/a&gt; site and made some joke
about how I should do it on my hipster bike. Very funny, yet another hipster
bike joke.  However I thought it sounded pretty exciting, and it would be fun
to call Agnes's joke. "Very funny, I'm going to do it." I emailed back.&lt;/p&gt;
&lt;p&gt;I got in touch with Lea who informed me a space had just become available as
another participant had withdrawn. The organizers encouraged riders to commit to
raising €750, for their chosen charity &lt;a href="http://www.dothiv.org/dothiv/idea-hiv-domain-2/"&gt;dotHIV&lt;/a&gt;. This sounded like
quite a bit in a week, so I asked our CEO if Retresco could get me started with
a few hundred Euro. He agreed to contribute €300 and my &lt;a href="https://www.betterplace.org/en/fundraising-events/prague_berlin"&gt;fund-raising
drive&lt;/a&gt; was on its way.&lt;/p&gt;
&lt;p&gt;The weekend before the ride was lovely in Berlin so my entire training program
consisted of cycling about 100km in total to some lovely lakes around Berlin.
Certainly a lot more pleasant than training for a charity run back in Melbourne.&lt;/p&gt;
&lt;p&gt;In the days leading up to the ride I started worrying the maybe taking my
beloved bike might not be such a great idea if there were any serious
mountains. It's a 1987 Koga Miyata frame fitted with a single 52/17 free wheel
gear, perfect for cycling around Berlin. When I emailed the group the first
response was "I really can NOT recommend to use a single speed bike" but then
there was another saying that there would be a couple of spare bikes if I
really struggled. Of course I decided to take my bike, determined to ride her
all the way back to Berlin.&lt;/p&gt;
&lt;p&gt;Following the advise given to the riders I visited a cycling shop and purchased
padded cycling shorts, a shirt, gloves, a water bottle holder, sunglasses and a
helmet, which was mandatory. I'd previously promised I'd never wear Lycra
cycling shorts, but this seemed an appropriate exception. I left €250
lighter, but feeling like a bit of a pro.&lt;/p&gt;
&lt;p&gt;&lt;img src="pro.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;I met up with most of the organizers and cyclists who took the organized bus
from Berlin to the start of our ride in Prague. I was a little surprised that
only a handful of people including myself took their own bikes, it was
comforting to find that most people, like me, had no experience cycling such
distances.&lt;/p&gt;
&lt;p&gt;We arrived in Prague to check-in and have diner at our place for the night, a
Boatel called The Admiral. On my dinner table amongst others was Casey, a fellow
Australian who runs the Google office in France and Eugine, an early-stage
investor at Mangrove Capital Partners. Dinner and conversations were good.&lt;/p&gt;
&lt;p&gt;&lt;img src="admiral.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;As we were in Prague, most of us decided to checkout the city. Our group
diminished to four as sensible people headed back to the Boatel to get some
sleep. We decided we should a least checkout the famous and trashy Karlovy
Lázně night club.&lt;/p&gt;
&lt;p&gt;&lt;img src="prague.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;Over breakfast we were given our days route, timings, break stops etc. Most of
us seemed a bit freaked out, but mostly excited by the 100km+ days schedule.&lt;/p&gt;
&lt;p&gt;&lt;img src="start.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;The ride out of Prague involved lots of very slow cycling, cheering, cobble
stones, tram tracks and quite a steep hill. As we reached the top of the hill
one of the guides cycled up to me and told me that if I could do this hill on my
single gear, I would probably be alright on all the accents we would encounter.
This was very good to hear and he was right as never ended up using one of the
spare bikes.&lt;/p&gt;
&lt;p&gt;&lt;img src="leaving_prague.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;I wasn't the only one who was surprised that a lunch of dried fruits and nuts
seemed a bit lean for a day we planned to cycle more than 100km. I eventually
worked out that it was the morning break and we'd only cycled 30km not 60km.&lt;/p&gt;
&lt;p&gt;That break was good though as Bjorn produced boxes of Wonderpots yoghurt and
fruit tubs he'd kept frozen for a day and a half on dried ice in our support
car. I think he found a few fans of the Yoghurt stores he founded in Berlin.
He was also one of several riders I got along with really well and hope to meet
up with again.&lt;/p&gt;
&lt;p&gt;&lt;img src="wonderpots.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;The basic pattern for a days cycling was leave 8-8:30. Morning and afternoon
breaks with mixed nuts, fruits, drinks etc. Lunch was a buffet cold meats,
salads and bread. There were also periodic breaks every 10-30km for water
re-fills and regrouping.&lt;/p&gt;
&lt;p&gt;&lt;img src="lunch.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;Due to some flooding at the hotel we were originally booked at for the second
night, it was rebooked at another hotel. This resulted in the first day being a
bit shorter and the second day being longer. As it was very hot, the
organizers gave us the option to cycle the initial distance planned for the
second day and get driven the remaining distance. Most people including me
decided we wanted to ride the additional 40km making it am epic 172km day.&lt;/p&gt;
&lt;p&gt;&lt;img src="nice-ride.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;Despite the long distance and the heat, we finished day two with a sprint over the
last couple of kilometres to the hotel. I hammered up to top four, but didn't
realise how far it was and watched almost the entire field overtake me in the
last kilometre or so.&lt;/p&gt;
&lt;p&gt;&lt;img src="czech_lake.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;I expected the first day to be OK, then wake up sore and suffer through the last
two. However the opposite seems to be true, on the second and third days I felt
I was getting stronger. While I was just surviving on the first day, on the
second and third I was using extra energy to chase down group ahead and move to
the front of groups to help carry them. This felt good, because I spent the
first day and a half sticking to the back wheel of the stronger riders.&lt;/p&gt;
&lt;p&gt;&lt;img src="forest.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="water.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;It was fun on the third morning when we got the route for the day back
to Berlin, 109km with no elevation gain, our smallest day. Unlike the first day
there was a sense of confidence, this was our Champs-Élysées, we'd done 172km
the day before and survived. This would be a cakewalk back to Berlin.&lt;/p&gt;
&lt;p&gt;&lt;img src="cakewalk.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;I was incredibly impressed with the group as a whole. Everyone made it back,
there were no major crashes or any issues I was aware of. Over the three days
of cycling I think I spoke to everyone for at least a bit and everyone seemed
to be really enjoying themselves.&lt;/p&gt;
&lt;p&gt;&lt;img src="finish.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;I'm very thankful to everyone who helped organize the event and everyone who
donated to &lt;a href="http://www.dothiv.org/dothiv/idea-hiv-domain-2/"&gt;dotHIV&lt;/a&gt;. I still have €100 to reach my target, so &lt;a href="https://www.betterplace.org/en/fundraising-events/prague_berlin"&gt;please
help&lt;/a&gt; get me over the line.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/prague-berlin</guid><pubDate>Thu, 08 Aug 2013 19:54:00 +0000</pubDate></item><item><title>PGP/GPG</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        PGP/GPG
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 06, 2013
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;pre&gt;&lt;code&gt;-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I just learnt how to use PGP/GPG to sign messages, encrypt
message for people and decrypt messages encrypted for me!

If you haven't learnt about PGP yet, maybe you should. It's fun,
easy and very useful if you want some Pretty Good Privacy.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.14 (Darwin)

iQIcBAEBAgAGBQJSATOgAAoJEAKnAXIHeOzVmz0P/RR1jVIJTtmYnj++19/nIv00
oosR0Ib/4E7klc+8Mnzc5CdQ2NFQLqX87+sSRd+tJZScuP6NVLzK95qKA+Ci5FHY
lWZ1sMeNun2VTuVmUmPQnoeNGl4DBM2O9O2mftNXkrtUJrmZLH4vlRoQb6eNcZF+
82oFay2ZZHBq5+cnw+TXFFohGUeqFcUTUs+ThrqUU6FlKScIMiUzskRwIA3/q7in
2qEhAE+0B68KWqRAgq2xvR9bemcfRTmAeGzY+pgEJTIYw1qtgOh3EN5JXXj3tzeC
NR0STttEuSm7SEIk/hUc5eoM/rAcHF8HGrfYoKh2ZckiivfUV+yERmNZs4E8S/lr
fMAwiDSioy0yXRB/ceZL3jv4XI5Oy//Gm35u9VU5ZPHtW2FcsLW6n1D/IhaieMLm
9LApWNxm3uxz2na+Q+YyM4YDJMqeUdYnqCe4XTtclUNvDOY+ZK7mABnaZkYHtHir
00Hp+2Bl/vpSZ0tzSedKvMnVJNYopNMK54iuKjgBT7GuMMirSiWtXUALer33cRII
llbC5Gf3lVf6/SsyX+/SuaiCrZRIGou6fJuIacl3VM3/kIoPjwy5cpr1fzxPMtg0
CYwq8MjuF0Uzyz8rHxskVQbfW0nWKPcK9JqCw89fLM/nv+LpuNB2o7mUzqvxuizi
WqFWSMBg/bWpb5lB9nx/
=6hUP
-----END PGP SIGNATURE-----
&lt;/code&gt;&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/pgp</guid><pubDate>Tue, 06 Aug 2013 19:54:00 +0000</pubDate></item><item><title>Setting up a pair programming environment on Amazon EC2</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Setting up a pair programming environment on Amazon EC2
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 01, 2012
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;a href="http://www.archlinux.org/"&gt;Arch Linux&lt;/a&gt; is a great distribution for a programming environment and there is an &lt;a href="https://aws.amazon.com/amis/archlinux"&gt;Arch AMI for Amazon EC2&lt;/a&gt;. I choose to run it on a Large EC2 instance in Tokyo, I downloaded the generated key-pair and copied its public domain name.&lt;/p&gt;
&lt;h2&gt;Connecting to the instance&lt;/h2&gt;
&lt;p&gt;The EC2 security group needs to be setup to allow inbound requests to port 22 for SSH.&lt;/p&gt;
&lt;p&gt;The key-pair is used to SSH into the instance as root.
The permissions on the key file must be set so only the owner can read it.
I also move keys to my ~/.ssh directory.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;~/Downloads/mykey.pem&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/
&lt;span class="gp"&gt;$ &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/mykey.pem
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;SSH into the public name using the key.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/mykey.pem&lt;span class="w"&gt; &lt;/span&gt;root@&lt;span class="o"&gt;[&lt;/span&gt;ec2-instance-name&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="gp"&gt;[root@ip-xx-xxx-xx-xx ~]$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Update the system&lt;/h2&gt;
&lt;p&gt;Firstly the system should be updated with the Arch Linux package management tool.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[root@ec2]$ &lt;/span&gt;pacman&lt;span class="w"&gt; &lt;/span&gt;-Syyu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It asked me something about a dependency conflict, I said yes. Things seem to be fine.&lt;/p&gt;
&lt;h2&gt;Creating users&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;adduser&lt;/code&gt; is an interactive tool which prompts for new user information and provides defaults where possible. A username and the defaults are fine for now. User passwords can be set with &lt;code&gt;passwd&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Great, users for everyone.&lt;/p&gt;
&lt;h2&gt;Allowing users remote access&lt;/h2&gt;
&lt;p&gt;The SSH configuration doesn't allow for plain text passwords sent through the tunnel. This can be allowed by updating &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and setting &lt;code&gt;PasswordAuthentication&lt;/code&gt; to &lt;code&gt;yes&lt;/code&gt;. But SSH keys are better, use them.&lt;/p&gt;
&lt;p&gt;The SSH daemon checks for the key in &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;. Put the users public key there.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[root@ec2]$ &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/home/username/.ssh
&lt;span class="gp"&gt;[root@ec2]$ &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;pub-key&lt;span class="w"&gt; &lt;/span&gt;/home/username/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The user can now login, the &lt;code&gt;-i&lt;/code&gt; flag can be used if the default key is not used.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;username@&lt;span class="o"&gt;[&lt;/span&gt;ec2-instance-name&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;All users are not equal&lt;/h2&gt;
&lt;p&gt;Privileged users should be allowed to perform actions as the root user.
This can be done by giving permission to the &lt;code&gt;wheel&lt;/code&gt; group and then adding users to the group.&lt;/p&gt;
&lt;p&gt;These rules are in &lt;code&gt;/etc/sudoers&lt;/code&gt;, because the file is important there is a tool called visudo which checks for syntax errors before saving.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[root@ec2]$ &lt;/span&gt;visudo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The line that allows the wheel group to perform actions as the root needs to be uncommented.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;%wheel ALL=(ALL) ALL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can then add users we trust to the wheel group.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[root@ec2]$ &lt;/span&gt;gpasswd&lt;span class="w"&gt; &lt;/span&gt;username&lt;span class="w"&gt; &lt;/span&gt;wheel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The sudo package needs to be installed at sometime.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[root@ec2]$ &lt;/span&gt;pacman&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;sudo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Prevent root login&lt;/h2&gt;
&lt;p&gt;Now a user can connect and perform actions as root, we can prevent logging in remotely as root.
To do this we update &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and set &lt;code&gt;PermitRootLogin&lt;/code&gt; to &lt;code&gt;no&lt;/code&gt;.
We need to restart the SSH process for this to take effect.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[tarn@ec2]$ &lt;/span&gt;rc.d&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;sshd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Install some development tools system wide&lt;/h2&gt;
&lt;p&gt;We will need heaps more, but here are some to get us started.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[tarn@ec2]$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pacman&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;screen&lt;span class="w"&gt; &lt;/span&gt;gcc&lt;span class="w"&gt; &lt;/span&gt;erlang&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;mono&lt;span class="w"&gt; &lt;/span&gt;openjdk6&lt;span class="w"&gt; &lt;/span&gt;nodejs&lt;span class="w"&gt; &lt;/span&gt;v8&lt;span class="w"&gt; &lt;/span&gt;patch&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;zlib&lt;span class="w"&gt; &lt;/span&gt;readline&lt;span class="w"&gt; &lt;/span&gt;libxml2&lt;span class="w"&gt; &lt;/span&gt;libxslt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;autoconf&lt;span class="w"&gt; &lt;/span&gt;automake&lt;span class="w"&gt; &lt;/span&gt;diffutils&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;libtool&lt;span class="w"&gt; &lt;/span&gt;bison&lt;span class="w"&gt; &lt;/span&gt;subversion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Installing packages requires root access as the packages install system wide,
only users with permission to sudo will be permitted to do it.&lt;/p&gt;
&lt;h2&gt;Multiuser Screen&lt;/h2&gt;
&lt;p&gt;To share a screen process, one user starts screen passing a socket name&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[person1@ec2]$ &lt;/span&gt;screen&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;hack-session
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;They then must enable multiuser by invoking screen with &lt;code&gt;CTRL-a&lt;/code&gt; and typing&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;:multiuser on
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally they must explictly allow a user, again by invoke screen with &lt;code&gt;CTRL-a&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;:acladd person2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The user &lt;code&gt;person2&lt;/code&gt; can now attach to the screen session&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;[person2@ec2]$ &lt;/span&gt;screen&lt;span class="w"&gt; &lt;/span&gt;-x&lt;span class="w"&gt; &lt;/span&gt;person1/hack-session
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can then even split your screen &lt;code&gt;CTRL-S&lt;/code&gt; and share two shells, perhaps Vim and a REPL? Awesome.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: When you allow someone to connect to your screen process you are effectively giving them permission to run as you.
Unless you want to remotely pair on system administration,
create an un-privleged user and use that to share a screen process.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Build it and they will come&lt;/h2&gt;
&lt;p&gt;With a shared remote programming environment I'll be able to pair with people I wouldn't otherwise. I'm nervious about pairing with programmers I know are awesome, but I also know it's a great way to learn, so lets bring it!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/pair-programming</guid><pubDate>Sun, 01 Apr 2012 09:53:00 +0000</pubDate></item><item><title>vim-slime</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        vim-slime
    &lt;/h1&gt;
    &lt;p&gt;
    Mar 26, 2012
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Today I found the awesomeness that is &lt;a href="https://github.com/jpalardy/vim-slime"&gt;vim-slime&lt;/a&gt;, it's been an exciting day for me.
&lt;a href="http://common-lisp.net/project/slime/"&gt;Slime&lt;/a&gt; is the "The Superior Lisp Interaction Mode for Emacs", I can almost hear the emacs crowd laughing.&lt;/p&gt;
&lt;p&gt;For those that use vim and haven't used Slime, vim-slime or &lt;a href="https://github.com/vim-scripts/VimClojure"&gt;something similar&lt;/a&gt;, this is why it's awesome:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Text can be sent from any process to the stdin of a &lt;a href="http://www.gnu.org/software/screen/"&gt;gnu screen&lt;/a&gt; or &lt;a href="http://tmux.sourceforge.net/"&gt;tmux&lt;/a&gt; session.
The process in this case is vim and the screen/tmux session is a terminal&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Screen is a &lt;a href="/journal/oh-screen-where-have-you-been"&gt;really neat&lt;/a&gt; terminal multiplexer (you can run multiple terminals in a terminal window).
The multiplexed shell processes are children of the screen process, which itself is not a child of the terminal window process.
This means a screen process and its child processes keep running if you close the terminal window.
Later you can re-connect to it, this is what makes vim-slime possible.&lt;/p&gt;
&lt;p&gt;Here is an screen shot, on the left is me in gVim writing some awful Clojure &lt;a href="#footnote-1"&gt;[1]&lt;/a&gt;.
On the right is a screen buffer in which I started a Clojure REPL.
When I want to try run some code I can send any vim text selection to the REPL in a keystroke (or two).&lt;/p&gt;
&lt;p&gt;&lt;img alt="vim slime screenshot" src="screenshot.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;It doesn't have to be a Clojure REPL either, we can send anything to a screen shell.
We could run git commands, find, grep, sed, etc. Like with the Clojure REPL we can even interact with any terminal programs that use STDIN.&lt;/p&gt;
&lt;p&gt;This concept can be taken even further,
You can even connect to a tmux session over SSH and share a terminal or a &lt;a href="http://remotepairprogramming.com/remote-pair-programming-with-tmux-and-vim-the"&gt;terminal program like vim to do remote pairing&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Hopefully remote pairing is the topic of my next post as there are a couple geographically distant people I know who are keen to do some pair hacking.
I stand to learn a lot!&lt;/p&gt;
&lt;p&gt;&lt;a name="footnote-1"&gt;[1]&lt;/a&gt;
 I learnt almost everything I know about Lisp from &lt;a href="http://www.ccs.neu.edu/home/matthias/BTLS/"&gt;The Little Schemer&lt;/a&gt;. Great book.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/vimslime</guid><pubDate>Mon, 26 Mar 2012 19:54:00 +0000</pubDate></item><item><title>UHC: Haskell to JavaScript</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;script type="text/javascript" src="libEH-RTS.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Base.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_BoxArray.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Char.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Enum.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Float.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_MutVar.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Read.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_ST.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_STRef.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Show.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_StackTrace.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Types.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Ptr.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_ByteArray.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_IOBase.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_OldException.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_OldIO.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Run.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Generics_Tuple.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Generics.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Bounded.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Eq.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Ord.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Ix.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="UHC_Array.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="Data_Maybe.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="Control_Monad.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="Prelude.mjs"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="Primes.mjs"&gt;&lt;/script&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        UHC: Haskell to JavaScript
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 05, 2012
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;If all goes well some JavaScript included on this page should have generated a list of all the primes numbers between 1 and 100 and printed them below.&lt;/p&gt;
&lt;script src="Main.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;p&gt;To generate these primes I wrote simple Primes module.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Primes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="nf"&gt;factors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;factors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;prime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;
&lt;span class="nf"&gt;prime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;factors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;primes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;primes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And a Main module to generate some primes and print them.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Primes&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;primes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is basic Haskell which can be compiled by &lt;a href="http://www.haskell.org/ghc/"&gt;GHC&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ ghc Main.hs
[1 of 2] Compiling Primes           ( Primes.hs, Primes.o )
[2 of 2] Compiling Main             ( Main.hs, Main.o )
Linking Main ...
$ ./Main
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It can also be compiled by &lt;a href="http://www.cs.uu.nl/wiki/UHC"&gt;UHC&lt;/a&gt; which I found interesting because &lt;a href="http://www.cs.uu.nl/wiki/bin/view/Ehc/UhcUserDocumentation#5_7_3_jscript_Core_based_JavaScr"&gt;UHC can compile to a JavaScript backend&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ ls
Main.hs  Primes.hs
$ uhc -tjs Main.hs
[1/2] Compiling Haskell      Primes      (Primes.hs)
[2/2] Compiling Haskell      Main        (Main.hs)
$ ls
Main.core  Main.hi  Main.hs  Main.html  Main.js
Main.mjs  Primes.core  Primes.hi  Primes.hs  Primes.mjs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The -tjs sets the target to JavaScript and uhc then generates JavaScript files, some intermediate files and a html page. UHC must be configured then built with the --enable-js flag for the JavaScript backend to work.&lt;/p&gt;
&lt;p&gt;The html page references the two generated JavaScript files and quite a few framework/runtime/library JavaScript files. These are all in the source of this page.&lt;/p&gt;
&lt;p&gt;The Primes program doesn't interact with the DOM at all, it just dumps a list out where it was called. This makes everything a lot simpler, but is not very useful.
The &lt;a href="https://github.com/norm2782/uhc-jscript"&gt;uhc-javascript project&lt;/a&gt; implements typed &lt;a href="http://www.cs.uu.nl/wiki/bin/view/Ehc/UhcUserDocumentation#5_1_Foreign_function_interface_F"&gt;Foreign function interfaces&lt;/a&gt; for browser JavaScript functions, the DOM and also some JavaScript libraries like jQuery and Backbone.
This makes it possible to write an entire client side application in Haskell, although I'm not sure if it's a good idea.&lt;/p&gt;
&lt;p&gt;I also don't know how complete or good UHC is as Haskell compiler.
I checked it could compile a simple &lt;a href="http://www.haskell.org/haskellwiki/Learning_Haskell_with_Chess"&gt;Chess&lt;/a&gt; program which I have been playing with thought it might be fun to try and get running in the browser for a post.
Turns out I'm going to have to level up my Haskell a fair bit to get the UI stuff working.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/uhc-haskell-to-javascript</guid><pubDate>Sun, 05 Feb 2012 18:06:00 +0000</pubDate></item><item><title>Basic long polling chat with Manos de Mono</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Basic long polling chat with Manos de Mono
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 22, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I fucking love the &lt;a href="http://jacksonh.tumblr.com/post/1159500924/manos-de-mono-the-manifesto"&gt;Manos do Mono manifesto&lt;/a&gt;. It has very grand ideals and a philosophy of simplicity which I really dig, it is also excellently illustrated with pictures of cats.
I've wanted try it since listening to &lt;a href="http://twitter.com/#!/jacksonh"&gt;Jackson Harper&lt;/a&gt; &lt;a href="http://herdingcode.com/?p=293"&gt;talking about it on a Herding Code podcast last year&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Installing Mono&lt;/h1&gt;
&lt;p&gt;I'm not sure if I needed to upgrade from version 2.6.7 to use Manos, which is in included with the Linux Mint distribution I use, but I knew I would need a newer version to for some other stuff I want to do. So I built and installed the latest Mono (2.11) from source, which was pretty straight forward.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;git://github.com/mono/mono.git
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mono
&lt;span class="gp"&gt;$ &lt;/span&gt;./autogen.sh&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr/local
&lt;span class="go"&gt;**Error**: You must have 'libtool' installed to compile Mono.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ok, install libtool.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;libtool
&lt;span class="gp"&gt;$ &lt;/span&gt;./autogen.sh&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr/local
&lt;span class="go"&gt;..&lt;/span&gt;
&lt;span class="go"&gt;configure: error: msgfmt not found. You need to install the 'gettext' package, or pass --enable-nls=no to configure.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I think I have gettext, oh well.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;./autogen.sh&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr/local&lt;span class="w"&gt; &lt;/span&gt;--enable-nls&lt;span class="o"&gt;=&lt;/span&gt;no
&lt;span class="gp"&gt;$ &lt;/span&gt;make
&lt;span class="gp"&gt;$ &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And make sure the new Mono works, awesome.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mono&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;span class="go"&gt;Mono JIT compiler version 2.11 (master/fbff787 Fri Jul 15 17:54:49 EST 2011)&lt;/span&gt;
&lt;span class="go"&gt;Copyright (C) 2002-2011 Novell, Inc, Xamarin, Inc and Contributors. www.mono-project.com&lt;/span&gt;
&lt;span class="go"&gt;        TLS:           __thread&lt;/span&gt;
&lt;span class="go"&gt;        SIGSEGV:       altstack&lt;/span&gt;
&lt;span class="go"&gt;        Notifications: epoll&lt;/span&gt;
&lt;span class="go"&gt;        Architecture:  amd64&lt;/span&gt;
&lt;span class="go"&gt;        Disabled:      none&lt;/span&gt;
&lt;span class="go"&gt;        Misc:          softdebug&lt;/span&gt;
&lt;span class="go"&gt;        LLVM:          supported, not enabled.&lt;/span&gt;
&lt;span class="go"&gt;        GC:            Included Boehm (with typed GC and Parallel Mark)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Installing Manos&lt;/h1&gt;
&lt;p&gt;Manos was easy to build and install from source.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://jacksonh@github.com/jacksonh/manos.git
&lt;span class="gp"&gt;$ &lt;/span&gt;./autogen.sh
&lt;span class="gp"&gt;$ &lt;/span&gt;make
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Getting started&lt;/h1&gt;
&lt;p&gt;Manos has a command line tool like rails to create, build and serve websites.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;manos
&lt;span class="go"&gt;manos usage is: manos [command] [options]&lt;/span&gt;

&lt;span class="go"&gt;  -h, -?, --help&lt;/span&gt;
&lt;span class="go"&gt;      --init, -i&lt;/span&gt;
&lt;span class="go"&gt;      --server, -s&lt;/span&gt;
&lt;span class="go"&gt;      --docs, -d&lt;/span&gt;
&lt;span class="go"&gt;      --build, -b&lt;/span&gt;
&lt;span class="go"&gt;      --show-environment, --se&lt;/span&gt;

&lt;span class="go"&gt;      --run, -r=VALUE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The --init option creates a new application.&lt;/p&gt;
&lt;div class="highlight-block highlight-console"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;manos&lt;span class="w"&gt; &lt;/span&gt;--init&lt;span class="w"&gt; &lt;/span&gt;ManosChat
&lt;span class="go"&gt;initing: ManosChat&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ManosChat/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ls
&lt;span class="go"&gt;ManosChat.cs  StaticContentModule.cs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which creates a new folder with two files. The initial app is setup to serve static content from a content folder.&lt;/p&gt;
&lt;p&gt;We'll replace those files with a ridiculously simple long polling "chat" application which will consist of three files; a C# file, a HTML page and a JavaScript file.&lt;/p&gt;
&lt;p&gt;The Manos application has four routes, one for the HTML page, the JavaScript file and routes to post messages and wait for updates. The routing metadata hopefully looks pretty familiar.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Wait method simply adds the request context to a list of waiting requests and leaves the browser requesting it waiting while the server moves on to handling the next request.&lt;/li&gt;
&lt;li&gt;The Send method pulls the message out of the request context and then loops through all the waiting connections and forwards the message on.&lt;/li&gt;
&lt;li&gt;The Home and Script methods serve content by simply reading it off disk. You read the files at start up and serve the from memory, but then I'd have to restart the server to change the contents! :)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is it!&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;Manos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System.Threading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ManosChat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ManosChat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ManosApp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IManosContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_waiting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ManosChat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_waiting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IManosContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;[Post ("/send")]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IManosContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_waiting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_waiting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;[Post ("/wait")]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IManosContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_waiting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;[Get("/")]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IManosContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAllText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;[Get("/app.js")]&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IManosContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAllText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"app.js"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My HTML looks something like this.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE HTML&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Manos Chat&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/app.js"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Chat&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"send-message"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Send"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And my JavaScript something like this.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;showMessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#messages'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;li/&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getMessages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/wait'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'data=none'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;showMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/send'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'message='&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#send-message'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#message'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Building and Running&lt;/h1&gt;
&lt;p&gt;The web application can be built and served using the Manos command-line tool&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
&lt;span class="nx"&gt;Running&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ManosChat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ManosChat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8080.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we point some browser windows at the server and start chatting.&lt;/p&gt;
&lt;p&gt;&lt;img alt="chat in the browser" src="chat.png"/&gt;&lt;/p&gt;
&lt;h1&gt;Some Notes&lt;/h1&gt;
&lt;p&gt;The chat browser windows are "Waiting for localhost" even though the page has fully loaded. This is because we are not responding to the request until we have a message, this is known as long polling. A common way to avoid this is to make the request from inside an iframe. And yes, it is possible for clients to miss messages between requests to wait in this chat application. Also requests timeouts are not handled correctly.&lt;/p&gt;
&lt;p&gt;The reason we can access the list of clients without thread synchronization mechanisms is because all our requests a called on the same thread.
This is great for this example, but it also means that if you block the handler thread (by making database query or something) then all other requests will have to wait.
This just means you need to do any time consuming work or IO on another thread. It may be worth noting that request are queued in another thread, so the server isn't blocking while we are handling a request.&lt;/p&gt;
&lt;p&gt;I hope to take this a little further and add Riak storage and maybe fix some of the obvious short failings of this example. Finally, props to Jackson Harper and everyone who has contributed to Manos and Mono, you are all awesome.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/manos-long-polling-chat</guid><pubDate>Fri, 22 Jul 2011 19:54:00 +0000</pubDate></item><item><title>Play by Play with Zed Shaw</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Play by Play with Zed Shaw
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 11, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I watched the &lt;a href="https://twitter.com/#!/zedshaw"&gt;Zed Shaw&lt;/a&gt; &lt;a href="http://peepcode.com/products/play-by-play-zed-shaw"&gt;episode from the Peepcode "Play by Play" series&lt;/a&gt; last night. Each episode in the series follows a different gun programmer doing their stuff solving a problem. Like &lt;a href="http://www.codersatwork.com/"&gt;"Coders at work"&lt;/a&gt; it has some interesting insights into how people go about it.&lt;/p&gt;
&lt;p&gt;I became interested in Zed after reading his infamous post &lt;a href="http://zedshaw.com/rants/rails_is_a_ghetto.html"&gt;Rails is a Ghetto&lt;/a&gt;, which ironically warns people against listening to people like himself. I also enjoyed &lt;a href="http://vimeo.com/2723800"&gt;this entertaining video&lt;/a&gt; of Zed talking about ACL's. More recently Zed has published &lt;a href="http://learnpythonthehardway.org/"&gt;"Learn Python the Hard Way"&lt;/a&gt;  which looks like a great introduction to programming. Oh, and he wrote the &lt;a href="http://mongrel2.org/"&gt;Mongrel&lt;/a&gt; web server.&lt;/p&gt;
&lt;p&gt;Whilst didn't learn a lot technically from the screen cast, I did learn I should use &lt;a href="http://www.r-project.org/"&gt;R for statistics and plotting&lt;/a&gt;, that &lt;a href="http://fossil-scm.org/index.html/doc/trunk/www/index.wiki"&gt;Fossil&lt;/a&gt; is a wrapper around various source control systems which adds issue tracking, wiki and such things, and a few other little tips.&lt;/p&gt;
&lt;p&gt;What I found really interesting is the tools he used and how he went about it, mostly because I saw a lot of similarities with &lt;a href="/journal/the-mouse-is-dead--long-live-the-keyboard"&gt;how I like to code&lt;/a&gt;. Thankfully I was also reminded that I should take breaks more often.&lt;/p&gt;
&lt;p&gt;The tools he used are &lt;a href="http://www.gnu.org/s/screen/"&gt;Screen&lt;/a&gt; (&lt;a href="/journal/oh-screen-where-have-you-been"&gt;which I've previously blogged about&lt;/a&gt;), Vim, Python, R, gcc, Fossil and &lt;a href="http://vimperator.org/vimperator"&gt;Vimperator&lt;/a&gt;. I have used all those tools except Fossil and use all but R and gcc regularly.&lt;/p&gt;
&lt;p&gt;I also liked the way he just got in and started hacking on the problem, saying that once he understood the problem he would typically throw the code away and start again and warned against trying to refactor your way out from your inital attemp. This advice really resonates with me, of course you write code better and cleaner when you've written it before and understand the problem.&lt;/p&gt;
&lt;p&gt;I also really liked his take on testing which was to use a variety of testing methods, including building out test clients. He didn't sound like he would waste any time trying to unit-test something that was difficult to unit test. A refreshing, no dogma, take on testing.&lt;/p&gt;
&lt;p&gt;He also talked about how he used heuristics to improve his coding efficiency and effectiveness (such as the code metrics tools he was building in the screencast). I would like to do more of this myself as it makes more sense to me than techniques such as the pomodoro.&lt;/p&gt;
&lt;p&gt;There is no doubt I have a way to go before getting anywhere near the productivity, skill and experience of Zed but I did find it encouraging the direction I am heading is a proven one. I just need to keep enjoying building stuff, and maybe take a break more often.&lt;/p&gt;
&lt;p&gt;This rant is no more.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/play-by-play-with-zed-shaw</guid><pubDate>Mon, 11 Jul 2011 19:54:00 +0000</pubDate></item><item><title>Actor Model</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;style type="text/css"&gt;
    #canvas-container
    { 
        height: 480px; 
        width: 640px; 
        position: relative; 
        margin-left: auto; 
        margin-right:auto; 
    }
&lt;/style&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Actor Model
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 01, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;A couple of months back &lt;a href="http://twitter.com/#!/abienert"&gt;Andrew Bienert&lt;/a&gt; organised a &lt;a href="http://melbourne.ozalt.net/2011/04/april-meeting-functional-programming.html"&gt;Melbourne Alt.NET meeting on functional programming&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://twitter.com/#!/adbrowne"&gt;Andrew Browne&lt;/a&gt; and myself put our hands up to do mini talks on Scala and Erlang and &lt;a href="http://twitter.com/#!/abienert"&gt;Andrew Bienert&lt;/a&gt; was keen to talk about F#.&lt;/p&gt;
&lt;p&gt;A few days before the meeting was &lt;a href="http://melbourne.ozalt.net/2011/04/april-meeting-functional-programming_27.html"&gt;originally scheduled&lt;/a&gt; &lt;a href="http://twitter.com/#!/adbrowne"&gt;Andrew Browne&lt;/a&gt; and I caught up on Skype to work out if we could somehow make our talks complimentary,
either by each talking about different concepts or showing the same concepts in Scala and Erlang.
We decided it would be best to show the same concepts in both Languages. &lt;/p&gt;
&lt;p&gt;We had previously also talked over beers about the &lt;a href="http://en.wikipedia.org/wiki/Actor_model"&gt;Actor model&lt;/a&gt; in Erlang and Scala and wanted to spend some time on that. I initially wanted to do the exercise proposed by &lt;a href="http://armstrongonsoftware.blogspot.com/"&gt;Joe Armstrong&lt;/a&gt; from his book &lt;a href="http://pragprog.com/titles/jaerlang/programming-erlang"&gt;Programming Erlang&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a ring benchmark. Create N processes in a ring. Send a message round the ring M times. So that a total of N * M messages get sent. Time how long this takes for different values of N and M.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is a fantastic exercise but we decided it was overly complex for a short presentation. We decided on a much simpler example with three actors; a Hello actor, a World actor and a Print actor. &lt;/p&gt;
&lt;p&gt;The Hello actor is sent a message and adds "hello" to the message then sends it to the world actor which adds "world" to the message and sends it to the print actor which prints the messages.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://twitter.com/#!/adbrowne"&gt;Andrew Browne&lt;/a&gt; &lt;a href="https://gist.github.com/936274"&gt;implemented it&lt;/a&gt; in Scala, I &lt;a href="https://gist.github.com/936268"&gt;implemented it&lt;/a&gt; in Erlang and &lt;a href="http://twitter.com/#!/abienert"&gt;Andrew Bienert&lt;/a&gt; joined the party and &lt;a href="https://gist.github.com/984031"&gt;implemented a version&lt;/a&gt; in F#.&lt;/p&gt;
&lt;p&gt;I found the similarities and the differences in the implementations very interesting. The meeting was &lt;a href="http://melbourne.ozalt.net/2011/05/may-meeting-functional-programming-take.html"&gt;eventually held&lt;/a&gt; and the presentation generated &lt;a href="http://twitter.com/#!/NickJosevski/status/74687100531904512"&gt;some interest amongst the attendees&lt;/a&gt;, so I thought it would be worthwhile posting about here.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/actors</guid><pubDate>Fri, 01 Jul 2011 19:54:00 +0000</pubDate></item><item><title>Cube</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;style type="text/css"&gt;
    #canvas-container
    { 
        height: 480px; 
        width: 640px; 
        position: relative; 
        margin-left: auto; 
        margin-right:auto; 
    }
&lt;/style&gt;


    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Cube
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 30, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;WebGL has recently seen extra attention caused by banter between the major browser players, which seems to becoming pretty common.&lt;/p&gt;
&lt;p&gt;Microsoft called it &lt;a href="http://blogs.technet.com/b/srd/archive/2011/06/16/webgl-considered-harmful.aspx"&gt;harmful&lt;/a&gt;, a developer from Mozilla raised &lt;a href="http://connect.microsoft.com/VisualStudio/feedback/details/676134/dos-vulnerability-in-silverlight-5s-3d-similar-to-webgl-dos-vulnerability"&gt;an issue on Microsoft's connect showing the same risk in Silverlight 5&lt;/a&gt; and Opera and Google bloggers &lt;a href="http://my.opera.com/haavard/blog/2011/06/22/microsoft"&gt;weighed&lt;/a&gt; &lt;a href="http://games.greggman.com/game/webgl-security-and-microsoft-bullshit/"&gt;in&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This week it seems to be more about &lt;a href="http://bixhorn.com/?p=153"&gt;enterprise&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think WebGL is &lt;a href="http://www.netmagazine.com/features/20-webgl-sites-will-blow-your-mind"&gt;awesome&lt;/a&gt; and I am glad people are &lt;a href="http://www.chromeexperiments.com/"&gt;pushing the boundaries&lt;/a&gt; and although I'm not really into computer gaming, I would like see the graphic technology as part of the web medium.&lt;/p&gt;
&lt;p&gt;To get a taste I just wanted to try doing something simple like a rotating cube.
Spoiled as we are there is a &lt;a href="https://developer.mozilla.org/en/WebGL/Getting_started_with_WebGL"&gt;MDN Getting Started with WebGL&lt;/a&gt; guide that walks through doing just that.&lt;/p&gt;
&lt;div id="canvas-container"&gt;
&lt;canvas height="480" id="canvas" width="640"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p id="notsupported" style="display:none"&gt;It appears your browser doesn't support WebGL.&lt;/p&gt;
&lt;p&gt;I roughly followed along writing in CoffeeScript and found a lot about what happens when you don't get it right.
There is quite a lot you need to do, correctly, just to draw a shape on screen programming directly against the API.&lt;/p&gt;
&lt;p&gt;After some playing I had the cube you should see above, if the browser supports WebGl anyway.
Someone has written &lt;a href="http://iewebgl.com/index.html"&gt;IEWebGL plugin&lt;/a&gt;, but I haven't tried it. I posted the &lt;a href="https://gist.github.com/1053750"&gt;source&lt;/a&gt; here.&lt;/p&gt;
&lt;script src="sylvester.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="glUtils.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="cube.js" type="text/javascript"&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/cube</guid><pubDate>Thu, 30 Jun 2011 19:54:00 +0000</pubDate></item><item><title>Tile Land</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Tile Land
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 18, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I was originally trying build an effect that pans and rotates around an infinite world of repeating background images.
To do this I was using an image the size of the viewing area, of course to panning around will often require drawing parts of several images.
If we are panning along the x and y axis we will need to display, at most, four images.&lt;/p&gt;
&lt;div&gt;
&lt;canvas height="140" id="translate-diagram-canvas" width="140"&gt;
Sorry, you need a browser that supports canvas to play. You should probably upgrade anyway, you're missing out.
&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;When the camera reaches one of the edges, it jumps back an entire image size along the same axis.
As the images are the same, moving any direction by an entire image size results in exactly the view.&lt;/p&gt;
&lt;p&gt;Adding rotating made everything a little more complicated. Now up to six images can be visible at times.&lt;/p&gt;
&lt;div&gt;
&lt;canvas height="210" id="rotate-diagram-canvas" width="210"&gt;
Sorry, you need a browser that supports canvas to play. You should probably upgrade anyway, you're missing out.
&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Playing around I worked out that with nine tiles I could make sure all the tiles that could be visible were drawn.
This also made it possible handle the infinite panning in a very similar way as before.&lt;/p&gt;
&lt;p&gt;Another complication was I wanted the world to rotate around the view, not around the worlds origin.
I posted about &lt;a href="/journal/transforms"&gt;my struggles with that&lt;/a&gt; earlier.&lt;/p&gt;
&lt;p&gt;I got this working and had achieved what I was trying to do, but I wanted to take it a little further.
Rather than just infinite repeating images, I wanted to make an infinite world.
All I had to do was keep track of where the view was in this infinite world and
instead of drawing the same image everywhere, I would choose each image to draw by where it was in world.&lt;/p&gt;
&lt;p&gt;It was after I got this working that I realised adding the ability to scale the view of this tiled world would be pretty straight forward.
If the view was zoomed out 100% then the view would be the size of exactly four images or tiles.
Adding another tile of padding around those four tiles would be enough to ensure all the tiles possible in the view would be drawn.&lt;/p&gt;
&lt;div&gt;
&lt;canvas height="240" id="scale-diagram-canvas" width="400"&gt;
Sorry, you need a browser that supports canvas to play. You should probably upgrade anyway, you're missing out.
&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Zooming out 200% would mean the view was the size of nine tiles, adding another tile of padding would again ensure all the tiles in the view would be displayed.
Clearly there is a pattern here.&lt;/p&gt;
&lt;p&gt;Putting all this together and adding some controls and debugging information I came up with this.
Note there is no limit on how far you can zoom or pan, but zooming out far enough will eventually bring any browser to it knees.&lt;/p&gt;
&lt;div&gt;
&lt;canvas height="500" id="canvas" width="500"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;p&gt;Move &lt;a href="#" id="left"&gt;left [h]&lt;/a&gt;/&lt;a href="#" id="right"&gt;right [l]&lt;/a&gt; and &lt;a href="#" id="up"&gt;up [k]&lt;/a&gt;/&lt;a href="#" id="down"&gt;down [j]&lt;/a&gt;.
Rotate &lt;a href="#" id="clockwise"&gt;clockwise [d]&lt;/a&gt; and &lt;a href="#" id="anti-clockwise"&gt;anti-clockwise [f]&lt;/a&gt;.
Zoom &lt;a href="#" id="zoom-in"&gt;in [z]&lt;/a&gt; and &lt;a href="#" id="zoom-out"&gt;out [x]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I have some ideas about what I would like to do with this from here.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A game of some sort, possibly similar to the &lt;a href="/journal/devsta-challenge-2008"&gt;Desktop Racer game&lt;/a&gt; I wrote for DevStar 2008.&lt;/li&gt;
&lt;li&gt;Use some free map tiles&lt;/li&gt;
&lt;li&gt;An infinite world of images from Flickr&lt;/li&gt;
&lt;li&gt;Make it 3D&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have some ideas let me know or grab the &lt;a href="https://gist.github.com/1032752"&gt;source&lt;/a&gt; and start hacking. I also published the &lt;a href="https://gist.github.com/1032754"&gt;not-so-pretty source&lt;/a&gt; for the diagrams in this post.&lt;/p&gt;
&lt;script src="tile-land.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="diagrams.js" type="text/javascript"&gt;&lt;/script&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/tile-land</guid><pubDate>Sat, 18 Jun 2011 19:54:00 +0000</pubDate></item><item><title>Asteroids</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Asteroids
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 13, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;As &lt;a href="http://twitter.com/#!/tarnacious/status/77694210643464192"&gt;threatened&lt;/a&gt;, Asteroids!&lt;/p&gt;
&lt;canvas height="600" id="asteroids-canvas" width="900"&gt;
Sorry, you need a browser that supports canvas to play. You should probably upgrade anyway, you're missing out.
&lt;/canvas&gt;
&lt;p&gt;Update: You don't need coins, you can press [f] to start. Apparently I should have made that more clear.&lt;/p&gt;
&lt;p&gt;Sorry no sounds, shields or extra weapons,
there are already &lt;a href="http://dougmcinnes.com/html-5-asteroids/"&gt;other&lt;/a&gt; &lt;a href="http://www.kevs3d.co.uk/dev/asteroids/"&gt;good&lt;/a&gt; asteroid games for the HTML5 canvas.
I wrote this to try &lt;a href="http://jashkenas.github.com/coffee-script/"&gt;CoffeeScript&lt;/a&gt;, which has been quite fun.
I posted the &lt;a href="https://gist.github.com/1022545"&gt;source as a gist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have some ideas about how I could make it multiplayer which I might consider trying,
of course &lt;a href="http://www.lazeroids.com/"&gt;that has been done too&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script type="text/javascript" src="app.js"&gt;&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/asteroids</guid><pubDate>Mon, 13 Jun 2011 19:54:00 +0000</pubDate></item><item><title>Canvas Transforms</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Canvas Transforms
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 04, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've been playing with HTML canvas transforms and have had to brush up on how transforms work.
It's really just matrices math; eigenvalues, dot products, triple products etc. 
I &lt;em&gt;should&lt;/em&gt; know this stuff pretty well as it was extensively covered in my Engineering undergrad studies and
I've previously played with them to do 2D and 3D computer graphics.&lt;/p&gt;
&lt;p&gt;However when I have the choice between understanding the math and just typing a few lines and seeing the effect, I often just do the latter. I know, I really need to get past that.&lt;/p&gt;
&lt;p&gt;I seemed to be able to get transforms to do what I &lt;em&gt;wanted&lt;/em&gt; them to do, but until I got them right
I was finding they often didn't do what I expected they would.&lt;/p&gt;
&lt;p&gt;So I decided I would explore it a bit using the transforms themselves, but I'm not sure if I've cleared up my understanding.
I probably should have just studied the math and written an asteroids game.&lt;/p&gt;
&lt;h2&gt;Rotate on axis then centre&lt;/h2&gt;
&lt;p&gt;The example transforms I looked at was rotating a square drawn from (0,0) to (50,50) about its centre, then 
moving it to (50,50).&lt;/p&gt;
&lt;p&gt;This is basically a series of transforms, where the order is important. If this makes perfect sense you can
probably just check out the animations and hopefully leave a comment explaining what is going on more clearly than I have.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;translate(50,50)
rotate(Math.PI/4)
translate(-25,-25)
draw(..)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My brain thought this looked a bit backward, and it wasn't helped when I tried to animate applying the transforms one by one.
Sure it worked out, but not as I expected it too.&lt;/p&gt;
&lt;canvas height="300" id="demo1" width="300"&gt;
&lt;strong&gt;Yo, this demo requires canvas. Best viewed with a browser that supports it.&lt;/strong&gt;
&lt;/canvas&gt;
&lt;p&gt;&lt;a href="#" id="demo1_start"&gt;Click to start animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I noticed this would look as I expected if I animated the transforms in reverse order while still applying them in the correct order.&lt;/p&gt;
&lt;canvas height="300" id="demo2" width="300"&gt;
&lt;strong&gt;Yo, this demo requires canvas. Best viewed with a browser that supports it.&lt;/strong&gt;
&lt;/canvas&gt;
&lt;p&gt;&lt;a href="#" id="demo2_start"&gt;Click to start animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The previous animations illustrate what was bothering me, but they are not exactly what I wanted to show in the first place. 
The transforms apply to the canvas and the square is drawn at (0,0) after the transforms are applied.&lt;/p&gt;
&lt;p&gt;To show that we need to invert everything (kind of, more on that in another post maybe) and move the axis instead.&lt;/p&gt;
&lt;canvas height="300" id="demo3" width="300"&gt;
&lt;strong&gt;Yo, this demo requires canvas. Best viewed with a browser that supports it.&lt;/strong&gt;
&lt;/canvas&gt;
&lt;p&gt;&lt;a href="#" id="demo3_start"&gt;Click to start animation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You would think by now I would have started to understand all this but I haven't really.
What is more annoying is I wanted to make a demo in which a user could add arbitrary transforms one at time,
but the only way I could animate that nicely is if I asked for the transforms in reverse order.
Perhaps that is the way to think about it.&lt;/p&gt;
&lt;p&gt;Anyway I suck, hopefully a commenter can explain this more clearly than I have. Until then I hope this helps you grok how transforms work, but the real lesson here is know ye' math. Stay at school kids.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
        &lt;script type="text/javascript" src="transform_animation.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript"&gt;
        window.onload = function() {

            // Demo 1
            var demo1_start = document.getElementById("demo1_start");
            var demo1 = document.getElementById('demo1').getContext('2d');
            drawStandard([], demo1)
            demo1_start.onclick = function() {
                demo1_start.style.display = "none";
                if (demo1) {
                    var transforms1 = [animateTranslate(50,50),
                        animateRotation(Math.PI/4),
                        animateTranslate(-25,-25) ];
                    animateTransforms(demo1, transforms1, [], addToStart, drawStandard,
                            function() { demo1_start.style.display = "block" } );
                }
                return false;
            }

            // Demo 2
            var demo2_start = document.getElementById("demo2_start");
            var demo2 = document.getElementById('demo2').getContext('2d');
            drawStandard([], demo2)
            demo2_start.onclick = function() {
                demo2_start.style.display = "none";
                if (demo2) {
                    var transforms2 = [animateTranslate(50,50),
                        animateRotation(Math.PI/4),
                        animateTranslate(-25,-25) ];
                    animateTransforms(demo2, transforms2.reverse(), [], addToEnd, drawStandard,
                            function() { demo2_start.style.display = "block" } );
                }
                return false;
            }

            // Demo 3
            var demo3_start = document.getElementById("demo3_start");
            var demo3 = document.getElementById('demo3').getContext('2d');
            drawStandard([], demo3)
            demo3_start.onclick = function() {
                demo3_start.style.display = "none";
                if (demo3) {
                    var alt = [animateTranslate(-50,-50),
                        animateRotation(-Math.PI/4),
                        animateTranslate(25,25) ];
                    animateTransforms(demo3, alt.reverse(), [], addToStart, drawAlternate,
                            function() { demo3_start.style.display = "block" } );
                }
                return false;
            }
        }
    &lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/transforms</guid><pubDate>Sat, 04 Jun 2011 19:54:00 +0000</pubDate></item><item><title>Fridge Magnets and the GAE Channel API</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Fridge Magnets and the GAE Channel API
    &lt;/h1&gt;
    &lt;p&gt;
    May 15, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Fridge magnets have been my &lt;a href="https://bitbucket.org/tarnacious/letters"&gt;playground web application&lt;/a&gt; while learning Erlang. 
The basic idea is some letters which you can drag around to form words or whatever. 
It is made a little more interesting as users all use the same set of letters,
and so users see letters being moved by other users.&lt;/p&gt;
&lt;p&gt;The application itself isn't particularly interesting, 
there has been a &lt;a href="http://lunchtimers.com/game/?game=letters&amp;amp;roomid=room1"&gt;Flash version of this&lt;/a&gt; for a long time. 
Of course there are a couple of things I find interesting about building such an application. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web clients must be updated when letters are moved by other players&lt;/li&gt;
&lt;li&gt;The server must signal the listening clients and maintain letter positions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Web browsers have traditionally been geared to making requests and receiving responses. 
When there is the need to go beyond that to more persistent connections there are several options (and combinations of these options)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have the web client make periodic requests&lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Comet_(programming)"&gt;Comet&lt;/a&gt;/Long-polling  &lt;/li&gt;
&lt;li&gt;Browser plug-ins with socket support like Flash&lt;/li&gt;
&lt;li&gt;The emerging WebSockets standards&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At some point since I last used App Engine, Google have added a &lt;a href="http://code.google.com/appengine/docs/python/channel/overview.html"&gt;Channel API&lt;/a&gt; for persistent connections.
The App Engine and Python are quite fun to play with, so I decided to hack together a version of Fridge Magnets using the API.&lt;/p&gt;
&lt;p&gt;As Google serves the JavaScript and the handlers for the persistent connection they could use any of the techniques.
It &lt;a href="http://www.quora.com/Is-Google-App-Engines-SDK-1-4-0-Channel-API-using-websockets"&gt;appears&lt;/a&gt; they are only currently doing long-polling.&lt;/p&gt;
&lt;p&gt;The app is at &lt;a href="http://gae-letters.appspot.com"&gt;gae-letters.appspot.com&lt;/a&gt; and the &lt;a href="https://bitbucket.org/tarnacious/letters_gae"&gt;source&lt;/a&gt; on bitbucket. &lt;/p&gt;
&lt;p&gt;It's all a bit hacky, but it weighs in at less than 150 lines of Python so I'm not too worried. 
While it was fun and pretty easy, there are some pretty seriously limitations of App Engine when trying to build such applications.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No notification when clients disconnect&lt;/li&gt;
&lt;li&gt;No synchronization methods I am aware of, which is probably &lt;em&gt;why&lt;/em&gt; Google can scale these apps&lt;/li&gt;
&lt;li&gt;I'm also seeing problems with browsers not seeing pushes after some time, but I haven't really looked into it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although didn't use the &lt;a href="http://code.google.com/appengine/docs/python/taskqueue/"&gt;Task Queue API&lt;/a&gt; which looks very useful,
it seems traditional web frameworks suck for building &lt;em&gt;this type&lt;/em&gt; of application.
Alternatively Erlang and probably Node.js and Twisted etc are made for it.&lt;/p&gt;
&lt;p&gt;Hopefully a robust, fast Erlang implementation will be the topic of my next post.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/fridge-magnets-app-engine-channel-api</guid><pubDate>Sun, 15 May 2011 22:24:00 +0000</pubDate></item><item><title>Old school, for fun</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Old school, for fun
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 23, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I used to love 4k demos and I learned to program in Pascal, C and Assembler trying to implement effects like this. Good times.&lt;/p&gt;
&lt;canvas height="384px" id="fire" style="padding-left:50px" width="800px"&gt;It appears your browser doesn't support canvas. That is quite sad, you should get one that does.&lt;/canvas&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="jquery.min.js"&gt;&lt;/script&gt;
&lt;script src="fire.js"&gt;&lt;/script&gt;



    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/old-school-for-fun</guid><pubDate>Sat, 23 Apr 2011 09:54:00 +0000</pubDate></item><item><title>Oh, Screen. Where have you been?</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Oh, Screen. Where have you been?
    &lt;/h1&gt;
    &lt;p&gt;
    Jan 08, 2011
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;So &lt;a href="http://www.gnu.org/software/screen/"&gt;GNU Screen&lt;/a&gt; has been around for a while (initially released in 1987!), but it was only recently at Yow! Brisbane 2010 that &lt;a href="http://twitter.com/rioter"&gt;@rioter&lt;/a&gt; kindly suggested I check it out.&lt;/p&gt;
&lt;p&gt;For the last year or so I've used Linux exclusively for my personal computing where I play with a little Python, Ruby, Lisp, JavaScript and Erlang with Vim and a terminal. I do like &lt;a href="/journal/the-mouse-is-dead--long-live-the-keyboard"&gt;freeing myself from my mouse&lt;/a&gt;. But there were a few area of my work flow that were bothering me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I didn't like how I navigated up the terminal buffer to see what had happed, I could use Ctrl+PageUp/PageDown to avoid the using the mouse, but jumping by pages isn't always ideal.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I used the mouse to copy text from the terminal buffer. This constantly depressed me.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I never worked out how to run a console shell inside Vim. This resulted in using my windows manger to switch between Vim and console windows. It made copying and pasting between the two more difficult as I had to use the system clipboard.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I sometimes had couple of windows running processes like web servers on a separate desktop.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These were my &lt;em&gt;biggest&lt;/em&gt; annoyances with my development environment on Linux. Enter Screen into &lt;em&gt;my world&lt;/em&gt; to solve them all.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="screen.png"/&gt;&lt;/p&gt;
&lt;p&gt;GNU Screen is a terminal multiplexer with windowing support. Programs created in Screen can persist after the shell that created them, it supports disconnecting, re-connecting and sharing programs.&lt;/p&gt;
&lt;p&gt;The terminal buffers in Screen don't have scrollbars but they do support a copy mode (Ctrl+a [). This allows you to move up, down and around the window with Vim like keyboard navigation and copy text which can be pasted into other screen buffers (Ctrl+a ]). Yay, two of my problems solved!&lt;/p&gt;
&lt;p&gt;Screen provides many functions for creating and switching between programs. A new shell be be created (Ctrl+a c) and named (Ctrl+a a). The connected programs can be listed and selected into the foreground with (Ctrl+a "). Now I can create shells and have them running in the background, then switch to them when I need them.&lt;/p&gt;
&lt;p&gt;It supports splitting the current screen window horizontally and vertically (Ctrl+a S and Ctrl+a |). The focus can be switched between windows (Ctrl+a Tab). This allows me to have Vim on the top and a console on the bottom. Awesome, now I don't even need to run the console inside Vim itself.&lt;/p&gt;
&lt;p&gt;There are many more use-cases for Screen but just these functions alone have resolved some of my major frustrations working with Vim in a Linux terminal.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/oh-screen-where-have-you-been</guid><pubDate>Sat, 08 Jan 2011 11:02:00 +0000</pubDate></item><item><title>Selenium on a Windows CI Server</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Selenium on a Windows CI Server
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 15, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've been wanting to post about using Selenium and TeamCity to build and test .NET websites on Windows for a while.
Unfortunately it hasn't been forthcoming as I only really use Windows/.NET at work these days.&lt;/p&gt;
&lt;p&gt;I decided I would get my shit together today and write, what may be my last post on .NET for a while,
in response to &lt;a href="http://twitter.com/DavidBurela"&gt;@DavidBurela&lt;/a&gt;'s &lt;a href="http://davidburela.wordpress.com/2010/10/13/developer-blog-banter-2-how-do-you-test-your-applications/"&gt;Developer Blog Banter&lt;/a&gt; topic
"How do you test your applications?"&lt;/p&gt;
&lt;p&gt;I'm going skip unit-testing (which do and try to get better at), BDD and JavaScript testing (which I would like to do and get better at) and focus on automated browser testing.&lt;/p&gt;
&lt;h2&gt;The Build Process&lt;/h2&gt;
&lt;p&gt;Watching automated test may be fun the first few runs, &lt;a href="http://xkcd.com/303/"&gt;but that won't last long&lt;/a&gt;.
Anyway if you want automated browsers tests, surely you want to automate the automated tests, right?&lt;/p&gt;
&lt;p&gt;The build process is going to need to somehow automate the browser and run the site the browser is browsing.
For this we use IIS which is scriptable with the PowerShell WebAdministration module (maybe I will write another post on .NET).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Standard Build Process" src="BuildProcess.png"/&gt;&lt;/p&gt;
&lt;p&gt;The problem with having the UI Test step in this iterative process is it very quickly starts taking ages.
This sucks, I want feedback as soon as possible.
TeamCity provides feedback after each stage, but we don't get our green light.&lt;/p&gt;
&lt;p&gt;This could be resolved by putting the UI tests in a nightly build, but I think a staged build or build pipeline would be better.&lt;/p&gt;
&lt;p&gt;The first stage compiles and unit tests and produces a deployable website as an artifact, this process is hopefully quite fast.
If the first stage is successful the next stage in the pipeline is triggered which can be setup in the TeamCity administration web interface.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pipeline Build Process" src="BuildProcessPipeline.png"/&gt;&lt;/p&gt;
&lt;p&gt;If you want to get awesome you can also start using Selenium Grid to distribute test agents or a cloud based solution like source labs&lt;/p&gt;
&lt;h2&gt;Tools&lt;/h2&gt;
&lt;p&gt;Our automated build process consists of a few tools.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jetbrains.com/teamcity/"&gt;TeamCity&lt;/a&gt; for our build management and CI server.
After years of using CC.Net and a very brief and painful exposure to TFS, I love TeamCity.
I also hear great thing about Hudson.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.nunit.org/"&gt;NUnit&lt;/a&gt; and included &lt;a href="http://www.nunit.org/index.php?p=nunit-console&amp;amp;r=2.4"&gt;NUnit-Console&lt;/a&gt;. It does what I want a unit test framework and runner. I am yet to be convinced to changed.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx"&gt;MSBuild&lt;/a&gt; with the &lt;a href="http://msbuildtasks.tigris.org/"&gt;MSBuild Community Tasks&lt;/a&gt; for our build scripts.
I think you have to have some sort of build script to do continuous integration when not using TFS.
I would like to be using script or DSL instead, but msbuild is doing the job for now.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt; for our browsers automation tests.&lt;/p&gt;
&lt;h2&gt;Creating Tests&lt;/h2&gt;
&lt;p&gt;SeleniumIDE is a Firefox plug-in for recording and running tests.&lt;/p&gt;
&lt;p&gt;&lt;img alt="SeleniumIDE" src="SeleniumIDE.png"/&gt;&lt;/p&gt;
&lt;p&gt;SeleniumIDE is pretty slick and importantly it's a tool non-developer-testers-types can quickly get their head round and start producing some tests.&lt;/p&gt;
&lt;p&gt;What we really want though, is to have our tests in code so we can run on them on build machine and version them in a repository.&lt;/p&gt;
&lt;h2&gt;Test Scripts&lt;/h2&gt;
&lt;p&gt;&lt;img alt="SeleniumID Export" src="SeleniumIDE_Export.png"/&gt;&lt;/p&gt;
&lt;p&gt;That should generated something like this.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Generated code" src="generatedtest.png"/&gt;&lt;/p&gt;
&lt;p&gt;This is pretty sweet, it generates test files you can pretty much just add to your project as NUnit tests.
For me this mean automation tests can be run by the nunit-console just as our unit tests projects were.&lt;/p&gt;
&lt;p&gt;The .NET client drivers are in the &lt;a href="http://seleniumhq.org/download/"&gt;Selenium Core download&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think it is worth considering generating C# test scripts may not necessarily be ideal.&lt;/p&gt;
&lt;h2&gt;Selenium&lt;/h2&gt;
&lt;p&gt;Selenium server run of the JVM - Yes Java - Suck it up, TeamCity does too :)&lt;/p&gt;
&lt;p&gt;Any version of the standard JRE should be fine.
If you've installed Java correctly it should be in your path and you should be able to type this in a console.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c:\&amp;gt;java -version

java version "1.6.0_18"
Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
Java HotSpot(TM) Client VM (build 16.0-b13, mixed mode, sharing)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should output the Java version and build information. If so you have successfully installed a java runtime. Yay!&lt;/p&gt;
&lt;p&gt;You need to download the &lt;a href="http://seleniumhq.org/download/"&gt;SeleniumRC&lt;/a&gt; JAR file.&lt;/p&gt;
&lt;p&gt;Once you have Java and the Selenium JAR you should be able to start the server from the command-line.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -jar selenium-server.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Selenium as a Service&lt;/h2&gt;
&lt;p&gt;I wanted to run SeleniumRC (the same Jar) as a service. Console windows on servers, not so cool.
Another, probably better, option is to have the build script itself instantiate the server and take down the server.
I went with the service.&lt;/p&gt;
&lt;p&gt;I found a &lt;a href="http://unintelligible.org/blog/2009/07/28/installing-selenium-rc-as-a-windows-service/|"&gt;solution&lt;/a&gt; but it &lt;a href="http://serverfault.com/questions/58025/install-service-in-windows-server-2008"&gt;didn't work on Windows 2008&lt;/a&gt;, luckily that lead me to the &lt;a href="http://iain.cx/src/nssm/"&gt;Non Sucking Service Manager&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Non sucking service manager" src="nssm.png"/&gt;&lt;/p&gt;
&lt;p&gt;The parameters I used were "-Xrs -jar [path/to/selenium-server.jar]"&lt;/p&gt;
&lt;h2&gt;Discussion&lt;/h2&gt;
&lt;p&gt;I went down the path of browsers automation because I was finding none of our other testing efforts, while valuable,
were actually ensuring the website would work.&lt;/p&gt;
&lt;p&gt;We were still have problems where a page or an entire site would return a HTTP 500 Server Error.
Sometimes it was code and a unit-test would would ensure the fix, Sometimes it was IOC or ORM/Database configuration.
What we needed was an end-to-end integration test. Browser automation provides these tests, and it helps.&lt;/p&gt;
&lt;p&gt;I actually don't see the tests themselves as being particularly fragile.
No doubt they can be, but I think it also encourages clean markup.
If the hierarchy or class/id attributes change in the markup then UI tests are not the only thing the could break.
Stylesheets, scripts and some server logic also depend on it.&lt;/p&gt;
&lt;p&gt;While I have found the testing value of browser automation is good,
I feel that mocking out the entire web framework (like gaeunit for appengine)
or testing at HTTP level could achieve the same thing more easily and run quicker.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/selenium-on-windows</guid><pubDate>Fri, 15 Oct 2010 22:24:00 +0000</pubDate></item><item><title>RxJs - Capture</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        RxJs - Capture
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 15, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;This is the presentation for &lt;a href="/journal/composing-events-with-rxjs"&gt;Composing Events with RxJS&lt;/a&gt; an &lt;a href="http://melbourne.ozalt.net/2010/08/august-meeting-lightning-talks-wrap-up.html"&gt;Alt.Net Melbourne Lightning Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An observable that yields for every capture click after the start button and
before the stop button has been clicked.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Setup observables&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buttonStart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#buttonStart"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buttonCapture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#buttonCapture"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buttonStop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#buttonStop"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Compose an event&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buttonStart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;buttonCapture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Captured Click"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;TakeUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buttonStop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Subscribe to, and handle event&lt;/span&gt;
&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;li/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#results"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;button id="buttonStart"&gt;Start&lt;/button&gt;
&lt;button id="buttonCapture"&gt;Capture&lt;/button&gt;
&lt;button id="buttonStop"&gt;Stop&lt;/button&gt;&lt;/p&gt;
&lt;ul id="results"&gt;&lt;/ul&gt;
&lt;p&gt;&lt;a href="/journal/rxjs-zip"&gt;Back (Zip)&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="jquery-1.10.2.min.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.aggregates.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.jQuery.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.joins.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;script&gt;
    $(function () {

        // Setup observables
        var buttonStart = $("#buttonStart").toObservable("click");
        var buttonCapture = $("#buttonCapture").toObservable("click");
        var buttonStop = $("#buttonStop").toObservable("click");

        // Compose an event
        var compose = buttonStart.SelectMany(function () {
            return buttonCapture.Select(function () {
                return "Captured Click";
                }).TakeUntil(buttonStop);
            });

        // Subscribe to, and handle event
        compose.Subscribe(function (result) {
            var val = $("&lt;li/&gt;").html(result);
            val.appendTo($("#results"));
        });

    });
&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/rxjs-capture</guid><pubDate>Sun, 15 Aug 2010 22:24:00 +0000</pubDate></item><item><title>RxJs - Merge</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        RxJs - Merge
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 15, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;This is the presentation for &lt;a href="/journal/composing-events-with-rxjs"&gt;Composing Events with RxJS&lt;/a&gt; an &lt;a href="http://melbourne.ozalt.net/2010/08/august-meeting-lightning-talks-wrap-up.html"&gt;Alt.Net Melbourne Lightning Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An observable that yields when either button is clicked&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Setup observables&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;button1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#button1"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Button 1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;button2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#button2"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Button 2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Compose an event&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;button1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Subscribe to, and handle event&lt;/span&gt;
&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;li/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#results"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;button id="button1"&gt;Button 1&lt;/button&gt;
&lt;button id="button2"&gt;Button 2&lt;/button&gt;&lt;/p&gt;
&lt;ul id="results"&gt;&lt;/ul&gt;
&lt;p&gt;&lt;a href="/journal/composing-events-with-rxjs"&gt;Previous (Index)&lt;/a&gt;
&lt;a href="/journal/rxjs-zip"&gt;Next (Zip)&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="jquery-1.10.2.min.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.aggregates.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.jQuery.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.joins.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;script&gt;
    $(function () {

        // Setup observables
        var button1 = $("#button1").toObservable("click").Select(function() {
            return "Button 1" });

        var button2 = $("#button2").toObservable("click").Select(function() {
            return "Button 2"; });

        // Compose an event
        var compose = button1.Merge(button2)

        // Subscribe to, and handle event
        compose.Subscribe(function (result) {
            var val = $("&lt;li/&gt;").html(result);
            val.appendTo($("#results"));
        });

    });
&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/rxjs-merge</guid><pubDate>Sun, 15 Aug 2010 22:24:00 +0000</pubDate></item><item><title>RxJs - Zip</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        RxJs - Zip
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 15, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;This is the presentation for &lt;a href="/journal/composing-events-with-rxjs"&gt;Composing Events with RxJS&lt;/a&gt; an &lt;a href="http://melbourne.ozalt.net/2010/08/august-meeting-lightning-talks-wrap-up.html"&gt;Alt.Net Melbourne Lightning Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An observable that yields and event for consecutive clicks. This can be useful
when the change since last event is required.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Setup observables&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;button1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#button1"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Button 1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;button2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#button2"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Button 2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Compose an event&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;button1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Zip pairs of events&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" previously "&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Subscribe to, and handle event&lt;/span&gt;
&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;li/&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"#results"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;button id="button1"&gt;Button 1&lt;/button&gt;
&lt;button id="button2"&gt;Button 2&lt;/button&gt;&lt;/p&gt;
&lt;ul id="results"&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="/journal/rxjs-merge"&gt;Previous (Merge)&lt;/a&gt;
&lt;a href="/journal/rxjs-capture"&gt;Next (Capture)&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="jquery-1.10.2.min.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.aggregates.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.jQuery.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.joins.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;script&gt;
    $(function () {

        // Setup observables
        var button1 = $("#button1").toObservable("click").Select(function() {
            return "Button 1" });

        var button2 = $("#button2").toObservable("click").Select(function() {
            return "Button 2"; });

        // Compose an event
        var compose = button1.Merge(button2)


        var compose = compose.Skip(1).Zip(compose, function (result1, result2) {
            return result1 + " previously " + result2; });

        // Subscribe to, and handle event
        compose.Subscribe(function (result) {
            var val = $("&lt;li/&gt;").html(result);
            val.appendTo($("#results"));
        });

    });
&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/rxjs-zip</guid><pubDate>Sun, 15 Aug 2010 22:24:00 +0000</pubDate></item><item><title>Composing events wit RxJs</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Composing events wit RxJs
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 15, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;This is the presentation for &lt;a href="/journal/composing-events-with-rxjs"&gt;Composing Events with RxJS&lt;/a&gt; an &lt;a href="http://melbourne.ozalt.net/2010/08/august-meeting-lightning-talks-wrap-up.html"&gt;Alt.Net Melbourne Lightning Talk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;*** Reactive Extensions ***&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reactive Extensions for JavaScript.&lt;/li&gt;
&lt;li&gt;There is also Reactive Extensions for C#.&lt;/li&gt;
&lt;li&gt;Reactive Extensions come from Microsoft Labs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;*** What is IEnumerable? ***&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provides Enumerator, allows lazy evaluation&lt;/li&gt;
&lt;li&gt;You can implement an IEnumerable with yield and yield break.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;*** What is Observable? ***&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you foreach(..) through IEnumerable you are waiting OnNext.&lt;/li&gt;
&lt;li&gt;With Observable you are notified OnNext.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;*** What is Rx ***&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Like Linq adds a generic common API to query, Rx attempts to add a common API to compose generic events.&lt;/li&gt;
&lt;li&gt;There are Select, Where, Join, Zip, Merge and many other constructs.&lt;/li&gt;
&lt;li&gt;Basically keeps track of events and callbacks.&lt;/li&gt;
&lt;li&gt;RxJs provides integration with several frameworks.&lt;/li&gt;
&lt;li&gt;Also provides common error handling, which I won't show.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="next" href="/journal/rxjs-merge"&gt;Next (lets see some code!)&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/composing-events-with-rxjs</guid><pubDate>Sun, 15 Aug 2010 20:24:00 +0000</pubDate></item><item><title>The Mouse is Dead, Long Live the Keyboard</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        The Mouse is Dead, Long Live the Keyboard
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 16, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;em&gt;First up a disclaimer, this is how I like to role. I enjoy it. I'm not saying
you shouldn't use a mouse or I'll never use one again.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I spend a lot of time programming on my netbook development environment
because I enjoy it. Without all the visual tooling I feel can focus on working
with the machine, learning its many tongues and tricks. I've run ubuntu on it
for about a year and have learned heaps about linux. There have been many
languages, tools and skills I would not have otherwise been able to learn.&lt;/p&gt;
&lt;p&gt;One thing I don't like about it is using the crappy mouse pad. It has long
been a pain point, even doing trivial things. Recently I saw a tweet by
&lt;a href="http://twitter.com/TheColonial"&gt;@TheColonial&lt;/a&gt; listing some mostly unfamiliar names and adding
themouseisdead hash tag. This was exciting, I had recently adopted vim and was loving
it.&lt;/p&gt;
&lt;p&gt;Last weekend I was a bit under the weather and not thinking clearly enough to
write code. So I used to time to set-up and learn a few new tools. I put
together a powerful, mouse-less development environment on my tiny netbook,
which I think is pretty fun to use.&lt;/p&gt;
&lt;h2&gt;xmonad&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://xmonad.org"&gt;xmonad&lt;/a&gt; is a great window manager (~1000 lines of haskell) which seems to
plug into most X systems. There are other ways of managing a windowed
environment, I never new existed! It is all keyboard driven, it works out non-
overlapping window layouts, you can move between multiple desktops and move
windows between them.&lt;/p&gt;
&lt;p&gt;Here are some typical tasks in a desktop with a few windows using xmonad:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Switch between window layouts, generated by haskell algorithms (mod-
space).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select between windows on screen (mod-tab).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Throw the selected window to another desktop (shift-mod-2).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change to the desktop (mod-2).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On Ubuntu it installed with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install xmonad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which installed its haskell dependencies and configured itself. It also added
an xmonad session option on the login screen, I wanted to use this and keep
the normal session intact.&lt;/p&gt;
&lt;p&gt;When you start an xmonad session you are presented with a completely empty
screen except for a background picture. You can start terminal session with
some keys (mod-shift-enter), but a menu would be nice.&lt;/p&gt;
&lt;h2&gt;dmenu&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://tools.suckless.org/dmenu/"&gt;dmenu&lt;/a&gt; is a generic dynamic menu for X systems, it allows menu items to be
selected efficiently with a keyboard.&lt;/p&gt;
&lt;p&gt;It installed with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install dwm-tools
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This adds another session to the login screen, now anything can be opened from
the xmonad session by bringing up dmenu (mod-p) and typing the first few
letters of the desired application.&lt;/p&gt;
&lt;h2&gt;Vim&lt;/h2&gt;
&lt;p&gt;I'm surprised and a little annoyed I didn't start using it sooner really, it
is a very powerful text editor. I still fumble through it but feel I'm
learning to increase my productivity and reduced my frustration working with
code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install vim
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now vim is available from the terminal. The vim-tutor is the best place to
start, then there is a large ecosystem of plugins.&lt;/p&gt;
&lt;h2&gt;Vimperator&lt;/h2&gt;
&lt;p&gt;Vimperator is a Firefox extension that provides command from the keyboard,
with vim idioms. It provides deep control of firefox, a basic browsing
scenario might work like:&lt;/p&gt;
&lt;p&gt;You can open a page in a new tab&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:tabopen theage.com.au
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then to open a link to a story in a new tab&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shift-f
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This adds unique two digit numbers to all links. Now any link can be opened by
typing the digits or the link text.&lt;/p&gt;
&lt;p&gt;To go to the new tab:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once read the tab can be deleted&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;d
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;TTYtter&lt;/h2&gt;
&lt;p&gt;Anything typed that doesn't start with "/" is a tweet (the -verify and
-slowpost options can help with potential problems that might cause) The
forward slash is used to invoke commands like "whois", "replies", "reply",
"dm", "follow", "thread" etc. It's built on curl, so we need that first.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get install curl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then download the &lt;a href="http://www.floodgap.com/software/ttytter/dist1/1.0.04.txt"&gt;perl script&lt;/a&gt; from the website, make it executable and
move it to the /usr/bin folder.&lt;/p&gt;
&lt;p&gt;I like everything about this app down to the ascii art in the menus. The dude
has some pretty cool stuff including a network of &lt;a href="gopher://gopher.floodgap.com/"&gt;gopher servers&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Vimium&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://vimium.github.com/"&gt;Vimium&lt;/a&gt; is not as powerful as Vimperator, but provides some vim-like
keyboard controls for Chrome. Easily installed as a chrome add-in.&lt;/p&gt;
&lt;h2&gt;Rock'n'roll time?&lt;/h2&gt;
&lt;p&gt;I can now manage windows over multiple virtual desktops. Use a fast, effective
menu to opening applications. Have extensive control of firefox and most
importantly, can tweet about it with TTYtter. I am excited and looking forward
to working with some code.&lt;/p&gt;
&lt;p&gt;My set-up still isn't right yet though; I need a status bar and decent start
script (the plain xmonad session just a blank canvas, an internet connection,
battery power monitor and a clock would be useful). I'm going to try &lt;a href="http://sites.google.com/site/gotmor/dzen"&gt;dzen&lt;/a&gt;
for this.&lt;/p&gt;
&lt;p&gt;And my keyboard skills on the special keys is way worse than than I'd like it
to be. The best way to fix that is to keep my hands on the keyboard, right?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Finally thanks to all the people responsible for the OSS platform, languages
and many tools. They are a pleasure to use.&lt;/em&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/the-mouse-is-dead--long-live-the-keyboard</guid><pubDate>Wed, 16 Jun 2010 23:40:00 +0000</pubDate></item><item><title>Revisiting dragging and inertia with RxJs</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link rel="stylesheet" type="text/css" href="example.css" /&gt;



    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Revisiting dragging and inertia with RxJs
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 07, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This is my first look at RxJs the &lt;a href="http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx"&gt;Reactive Extensions for Javascript&lt;/a&gt;. The
demo isn't remotely useful or practical but it was complicated enough to get a
feel for a range of Rx constructs and how they can be used in the Javascript
language.&lt;/p&gt;
&lt;p&gt;The excellent &lt;a href="http://codebetter.com/blogs/matthew.podwysocki/default.aspx"&gt;articles by Matthew Podwysocki at CodeBetter&lt;/a&gt; provided the
information I needed to get started and his answers to my questions on Twitter
(&lt;a href="http://twitter.com/mattpodwysocki/status/14866659412"&gt;1&lt;/a&gt; &lt;a href="http://twitter.com/mattpodwysocki/status/14669353423"&gt;2&lt;/a&gt; &lt;a href="http://twitter.com/mattpodwysocki/status/14669684258"&gt;3&lt;/a&gt; &lt;a href="http://twitter.com/mattpodwysocki/status/14669738261"&gt;4&lt;/a&gt;) helped fill the gaps for me. The dragging,
easing and inertia maths is lifted from the excellent &lt;a href="http://channel9.msdn.com/continuum/tutorials/Dragging/"&gt;project "Rosetta Stone"
tutorials&lt;/a&gt; but it isn't very complicated.&lt;/p&gt;
&lt;div&gt;
&lt;div id="demo_container"&gt;
&lt;div id="panel"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;What's going on here man?&lt;/h2&gt;
&lt;p&gt;It's a type of drag and release behaviour that is a little bit more
complicated than a normal drag and drop and a lot less useful. Instead of just
moving the drag target with the mouse, it will ease towards it and once
released it will slow to a stop. It allows you to kind-of throw the tiles.&lt;/p&gt;
&lt;p&gt;The behaviour involves mouse button events, movements and regular frames to
animate the drag items movement. Rx provides and API to co-ordinate these
events.&lt;/p&gt;
&lt;p&gt;The behavour could be desribed as:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Select mouseDowns
    SelectMany mouseMoves
        Select frames
            Yield positions
        Until mouse
    Concat
        Select frame
            Yield positions
        Until mouseDown Merge (or) itemStopedMoving
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is almost exactly how the Rx API works. The Select, SelectMany, Until,
Concat and Merge are all Rx functions. The code looks very similar too except
there is a closure for each drag motion but it could be factored differently.&lt;/p&gt;
&lt;h2&gt;Enough already, show me the codez&lt;/h2&gt;
&lt;p&gt;No problems, here is the pure Javascript code which can be run outside the
browser in something like Rhino. Separating the non-browser dependant part
helped enormously allowing me to trigger the relevant events and test the
output in a controlled, not-so insane way.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;drag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleOutOfBounds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dragTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dragOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newPos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.96&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;isDragOutComplete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dragMotion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;movementComplete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Rx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetPosition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;easeTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;easeFrame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleOutOfBounds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dragTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;targetPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;easeFrame&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;TakeUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mouseMove&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;easeToStop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frameEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;handleOutOfBounds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dragOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDragOutComplete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;movementComplete&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OnNext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targetPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mouseMove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;easeTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TakeUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mouseUp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;easeToStop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TakeUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mouseDown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movementComplete&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mouseDown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dragMotion&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The RxJs extension for jQuery are used to compose the desired observable
events from the browser.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getDomEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getDragItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"px"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"top"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"px"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getOffset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getDragStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mouseEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;mouseEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mouseEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getDragItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getOffset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mouseUp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mouseup"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;mouseDown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mousedown"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getDragStart&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;mouseMove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mousemove"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getMouse&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Rx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dragElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getDomEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;drag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally we we can subscribe to the composed observable and update the DOM
element accordingly.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;".tile"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;dragElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;})});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;What next&lt;/h2&gt;
&lt;p&gt;It's interesting there is no reason we couldn't take the observable event
stream we created here and compose it with more events. For example we could
subscribe to this event stream until the tile position was in the correct
position or until an escape key is pressed.&lt;/p&gt;
&lt;p&gt;For me Rx is a good solution to a very real problem domain. It's been fun
learning about Rx and also a bit more about Javascript. I'm looking forward to
using RxJs in real web applications.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="jquery.min.js"&gt;&lt;/script&gt;
&lt;script src="rx.js" type="text/javascript" &gt;&lt;/script&gt;
&lt;script src="rx.joins.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="rx.jQuery.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="drag.js" type="text/javascript" &gt;&lt;/script&gt;
&lt;script src="dom_drag.js" type="text/javascript"&gt;&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/revisiting-dragging-and-inertia-with-rxjs</guid><pubDate>Mon, 07 Jun 2010 19:54:00 +0000</pubDate></item><item><title>Map-Reduce on Mongo</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Map-Reduce on Mongo
    &lt;/h1&gt;
    &lt;p&gt;
    May 12, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I'm doing a presentation on non-relational databases at &lt;a href="http://www.dddmelbourne.com/"&gt;DDD Melbourne&lt;/a&gt;
this weekend where I am going to demonstrate a map-reduce example with MongoDB
and server side Javascript. I've been interested in both independently
recently and it's been fun getting them to working together with some
Javascript TDD to boot.&lt;/p&gt;
&lt;p&gt;I needed a good example to demonstrate map-reduce and decided finding word
occurrences across a series of documented seemed a simple enough scenario that
is suited to being solved by a map-reduce query.&lt;/p&gt;
&lt;p&gt;Below is an example of how we might solve this in plain C#&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;wordCounts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Setup some data&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s"&gt;"Peter Piper picked a peck of pickled peppers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s"&gt;"A peck of pickled peppers Peter Piper picked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s"&gt;"If Peter Piper picked a peck of pickled peppers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s"&gt;"Where's the peck of pickled peppers Peter Piper picked?"&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// select all words, group, count&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wordCounts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;                              &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;                              &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;KeyValuePair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Print out the results&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wordCounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{0} {1}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This prints each different word in all the lines and the number of the times
it occurs. The collection of strings is isomorphic to a collection of
documents in the MongoDB for this example.&lt;/p&gt;
&lt;p&gt;The SelectMany flattens lists of words from each line to a single list of
words and the Group provides keys for each word, this is very similar to what
the map function in the map-reduce query does.&lt;/p&gt;
&lt;p&gt;The Select function is similar to the reduce function, but as we will see some
additional considerations need to be made to allow it to be distributed.&lt;/p&gt;
&lt;p&gt;I saw a &lt;a href="http://ayende.com/Blog/archive/2010/03/14/map-reduce-ndash-a-visual-explanation.aspx"&gt;good diagram&lt;/a&gt; ayande published on his blog but I didn't understand
why he had multiple instance of the same document being mapped.&lt;/p&gt;
&lt;p&gt;I created my own low key diagram to help demonstrate how a functional map-
reduce could be distributed. The diagram shows the initial items can be split
in half and reduced completely independently. This is interesting as it means
our query can be distributed, but it also means we have to handle reducing a
little differently.&lt;/p&gt;
&lt;p&gt;It's also worth noting that this example shows a balanced tree, but it could
be unbalanced and even introduce some redundancy.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="map-reduce-example.png"/&gt;&lt;/p&gt;
&lt;p&gt;MongoDB allows clients to send JavaScript map and reduce functions that will
get eval'd and run on the server. Here is the map function.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wordMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// try find words in document text&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\w+/g&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// loop every word in the document&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// emit every word, with count of one&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The misunderstood Javascript "this" will be the context from which the
function is called. Mongo will call function each document in the collection
we are querying, and we can call it from a test context. Unlike the SelectMany
the map function doesn't return a list, instead it calls an emit function
which it expects to be defined.&lt;/p&gt;
&lt;p&gt;We can write unit tests for this function by calling the function from a test
mock context, calling a mock emit function (using Javascript as our mocking
framework, wow).&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"src/js/wordMap.js"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;testCases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wordMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;empty_string_emits_nothing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;single_word_emits_single_word&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"findme"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"findme"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;two_different_words_emits_twice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"for bar"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;two_same_words_emits_twice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test test"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The reduce function must reduce a list of a chosen type to a single value of
that same type; it must be transitive so it doesn't matter how the mapped
items are grouped.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wordReduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly we can test this method does exactly what we expect it to.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"src/js/wordReduce.js"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;testCases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reduce_one_items_returns_count_of_one&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wordReduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reduce_multiple_items_returns_item_count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wordReduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reduce_sums_counts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wordReduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reduce_is_transitive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wordReduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}].&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;wordReduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I'm using &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt; to run the Javascript so I used &lt;a href="http://code.google.com/p/rhinounit/"&gt;RhinoUnit&lt;/a&gt; as a test
runner as it also uses the JVM and runs as an ANT scriptdef task, the setup
was pretty painless. Here are the relevant ANT script sections&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;scriptdef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"rhinounit"&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"lib/rhinoUnitAnt.js"&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="na"&gt;language=&lt;/span&gt;&lt;span class="s"&gt;"javascript"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;attribute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"options"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;attribute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ignoredglobalvars"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;attribute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"haltOnFirstFailure"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;attribute&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"rhinoUnitUtilPath"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;element&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fileset"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"fileset"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/scriptdef&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"javascript-tests"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;rhinounit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;options=&lt;/span&gt;&lt;span class="s"&gt;"{verbose:true, stackTrace:true}"&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="na"&gt;haltOnFirstFailure=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="na"&gt;rhinoUnitUtilPath=&lt;/span&gt;&lt;span class="s"&gt;"lib/rhinoUnitUtil.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;fileset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"*.js"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/fileset&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/rhinounit&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The word count example recreated in Mongo using a Python client and passing
the map/reduce functions to the server.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pymongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pymongo.code&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;# open connection and connect to 'ddd' database&lt;/span&gt;
&lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ddd&lt;/span&gt;

&lt;span class="c1"&gt;# remove any existing data&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# insert some data&lt;/span&gt;
&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"data/peter_piper.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"text"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# load map and reduce functions&lt;/span&gt;
&lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"src/js/wordMap.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;reduce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"src/js/wordReduce.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# run the map-reduce query&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# print the results&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And it worked! I'd like to run the query on a larger result-set, but there
isn't much point on this tiny low-spec'd netbook.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/mapreduce-on-mongo</guid><pubDate>Wed, 12 May 2010 19:00:00 +0000</pubDate></item><item><title>DevEvening NoSql/MongoDB Presentation</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        DevEvening NoSql/MongoDB Presentation
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 07, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;The slides and demo I'll show in my NoSql presentation for tomorrows
DevEvenings Melbourne ORM Smackdown. I hope to take the time and write a more
considered post of my findings and opinions as I've found it very interesting.&lt;/p&gt;
&lt;p&gt;Here is a link to &lt;a href="2010_4_no_sql.odp"&gt;the slides&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First I'll use some Python and the PyMongo module to connect to MongoDB, list
databases, insert documents and get them out again.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Python 2.6.4 (r264:75706, Dec  7 2009, 18:45:15)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
&amp;gt;&amp;gt;&amp;gt; from pymongo import Connection
&amp;gt;&amp;gt;&amp;gt; connection = Connection()
&amp;gt;&amp;gt;&amp;gt; connection.database_names()
[u'files', u'working', u'demo', u'downloads', u'posts', u'local',u'admin']
&amp;gt;&amp;gt;&amp;gt; db = connection.demo
&amp;gt;&amp;gt;&amp;gt; import datetime
&amp;gt;&amp;gt;&amp;gt; db.messages.insert( { 'author' : 'tarn', 'date': datetime.now(),'message' : 'Hello Mongo' } )
ObjectId('4bbc4beec73d721445000003')
&amp;gt;&amp;gt;&amp;gt; db.messages.find_one()
{u'date': datetime.datetime(2010, 4, 7, 19, 10, 6, 355000),
 u'message': u'Hello Mongo',
 u'_id': ObjectId('4bbc4beec73d721445000003'),
 u'author': u'tarn'}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then have a look at some content I have in the database&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; db = connection.working
&amp;gt;&amp;gt;&amp;gt; db.posts.count()
106
&amp;gt;&amp;gt;&amp;gt; for post in db.posts.find()[:5]:
...     print post["date"],post["title"],"by",post["author"]
...
2010-01-25 18:06:00 Python Silverlight/Moonlight 2 Xapping by tarn
2010-03-12 13:53:00 Devevenings Presentation - IOC/Unit Testing/Mocking in ASP.NET MVC by tarn
2010-02-17 19:25:00 Revisiting Modal Binding an Interface, now with DictionaryAdapterFactory by tarn
2009-12-02 18:35:00 Creating Silverlight apps in the browser by tarn
2009-10-02 23:08:00 #.think.in infoDose #43 (11th September - 22nd September) by brodie
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;File storage using the GridFS class from the gridfs module. Show some files
and then write a file out to the file system.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from gridfs import GridFS
&amp;gt;&amp;gt;&amp;gt; fs = GridFS(connection.files)
&amp;gt;&amp;gt;&amp;gt; len(fs.list())
116
&amp;gt;&amp;gt;&amp;gt; for file in fs.list()[:5]:
...     print file
...
post/debugging-ironpython-with-my-excalibur/image.png
post/debugging-ironpython-with-my-excalibur/image_thumb.png
post/devevenings-presentation---iocunit-testingmocking-in-asp.net-mvc/20102f32fdevevening_presentation.pptx
post/devevenings-presentation---iocunit-testingmocking-in-asp.net-mvc/20102f32fguestbook.zip
post/think.in-infodose-40-5th-august---16th-august/image.png
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; with open('image.png','w') as out_file:
...     with fs.open('post/debugging-ironpython-with-my-excalibur/image.png') as in_file:
...             out_file.write(in_file.read())
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now to some C# (mono) and a controller class for a basic web application to
view the data. It serves files found in the database, but only sends bach the
correct MIME type for "image/png". Lazy.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;HomeController&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;BlogRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_blogRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_blogRepository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BlogRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ViewData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_blogRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetPosts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ViewData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_blogRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;FileStreamResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_blogRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"post/"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a very basic repository that provides the data for the demo
application. I did only enough with the C# provider to get it working and try
to disconnect my connections.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;BlogRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Mongo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;BlogRepository&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connstr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConfigurationManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppSettings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"connectionString"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connstr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GridFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;CopyStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;SeekOrigin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Begin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GetPosts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"working"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;using&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICursor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;())){&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"working"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"_id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Oid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;CopyStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;32768&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So that's where I got for my demo for the DevEvenings ORM Smackdown. No doubt
I will continue looking into MongoDB and other object/document databases.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/devevening-nosql</guid><pubDate>Wed, 07 Apr 2010 21:12:00 +0000</pubDate></item><item><title>Scraping this blog</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Scraping this blog
    &lt;/h1&gt;
    &lt;p&gt;
    Mar 18, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I have created a monster and this post is about killing it off by scraping the
contents of this blog into structured Python objects. Sometime later I will
convert the HTML content to markdown and download the images and other
resources locally.&lt;/p&gt;
&lt;p&gt;I want to put the contents into a ZODB object database to get a feel for
working with object database. A greated goal is to migrate the content a new
blog engine. I don't want to go into why I felt I need to scrape it or why I
want to migrate to another blog engine as it's depressing.&lt;/p&gt;
&lt;p&gt;Moving on, I wanted to put the content into these classes&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The scraping code is not elegant but was quite fun to write as I could write
it all from an interactive console session. I found BeautifulSoup was
fantastic in making HTML into something that was easy to work with, although I
would have liked to have used jQuery/CSS style selectors.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;BeautifulSoup&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;urllib2&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ParseComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prettify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'href'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'href'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\d*/\d*/\d* \d*.\d*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;renderContents&lt;/span&gt;&lt;span class="p"&gt;())[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;/%m/%Y %H:%M'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;comment&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ParsePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'posthead.*'&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'div'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"entry"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'div'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"descr"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'%B &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;, %Y %H:%M'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'div'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"descr"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"rel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
    &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'div'&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"commentlist"&lt;/span&gt;&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="s1"&gt;'div'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ParseComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commentSoup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;commentSoup&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;DownloadPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;postHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://blog.sharpthinking.com.au/'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;postSoup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postHtml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ParsePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postSoup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetPosts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://blog.sharpthinking.com.au/archive.aspx"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;postUrls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'href'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/post/.*'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DownloadPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;postUrls&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I'm sure there is better way, but this was better than any way I've used
previously. Anyway I've done a lot of work untangling the mess I created.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; posts = GetPost()
&amp;gt;&amp;gt;&amp;gt; for post in posts[:5]:
...     print post.date, post.title
...

2010-03-17 22:16:00 OMG. It's a JavaScript Rhino
2010-03-12 13:53:00 Devevenings Presentation - IOC/Unit Testing/Mocking in ASP.NET MVC
2010-02-20 17:18:00 Revisiting Pygments in the browser with Silverlight, now with BackgroundWorker
2010-02-17 19:25:00 Revisiting Modal Binding an Interface, now with DictionaryAdapterFactory
2010-02-16 20:34:00 Modal Binding an Interface with DynamicProxy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I wanted to put the contents into the object database tonight, but I have
pickled it to be revisited later.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/scraping-this-blog</guid><pubDate>Thu, 18 Mar 2010 23:31:00 +0000</pubDate></item><item><title>OMG. It's a JavaScript Rhino</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        OMG. It's a JavaScript Rhino
    &lt;/h1&gt;
    &lt;p&gt;
    Mar 17, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;JavaScript is a slightly flawed language but it's got elegant parts too. All
languages do to some degree, it's just JavaScript seems to have both in
extremes. Whatever you think of it, history has made it the language for
scripting the client-side web. It has become a mainstream language that shows
no sign of falling off.&lt;/p&gt;
&lt;p&gt;The excelent book &lt;a href="http://oreilly.com/catalog/9780596517748"&gt;JavaScript: The Good Parts&lt;/a&gt; by Douglas Crockford,
working with the jQuery library and learning a little Lisp has lead me to
really embrace JavaScript.&lt;/p&gt;
&lt;p&gt;It's no secret that I like programming with interactive consoles and decided I
wanted find out if there was an interactive console for JavaScript. A language
that only lives inside web browser environment didn't seem right to me.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developer.mozilla.org/en/Rhino"&gt;Rhino&lt;/a&gt; is a JavaScript implementation on the JVM. It has a compiler, a
debugger and interactive console.&lt;/p&gt;
&lt;p&gt;To get started you obviously need a version of the JVM. That's not to
difficult. On Windows I just downloaded a &lt;a href="http://java.sun.com/javase/downloads/index.jsp"&gt;Sun Java 6 installer&lt;/a&gt;. On my
Ubuntu install I installed openjdk but found Rhino didn't work. So I installed
sun-java6, which worked.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install sun-java6-bin sun-java6-jre sun-java6-jdk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find what version you've installed by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ java -version
java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03)
Java HotSpot(TM) Client VM (build 14.1-b02, mixed mode, sharing)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Excellent. The &lt;a href="https://developer.mozilla.org/en/Rhino"&gt;Rhino&lt;/a&gt; binaries includes js.jar which is needed for the
console. Now should be able to run the jar&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ java -jar js.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When all goes well this will take you into the Rhino shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Rhino 1.7 release 2 2009 03 22
js&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can start playing with the language.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;js&amp;gt; get_counter = function() { var counter = 0; return function() { 
     print(counter); counter++; } };
..
js&amp;gt; counter1 = get_counter();
js&amp;gt; counter1();
0
js&amp;gt; counter1();
1
js&amp;gt; counter2 = get_counter();
js&amp;gt; counter2()
0
js&amp;gt; counter1()
2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is cool and there is some of the weirdness&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;js&amp;gt; '5' + 3
53
js&amp;gt; '5' - 2
3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And some interesting features&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;js&amp;gt; parseInt('06')
6
js&amp;gt; parseInt('08')
NaN
js&amp;gt; parseInt('10')
10
js&amp;gt; parseInt('010')
8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'm looking forward to learning more about writing code in JavaScript.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/omg--its-a-javascript-rhino</guid><pubDate>Wed, 17 Mar 2010 22:16:00 +0000</pubDate></item><item><title>Devevenings Presentation - IOC/Unit Testing/Mocking in ASP.NET MVC</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Devevenings Presentation - IOC/Unit Testing/Mocking in ASP.NET MVC
    &lt;/h1&gt;
    &lt;p&gt;
    Mar 12, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Here are the slides and the mysterious code that was never shown from my
&lt;a href="http://www.deveve.net/"&gt;DevEvening&lt;/a&gt; presentation.&lt;/p&gt;
&lt;p&gt;Appologies it's taken a while to get them up, I was hoping to write a bit of a
post about what I covered and some of the discussion that came up. That never
happened.&lt;/p&gt;
&lt;p&gt;&lt;a href="2010_3_Devevenings_Presentation.pptx"&gt;Devevening_Presentation.pptx (565.68 kb)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="2010_3_Guestbook.zip"&gt;Guestbook.zip (3.42 mb)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I think the presentation went well, the group was really good and we got some
good discussions happening before being interrupted by delicious paramas of
the world.&lt;/p&gt;
&lt;p&gt;I'm looking forward to the next meeting, except it appears I've signed up to
represent NoSql (of which I currently know very little about) in an ORM
smackdown.&lt;/p&gt;
&lt;p&gt;That's what happens when you have meetings at a pub. Anyway it should fun and
I'm looking forward to learning enough about NoSql to adequatly represent it
in the smackdown.&lt;/p&gt;
&lt;p&gt;See ya there.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/devevenings-presentation--ioc</guid><pubDate>Fri, 12 Mar 2010 13:53:00 +0000</pubDate></item><item><title>Revisiting Pygments in the browser with Silverlight, now with BackgroundWorker</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Revisiting Pygments in the browser with Silverlight, now with BackgroundWorker
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 20, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;A couple of week ago I &lt;a href="http://blog.sharpthinking.com.au/post/2010/01/31/Pygments-in-the-browser-with-Silverlight.aspx"&gt;blogged&lt;/a&gt; about using &lt;a href="http://www.google.com.au/url?sa=t&amp;amp;source=web&amp;amp;ct=res&amp;amp;cd=1&amp;amp;ved=0CAgQFjAA&amp;amp;url=http%3A%2F%2Fpygments.org%2F&amp;amp;ei=8IV_S8e6CIHUsgPtofz1Aw&amp;amp;usg=AFQjCNGVIxeCqQ14mGgLNznFat5nyACb5Q"&gt;Pygments&lt;/a&gt; to do live
syntax highlighting in the browser using Silverlight.&lt;/p&gt;
&lt;p&gt;A major problem with the sample was that it did the pygmentizing on the UI
thread which caused most browsers to become unresponsive. Today I wanted to
fix that by using the &lt;a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker%28VS.95%29.aspx"&gt;BackgroundWorker&lt;/a&gt; to do the pygmentizing in a
background thread.&lt;/p&gt;
&lt;p&gt;Firstly I refactored the pygmentizing into a method that didn't interact with
the UI.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pygmentize_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="c1"&gt;# attempt to pygmentize input with current language&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pygments&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;highlight&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pygments.lexers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_lexer_by_name&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pygments.formatters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HtmlFormatter&lt;/span&gt;

        &lt;span class="n"&gt;lexer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_lexer_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stripall&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HtmlFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linenos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cssclass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;markup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;markup&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Error Generating Markup"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I then added a method that could be passed into a DoWorkEventHandler. It gets
it arguments as a tuple from the event arguments and then sets the event
argument result with the marked up HTML. The lack of explicit typing and use
of tuples is good example of how some python idioms can be used when working
with the .NET framework.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="c1"&gt;# do work off UI thread.&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pygmentize_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Argument&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Argument&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The required BackgroundWorker and DoWorkEventHandler can be simply imported
from the System.ComponentModel namespace.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BackgroundWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DoWorkEventHandler&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The BackgroundWorker can then be setup and started. Again it's syntactically
nice how the tuple can be created and passed as a RunWorkerAsync parameter.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_pygmentize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="c1"&gt;# update application state&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pygmentizing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pygmentizing.."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# get paremters&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="c1"&gt;# setup background worker&lt;/span&gt;
    &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BackgroundWorker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DoWork&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;DoWorkEventHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunWorkerCompleted&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;complete&lt;/span&gt;

    &lt;span class="c1"&gt;# start the worker&lt;/span&gt;
    &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunWorkerAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The completed event handler is the responsible for taking the markup generated
by the BackgroundWorker and updating the DOM. It also fires off another worker
if the source has changed since the last worker started.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="c1"&gt;# handle errors/exceptions in worker&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"innerHTML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="c1"&gt;# show the result&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"innerHTML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_changed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="c1"&gt;# input has changed, starty pygmentize again&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_pygmentize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

        &lt;span class="c1"&gt;# no work queued&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pygmentizing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hide_message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The update has made the sample much more responsive, however it appears
downloading the Silverlight application is still causing some browers to
become a little unresponsive which is annoying. I will be interested to find
out if this effect can be mitigated.&lt;/p&gt;
&lt;p&gt;The actually pygmentizing processing could possibly be made a little faster by
reusing the BackgroundWorker and only doing the Pygment imports once but the
responsiveness of the browser has improved the sample enormously.&lt;/p&gt;
&lt;p&gt;Check out the updated demo &lt;a href="http://markdown-madness.appspot.com/silverlight-pygments"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/revisiting-pygments-in-the-browser-with-silverlight--now-with-backgroundworker</guid><pubDate>Sat, 20 Feb 2010 17:18:00 +0000</pubDate></item><item><title>Revisiting Modal Binding an Interface, now with DictionaryAdapterFactory</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Revisiting Modal Binding an Interface, now with DictionaryAdapterFactory
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 17, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;After &lt;a href="http://www.sharpthinking.com.au/post/2010/02/16/ModalBinding-an-Interface-with-DynamicProxy.aspx"&gt;my post&lt;/a&gt; yesterday &lt;a href="http://kozmic.pl/"&gt;Krzysztof Kozmic&lt;/a&gt; (&lt;a href="http://twitter.com/kkozmic"&gt;@kkozmic&lt;/a&gt;) &lt;a href="http://twitter.com/kkozmic/statuses/9191658727"&gt;messaged
me&lt;/a&gt; saying I should use the &lt;a href="http://www.castleproject.org/components/dictionaryadapter/basics.html"&gt;Castle DictionaryAdapterFactory&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;For the record Krzysztof has a &lt;a href="http://kozmic.pl/archive/2008/12/16/castle-dynamicproxy-tutorial-part-i-introduction.aspx"&gt;great series of posts on DynamicProxy&lt;/a&gt; and
was also very helpful &lt;a href="http://twitter.com/kkozmic/statuses/9134188880"&gt;answering&lt;/a&gt; a specific &lt;a href="http://twitter.com/tarnacious/status/9134151576"&gt;question of mine&lt;/a&gt;
yesterday.&lt;/p&gt;
&lt;p&gt;Anyway this is great news! I can do the same thing only referencing &lt;a href="https://sourceforge.net/projects/castleproject/files/DictionaryAdapter%20Component/1.1/CastleDictionaryAdapter-1.1.0.zip/download"&gt;one
assembly&lt;/a&gt;. The DictionaryAdapterFactory returns an object that passes all
my tests from yesterday and slides staight into the model binder.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeInterfaceModelBinder&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DefaultModelBinder&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt; &lt;span class="n"&gt;controllerContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelBindingContext&lt;/span&gt; &lt;span class="n"&gt;bindingContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;modelType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DictionaryAdapterFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It was fun playing with DynamicProxy yesterday, but I did &lt;a href="http://twitter.com/tarnacious/status/9180071375"&gt;think&lt;/a&gt; it might
not have been a good idea for a model binder. I'm glad I was pointed in the
right direction.&lt;/p&gt;
&lt;p&gt;Cheers Krzysztof!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/revisiting-modal-binding-an-interface--now-with-dictionaryadapterfactory</guid><pubDate>Wed, 17 Feb 2010 19:25:00 +0000</pubDate></item><item><title>Modal Binding an Interface with DynamicProxy</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Modal Binding an Interface with DynamicProxy
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 16, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;strong&gt;Update: I posted an &lt;a href="http://www.sharpthinking.com.au/post/2010/02/17/Revisting-Modal-Binding-an-Interface-now-with-DictionaryAdapterFactorye2808f.aspx"&gt;update to this post&lt;/a&gt; which uses the Castle
DictionaryAdapterFactory after getting some feedback from this post.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This started while I was playing with some ASP.NET MVC architectural ideas in
simple application.&lt;/p&gt;
&lt;p&gt;I wanted to less code than other projects I'd worked on but still have it
decoupled and testable.&lt;/p&gt;
&lt;p&gt;At some point I started ranting on twitter (&lt;a href="http://twitter.com/tarnacious/status/9090317966"&gt;1&lt;/a&gt;, &lt;a href="http://twitter.com/tarnacious/status/9090453396"&gt;2&lt;/a&gt;, &lt;a href="http://twitter.com/tarnacious/status/9090509896"&gt;3&lt;/a&gt;) about
wanting a Model Binder that could bind to an interface without needing a
concrete type.&lt;/p&gt;
&lt;p&gt;It was quickly &lt;a href="http://twitter.com/craigtech/statuses/9091802953"&gt;pointed out&lt;/a&gt; that what I &lt;em&gt;probably&lt;/em&gt; wanted was a Model
Binder that resolves an implementation at runtime with IOC.&lt;/p&gt;
&lt;p&gt;While this was &lt;em&gt;probably&lt;/em&gt; true as I was using already using StuctureMap and it
did seemed like a much better idea I still some &lt;a href="http://twitter.com/tarnacious/status/9092355362"&gt;my reservations&lt;/a&gt;. But
mainly &lt;a href="http://twitter.com/tarnacious/status/9133991635"&gt;I wanted to try&lt;/a&gt; using the &lt;a href="http://www.castleproject.org/dynamicproxy/index.html"&gt;Castle DynamicProxy&lt;/a&gt; in an
application.&lt;/p&gt;
&lt;p&gt;Clearly the reason the default model binder can't bind to an interface is
because it can't create an instance of it. Thanks to some nice extensibility
hooks in the default model binder, all I had to do was override the
CreateModel method and return my DynamicProxy instance. The default model
binder then could do the rest of binding work as normal.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;FakeInterfaceModelBinder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DefaultModelBinder&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;controllerContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ModelBindingContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bindingContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modelType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ProxyGenerator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInterfaceProxyWithoutTarget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FakePropertiesInterceptor&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What I needed then was a DynamicProxy object that had properties the behaved
like you'd &lt;em&gt;expect&lt;/em&gt; properties to on a normal class. I came up with some test
scenarios I'd expect the proxy to pass using a test interface.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IDynamicProxyTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I figured the Model Binder would set values on the Dynamic Proxy object and
the controller would get the properties back to validate and pass them to the
data layer or something like that. I came up with these tests.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;[Test]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;If_date_has_been_set_then_return_it&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="na"&gt;[Test]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;If_string_has_been_set_then_return_it&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello Proxy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello Proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="na"&gt;[Test]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;If_int_has_been_set_then_return_it&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After implementing these I was &lt;a href="http://twitter.com/tarnacious/status/9133692420"&gt;surprised&lt;/a&gt; to find the default model binder
did some gets on the properties before sets. So I added some more tests.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;[Test]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;For_date_instance_if_value_not_set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="na"&gt;[Test]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;For_int_instance_if_value_not_set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="na"&gt;[Test]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;For_string_instance_if_value_not_set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateProxy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;My property interceptor isn't as clean as I'd like it but did the job passing
tests.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ProperyInterceptor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IInterceptor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ProperyInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IInvocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsSetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsGetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReturnValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;GetDefaultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetDefaultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IInvocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReturnType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReturnValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReturnValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Activator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReturnType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IInvocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;IsSetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IInvocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSpecialName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"set_"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ordinal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;IsGetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IInvocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSpecialName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"get_"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ordinal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I used the &lt;a href="http://mokhan.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx"&gt;better model binder&lt;/a&gt; Jimmy Bogard discusses as it makes
writing binders for derived types and specific "types of type" more straight
forward.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;InterfaceBinder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FakeInterfaceModelBinder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IFilteredModelBinder&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;IsMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modelType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modelType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I did try writing a test which ran my model binder, but found it too much
trouble. When I tried it out in a sample web project it worked exactly as I
expected.&lt;/p&gt;
&lt;p&gt;While this was fun I don't actually think it's a very good idea. I was
thinking about the interface as DTO's and it breaks down if you want any
additional methods on the interface, as they just won't work. Anyway it's been
fun as always and maybe I'll find a good place to use these dynamic proxies in
the future.&lt;/p&gt;
&lt;p&gt;Source code with the sample website is all &lt;a href="http://static.sharpthinking.com.au/2010/DynamicProxyModelBinder.zip"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/modal-binding-an-interface-with-dynamicproxy</guid><pubDate>Tue, 16 Feb 2010 20:34:00 +0000</pubDate></item><item><title>Scripting your Data Model</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Scripting your Data Model
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 14, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I really like the way you can script your data model from a python REPL
console on the Django and the Google App Engine web frameworks.&lt;/p&gt;
&lt;p&gt;For me it is a hands down &lt;a href="http://twitter.com/tarnacious/status/8841617430"&gt;better way&lt;/a&gt; of working with data in your domain
model than writing SQL.&lt;/p&gt;
&lt;p&gt;I've since wanted to &lt;a href="http://twitter.com/tarnacious/status/8841455390"&gt;do it in .NET projects&lt;/a&gt; I work on, but it wasn't till
I was playing with Castle ActiveRecord yesterday that I decided I'd try it
out.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://martinfowler.com/eaaCatalog/activeRecord.html"&gt;ActiveRecord pattern&lt;/a&gt; is an intuitive way of programming with data
persistence, so it's also nice to script with.&lt;/p&gt;
&lt;p&gt;It turned out to be really easy in the project I was playing with. I didn't
need to write a single additional line of C# as I already had the
configuration decoupled for integration testing with an in-memory SQLite
database.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;SimpleBlog.Data&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IBootstrapperTask&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetDefaultSettings&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;InPlaceConfigurationSource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InPlaceConfigurationSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActiveRecordBase&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;ActiveRecordStarter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;ActiveRecordStarter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterAssemblies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I then just wrote this little script to help with the configuration, it uses
the static method above and passes in the properties for working with a
development database.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;clr&lt;/span&gt;

&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReferenceToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SimpleBlog.Data.dll"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;SimpleBlog.Data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;

&lt;span class="c1"&gt;# NHibinate Setting&lt;/span&gt;
&lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"connection.driver_class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="s2"&gt;"NHibernate.Driver.SqlClientDriver"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"dialect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s2"&gt;"NHibernate.Dialect.MsSql2005Dialect"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"connection.provider"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s2"&gt;"NHibernate.Connection.DriverConnectionProvider"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"connection.connection_string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s2"&gt;"Data Source=[CONNECTION_STRING]"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;# Add&lt;/span&gt;

&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"proxyfactory.factory_class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s2"&gt;"NHibernate.ByteCode.Castle.ProxyFactoryFactory,NHibernate.ByteCode.Castle"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using the helper script it's pretty easy to get in and start working with the
data in the data model.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;IronPython 2.6 (2.6.10920.0) on .NET 2.0.50727.4927
Type "help", "copyright", "credits" or "license" for more information.
&amp;gt;&amp;gt;&amp;gt; import ActiveRecord
&amp;gt;&amp;gt;&amp;gt; from SimpleBlog.Data.Models import *
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; post = Post()
&amp;gt;&amp;gt;&amp;gt; post.Title = "Working with SQL sucks!"
&amp;gt;&amp;gt;&amp;gt; post.Content = "Try using a scripting langauge instead. It rocks!"
&amp;gt;&amp;gt;&amp;gt; post.Author = "tarn"
&amp;gt;&amp;gt;&amp;gt; post.Save()
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; post.Id

2

&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; posts = Post().FindAll()
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; for p in posts:
...     print p.Title, "by", p.Author
...

Hey, It's alive by tarn
Working with SQL sucks! by tarn

&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a simple example of a database agnostic data script using your domain
model and a powerful scripting language. I think scripting data models like
this could add a lot of value in many .NET development scenarios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/scripting-your-data-model</guid><pubDate>Sun, 14 Feb 2010 14:13:00 +0000</pubDate></item><item><title>Pygments in the browser with Silverlight</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Pygments in the browser with Silverlight
    &lt;/h1&gt;
    &lt;p&gt;
    Jan 31, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;** 17-02-2010 I've updated the demo to use the BackgroundWorker and &lt;a href="http://blog.sharpthinking.com.au/post/2010/02/20/Revisiting-Pygments-in-the-browser-with-Silverlight-now-with-BackgroundWorker.aspx"&gt;posted
about the update&lt;/a&gt; **&lt;/p&gt;
&lt;p&gt;I decided it might be fun to try get &lt;a href="http://www.freewisdom.org/projects/python-markdown/"&gt;Python Markdown&lt;/a&gt; and &lt;a href="http://pygments.org/"&gt;Pygments&lt;/a&gt;
running in the browser to enhance a markdown preview experience by eliminating
the server-side round trips and provide a more responsive preview.&lt;/p&gt;
&lt;p&gt;I managed to get it working entirely in Python but I found the application
size excessively large (almost 3mb) and, more annoyingly, it seems to block
the entire browser when it initially loads the pygments module. I think there
is some sort of silverlight background thread I should be using.&lt;/p&gt;
&lt;p&gt;I think it would work better with &lt;a href="http://code.google.com/p/markdownsharp/"&gt;MarkdownSharp&lt;/a&gt; as pure C# silverlight
applications are a fair bit leaner in size and probably run a little quicker
than dynamic language applications. But this was for fun and I prefer coding
in Python when not working.&lt;/p&gt;
&lt;p&gt;** 17-02-2010 I've since found the MarkdownSharp doesn't do syntax
hightlighting **&lt;/p&gt;
&lt;p&gt;I ended up having to write so little Python code to get this working that I
can include it all here, syntax highlighted with Pygments of course.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from System.Windows import Application
from System.Windows.Controls import UserControl
from System.Windows.Browser import HtmlPage
from System import EventHandler

class App:

    def __init__(self):

        # load relevent HTML DOM elements
        self.input = HtmlPage.Document.GetElementById("input")
        self.source = HtmlPage.Document.GetElementById("output")
        self.language = HtmlPage.Document.GetElementById("lang")

        # fire javascript functions to indicate the application has been load
        HtmlPage.Window.CreateInstance("silverlight_loaded");

        # pygmentize initial
        self.pygmentize()

        # register events
        self.input.AttachEvent('onkeyup', EventHandler(self.update_handler ))
        self.language.AttachEvent('onchange', EventHandler(self.update_handler ))

        # fire javascript function to indicated the pygments has been loaded
        HtmlPage.Window.CreateInstance("pygments_loaded");


    # handle language or input changes by pygmentizing
    def update_handler(self, sender, e):
        self.pygmentize()


    def pygmentize(self):
        input = self.input.GetProperty("value")

        # attempt to pygmentize input with current language
        try:
            from pygments import highlight
            from pygments.lexers import get_lexer_by_name
            from pygments.formatters import HtmlFormatter

            lexer = get_lexer_by_name(self.language.value, stripall=True)
            formatter = HtmlFormatter(linenos=False, cssclass="source")
            markup = highlight(input, lexer, formatter)

            # update the preview
            self.source.SetProperty("innerHTML",markup)

        except:

            # indicate there was an error in pygmentize
            self.source.SetProperty("innerHTML", "Error Generating Markup")


# Do it!
App()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Despite the fact that there isn't much code, the development experience
writing silverlight application in python is a bit of a pain in the arse.
Granted it's much better with the python console in the browser and better
error reporting in most recent SDK, but it still sucks; debugging and logging
support is very limited and on some errors the application dies without
reporting anything.&lt;/p&gt;
&lt;p&gt;Another difficulty is having to manually copy all the python standard library
modules required by the module from the library folders into the application
(which explains something about the bloated application size). And even though
the code in the demo works, some very similar code from the pygments quick
start doesn't.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href="http://markdown-madness.appspot.com/silverlight-pygments"&gt;live demo&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/pygments-in-the-browser-with-silverlight</guid><pubDate>Sun, 31 Jan 2010 23:40:00 +0000</pubDate></item><item><title>Python Silverlight/Moonlight 2 Xapping</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Python Silverlight/Moonlight 2 Xapping
    &lt;/h1&gt;
    &lt;p&gt;
    Jan 25, 2010
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;h2&gt;Context&lt;/h2&gt;
&lt;p&gt;I was asked do a presentation about developing Silverlight applications to the
&lt;a href="http://wiki.python.org/moin/MelbournePUG"&gt;Melbourne Python User-group&lt;/a&gt;. Hell yeah, I love participating in user
groups but I obviously needed some content. Unfortunately I didn't know this
local user-group existed, so I've haven't been and have little idea of number
of people or the type of stuff they do. This has made preparing relevant
content a little challenging, but hopefully I can find the right mix.&lt;/p&gt;
&lt;p&gt;Given it's a Python user-group I felt I might look like a bit of dick rolling
with Windows and doing a demo with an environment and .NET tools that some
part of the group couldn't/don't use. Besides I've always felt the Python
language support and being able to build on any platform is a very compelling
reason for non-.NET developers get into building Silverlight applications.&lt;/p&gt;
&lt;p&gt;I don't yet have a Mac (I'm really starting to want one) so my other option
was to do the demo on Linux. This presented some difficulties and some
opportunities. Firstly I'm still a bit of Linux noob and, although I'm wrapped
to have learned a lot in the last couple of weeks there has been some pain and
some compromise.&lt;/p&gt;
&lt;p&gt;Also Microsoft don't have a Silverlight browser plug-in for Linux, that
support is provided by &lt;a href="http://www.mono-project.com/Moonlight"&gt;Moonlight&lt;/a&gt;. Moonlight is currently at version 2 and
although it supports some of the features of Silverlight 3 it doesn't have the
key version 3 features which have really improved the Python development
environment (namely IronPython 2.6 support) and application deployment (namely
allowing static assemblies that add dynamic languages support to be downloaded
independently of the application and therefore cached in the browser).&lt;/p&gt;
&lt;p&gt;Plugin and development support on both Mac and Windows is much better, but I'm
happy here sacrificing some of the newer features for inclusivity (and
apparently inventing words). We're targeting Silverlight 2/Moonlight 2 and
effectively using no development tools besides a couple of Python scripts
which I'll get onto.&lt;/p&gt;
&lt;h2&gt;Dynamic Language Silverlight Applications&lt;/h2&gt;
&lt;p&gt;Silverlight applications are packaged into binary files with an "xap"
extension. It turns out these packages are just standard zip files containing
all the "stuff" needed by the application. The minimum "stuff" needed in
dynamic language application is;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;An XML based application manifest file (app.manifest) that describes the
target runtime, language type, entry point and some other information needed
to run the application&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A collection of assemblies that add dynamic language and Python support to
the Silverlight runtime.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A plain text file containing the your Python script to make the
application dance.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The package can contain more Python scripts, binary assemblies (which can be
imported as modules) and XAML files (which are declarative xml type format
typically used for describing user interfaces). Resources such as images and
video can also be included in the package but it usually make more sense for
the application to download them independently when they are required.&lt;/p&gt;
&lt;p&gt;Serving the application on a webpage is also quite straight forward. An object
tag in the html has attributes to tell the browser the URL to download the
application and that the Silverlight/Moonlight plugin should be used to run
the application.&lt;/p&gt;
&lt;h2&gt;Helper Scripts and Templates&lt;/h2&gt;
&lt;p&gt;With a couple of templates and Python scripts we can get started developing
Silverlight applications in any environment with that has Python and a browser
with a Silverlight/Moonlight plug-in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;xap.py&lt;/strong&gt; builds xaps by adding all the files from a source code directory
and an assemblies directory into xap file and writing it to disk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;http_server.py&lt;/strong&gt; is the simplest python server which just serves all the
files in the current directory on a specified port.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt; contains the object tag referencing sample.xap and a tiny bit
of Javascript to catch, format and display critical error messages that may be
caused by the application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;app.manifest&lt;/strong&gt; is a template with everything needed to describe a typical
Python Silverlight application. The template should be fine as-is unless
additional assemblies are required or you want to change the script entry
point. The template is surprisingly small and undaunting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;app.py&lt;/strong&gt; is the script entry point of the application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;assemblies&lt;/strong&gt; a folder with all the additional assemblies need by the
Silverlight runtime to run Python scripts.&lt;/p&gt;
&lt;h2&gt;Packaging and Running an Application&lt;/h2&gt;
&lt;p&gt;So you've skipped the previous three sections and jumped straight here. I
don't blame you. Here's how to get going with the script and templates
described above.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="http://sharpthinking.com.au/file.axd?file=2010%2f1%2fsample_silverlight.zip"&gt;this zip&lt;/a&gt; containing the assemblies, scripts and templates and
unzip it someone, anywhere.&lt;/p&gt;
&lt;p&gt;From the terminal/console/command-line go the the place you unzipped it to and
run the following command to build an xap.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python xap.py samples.xap assemblies source
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A sample.xap should be generated in the same directory.&lt;/p&gt;
&lt;p&gt;To fire-up the basic web server just run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python http_server.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then just fire up a browsers and go to http://localhost:8000/ and you should
see the sample Silverlight application running.&lt;/p&gt;
&lt;p&gt;That's it. Now you can edit app.py to add functionality and repeat.&lt;/p&gt;
&lt;h2&gt;Going further&lt;/h2&gt;
&lt;p&gt;Clearly this a very bare bones approach to development which has very limited
development and debugging tools (and a server that can only serve files).
There is more tooling out there, but surprisingly litte for Python developers.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://monodevelop.com/Download/What%27s_new_in_MonoDevelop_2.2"&gt;MonoDevelop 2.2&lt;/a&gt;, the Moonlight SKD and Python bindings may provide some
sort of IDE support but it's only just been released, it is not easy to get up
and running and I not sure if it even supports packaging or debugging of
dynamic language Silverlight applications.&lt;/p&gt;
&lt;p&gt;You might think Visual Studio 2008/2010 would provide significant additional
support, but as far as I'm aware only the Windows Debugger which is free in
the Windows SDK is much help.&lt;/p&gt;
&lt;p&gt;Chiron is a .NET tool provided in the &lt;a href="http://silverlight.net/learn/dynamic-languages/"&gt;Microsoft Dynamic Language SDK&lt;/a&gt;
which can package and server XAP's much like I've described above. I found I
could compile it on Mono, but couldn't get it serving files. I don't think
your missing much here.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.kaxaml.com/"&gt;XamlPad&lt;/a&gt; is my favourite free XAML editor, but it doesn't work on Linux.
Blend and Expression Studio are pay to play Microsoft tools for designing and
developing XAML. Maybe someone should write a good one in Silverlight.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.ironpython.com/"&gt;IronPython&lt;/a&gt; can be downloaded and run on Mono.&lt;/p&gt;
&lt;h2&gt;Some Thoughts&lt;/h2&gt;
&lt;p&gt;It's easy to get started writing and deploying Silverlight applications in
Python on any platform but I think it's fair to say the tooling and support is
just not there yet. On any platform.&lt;/p&gt;
&lt;p&gt;The Silverlight 2/Moonlight implementations of Python are still missing large
parts of the Python standard library, which will come as a bit of a shock to
Python developers and makes it difficult to import modules that depend on it.
Almost the entire Silverlight 2 Base Class Library is accessible through
Python but although the Python/.NET integratation is very impressive, working
with the BCL does seem to limit the expressivness of the code.&lt;/p&gt;
&lt;p&gt;Silverlight 3 appears to have some additional built-in support for interacting
with the application at runtime through a Python REPL console, it supports
IronPython 2.6 (which implements a lot more of the Python standard library)
and has additional deployment improvements discussed above.&lt;/p&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;I hope to do another post before the meeting with some examples I'll be
showing, but as always I won't promise anything (in case I get distracted
writing a Socket Server in Erlang I've been thinking about). In the mean time
I do have some examples on PythonSilverScripting that can be run from the
browser.&lt;/p&gt;
&lt;p&gt;I'd love to know the tools other people are using to develop
Python/Silverlight and thoughts people have of it. If your in Melbourne and
your at all interested you should come down to the &lt;a href="http://wiki.python.org/moin/MelbournePUG"&gt;meeting&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update 26/01/10: Updated scripts and templates to work in Windows&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is a &lt;a href="http://sharpthinking.com.au/file.axd?file=2010%2f1%2fsample_silverlight.zip"&gt;zip&lt;/a&gt; with the bits and pieces.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/python-silverlight</guid><pubDate>Mon, 25 Jan 2010 18:06:00 +0000</pubDate></item><item><title>Python and Farseer: 2d Physics in Silverlight</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Python and Farseer: 2d Physics in Silverlight
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 19, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Ive got a tiny Acer Aspire netbook I use while commuting to work. Its a very
low specd machine but it has a very functional keyboard and I love programming
on it!&lt;/p&gt;
&lt;p&gt;I think I enjoy programming on it because it &lt;em&gt;doesnt&lt;/em&gt; have Visual Studios
installed. I have the &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=e6e1c3df-a74f-4207-8586-711ebe331cdc&amp;amp;displaylang=en"&gt;Windows SDK&lt;/a&gt; so I can build .NET projects with
MsBuild, but I mainly use it for developing Python code. With some free tools
and &lt;a href="http://notepad-plus.sourceforge.net/uk/site.htm"&gt;notepad++&lt;/a&gt; &lt;a href="http://sourceforge.net/projects/npp-plugins/"&gt;plug-ins&lt;/a&gt; its a great light weight environment for
coding simple Django and Google App Engine web applications and dynamic
language .NET applications.&lt;/p&gt;
&lt;p&gt;Im not really into computer gaming, but I do like 2d physics games. I remember
losing days work when I first found &lt;a href="http://magic.pen.fizzlebot.com/"&gt;Magic Pen&lt;/a&gt;. I decided to try using the
cool &lt;a href="http://www.codeplex.com/FarseerPhysics"&gt;Farseer Physics Engine&lt;/a&gt;, which is described on the Codeplex page as;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Farseer Physics Engine is an easy to use 2D physics engine designed for
Microsofts &lt;strong&gt;XNA&lt;/strong&gt; and &lt;strong&gt;Silverlight&lt;/strong&gt; platforms. The Farseer Physics Engine
focuses on simplicity, useful features, and enabling the creation of fun,
dynamic games.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;object data="data:application/x-silverlight," height="300" type="application/x-silverlight-2" width="500"&gt;
&lt;param name="source" value="physics.xap"/&gt;
&lt;param name="background" value="white"/&gt;
&lt;param name="windowless" value="true"/&gt;
  Unable to Microsoft Silverlight application.
  &lt;a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"&gt;
  Get Microsoft Silverlight
  &lt;/a&gt;
&lt;/object&gt;
&lt;iframe style="visibility:hidden;height:0;width:0;border:0px"&gt;&lt;/iframe&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;The Farseer engine is written in C# and I found once Id installed the
&lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=9442b0f2-7465-417a-88f3-5e7b5409e9dd&amp;amp;displaylang=en"&gt;Silverlight SDK&lt;/a&gt; I could use MSBuild to build the Farseer engine that
targets the Silverlight runtime. The Farseer engine doesnt itself reference
anything Silverlight specific, but it does need to be built against the
limited .NET framework available in Silverlight.&lt;/p&gt;
&lt;p&gt;Ive &lt;a href="http://blog.sharpthinking.com.au/post/2009/01/26/Silverlight-Dynamic-Languages-SDK.aspx"&gt;previously blogged&lt;/a&gt; about writing dynamic language Silverlight
applications; the &lt;a href="http://sdlsdk.codeplex.com/(http://sdlsdk.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=25120)"&gt;Silverlight Dynamic Languages SDK&lt;/a&gt; contains the
Silverlight IronPython assemblies and a command line application called
Chiron. Chiron is a web server that runs from the command line and dynamically
packages up XAPs as they are requested.&lt;/p&gt;
&lt;h2&gt;A little about XAPs&lt;/h2&gt;
&lt;p&gt;XAPs are packaged Silverlight applications that are downloaded and run on the
clients Silverlight runtime. It turns out the XAP is just zip files containing
the assemblies, resources and in the case of dynamic language applications,
some plain text source code!&lt;/p&gt;
&lt;p&gt;Additionally there is an AppManifest.xaml included in the XAP which tells
Silverlight about what is included in the XAP and where to find an entry
point. Below is an example of an AppManifest.xaml automatically generated by
Chiron.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            RuntimeVersion="2.0.31005.0"
            EntryPointAssembly="Microsoft.Scripting.Silverlight"
            EntryPointType="Microsoft.Scripting.Silverlight.DynamicApplication"&amp;gt;
  &amp;lt;Deployment.Parts&amp;gt;
    &amp;lt;AssemblyPart Source="Microsoft.Scripting.Silverlight.dll" /&amp;gt;
    &amp;lt;AssemblyPart Source="Microsoft.Scripting.ExtensionAttribute.dll" /&amp;gt;
    &amp;lt;AssemblyPart Source="Microsoft.Scripting.Core.dll" /&amp;gt;
    &amp;lt;AssemblyPart Source="Microsoft.Scripting.dll" /&amp;gt;
    &amp;lt;AssemblyPart Source="IronPython.dll" /&amp;gt;
    &amp;lt;AssemblyPart Source="IronPython.Modules.dll" /&amp;gt;
    &amp;lt;AssemblyPart x:Name="FarseerPhysics" Source="bin/FarseerPhysics.dll"/&amp;gt;
  &amp;lt;/Deployment.Parts&amp;gt;
&amp;lt;/Deployment&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would probably be more interesting compared to a non dynamic language
manifest file, but here are a few things I found interesting about it;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;There is a whole lot of additional referenced assemblies required to
compile Pyhton code in the Silverlight client, and yes, they are included in
the XAP sent to the client.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The FarseerPhysics assembly is also included the XAP and is referenced in
the manifest.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The EntryPointType is really the only difference between XAP manifest
files generated for statically typed languages. For dynamic languages there
are additional assemblies that compile the dynamic code, there doesnt seem any
reason you couldnt feasibly write EntryPoint assemblies that interpreted &lt;a href="http://lolcode.com/"&gt;lol-
cat&lt;/a&gt; or &lt;a href="http://www.muppetlabs.com/~breadbox/bf/"&gt;brainfuck&lt;/a&gt; code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And, Im still targeting Silverlight 2! (Just havent got round to
upgrading..)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Writing an application&lt;/h2&gt;
&lt;p&gt;I started by having a play with the Farseer engine by writing some IronPython
scripts and running them from the console. As its just a physics engine, it
can be coupled with any user interface, even a console.&lt;/p&gt;
&lt;p&gt;When I tried to get the same script working in Silverlight I did encounter
some difficulties referencing the Farseer assembly. I had to use the
assemblies strong name, which I found using &lt;a href="http://www.red-gate.com/products/reflector/"&gt;Reflector&lt;/a&gt;, but seems
unnecessary given it was sent in the same XAP as the code referencing it!&lt;/p&gt;
&lt;p&gt;Instead of just hacking this demo together in one giant script I decided to
structure my code a little, including separating the view and the simulation
model over different namespaces. Generally Im learning heaps about what is
pythonic, what I carry over from C/C++/C# and what is good software design.&lt;/p&gt;
&lt;p&gt;Im not entirely happy with my view classes, which are just wrappers around
Silverlight controls. Even though the Silverlight control can be accessed
through a property, I feel this isnt the elegant solution I should be able to
build in a dynamic language. It seemed more right for the physics wrappers,
but still overly verbose.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I limited the example for this post to a very simple, but interactive 2d
physics demo. Clicking any of the objects in the simulation creates a spring
between the object and the mouse cursor until the mouse click is released or
leaves the simulation view area.&lt;/p&gt;
&lt;p&gt;The Farseer physics engine is a lot of fun to use, its easy to get into and
you can do some really cool stuff with it.&lt;/p&gt;
&lt;p&gt;The &lt;a href="physics.xap"&gt;source code&lt;/a&gt; is sent as plain text and compiled (or interpreted) on
client. As the XAP is just a normal zip file, it can unzipped by almost any
zip tool. Inside is all the plain text source code, compiled assemblies and
manifest file.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="image_thumb.png"/&gt;&lt;/p&gt;
&lt;p&gt;Its awesome to be able to develop Silverlight applications with physics
libraries using a high level language and light weight development
environment. Happy hacking..&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/python-and-farseer-2d-physics-in-silverlight</guid><pubDate>Sun, 19 Jul 2009 16:59:00 +0000</pubDate></item><item><title>NGourd BDD for WPF Applications</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        NGourd BDD for WPF Applications
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 08, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I checked out &lt;a href="http://code.google.com/p/ngourd/"&gt;NGourd&lt;/a&gt;, a project to build a &lt;a href="http://en.wikipedia.org/wiki/Behavior_Driven_Development"&gt;BDD&lt;/a&gt; tool based on
&lt;a href="http://cukes.info/"&gt;Cucumber&lt;/a&gt; which &lt;a href="http://wolfbyte-net.blogspot.com/"&gt;Michael Minutillo&lt;/a&gt; has started. Id never previously
used or even heard of Cucumber, but as I read on I liked a lot about it. Its
an agile development tool and it has a colourful console!&lt;/p&gt;
&lt;p&gt;The concept is illustrated beautifully at &lt;a href="http://cukes.info/" title="http://cukes.info/"&gt;http://cukes.info/&lt;/a&gt;, but here is
my take anyway. Behavior is described in the form of a series of plain text
steps. The steps are then implemented as parameterized test methods. Using
some reflection and regular expressions the plain text steps are mapped to
corresponding implementations and run as red/green light tests.&lt;/p&gt;
&lt;p&gt;I started wondering if WPF applications could be tested with NGourd. Theres a
great IronPython sample in which a WPF application is started in a new thread
and built dynamically from the IronPython interactive console.&lt;/p&gt;
&lt;p&gt;Could something like this could be done in NGourd steps?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Scenario: Can Perform basic arithmetic

Given I start the application
When I enter 2 + 2
And I click GO
Then I should see 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using a thread to run the application and a dispatcher to invoke methods on
it I had a crude framework to write the steps to make a
System.Windows.Application dance. The steps class library has references to
the application (to run and test it) and to the NGourd.Core (which has the
Attributes I need to decorate the step classes and methods). Ive exposed the
controls of application publically to make the test easier to write.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;[Steps]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Behaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_thread&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Dispatcher&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_dispatcher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[Step("start the application")]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;StartApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;AutoResetEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AutoResetEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_thread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eventArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;_dispatcher&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;Dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromThread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_thread&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetApartmentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ApartmentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitOne&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[Step(@"enter (.*)")]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;EnterScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MainWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MainWindow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Writing the application&lt;/h3&gt;
&lt;p&gt;The demo application is a trivial calculator that executes Python statements.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="calc.png"/&gt;&lt;/p&gt;
&lt;p&gt;The method below &lt;em&gt;is&lt;/em&gt; the model for this application. Its worth mentioning its
very easy to include C# objects to the scope of the ScriptSource, but only for
this post, Ive restrained myself to the more simple behavior I specified
earlier (but I did add it to the demo which can be downloaded &lt;a href="http://static.sharpthinking.com.au/2009/ngourd-calculator/DLRCalculator.zip"&gt;here&lt;/a&gt;)&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;EvaluateExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ScriptEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateEngine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ScriptRuntime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ScriptSource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateScriptSourceFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="n"&gt;SourceCodeKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;??&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course this could be unit tested itself.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;[TestMethod]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ModelCanDoBasicArithmetic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EvaluateExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2+2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So what are we testing? I guess its this&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Execute_Click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EvaluateExpression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only thing I had to do the application specifically to get it working in
test environment was delete an attribute in App.xaml root element&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;StartupUri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Window1.xaml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This has consequences. No window loads on start up! By added a constructor in
my App class I was able to remedy this. This may have wider reaching
consequences Im not aware of, and Im also not sure if using the OnLoad event
is better practice. Anyway its working fine for now.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;partial&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;MainWindow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MainWindow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It was &lt;em&gt;fun&lt;/em&gt; implementing the steps in my scenario then writing the
application and watching the behavior test turn green like a cucumber!&lt;/p&gt;
&lt;h3&gt;Isolating scenarios in separate application domains&lt;/h3&gt;
&lt;p&gt;Even though everything looked looks like it was working, I wanted to add
another scenario (which wont surprise readers of my recent posts)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Scenario: Can Execute Python statements

Given I start the application
When I enter hello.upper()
And I click GO
Then I should see HELLO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It turns out this extra test didnt need any additional step implementations or
changes to the WPF application itself, but did show a very real deficiency in
what I was doing. I was reminded of the rule enforced by InvalidOperationException:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cannot create more than one System.Windows.Application instance in the same
AppDomain.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I knew of this rule, I still &lt;em&gt;tried&lt;/em&gt; to get round it anyway. Can I close one
somehow? Delete one? Delete one and de-reference its friends and family?&lt;/p&gt;
&lt;p&gt;Creating a new AppDomain seems a clever way round the rules reasoning, but the
wary fear this may be some type of pandora's box, at very least additional
complexities. Just thinking of MarshalByRefObject proxies makes me feel I
should be spending more time enjoying life with &lt;em&gt;my&lt;/em&gt; friends and family.&lt;/p&gt;
&lt;p&gt;I got some way using DoCallBack until I realized I couldnt make the steps
parameterized as DoCallBack doesnt have any parameters! (I also stubbornly
know it throws runtime errors if one tries to use the looser lambda scoping to
cheekily pass data across application domains, luckily though its the same
stubboness that stopped me giving up this post all together).&lt;/p&gt;
&lt;p&gt;As I was thinking I &lt;em&gt;should&lt;/em&gt; use WCF for communication betweens application
domains, another way I &lt;em&gt;could&lt;/em&gt; do to it clicked. I just needed to create my
original steps class in a new application domain. By deriving the test class
from MarshalByRefObject I could call its step methods from the control domain
using it dynamic proxy. I then created a wrapper class which creates a new
AppDomain, creates the previous steps in the new domain and uses the dynamic
proxy to call the steps. This wrapper class now has the Step attributes for
NGourd to pick up.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;[Steps]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;BehaviourWrapper&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;AppDomain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Behaviour&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;behaviourProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppDomain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TestDomain"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;behaviourProxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstanceFromAndUnwrap&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DLRCalculator.BehaviourTests.dll"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="s"&gt;"DLRCalculator.BehaviourTests.Behaviour"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Behaviour&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;behaviourProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;After&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;AppDomain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[Step("start the application")]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;StartApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;behaviourProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartApplication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[Step(@"enter (.*)")]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;EnterScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;behaviourProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnterScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[Step(@"click GO")]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ClickGo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;behaviourProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValidateOutput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[Step(@"should see (.*)")]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ValidateOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;behaviourProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ValidateOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Running some tests with NGourd&lt;/h3&gt;
&lt;p&gt;Im impressed with NGourd and have generally had a lot of fun and success
playing with it. Its a little disappointing that no results are displayed
until all the tests are complete. Perhaps its because my tests take ages and
it would be nice to see what theyre doing (starting an AppDomain and running
an Application in it isnt a lightweight task). Mainly though, I wanted to
include an action shot of it firing up windows during the test.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="screenshot.png"/&gt;&lt;/p&gt;
&lt;h3&gt;Some thoughts and reflection&lt;/h3&gt;
&lt;p&gt;I didnt write this to actually use it in a real project, this was a personal
project deliberately devised to cover some some things I was interesting in
learning about and trying out. To do automation on a real project I would look
further into &lt;a href="http://white.codeplex.com/"&gt;White&lt;/a&gt; an open source project released by
&lt;a href="http://www.thoughtworks.com/"&gt;ThoughtWorks&lt;/a&gt;, which I hear is really good.&lt;/p&gt;
&lt;p&gt;The concept of Cucumber is pretty cool and I was nice to have a look at the
NGourd source code. In writing this post I enjoyed putting in practice what I
new could be done relatively easily (creating and managing multiple
AppDomains, Applications and Threads in a process) and I was happy to find I
had no problems at all with NGourd itself getting the UI tests working.&lt;/p&gt;
&lt;p&gt;This is all I planned to do with NGourd, the UI steps and the Calculator, but
Id be interested to hear what you think of all this rambling. I know &lt;em&gt;I&lt;/em&gt; got a
lot out of it, did you? What of the other behavior driven development tools
&lt;a href="http://codebetter.com/blogs/aaron.jensen/archive/2008/05/08/introducing-machine-specifications-or-mspec-for-short.aspx"&gt;MSpec&lt;/a&gt;, &lt;a href="http://nspec.tigris.org/"&gt;NSpec&lt;/a&gt; or &lt;a href="http://nbehave.org/"&gt;NBehave&lt;/a&gt;? What are your experiences?&lt;/p&gt;
&lt;p&gt;Ive upload the project from this post &lt;a href="http://static.sharpthinking.com.au/2009/ngourd-calculator/DLRCalculator.zip"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/ngourd-bdd-for-wpf-applications</guid><pubDate>Mon, 08 Jun 2009 10:40:00 +0000</pubDate></item><item><title>IronPython In Action</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        IronPython In Action
    &lt;/h1&gt;
    &lt;p&gt;
    May 25, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;&lt;img class="right" src="image.png"/&gt;&lt;/p&gt;
&lt;p&gt;While twittering (or &lt;a href="http://www.googlefight.com/index.php?lang=en_GB&amp;amp;word1=twittering&amp;amp;word2=tweeting"&gt;tweeting&lt;/a&gt;) about an &lt;a href="/post/2009/05/11/IronPython-Presentation.aspx"&gt;IronPython presentation&lt;/a&gt; I
was doing, &lt;a href="http://twitter.com/voidspace"&gt;@voidspace&lt;/a&gt; messaged me asking if I might mention his book. I'd
contacted Michael Foord a while back about &lt;a href="http://code.google.com/p/dynamic-script-control/"&gt;some code&lt;/a&gt; I was playing around
with. I also knew of him from his &lt;a href="http://www.voidspace.org.uk/python/index.shtml"&gt;blog&lt;/a&gt;, his prolific contribution to
&lt;a href="http://ironpython-urls.blogspot.com/"&gt;IronPythonURLs&lt;/a&gt; and as the co-author of the new book &lt;a href="http://www.manning.com/foord/"&gt;IronPython in
Action&lt;/a&gt;. He's so all over everything IronPython he even &lt;a href="http://ironpython-urls.blogspot.com/2009/04/executing-ironpython-in-err-ironpython.html"&gt;reads&lt;/a&gt; this
blog.&lt;/p&gt;
&lt;p&gt;Mainly because I was too cheap to buy myself a copy it,
I hadn't read his  book. I was wrapped when he offered to get me a copy if I'd
review it on this blog. I wanted to read it as I was learning IronPython and I
knew it would be a good book, I'd read chapters &lt;a href="http://www.manning.com/foord/SampleChapter1.pdf"&gt;1&lt;/a&gt; and &lt;a href="http://www.manning.com/foord/SampleChapter7.pdf"&gt;7&lt;/a&gt; which are
online. I happily recommended it in my presentation, here's what I thought
about the book.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;IronPython in Action&lt;/em&gt; is a fantastic resource for anyone learning IronPython
and for anyone wondering what this dynamic language IronPython is all about
and whether they &lt;em&gt;should&lt;/em&gt; learn it. It feels nicely balanced for both .NET
users looking to learn about IronPython, and for Python users looking to learn
about IronPython on .NET.&lt;/p&gt;
&lt;p&gt;I prefer software books that have plenty of discussion and can be read from
start to finish otherwise I end up skipping through chapters I never go back
too. I found this book well written and fun to read with interesting insight
all the way through. There is enough about the python language and .NET to get
you through the book, and rather than being a reference book it instead
provides information on how to learn more.&lt;/p&gt;
&lt;p&gt;The book is divided into four main sections. The first section &lt;em&gt;Getting
Started with Iron Python&lt;/em&gt; contains a good introduction and history of
IronPython on the .NET Framework.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Core Development Techniques&lt;/em&gt; section is a walkthrough of building a
simple Windows Forms application. I think this section is fantastic as the
text places high value on Agile Development philosophy, using a Model-View-
Controller pattern and Test Driven Development. I think these are exciting
ways to develop software on the .NET framework. There is also a really good
section discussing the difficulties, pitfalls and work-arounds using
IronPython and dynamic programming languages generally on the .NET framework.&lt;/p&gt;
&lt;p&gt;The third section &lt;em&gt;IronPython and Advanced .NET&lt;/em&gt; contains chapters about using
the major pillars of the .NET framework with IronPython. It has chapters on
WPF, Powershell, ASP.NET, Databases, Webservices and Silverlight. It really is
amazing how much of the framework can be used in IronPython.&lt;/p&gt;
&lt;p&gt;The final section &lt;em&gt;Reaching Out with IronPython&lt;/em&gt; has fantastic chapters on
extending IronPython with C#/VB and embedding an IronPython Engine in C#/VB
applications to provide extensibility.&lt;/p&gt;
&lt;p&gt;I highly recommend this book, I enjoyed reading it and learned a lot from it.
If you're looking for a little more insight, the &lt;a href="http://blogs.msdn.com/hugunin/archive/2009/04/14/ironpython-in-action.aspx"&gt;forward by Jim Hugunin&lt;/a&gt;
frames the language, the technology and author well.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/ironpython-in-action</guid><pubDate>Mon, 25 May 2009 10:39:00 +0000</pubDate></item><item><title>IronPython Presentation</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        IronPython Presentation
    &lt;/h1&gt;
    &lt;p&gt;
    May 11, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Im doing an introduction to IronPython presentation at the Victoria .NET user
group tomorrow, I've been attending the user group for about a year and Im
pretty excited about presenting. I think python is a fantastic language and
its great to be able to build software on the .NET framework with it.&lt;/p&gt;
&lt;h2&gt;The Slides&lt;/h2&gt;
&lt;p&gt;&lt;img alt="" src="introduction.png"/&gt;&lt;/p&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;I wanted to start with the second slide and have a photo of a pub on the
contact slide but decided to get through this before trying out PowerPoint
jokes.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="why-python.png"/&gt;&lt;/p&gt;
&lt;h4&gt;Why Python&lt;/h4&gt;
&lt;p&gt;I just plan to discuss &lt;a href="/journal/a-taste-of-python/"&gt;Why I started using Python.&lt;/a&gt; The cartoon is, of
course, &lt;a href="http://xkcd.com/353/"&gt;XKCD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="python.png"/&gt;&lt;/p&gt;
&lt;h4&gt;Python&lt;/h4&gt;
&lt;p&gt;Basic information about the language and its background. Would have liked to
have added &lt;a href="http://www.python.org/doc/essays/metaclasses/"&gt;meta programming&lt;/a&gt; or at least .. to the paradigms.&lt;/p&gt;
&lt;p&gt;I think its a fantastic language so I'll mention its expressive, fun,
productive or whatever adjectives I've got in my head at the time.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="who-uses_it.png"/&gt;&lt;/p&gt;
&lt;h4&gt;People using Python&lt;/h4&gt;
&lt;p&gt;I really want to demonstrate that Python is real language, being used by real
people, to write real code.&lt;/p&gt;
&lt;p&gt;I probably should also have mentioned &lt;a href="http://code.google.com/appengine/"&gt;Google App Engine&lt;/a&gt; here which I've
been tinkering with a bit recently and think is pretty cool.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="iron-python.png"/&gt;&lt;/p&gt;
&lt;h4&gt;IronPython&lt;/h4&gt;
&lt;p&gt;The Python language on .NET. Most of the demo is doing cool stuff in .NET with
the Python language. I think it goes further than this and allows developers
to use their own paradigms and patterns to do .NET development.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="code-comparision.png"/&gt;&lt;/p&gt;
&lt;h4&gt;C# and IronPython comparison&lt;/h4&gt;
&lt;p&gt;I saw this example in the awesome &lt;a href="http://www.manning.com/foord/"&gt;Iron Python in Action&lt;/a&gt; book. I included
it as its a great introduction IronPython code, as it clearly and simply shows
differences on similarities with C#.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="dynamic-language-runtime.png"/&gt;&lt;/p&gt;
&lt;h4&gt;Dynamic Language Runtime&lt;/h4&gt;
&lt;p&gt;The &lt;a href="http://dlr.codeplex.com/"&gt;Dynamic Language Runtime&lt;/a&gt; project is open source. Its quite well
&lt;a href="http://en.wikipedia.org/wiki/Dynamic_Language_Runtime"&gt;described here&lt;/a&gt; as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A dynamic &lt;a href="http://en.wikipedia.org/wiki/Type_system"&gt;type system&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Dynamic_dispatch"&gt;Dynamic method dispatch&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Runtime_code_generation"&gt;Dynamic code generation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://www.codeplex.com/IronLisp"&gt;IronLisp&lt;/a&gt; has been superseded by the &lt;a href="http://www.codeplex.com/IronScheme"&gt;IronScheme&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="dynamic-typing.png"/&gt;&lt;/p&gt;
&lt;h4&gt;Dynamic Typing&lt;/h4&gt;
&lt;p&gt;This slide is just a brief overview of what &lt;a href="http://en.wikipedia.org/wiki/Duck_typing"&gt;duck typing&lt;/a&gt; is.&lt;/p&gt;
&lt;p&gt;It is great for writing test objects, but I have a slide for the later.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="interactive-interpreter.png"/&gt;&lt;/p&gt;
&lt;h4&gt;Interactive Interpreter&lt;/h4&gt;
&lt;p&gt;I think its awesomely cool, and I use it through the entire demo so I thought
it might be worth warning people.&lt;/p&gt;
&lt;h2&gt;The Demo&lt;/h2&gt;
&lt;p&gt;The demo is a series of scripts run into the IronPython Interactive Console.
I've uploaded the very small collection of scripts for this demo &lt;a href="http://static.sharpthinking.com.au/2009/iron_python_presentation/demo.zip"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;IronPython and the CLR&lt;/h4&gt;
&lt;p&gt;Create a string, show how its methods can be reflected. Import the CLR and
show how the .NET String methods are now available.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Introduction to IronPython and the CLR&lt;/span&gt;

&lt;span class="c1"&gt;# Standard first Python console command&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="c1"&gt;# Create a string&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Hello IronPython'&lt;/span&gt;

&lt;span class="c1"&gt;# Inspect its methods, note that its only Python methods.&lt;/span&gt;
&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Python has a method to make the text uppercase&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Import the CLR&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;clr&lt;/span&gt;

&lt;span class="c1"&gt;# Now look at the same objects methods,&lt;/span&gt;

&lt;span class="c1"&gt;# We now see the .NET string methods!&lt;/span&gt;
&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# And can use them!&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToUpper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="python-and-the-clr.png"&gt;Screen Shot&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Using a .NET Classes&lt;/h4&gt;
&lt;p&gt;Create a &lt;a href="http://msdn.microsoft.com/en-us/library/system.net.webclient.aspx"&gt;WebClient&lt;/a&gt; object from the .NET framework to download an RSS
feed. Use an xml2py module written by &lt;a href="http://www.devhawk.net/"&gt;Harry Pierson&lt;/a&gt; to deserialise the
RSS feed into an object graph. Then send the dates and titles to the &lt;a href="http://msdn.microsoft.com/en-us/library/system.speech.synthesis.speechsynthesizer.aspx"&gt;Speech
Synthesizer&lt;/a&gt;. I've seen this done before in an IronPython presentation, and
just thought it was to much fun to leave out.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;clr&lt;/span&gt;

&lt;span class="c1"&gt;# Create a WebClient and download a string of XML&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Net&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DownloadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://feeds.theage.com.au/rssheadlines/technology.xml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Lets have a look at first 60 characters of the xml..&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# If bad stuff happens in the presentation..&lt;/span&gt;

&lt;span class="c1"&gt;#xml = open('theage.rss').read()&lt;/span&gt;

&lt;span class="c1"&gt;# Deserialize to objects using xml2py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;devhawk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xml2py&lt;/span&gt;
&lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xml2py&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Print the results&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;

&lt;span class="c1"&gt;# Import Speech&lt;/span&gt;
&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'System.Speech'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Speech&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Synthesis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpeechSynthesizer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Say titles and dates using the SpeechSynthesizer&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;. &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SpeakAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="webclient-xml2py-speech.png"&gt;Screen Shot&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;SQL&lt;/h4&gt;
&lt;p&gt;I really dont want to do this in the presentation. I did spend a fair amount
of time playing round with asynchronous SQL queries for use in a WPF
application, but it seemed too complicated for the demo so I've left it out. I
use&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# import a simple wrapper around System.Data.SqlClient&lt;/span&gt;
&lt;span class="c1"&gt;# the wrapper is on my blog, I just wanted to show the AdventureWorks&lt;/span&gt;
&lt;span class="c1"&gt;# database, it seems a bit of .NET presentation tradition.&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;demo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;

&lt;span class="c1"&gt;# setup a query on the AdventureWords database&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SELECT * FROM Production.Product'&lt;/span&gt;

&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Data Source=localhost\SQLEXPRESS;InitialCatalog=AdventureWorks;Integrated Security=SSPI;'&lt;/span&gt;

&lt;span class="c1"&gt;# execute the query&lt;/span&gt;
&lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SqlQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# print some results&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;&lt;/h4&gt;
&lt;h4&gt;WPF, Data Binding and Exec&lt;/h4&gt;
&lt;p&gt;I think there is some pretty cool stuff in this demo. Although it takes a lot
more to write real applications it does open new ways to develop and test .NET
applications.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# More pythonic way of getting the feed.. lets hope I have internet&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;devhawk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xml2py&lt;/span&gt;
&lt;span class="n"&gt;rss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xml2py&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://feeds.theage.com.au/rssheadlines/technology.xml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Importing avalon starts a new thread to run the WPF application&lt;/span&gt;
&lt;span class="c1"&gt;# It sets up some dispatures so the console can talk to the GUI&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;samples&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;avalon&lt;/span&gt;

&lt;span class="c1"&gt;# Create a window, show it and load some XAML into it&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;avalon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;xaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;avalon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadXaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'demo.xaml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xaml&lt;/span&gt;

&lt;span class="c1"&gt;# Loading names is really cool, it traverses the control tree&lt;/span&gt;
&lt;span class="c1"&gt;# described in the XAML and creates a dictionary of names and&lt;/span&gt;
&lt;span class="c1"&gt;# controls. We can add those dictionary items to the local scope,&lt;/span&gt;
&lt;span class="c1"&gt;# so we get the same effect as a generated designer files in&lt;/span&gt;
&lt;span class="c1"&gt;# Visual Studio.&lt;/span&gt;

&lt;span class="n"&gt;avalon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xaml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# We can bind a Python list of Python objects to the ListView&lt;/span&gt;
&lt;span class="n"&gt;listView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ItemsSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;

&lt;span class="c1"&gt;# Create a button click callback that will execute the script&lt;/span&gt;
&lt;span class="c1"&gt;# in the query TextBox.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;OnClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Script threw an exception"&lt;/span&gt;
  &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="c1"&gt;# Wire up the click event to our function&lt;/span&gt;
&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnClick&lt;/span&gt;

&lt;span class="c1"&gt;# We can now execute this on the client&lt;/span&gt;
&lt;span class="n"&gt;listView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ItemsSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'to'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="execute-statements-databinding.png"&gt;Screen Shot&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;More Slides&lt;/h2&gt;
&lt;p&gt;&lt;img alt="" src="testing-in-python.png"/&gt;&lt;/p&gt;
&lt;h3&gt;Testing in Python&lt;/h3&gt;
&lt;p&gt;Its really cool you can do true test driven development in that you can write
unit tests for classes and methods that havent yet been written. Some cool GUI
testing is also possible using some the stuff in this presentation.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="tools-sdks.png"/&gt;&lt;/p&gt;
&lt;h3&gt;Tools / SDKs&lt;/h3&gt;
&lt;p&gt;Im not expecting to mention &lt;a href="www.codeplex.com/IronPythonStudio"&gt;Python Studio&lt;/a&gt; or &lt;a href="http://github.com/devhawk/ipydbg/tree/master"&gt;IpyDbg&lt;/a&gt; as Ill
probably be running out of time.&lt;/p&gt;
&lt;p&gt;I really should have included a link to &lt;a href="http://www.python.org/"&gt;python.org&lt;/a&gt;, Ill make sure I
mention it.&lt;/p&gt;
&lt;p&gt;You can get &lt;a href="www.codeplex.com/IronPython"&gt;IronPython here&lt;/a&gt; and the &lt;a href="www.codeplex.com/sdlsdk"&gt;Silverlight Dynamic Languages
SDK&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="resources.png"/&gt;&lt;/p&gt;
&lt;h3&gt;Resources&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.diveintopython.org/"&gt;Dive Into Python&lt;/a&gt; Free online, python for programmers.&lt;/p&gt;
&lt;p&gt;&lt;a href="www.manning.com/foord/"&gt;IronPython In Action&lt;/a&gt; Python and everything .NET with TDD and MVC
practices.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.ironpython.info/index.php/Main_Page"&gt;IronPython Cookbook&lt;/a&gt; Online collection of IronPython .NET scripts.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://ironpython-urls.blogspot.com/"&gt;IronPython Urls&lt;/a&gt;, MSDN,&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.devhawk.net/"&gt;Devhawk&lt;/a&gt; Harry Pierson, Program Manager at Microsoft.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.voidspace.org.uk/ironpython/index.shtml"&gt;Voidspace&lt;/a&gt; Michael Foord, Author IronPython in Action.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="thanks.png"/&gt;&lt;/p&gt;
&lt;h3&gt;Thanks&lt;/h3&gt;
&lt;p&gt;By this stage Ill almost certainly be running late, and probably be really
worried about some minor disaster during the presentation.&lt;/p&gt;
&lt;p&gt;I hope it goes well and everyone takes something out of it, I look forward to
hearing what you thought.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/ironpython-presentation</guid><pubDate>Mon, 11 May 2009 10:25:00 +0000</pubDate></item><item><title>Debugging IronPython with my Excalibur</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Debugging IronPython with my Excalibur
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 11, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;My interest in a light-weight command-line debugger developed a while back
after I watched one used in a Ruby on Rails video. I suspect there are many
.Net developers with more experience than me who have used command line
debuggers and never want to go back. The more &lt;em&gt;I&lt;/em&gt; code, the more I long to
leave my mouse behind and communicate with my development tools using only my
keyboard, &lt;a href="http://en.wikipedia.org/wiki/Excalibur"&gt;Excalibur&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have been following a series of blogs by the awesome &lt;a href="http://cid-0d9bc809858885a4.profile.live.com/?sa=794422412"&gt;Harry Pierson&lt;/a&gt; (aka
&lt;a href="http://devhawk.net"&gt;DevHawk&lt;/a&gt;) on writing an &lt;a href="http://github.com/devhawk/ipydbg/tree/master"&gt;IronPython debugger&lt;/a&gt;. When I decided I
actually wanted to play with the debugger, I found myself going back over
these blogs taking notes as I went. This post is an attempt to summarize all
this information and provide the links for going deeper, for myself if no one
else.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/02/27/Writing+An+IronPython+Debugger+Introduction.aspx"&gt;Writing an IronPython Debugger: Introduction&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Explains why he built a debugger when there are other alternatives. The Visual
Studios debugging was required to much mouse clicking, although he has posted
about doing that too in &lt;a href="http://devhawk.net/2008/05/08/Debugging+IronPython+Code+In+Visual+Studio.aspx"&gt;Debugging IronPython Code in Visual Studio&lt;/a&gt;.
&lt;a href="http://msdn.microsoft.com/en-us/library/ms229861.aspx"&gt;MDbg&lt;/a&gt; which is a .Net Command Line Debugger doesn't support &lt;a href="http://blogs.msdn.com/jmstall/archive/2004/12/31/344832.aspx"&gt;Just My
Code&lt;/a&gt; debugging. He also notes the reason for not using/porting &lt;a href="http://docs.python.org/library/pdb.html"&gt;pdb (The
Python Debugger)&lt;/a&gt; is due to IronPython not yet implementing &lt;a href="http://docs.python.org/library/sys.html#sys.settrace"&gt;settrace&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/02/27/Writing+An+IronPython+Debugger+MDbg+101.aspx"&gt;Writing an IronPython Debugger: MDbg 101&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Describes the basics of &lt;a href="http://msdn.microsoft.com/en-us/library/ms229861.aspx"&gt;MDbg&lt;/a&gt;. I found it interesting there is also an
&lt;a href="http://blogs.msdn.com/jmstall/archive/2005/08/31/Mdbg_Python_ext.aspx"&gt;IronPython extension for MDbg&lt;/a&gt; which isn't for debugging IronPython code,
but to interact with MDbg using IronPython. Sounds cool, I'll have check it
out some time.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/02/28/Writing+An+IronPython+Debugger+Hello+Debugger.aspx"&gt;Writing an IronPython Debugger: Hello, Debugger!&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Goes over the worlds simplest debugger, and it really is pretty straight
forward. It just passes the path of ipy.exe and the python code to debug as a
parameter into CorDebug (provided by MDbg). CorDebug starts the process (it
can be used to debug any .Net process) and provides some events to hook into;
OnCreateAppDomain and OnProcessExit. The world simplest debugger basically
just prints some text when the AppDonain is starts and stops.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/02/Writing+An+IronPython+Debugger+Setting+A+Breakpoint.aspx"&gt;Writing an IronPython Debugger: Setting a Breakpoint&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Provides some background information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ipy.exe produces debug information when the -D parameter is used.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IL generation is dynamic and in memory so the debugger API provides the
Symbols (equivalent to &lt;a href="http://msdn.microsoft.com/en-us/library/ms241903.aspx"&gt;.pdb files&lt;/a&gt;) as a Stream and has &lt;a href="http://msdn.microsoft.com/en-us/library/ms230148.aspx"&gt;a callback when
the Symbols change&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The .Net framework has &lt;a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.symbolstore.aspx"&gt;an API to read and write these symbols&lt;/a&gt; a
files and MDbg provides a &lt;a href="http://github.com/devhawk/ipydbg/blob/5858695ff85ed4740ad06466d4f54394e7f00f9b/CorDebug/CorSymbolStore/symbinder.cs"&gt;wrapper&lt;/a&gt; to read them from a Stream.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then covers the python code to load the symbols, translate document/line into
a function/offset, set a breakpoint. Does not implement user defined
breakpoints in the post.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/04/Writing+And+IronPython+Debugger+Adding+Interactivity.aspx"&gt;Writing and IronPython Debugger: Adding Interactivity&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Covers the basics of how the interactivity will happen, which I've just
realized isn't that complicated. The process being debugged just runs until a
breakpoint event occurs (or the symbols change or the process completes). Once
the process has stopped the the breakpoint the interactivity can occur. It
would be nice to be able to set breakpoints while the process is running, but
it does keep it more simple for now.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/09/Writing+An+IronPython+Debugger+Dynamic+Stack+Trace.aspx"&gt;Writing an IronPython Debugger: Dynamic Stack Trace&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Background info on MDbg:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Two &lt;a href="http://msdn.microsoft.com/en-us/library/ms233401.aspx"&gt;stack chains&lt;/a&gt; in a typical managed app; unmanaged and managed.
The debugger for managed code so only the managed chain used&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Each contains a collection of &lt;a href="http://msdn.microsoft.com/en-us/library/ms230151.aspx"&gt;stack frames&lt;/a&gt; which is the familiar
call stack. There are three types of stack frame; IL, native and internal.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Explains &lt;a href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.dynamicmethod.aspx"&gt;Dynamic Methods&lt;/a&gt; are usually used created by IronPython but
can't be debugged and are implemented as non-dynamic methods when the -D
option is set on ipy.exe.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MDbg has a wrapper around the &lt;a href="http://devhawk.net/ct.ashx?id=48c4a70d-e243-43e5-9709-85d2119c592b&amp;amp;url=http%3a%2f%2fmsdn.microsoft.com%2fen-us%2flibrary%2fms404384.aspx"&gt;unmanaged metadata API&lt;/a&gt; to get method
information for displaying the call stack.&lt;/p&gt;
&lt;p&gt;The command in ipydbg is used to view the trace from the interactive console
is "T".&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/12/Writing+An+IronPython+Debugger+Refactoring.aspx"&gt;Writing an IronPython Debugger: Refactoring&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Mostly discussion about code changes and refactoring. Add code to
automatically generate the MTA apartment state if the -X:MTA argument is not
used. Explains the effect of this on the debugger design.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/13/Writing+An+IronPython+Debugger+Stepping+Thru+Code.aspx"&gt;Writing an IronPython Debugger: Stepping Thru Code&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Introduces console commands for step (S), step in (I), step out (O)&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/13/Writing+An+IronPython+Debugger+Debugging+Just+My+Code.aspx"&gt;Writing an IronPython Debugger: Debugging Just My Code&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Skipped over this one, and feel I may start skipping over a few where I'm not
specially interested in the implementation. I can always refer back to them
later if I really need to understand something.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/19/Writing+An+IronPython+Debugger+Showing+Source+Code.aspx"&gt;Writing an IronPython Debugger: Showing Source Code&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Describes an issue when stepping into a Python function the CLR breaks at some
infrastructure code, presumably there to manage Pythons decorators and such.
Hence there is no line of user code to display. To resolve this an additional
automatic step is added when stepping into a function so it can be mapped to a
line of user code.&lt;/p&gt;
&lt;p&gt;To avoid multiple hits to the file system source files used to retrieve user
code are cached.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/19/Writing+An+IronPython+Debugger+Colorful+Console.aspx"&gt;Writing an IronPython Debugger: Colorful Console&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Describes how he choose to implement colors in the console, while bemoaning
the stateful nature of the &lt;a href="http://msdn.microsoft.com/en-us/library/system.console.foregroundcolor.aspx"&gt;console foreground colours&lt;/a&gt; (ie when you
change the foreground colour it will stay that way until you change it back).
I'm sure I'll come back to this post when I want to add colours to the console
but for now I skipped through it, it doesn't have much to do with writing or
using an IronPython debugger. I guess that's why he discusses moving it out of
the ipydbg and into its &lt;a href="http://devhawk.net/2009/03/19/IronPython+ConsoleColorMgr.aspx"&gt;own module&lt;/a&gt; in his next post.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/21/Writing+An+IronPython+Debugger+A+Little+HackhelliperrhellipCleanup.aspx"&gt;Writing an IronPython Debugger: A Little HackerrCleanup&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Discusses some issues regarding mapping to the debugger COM object instance.
This didn't sound like a whole log of fun and I hope I don't have to come back
and fully understand this one.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/25/Writing+An+IronPython+Debugger+Getting+Local+Variables.aspx"&gt;Writing an IronPython Debugger: Getting Local Variables&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Uses the &lt;a href="http://devhawk.net/ct.ashx?id=c693512a-3698-478f-a95e-20d1be919b67&amp;amp;url=http%3a%2f%2fgithub.com%2fdevhawk%2fipydbg%2fblob%2f442527b0aed3ac2f7ecf6ab8f5f7e93ad03090f2%2fCorDebug%2fCorDebug%2fThread.cs%23L448"&gt;GetLocalVariable(int index)&lt;/a&gt; and &lt;a href="http://devhawk.net/ct.ashx?id=c693512a-3698-478f-a95e-20d1be919b67&amp;amp;url=http%3a%2f%2fgithub.com%2fdevhawk%2fipydbg%2fblob%2f442527b0aed3ac2f7ecf6ab8f5f7e93ad03090f2%2fCorDebug%2fCorDebug%2fThread.cs%23L475"&gt;GetLocalVariablesCount()&lt;/a&gt;
methods from the MDbg CorFame class. This post finally made me look up what
lexical scoping meant, its a term I've heard heaps usually in discussions
about compilers that I never really understood. Its fair to say that I still
not confident in my understanding of it.&lt;/p&gt;
&lt;p&gt;Discusses matching up debug symbols with variable names from the user code.
Doesn't actually evaluate the variable in this post. Notes get_locals from the
IronPython process emits some locals used internally, these are prefixed with
a dollar sign.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/03/31/Writing+An+IronPython+Debugger+Displaying+Values.aspx"&gt;Writing an IronPython Debugger: Displaying Values&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This post covers some pretty tricky interfaces for dealing with all the
different types, which requires a pretty good understand of how the CLR
handles variables under the cover. I didn't read into to much detail but will
consider revisiting it later as the content is quite interesting and it's good
to understand core CLR stuff.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/04/01/Writing+An+IronPython+Debugger+Command+Routing.aspx"&gt;Writing an IronPython Debugger: Command Routing&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Discusses how console commands are routed to functions. This really has
nothing to do with debugging IronPython but it's interesting the way it's
implemented. He starts by implementing a switch using a dictionary of input
commands and functions (Python has no C# switch keyword equivalent). He then
takes it further by making use of Python &lt;a href="http://www.python.org/dev/peps/pep-0318/"&gt;decorators&lt;/a&gt; to bind commands to
functions.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/04/06/Writing+An+IronPython+Debugger+Getting+Arguments.aspx"&gt;Writing an IronPython Debugger: Getting Arguments&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Discusses getting function arguments as locals.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/04/06/Writing+An+IronPython+Debugger+REPL+Console.aspx"&gt;Writing an IronPython Debugger: REPL Console&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Discusses implementing a &lt;a href="http://en.wikipedia.org/wiki/REPL"&gt;REPL&lt;/a&gt; console in the debugger with the
IPyDebugProcess object available in the console scope. This is awesome for
exploring the API using python, just by reflecting the methods of the process
object I realised it would be trivial to add a command to list the source
files currently being debugged as it's just a property on the object.
Explorative coding using reflection is a really powerful concept in Python.&lt;/p&gt;
&lt;p&gt;In the current implementation is a new local scope is created and used by the
by the REPL console, it does not yet support executing code in the process
being debugged.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://devhawk.net/2009/04/08/Writing+An+IronPython+Debugger+Breakpoint+Management.aspx"&gt;Writing an IronPython Debugger: Breakpoint Management&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Implements console commands for setting Breakpoints, as the original post was
about how it worked and only implemented breaking on the first line of the
user code. Introduces multi-key commands for breakpoints. Adds a breakpoint
function (B) with sub functions add (A), list (L), enable (E), disable (D).&lt;/p&gt;
&lt;p&gt;Here's a screenshot of me debugging a really simple app which might help
explain what I've been talking about for this whole post.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="http://static.tarnacious.net.s3.amazonaws.com/image_thumb.png" title="image"/&gt;&lt;/p&gt;
&lt;p&gt;I'm pretty amazed by the productivity and brilliance of Harry Pierson, I've
learned heaps reading &lt;a href="http://devhawk.net/"&gt;his blogs&lt;/a&gt; and I'm really impressed by the progress
he's making with IpyDbg. The debugger still has a long way to being a really
useful tool, but its coming along very quickly. I think I will have to try
using MDbg with the Python extensions, but I really hope to make some use of
ipydbg and maybe even find something I can contribute to it.&lt;/p&gt;
&lt;p&gt;If your interested in trying out the debugger I recommend checking &lt;a href="http://devhawk.net/"&gt;his
blog&lt;/a&gt; and the &lt;a href="http://github.com/devhawk/ipydbg/tree/master"&gt;latest version&lt;/a&gt; of the project on GitHub.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/debugging-ironpython-with-my-excalibur</guid><pubDate>Sat, 11 Apr 2009 10:50:00 +0000</pubDate></item><item><title>Code Camp Oz 09</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Code Camp Oz 09
    &lt;/h1&gt;
    &lt;p&gt;
    Apr 09, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;My first &lt;a href="http://www.codecampoz.com/"&gt;Code Camp Oz&lt;/a&gt; was the best Microsoft event I've attended. It's
basically a large user group meeting held over an entire weekend at Charles
Stuart University in Wagga Wagga. Its a long way to travel but some cool
people from the Australian .Net development community attend and it's a great
opportunity to get to know them.&lt;/p&gt;
&lt;p&gt;I found the meeting people aspect more interesting than the presentations
which was the reason I came anyway, I can watch the Mix video anytime. I met
an awesome group of guys from Albury outside a Silverlight 3 presentation. Due
to lateness we spent the entire presentation outside chatting which was lots
of fun. I didn't realize until I signed into Twitter one of the guys was &lt;a href="http://www.madprops.org"&gt;Matt
Hamilton&lt;/a&gt; who I knew of from his massive Stack Overflow reputation.&lt;/p&gt;
&lt;p&gt;The Alt.Net presentation was one I'd penciled in as interesting before the
event and was pleased find I shared common development values and principles
with the Alt.Net community. I had a good chat with the presenter &lt;a href="http://hackingon.net/"&gt;Liam
McLennan&lt;/a&gt; and ended up hanging out with him a fair bit during the weekend.&lt;/p&gt;
&lt;p&gt;Saturday night at the pub was good fun. I had an interesting chat about what
was involved in becoming a Microsoft MVP and the &lt;a href="http://www.codinghorror.com/blog/archives/000771.html"&gt;value of vender
certification&lt;/a&gt;. It turns out you don't need to have any Microsoft
Certification to become an MVP which I think is good. For me personally I
think there are other things I'd prefer to do to become a better developer.&lt;/p&gt;
&lt;p&gt;An interesting aspect of the event was Twitter. I've never really got that
into Twitter, but the constant background commentary and discussion really
added to the experience. The informal way you can listen and contribute to
conversation is great. It meant I didn't take any notes, but I can always
search the #ccoz09 tags for quotes, links and photos from the event.&lt;/p&gt;
&lt;p&gt;I think it would be really cool break from the power point presentations and
maybe looking at some open source code like the ASP.NET MVC framework at this
type of event. I could see this working with a series of presenters reviewing
the architecture and highlighting certain areas of code for discussion. I'm
not sure if this would actually work, but I think it could be really fun and
interesting.&lt;/p&gt;
&lt;p&gt;I was a little concerned my return train departed Wagga at 3am Monday morning
and arrived in Melbourne at 8am, but I met a couple of guys to who gave me a
lift back to Melbourne on Sunday and happily abandoned my 3am train.&lt;/p&gt;
&lt;p&gt;Overall it was a really well organized and attended event. I extend my
gratitude to &lt;a href="http://notgartner.wordpress.com/"&gt;Mitch Denny&lt;/a&gt; and all the people involved in making it happen.&lt;/p&gt;
&lt;p&gt;Hopefully I'll see you all at Code Camp Oz 2010.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/code-camp-oz-09</guid><pubDate>Thu, 09 Apr 2009 10:52:00 +0000</pubDate></item><item><title>Geocoding and Maps without JavaScript</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Geocoding and Maps without JavaScript
    &lt;/h1&gt;
    &lt;p&gt;
    Mar 15, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Since playing with &lt;a href="http://jquery.com/"&gt;JQuery&lt;/a&gt; I've started thinking more about &lt;a href="http://en.wikipedia.org/wiki/Unobtrusive_JavaScript"&gt;unobtrusive
JavaScript&lt;/a&gt; and graceful degradation. Despite &lt;a href="http://www.w3schools.com/browsers/browsers_stats.asp"&gt;less than 5% of users
browsing without JavaScript enabled&lt;/a&gt;, I found it very interesting disabling
JavaScript on my Browser and checking out which sites still work and which
ones fail. I found the &lt;a href="http://maps.live.com.au/index.aspx"&gt;Microsoft Live Search Maps&lt;/a&gt; has absolutely no clue
your browsing the site without JavaScript, it just renders a loading spinning
GIF.&lt;/p&gt;
&lt;p&gt;Without JavaScript Enabled&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="live_nojs.png"/&gt;&lt;/p&gt;
&lt;p&gt;With JavaScript Enabled&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="live_withjs.png"/&gt;&lt;/p&gt;
&lt;p&gt;Google maps performs much better, providing a rich client side library with
JavaScript enabled and a functional mapping service without it.&lt;/p&gt;
&lt;p&gt;Without JavaScript Enabled&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="maps_nojs.png"/&gt;&lt;/p&gt;
&lt;p&gt;With JavaScript Enabled&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="maps_nojs.png"/&gt;&lt;/p&gt;
&lt;p&gt;There are also plenty of sites that lose some or all of their functionality,
but let you know why. &lt;a href="http://www.youtube.com/"&gt;YouTube&lt;/a&gt; can't play videos, but you can browse them
and it provides an message indicating why the movies don't work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hello, you either have JavaScript turned off or an old version of Adobe's
Flash Player. &lt;a href="http://www.adobe.com/go/getflashplayer/"&gt;Get the latest Flash player&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://stackoverflow.com"&gt;StackOverflow&lt;/a&gt; has graceful degradation implemented well. You can still
ask and answer questions but there is a banner at the top of the page&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Stack Overflow works best with JavaScript enabled&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You then can't expand comments and you don't get the cool Ajax features that
indicate when you've earned a badge or someone else has answered the question
your currently answering, but its still pretty functional.&lt;/p&gt;
&lt;h3&gt;Geocoding without JavaScript&lt;/h3&gt;
&lt;p&gt;It turns out the good guys at Google have provided a REST API to do geocoding
without using their fantastic client side JavaScript libraries. Its all
documented in the &lt;a href="http://code.google.com/apis/maps/documentation/geocoding/"&gt;Geocoding API Developer's Guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Google do state this in their terms for the REST geocoding service;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This service is designed for geocoding static (known) addresses using a REST
interface, for placement of application content on a map. For dynamic
geocoding of user-defined addresses (for example, within a user interface
element), consult the documentation for the &lt;a href="http://code.google.com/apis/maps/documentation/services.html#Geocoding"&gt;JavaScript Client Geocoder&lt;/a&gt;
or the &lt;a href="http://code.google.com/apis/maps/documentation/flash/services.html#Geocoding"&gt;Maps API for Flash Client Geocoder&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don't think this is a huge problem as a well designed website will only use
this geocoding service for the 5% of users who don't have Javascript enabled.
If JavaScript is enabled, the DOM can be updated using the rich JavaScript
functionality.&lt;/p&gt;
&lt;p&gt;I wrote a little script to wrap around the service, I wrote the script in
Python as I'm hoping to use it on Google App Engine, but more about that
later. To use this script you'll need to &lt;a href="http://code.google.com/apis/maps/signup.html"&gt;sign up to get a Google Maps API
key&lt;/a&gt; and use it to set the GeoCoder.geoCodeKey property.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;urllib2&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;xml.dom&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;minidom&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GeoCoder&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://maps.google.com/maps/geo?q=&lt;/span&gt;&lt;span class="si"&gt;%(address)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;output=xml&amp;amp;oe=utf8&amp;amp;sensor=false&amp;amp;key=&lt;/span&gt;&lt;span class="si"&gt;%(key)s&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetUrlKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeUrl&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetKml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUrlKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KmlParser&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;nodeName&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodeName&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetCoordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'latitude'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'longitude'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ParseAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'country'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'CountryName'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'street'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'ThoroughfareName'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'postcode'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'PostalCodeNumber'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'coordinates'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCoordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;dom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minidom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;placeNode&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Placemark'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;placeNode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;dom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetAddresses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KmlParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GeoCoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetKml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;GetAddresses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Enter Address to Geocode, using the Google Geocoding API"&lt;/span&gt;


&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;xml.dom&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;minidom&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GeoCoder&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://maps.google.com/maps/geo?q=&lt;/span&gt;&lt;span class="si"&gt;%(address)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;output=xml&amp;amp;oe=utf8&amp;amp;sensor=false&amp;amp;key=&lt;/span&gt;&lt;span class="si"&gt;%(key)s&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetUrlKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeUrl&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geoCodeKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetKml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUrlKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KmlParser&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;nodeName&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodeName&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;childNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetCoordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"coordinates"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'latitude'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'longitude'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ParseAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'address'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'country'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'CountryName'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'street'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'ThoroughfareName'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'postcode'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'PostalCodeNumber'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="s1"&gt;'coordinates'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCoordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

        &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;dom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minidom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;placeNode&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Placemark'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;placeNode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;dom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetAddresses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;KmlParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GeoCoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetKml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;GetAddresses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])):&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Enter Address to Geocode, using the Google Geocoding API"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which makes geocoding pretty easy from Python&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit(Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.

&amp;gt;&amp;gt;&amp;gt; import geocoder
&amp;gt;&amp;gt;&amp;gt; addresses = geocoder.GetAddresses('main st, melbourne, victoria')
&amp;gt;&amp;gt;&amp;gt; for a in addresses: print geocoder.AddressToString(a)
...
Main St, Mordialloc VIC 3195, Australia (145.0858270,-38.0058450)
Main St, Lilydale VIC 3140, Australia (145.3507800,-37.7575460)
Main St, Mornington VIC 3931, Australia (145.0407850,-38.2231010)
Main St, Greensborough VIC 3088, Australia (145.1058060,-37.7025570)
Main St, Diamond Creek VIC 3089, Australia (145.1478460,-37.6744410)
Main St, Thomastown VIC 3074, Australia (144.9994220,-37.6787790)
Main St, Blackburn VIC 3130, Australia (145.1491980,-37.8258150)
Main St, Upwey VIC 3158, Australia (145.3366500,-37.9050420)
Main St, Croydon VIC 3136, Australia (145.2811170,-37.7961170)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Google also note this in the terms;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Geocoding is a time and resource intensive task. Whenever possible, pre-
geocode known addresses (using the Geocoding Service described here or another
geocoding service), and store your results in a temporary cache of your own
design.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even though the geocoding service is actually quite quick, it does make a lot
of sense not to make a call to an external service if its not required. I
suggest following this advice.&lt;/p&gt;
&lt;h3&gt;Maps without JavaScript&lt;/h3&gt;
&lt;p&gt;Normally some very clever JavaScript dynamically adds new map tile images to
the DOM when sections of the map need to be rendered out, so the normal Google
Maps service won't work without JavaScript. Luckily Google provide a REST
Static Maps service which serves up map images. Its all documented in the
&lt;a href="http://code.google.com/apis/maps/documentation/staticmaps/"&gt;Static Maps API Developer's Guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wrote this Python script to get a map URL, again a &lt;a href="http://code.google.com/apis/maps/signup.html"&gt;Google Maps API key&lt;/a&gt;
is required.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MAPS_API_KEY'&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://maps.google.com/staticmap?'&lt;/span&gt;
&lt;span class="n"&gt;colours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'brown'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'green'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'purple'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'yellow'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'blue'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gray'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'orange'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'white'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetImageUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;markers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;markers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;markers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'markers'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'|'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'&amp;amp;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'='&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;# Generate map URL with 10 locations in Melbourne Australia&lt;/span&gt;
    &lt;span class="n"&gt;letters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ABCDEFGHI'&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;'144.9994220'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-37.6787790'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.2811170'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-37.7961170'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.0407850'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-38.2231010'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.3507800'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-37.7575460'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.3366500'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-37.9050420'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.1491980'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-37.8258150'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.1058060'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-37.7025570'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.1478460'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-37.6744410'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'145.0858270'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'-38.0058450'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

    &lt;span class="n"&gt;markers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;colours&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'size'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;'256x256'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'maptype'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;'roadmap'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sensor'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;'false'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'markers'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;markers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;GetImageUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;markers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using this script and little more Python hacking I could quickly generate some
HTML with a map and links to more detailed maps.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://maps.google.com/staticmap?key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;maptype=roadmap&amp;amp;markers=-37.6787790,144.9994220,browna|-37.7961170,145.2811170,greenb|-38.2231010,145.0407850,purplec|-37.7575460,145.3507800,yellowd|-37.9050420,145.3366500,bluee|-37.8258150,145.1491980,grayf|-37.7025570,145.1058060,orangeg|-37.6744410,145.1478460,redh|-38.0058450,145.0858270,whitei&amp;amp;size=256x256"/&gt;&lt;/p&gt;
&lt;p&gt;A. &lt;a href="http://maps.google.com/staticmap?center=-37.6787790,144.9994220&amp;amp;zoom=16&amp;amp;markers=-37.6787790,144.9994220,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Thomastown VIC 3074, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;B. &lt;a href="http://maps.google.com/staticmap?center=-37.7961170,145.2811170&amp;amp;zoom=16&amp;amp;markers=-37.7961170,145.2811170,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Croydon VIC 3136, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;C. &lt;a href="http://maps.google.com/staticmap?center=-38.2231010,145.0407850&amp;amp;zoom=16&amp;amp;markers=-38.2231010,145.0407850,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Mornington VIC 3931, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;D. &lt;a href="http://maps.google.com/staticmap?center=-37.7575460,145.3507800&amp;amp;zoom=16&amp;amp;markers=-37.7575460,145.3507800,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Lilydale VIC 3140, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;E. &lt;a href="http://maps.google.com/staticmap?center=-37.9050420,145.3366500&amp;amp;zoom=16&amp;amp;markers=-37.9050420,145.3366500,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Upwey VIC 3158, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;F. &lt;a href="http://maps.google.com/staticmap?center=-37.8258150,145.1491980&amp;amp;zoom=16&amp;amp;markers=-37.8258150,145.1491980,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Blackburn VIC 3130, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;G. &lt;a href="http://maps.google.com/staticmap?center=-37.7025570,145.1058060&amp;amp;zoom=16&amp;amp;markers=-37.7025570,145.1058060,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Greensborough VIC 3088, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;H. &lt;a href="http://maps.google.com/staticmap?center=-37.6744410,145.1478460&amp;amp;zoom=16&amp;amp;markers=-37.6744410,145.1478460,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Diamond Creek VIC 3089, Australia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I. &lt;a href="http://maps.google.com/staticmap?center=-38.0058450,145.0858270&amp;amp;zoom=16&amp;amp;markers=-38.0058450,145.0858270,red&amp;amp;maptype=roadmap&amp;amp;key=ABQIAAAAgcoNW1AYyhMSY_oVDddvYBS9ThHTpu8P8555wy_Ce5KyxGr84xQ7LWK4Yn6Frdz52K2bWADxqDBtgg&amp;amp;sensor=false&amp;amp;size=256x256"&gt;Main St, Mordialloc VIC 3195, Australia&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Putting it all together&lt;/h3&gt;
&lt;p&gt;There is still some consideration to put this all together in website that
supports server side mapping and geocoding without JavaScript and enhances the
mapping with unobtrusive JavaScript.&lt;/p&gt;
&lt;p&gt;Page responses need to return HTML that will work without JavaScript. If the
browser executes the pages JavaScript, the script can attach an events and
disable default behaviors to implement the richer client side functionality.&lt;/p&gt;
&lt;p&gt;On a page a user entered address can be geocoded, the requested HTML should
contain a form that can post back to the server to do the geocoding. If client
side JavaScript is not used the server can handle the post and use the
geocoding service to respond appropriately, of course with JavaScript the
addresses can be added to the DOM dynamically, probably with some slide or
fade effect.&lt;/p&gt;
&lt;p&gt;On pages that display maps, the static image URL can be sent in the requested
HTML and used if the JavaScript Google Maps can't be loaded. As on Google Maps
some additional controls can be added to allow some panning and zooming
without JavaScript.&lt;/p&gt;
&lt;p&gt;I'm going to see if I can get the all working on the Google App Engine, I'll
let you know how I go.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/geocoding-and-maps-without-javascript</guid><pubDate>Sun, 15 Mar 2009 11:00:00 +0000</pubDate></item><item><title>Will you Django with me?</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Will you Django with me?
    &lt;/h1&gt;
    &lt;p&gt;
    Mar 13, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I decided I'd have a look at &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; to get a feel for a different web
framework and to learn more about Python itself. Its no secret that I've been
really interested in IronPython recently and I'm excited to give Python a go
without the backing of the .NET framework (but with a complete standard
library implementation, unlike IronPython).&lt;/p&gt;
&lt;p&gt;For this post I basically just followed the installation guide and the first 4
tutorials in the excellent &lt;a href="http://docs.djangoproject.com/en/dev/"&gt;Django Documentation&lt;/a&gt;. Its not my intention to
reproduce the tutorials in this post, I really just want to discuss things I
found interesting while learning the framework.&lt;/p&gt;
&lt;h3&gt;Installing&lt;/h3&gt;
&lt;p&gt;After some fooling around I found the following versions of Python, Django and
MySql were the easiest way to get started using Django on my Vista box. All of
the versions below have windows installers so its pretty straight forward.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.python.org/download/releases/2.5.4/"&gt;Python 2.5.4&lt;/a&gt; (MySQL Python didn't have an installer for 2.6)&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.djangoproject.com/download/"&gt;Django 1.0.2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://dev.mysql.com/downloads/mysql/5.0.html"&gt;MySql 5.0&lt;/a&gt; (I had problems installing 5.1 on my Vista Box, so I reverted
to 5.0 which just worked)&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sourceforge.net/projects/mysql-python"&gt;MySql Python Module&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I deliberately didn't install the &lt;a href="http://dev.mysql.com/downloads/gui-tools/5.0.html"&gt;GUI tools for MySql&lt;/a&gt; as I wanted to get
hardcore from the console. Djangos ORM can be used directly from the Python
console and MySql has a complete command line interface.&lt;/p&gt;
&lt;h3&gt;Getting Started&lt;/h3&gt;
&lt;p&gt;Django comes with a helper script to assist setting up and managing a site. To
do things like create a new web project all you need to do is run following
command:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python django-admin.py startproject [sitename]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates the skeleton project structure and files.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;.
..
manage.py
settings.py
urls.py
__init__.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can immediately validate everything is on the right track by running the
development server.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I was pretty excited I could just create a new database from the same console
window&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c:\Working&amp;gt;mysql -u root -p
Enter password: *******
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.0.77-community-nt MySQL Community Edition (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql&amp;gt; CREATE DATABASE demodb;
Query OK, 1 row affected (0.03 sec)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All I then needed to do was add my database credentials to the settings.py.
Once this was setup I could use the syncdb command to build all the tables
required by the framework (which includes an authorization module).&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python manage.py syncdb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which, without me doing anything else, creates the tables.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Models&lt;/h3&gt;
&lt;p&gt;As with most MVC web application frameworks, there is a standard project
structure convention. Django offers another command to quickly create an app,
which is basically a small web application.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python manage.py startapp weblog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates a sub folder with the following files empty files&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;__init__.py
models.py
views.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then custom models can be added to the models python file. (Yes, I see that we
probably would want more characters for a post, but I just want to get
something going for now)&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pub_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'date published'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To make the next bit of magic happen, we have to register this class in the
main settings python file of this project.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'django.contrib.auth'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'django.contrib.contenttypes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'django.contrib.sessions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'django.contrib.sites'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'DjangoSite.weblog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the syncdb command can be used again to create the new tables for our
model.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c:\Working\DjangoSite\DjangoSite&amp;gt;python manage.py syncdb
Creating table weblog_post
Creating table weblog_comment
Installing index for weblog.Comment model
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can now start checking out the ORM provided by Django in the interactive
Python console.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from DjangoSite.weblog.models import *
&amp;gt;&amp;gt;&amp;gt; import datetime
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; post = Post(content = "Django Rocks!", pub_date=datetime.datetime.now())
&amp;gt;&amp;gt;&amp;gt; post.save()
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; comment = Comment(post = post, comment="+1")
&amp;gt;&amp;gt;&amp;gt; comment.save()
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; findPost = Post.objects.get(id=post.id)
&amp;gt;&amp;gt;&amp;gt; findPost.content
u'Django Rocks!'
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; comments = findPost.comment_set.all()
&amp;gt;&amp;gt;&amp;gt; for c in comments:
...     print c.comment
...
&amp;gt;&amp;gt;&amp;gt; findPost.delete()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That's pretty cool, and just 6 lines of code to create the models!&lt;/p&gt;
&lt;h3&gt;Administration&lt;/h3&gt;
&lt;p&gt;A few lines need to be uncommented in the urls python file, another line added
to the installed apps list and the new table needs to added to the database to
enable the default administration features. For the mean time this will serve
pages to manage user accounts, later we'll be able to use the administration
features to create and a web administration interface for any of our models.&lt;/p&gt;
&lt;p&gt;The urlpatterns object is pretty cool. Its basically the URL routing and it
uses regular expressions to map requests to handler scripts. More on this
before this post is finished.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf.urls.defaults&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Uncomment the next two lines to enable the admin:&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;

&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;autodiscover&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s1"&gt;'^admin/(.*)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now if we run the server admin pages can be found from /admin directory of the
site.&lt;/p&gt;
&lt;p&gt;I won't go into to much detail about how its done but by simply registering a
model, web pages are provided to manage that model (finding, adding, deleting
and updating). Its all very clever, the framework does lots of introspection
to display the correct html controls for each field type.&lt;/p&gt;
&lt;p&gt;To separate the UI from the model, a class can be created which is used to
guide how the admin framework renders the pages for the model, it is then used
to map fields to properties on the model object.&lt;/p&gt;
&lt;h3&gt;Views&lt;/h3&gt;
&lt;p&gt;Getting some weblog pages setup is amazingly straight forward. Its just a
matter of adding a regular expression into the urlpatterns that matches the
type of URL you want to handle and a function to handle it. The most basic
handler method is what you'd expect, it takes a http request and returns a
http response.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"This is the posts page!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This can be expanded to actually use the weblog models. The code below gets
the 5 most recent posts and creates a string with each post content separated
with a &lt;br/&gt; tag.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;DjangoSite.weblog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Comment&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;lastestPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-pub_date'&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;br /&amp;gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lastestPosts&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Well that's pretty cool, but I don't think we'll be writing entire pages using
standard Python. Besides, these look more like controllers than views, there
must be more to it.&lt;/p&gt;
&lt;h3&gt;Templates&lt;/h3&gt;
&lt;p&gt;With templates, we can throw objects at a template to be rendered. The index
method can now be separated from the rendering of HTML&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;DjangoSite.weblog.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Comment&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;latestPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-pub_date'&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'weblog/index.html'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;latestPosts&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the template (weblog/index.html) looks something like this&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{% if posts %}
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{% for post in posts %}
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ post.content }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{% else %}
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;There are no posts&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
{% endif %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It worth noting this isn't just Python embedded in HTML. I think this text
from the Django documentation describes this design decision:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;..you'll want to bear in mind that the Django template system is not simply
Python embedded into HTML. This is by design: the template system is meant to
express presentation, not program logic&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It makes sense to me.&lt;/p&gt;
&lt;h3&gt;Forms&lt;/h3&gt;
&lt;p&gt;A forms post data can be retrieved from a dictionary on the request object.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;postTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resquest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"postTitle"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I'm sure Django has support for mapping posts to model objects, but I haven't
seen it yet.&lt;/p&gt;
&lt;h3&gt;Some more magic&lt;/h3&gt;
&lt;p&gt;It turns out there is heaps more support in Django for handling generic web
problems. For example creating list and details pages is so common that Django
has some generic functionality to help. You just need to provide a model and
write some templates to describe how to render the models.&lt;/p&gt;
&lt;p&gt;It appears there is heaps more of the magic throughout the framework some
things I haven't yet looked into are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Advanced form processing&lt;/li&gt;
&lt;li&gt;Using the RSS framework&lt;/li&gt;
&lt;li&gt;Using the cache framework&lt;/li&gt;
&lt;li&gt;Using the comments framework&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;I've just stepped through all the basic steps to building a functional
database driven web application. Overall I'm pretty impressed; it didn't take
much time, code or leaps in faith to get it going. It appears to do generic
things, like what I was doing, is even easier as the framework has generic
support for it.&lt;/p&gt;
&lt;p&gt;I am glad I got into it from the ground up, I think I would have been a lot
more skeptical had I written a simple weblog without writing a line of Python.
I've come to learn that its not about the 90% things that are easy to do in a
web application framework, its about the 10% of things you want to do but
don't work out of the box. Of course I haven't had enough exposure with Django
to say how deep the application framework actually goes. I personally feel
that Django and Python are a fantastic language and framework to deal with the
10% of requirements that don't come out of the box.&lt;/p&gt;
&lt;p&gt;On anther note, I personally love coding from the console rather than an IDE,
even with limited exposure I was getting pretty quick navigating my project,
running the Django web development helpers and managing MySql. I personally
think it's faster developing from a console with all your tools accessible
without taking your hands of the keyboard.&lt;/p&gt;
&lt;p&gt;Its been lots of fun playing with the framework. I'd definitely like to build
a site in Django some time, but I'm not sure when I'll find the time.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/will-you-django-with-me</guid><pubDate>Fri, 13 Mar 2009 11:02:00 +0000</pubDate></item><item><title>Executing IronPython in, err.. IronPython</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Executing IronPython in, err.. IronPython
    &lt;/h1&gt;
    &lt;p&gt;
    Mar 06, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I just read the latest post from &lt;a href="http://www.secretgeek.net/"&gt;secretGeek&lt;/a&gt;, a brilliantly funny blog
which often makes my day with posts like &lt;a href="http://www.secretgeek.net/self_click_next.asp"&gt;IT Industry Revolutionised By Labour
Saving Device&lt;/a&gt; and &lt;a href="http://www.secretgeek.net/Defensive.asp"&gt;Defensive Programming&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The latest post, &lt;a href="http://www.secretgeek.net/host_ironpython.asp"&gt;A 3 minute guide to embedding IronPython in a C#
application&lt;/a&gt;, is a demo of a Windows application that can execute an
IronPython script from a TextBox at runtime. Another TextBox control on the
form is added to the IronPython script scope so the script can interact with
it. This is both cool and powerful, the concept has been used in an
&lt;a href="http://www.resolversystems.com/"&gt;IronPython powered spreadsheet from Revolver Systems&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I thought it would be kind of fun to create an IronPython script that created
a Windows Form which could execute a script. This means I could run the script
and get window which I could then use to run the script again and get anther
window, then..&lt;/p&gt;
&lt;p&gt;I posted the script in the comments and then decided it was pretty fun and it
would be worth writing a post about myself. Below is the Form class script:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;clr&lt;/span&gt;
&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReferenceByPartialName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"System.Windows.Forms"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'IronPython'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Microsoft.Scripting'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows.Forms&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;IronPython.Hosting&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Scripting&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SourceCodeKind&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MetaNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TextBox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TextBox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;420&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Multiline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;310&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Execute"&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateScriptSourceFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;SourceCodeKind&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Statements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateScope&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only trick is, to start the application from the IronPython console
(ipy.exe) you need to include this line at the end which creates the class and
creates a standard application message loop.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MetaNote&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the script is executed from within the application itself, you only need
to create in instance of the class and call its Show method.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;MetaNote&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then the fun..&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="image.png"/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/executing-ironpython-in--err---ironpython</guid><pubDate>Fri, 06 Mar 2009 10:57:00 +0000</pubDate></item><item><title>Get in the Queue, your locks don't work here</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Get in the Queue, your locks don't work here
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 26, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I am, of course, talking about a pattern in high scale web application design,
Queues. More specifically the Queue service on the Windows Azure cloud
computing platform.&lt;/p&gt;
&lt;p&gt;So when do you use Queues?&lt;/p&gt;
&lt;p&gt;Its common to use thread synchronization objects in a web application to
serialize access to a resource, but this thread synchronization approach
doesn't work so well when you have multiple applications, over multiple
servers competing for exclusive access to a global resource.&lt;/p&gt;
&lt;p&gt;The first scenario I've found which suits the Queue service is in a user
driven content ranking system. I suspect I'll find many more places to use the
Queue services as I continue to learn more about developing and designing
applications on the cloud platform.&lt;/p&gt;
&lt;p&gt;In the web application I'm building I want users to be able to rate dynamic
content and I also want to record the number of views. This is a very common
concept which allows the users to provide feedback which can then be shared
with other users and used to dynamically rank the content.&lt;/p&gt;
&lt;p&gt;I can start by &lt;a href="/journal/azure-table-storage-in-ironpython"&gt;implementing some tables&lt;/a&gt;, one to store views and one to
store votes. All I need to do is insert a row into the corresponding table for
every view or vote. I can then query the tables to get all the views and votes
for each content item. While this will work, it won't scale. Imagine querying
a YouTube video view table! It's certainly not the sort of task you could do
while the user is waiting for a page to render.&lt;/p&gt;
&lt;p&gt;You may be able to resolve this problem with a background process, using some
thread locks, to periodically calculate and cache this information on each web
server. While I may end up using some caching on each web server, storing all
the pages and their counts on each web server may not be feasible. Another
problem with this is all the web servers are doing the same data analysis,
which doesn't seem a efficient use of resources.&lt;/p&gt;
&lt;p&gt;A nice solution for this type of problem is to use the Queue service and a
table for the summarized data. Instead of adding page views and votes to the
original tables, they can be added to a queue. A worker role can then be
implemented to periodically check this queue, potentially doing some
validation (or other work) on each item, and then update the reference table.
This could be scaled even further, if required, by having multiple worker
roles processing items from queue.&lt;/p&gt;
&lt;p&gt;A not entirely desirable effect of using this pattern is that votes and pages
views are not immediately registered when a user votes or visits a page. I
guess this is more desirable than your users not being able to view pages when
a single server web server reaches capacity or users getting page time-outs as
you process billions of table rows while they wait.&lt;/p&gt;
&lt;p&gt;Continuing a theme in my recent posts, I'm going to have a play with the
service using IronPython.&lt;/p&gt;
&lt;p&gt;I've been using this little helper module which just imports the StorageClient
assembly and makes it easy to create development StorageAccountInfo objects as
the have the development credentials as default values.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;clr&lt;/span&gt;
&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"StorageClient.dll"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Samples.ServiceHosting.StorageClient&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endPointUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:10002/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'devstoreaccount1'&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountSharedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;GetStorageAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;StorageAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endPointUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountSharedKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlobAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endPointUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:10000/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QueueAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endPointUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:10001/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It's then pretty easy to create a queue and add an item to it&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;AzureQueueHelper&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# create a connection to the development storage service&lt;/span&gt;
&lt;span class="n"&gt;queueStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QueueStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueAccount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStorageAccountInfo&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# create a queue by name&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queueStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# this will create the queue if it does't exist, but will do nothing if it already exists&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateQueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# create and send a message&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"testString"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then get it back in another process&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;AzureQueueHelper&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# create a connection to the development storage service&lt;/span&gt;
&lt;span class="n"&gt;queueStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QueueStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueAccount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStorageAccountInfo&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# create a queue by name&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queueStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# this will create the queue if it does't exist, but will do nothing if it already exists&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateQueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# the is the time you have exclusive access to the queue item&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

   &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentAsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="c1"&gt;# message must be explicitly removed from the queue&lt;/span&gt;
   &lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The asynchronous methods can be used to create a simple little event based
queue processing server.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;AzureQueueHelper&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Threading&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# auto reset event to keep it alive&lt;/span&gt;
&lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoResetEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# create a connection to the development storage service&lt;/span&gt;
&lt;span class="n"&gt;queueStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QueueStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueueAccount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStorageAccountInfo&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# create a queue by name&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queueStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# this will create the queue if it does't exist, but will do nothing if it already exists&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateQueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# handle message from the queue&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;MessageReceieved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentAsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# setup polling, should keep reading untill the queue is empty, then poll for 5 seconds&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessageReceived&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;MessageReceieved&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PollInterval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;messageQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartReceiving&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;# repeat until are is set (forever in this example)&lt;/span&gt;
&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitOne&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Its really amazing how little is required to extended this little server to
the worker process discussed in this post using just the table services I've
&lt;a href="/journal/azure-table-storage-in-ironpython"&gt;previously posted&lt;/a&gt; about.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/get-in-the-queue--your-locks-don-t-work-here</guid><pubDate>Thu, 26 Feb 2009 01:21:00 +0000</pubDate></item><item><title>Import AntiGravity and I'll see you on Cloud Azure</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Import AntiGravity and I'll see you on Cloud Azure
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 21, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;On the topic of setting up tables on Windows Azure &lt;a href="http://blog.smarx.com/"&gt;Steve Marx&lt;/a&gt; &lt;a href="http://blog.smarx.com/posts/try-to-create-tables-only-once"&gt;writes&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Probably the best solution is to have separate initialization code that
creates your tables.  This is analogous to the pattern of having CREATE TABLE
commands scripted in T-SQL which you run once to set up the database.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In another post &lt;a href="http://blogs.msdn.com/ploeh/default.aspx"&gt;Mark Seemann&lt;/a&gt; extends on this and demonstrates &lt;a href="http://blogs.msdn.com/ploeh/archive/2008/12/04/creating-azure-tables-from-script.aspx"&gt;creating
tables with a PowerShell script&lt;/a&gt; and writes&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You could obviously write a little utility that references StorageClient and
your custom TableStorageDataServiceContext.&lt;/p&gt;
&lt;p&gt;Another, in my opinion, better option for such a one-off script is a
PowerShell script&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I agree but I think you can actually take this a lot further. In my experience
working on enterprise applications it is also common to use one-off SQL
scripts to add, update, maintain and manage data. The problem with this is you
often have a very good ORM available which has logic to protect the state of
the data and the business requirements. I feel that if you can also expose
these business objects to a scripting environment, you potentially have a very
powerful way of managing your enterprise information. This is probably even
more relevant in the cloud data services where there is no equivalent of low
level T-SQL you can optimize, you can have full control from scripting
environment with the tools and armour of your enterprise objects.&lt;/p&gt;
&lt;p&gt;Anyway, enough rambling. In my previous post I demonstrated &lt;a href="http://blog.sharpthinking.com.au/post/2009/02/20/Azure-Table-Storage-in-IronPython.aspx"&gt;working with
Azure table storage data in IronPython&lt;/a&gt;, and briefly glossed over creating
the tables. In this post I'm going to walk through dynamically creating table
in the cloud with IronPython and what currently needs to be done to get them
working on the local development storage server.&lt;/p&gt;
&lt;p&gt;I'm going to create some of my own customs tables and some tables for the
standard providers (Membership, Roles and Session) as I am building a simple
MVC application for the cloud. I've got DataModels.dll with my custom models
and the AspProviders.dll from the Azure SDK Samples with the standard provider
models. All the models extend TableStorageEntity.&lt;/p&gt;
&lt;p&gt;Both assemblies also extend TableStorageDataServiceContext classes which are
similar to the DataContext class in Linq2Sql. They have IQueryable&lt;t&gt; fields
templated to model classes, which represent tables. The base class has
additional functionality for tracking, adding, deleting and saving model
objects. We will be able to create tables by reflecting these
TableStorageDataServiceContext classes.&lt;/t&gt;&lt;/p&gt;
&lt;h3&gt;Creating a database the local development storage server&lt;/h3&gt;
&lt;p&gt;Unfortunately a current restriction of the local development storage server is
that you can't dynamically create tables. To create tables on the local
development server the DevTableGen.exe tool is used.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;DevTableGen /database:TarnsDevDB /forceCreateAspProviders.dll;DataModels.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which should produce an output like below, indicating that the tables had been
created. You can see the provider model tables and my custom tables have been
created.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Windows(R) Azure(TM) Development Table database generation tool version 1.0.0.0 
for Microsoft(R) .NET Framework 3.5 Copyright (c) Microsoft Corporation. All rights reserved.

DevTableGen : Generating database 'TarnsDevDB'
DevTableGen : Generating table 'Membership' for type 'Microsoft.Samples.ServiceHosting.AspProviders.MembershipRow'
DevTableGen : Generating table 'Roles' for type 'Microsoft.Samples.ServiceHosting.AspProviders.RoleRow'
DevTableGen : Generating table 'Sessions' for type 'Microsoft.Samples.ServiceHosting.AspProviders.SessionRow'
DevTableGen : Generating table 'PostTable' for type 'DataModels.PostDataModel'
DevTableGen : Generating table 'CommentTable' for type 'DataModels.PostDataModel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;UI for development storage server should be found in the task bar. As it only
supports one concurrent database, you may have to select the database just
created.&lt;/p&gt;
&lt;h3&gt;Dynamically managing a database in the cloud&lt;/h3&gt;
&lt;p&gt;I created a simple IronPython helper module to assist creating and managing
the table schema in the cloud. Like in the previous post, it's built on the
StorageClient library that ships with the Windows Azure SDK. You'll notice
that its expecting the StorageClient assembly to be in one of its script
paths.&lt;/p&gt;
&lt;p&gt;There was some frustration writing it; All the TableStorageDataServiceContext
classes are internal, this meant I had to write the LoadFromAssembly method
and do some reflection to get the types.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;clr&lt;/span&gt;
&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"StorageClient.dll"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Samples.ServiceHosting.StorageClient&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Reflection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Assembly&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endPointUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:10002/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'devstoreaccount1'&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountSharedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=='&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TableHelper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storageInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StorageAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endPointUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountSharedKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tableStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TableStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storageInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;AddTables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataServiceContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;TableStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateTablesFromModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataServiceContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storageInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;DeleteAllTables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tableStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListTables&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tableStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;PrintTables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tableStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListTables&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
         &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;LoadFromAssembly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;assemblyName&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="c1"&gt;# A bit of screwing round with reflection, because the types we want are internal&lt;/span&gt;
      &lt;span class="n"&gt;assembly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assemblyName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;baseType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TableStorageDataServiceContext&lt;/span&gt;
      &lt;span class="n"&gt;contexts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseType&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;baseType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetTypes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;contexts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;
         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using this module we can easily create and manage tables from the IronPython
interactive console, or with a script. Below is an example (with the
credentials removed)&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# load the helper module&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;AzureTableHelper&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# create an account and set creadentials&lt;/span&gt;
&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endPointUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[Table Storage Url]'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountSharedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'[Shared Key]'&lt;/span&gt;
&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accountName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'[Account Name]'&lt;/span&gt;

&lt;span class="c1"&gt;# create the helper with the account details&lt;/span&gt;
&lt;span class="n"&gt;tableHelper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TableHelper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# delete tables&lt;/span&gt;
&lt;span class="n"&gt;tableHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteAllTables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# load tables from the AspProviders assembly&lt;/span&gt;
&lt;span class="n"&gt;tableHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadFromAssembly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"AspProviders.dll"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# load tables from the DataModels assembly&lt;/span&gt;
&lt;span class="n"&gt;tableHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadFromAssembly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DataModels.dll"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# print all the tables to the console&lt;/span&gt;
&lt;span class="n"&gt;tableHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrintTables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is a limitation of scripting tables this way as &lt;a href="http://blogs.msdn.com/ploeh/default.aspx"&gt;Mark Seemann&lt;/a&gt;
&lt;a href="http://blogs.msdn.com/ploeh/archive/2008/12/04/creating-azure-tables-from-script.aspx"&gt;notes&lt;/a&gt;,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Currently, the script has one limitation: Deleting a table using the
StorageClient API only marks the table for deletion, so the operation returns
much to soon. This means that if you are trying to recreate a table by the
same name, a conflict will occur, and the table will not be created. You can
work around this limitation by waiting a little while and then run the script
again.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think this part of the Azure framework is pretty exciting and I'm looking
forward to checking out other parts.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/import-antigravity-and-ill-see-you-on-cloud-azure</guid><pubDate>Sat, 21 Feb 2009 00:03:00 +0000</pubDate></item><item><title>Azure Table Storage in IronPython</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Azure Table Storage in IronPython
    &lt;/h1&gt;
    &lt;p&gt;
    Feb 20, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;My goal was to write a bit of scaffolding to make using the storage service
fun and easy from in IronPython. The Windows Azure SDK comes with a library in
the samples which does the low level work interfacing with the API and
provides some nice classes to work with. There are methods to generate table
schemas by reflecting on model classes and another sample implements all the
standard .NET providers.&lt;/p&gt;
&lt;p&gt;I was hoping to write all the scaffolding and the model classes in IronPython
but, in the first of a series of set backs, I found the development storage
server behaves differently than the cloud. For some reason you need to create
tables on the development storage server using a command line tool, passing
your model assemblies as arguments. Apparently this will be fixed soon, but
trying to stay focused I decided I'd have to write my models in C# for now.&lt;/p&gt;
&lt;p&gt;Once I had an assembly with some models I could use the DevTableGen.exe
command line tool that comes with the Azure SDK to create tables on my
development storage server.&lt;/p&gt;
&lt;p&gt;I don't think there are currently any good tools for visualizing and editing
data, but I'm sure by the time its released it will integrate into Server
Explorer and Query Analyzer (or perhaps it'll just be a Firefox plug-in). I've
seen a presentation where HTTP requests are hand coded to retrieve data, but I
wasn't up for that and tried importing the sample library into an IronPython
console. I got enough working to convince me everything was going to work out,
I just needed find out how to use extension methods in IronPython...&lt;/p&gt;
&lt;p&gt;Much to my surprise and disappointment, consuming extension methods in
IronPython is still difficult. There does appear to be a way to &lt;a href="http://blogs.msdn.com/saveenr/archive/2009/02/08/ironpython-consuming-extension-methods-part-iii.aspx"&gt;bind the
extension methods&lt;/a&gt;, but I haven't seen an example binding all the Linq
extension methods. This is a problem as it means Linq expression trees can't
be easily built in IronPython. Passing expression trees as queries is ideal as
the cloud can do the filtering, sorting and only return the data you want.&lt;/p&gt;
&lt;p&gt;I decided to put this problem on ice and write helpers in C# that returned a
List&lt;t&gt; of all the rows. This won't work well with lots of data, but it will
work find on small tables.&lt;/t&gt;&lt;/p&gt;
&lt;p&gt;I wrapped the required credentials to connect to the service in an Account
class, mainly so I could hard code the development credentials which are
always the same.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;EndPoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;AccountName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;SharedKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;development&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt;
        &lt;span class="n"&gt;EndPoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:10002/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;AccountName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"devstoreaccount1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;SharedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UV&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I later I will try creating Models on the DLR, but for now I created them in a
separate assembly using C#.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;DataModels&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;PostDataModel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableStorageEntity&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;PostDataModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;PostDataModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;PartitionKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;RowKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A context is required that derives from TableStorageDataServiceContext, the
field names in the context are used for table names and the type of the field
describes the table row. The DataServiceContext works a bit like the LinqToSql
data context keeping track of all the objects it returns.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;DataModels&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;DataServiceContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableStorageDataServiceContext&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DataServiceContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StorageAccountInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PostDataModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PostTable&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PostDataModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PostTable"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PostDataModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CommentTable&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PostDataModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CommentTable"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I wrote a generic model class to hide all the details and provide a simple
wrapper to access create, read and select operations on a table. There's a
couple of tests in the solution that demonstrate how this work, but basically
you just need a custom context (V), a model (T) and table name. The generic
model can then be used to insert, select and delete from the table.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableStorageDataServiceContext&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StorageAccountInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AccountInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableStorage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableStorage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;AccountInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StorageAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndPoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SharedKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TableStorage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AccountInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Activator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AccountInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;MethodInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;GetMethods&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"get_"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// field doesn't exist&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fieldValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DataServiceQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fieldValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TableStorageDataServiceQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TableStorageDataServiceQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DataServiceQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;queryResults&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteAllWithRetries&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;queryResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// AttachTo is not required if the item was created by the context&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We've done all the C# code, now lets see how it can all be tied together in an
IronPython console.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;IronPython 2.0 (2.0.0.0) on .NET 2.0.50727.3053
Type "help", "copyright", "credits" or "license" for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; # import everything we need
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; import clr
&amp;gt;&amp;gt;&amp;gt; clr.AddReference("DataModels.dll")
&amp;gt;&amp;gt;&amp;gt; clr.AddReference("DataHelper.dll")
&amp;gt;&amp;gt;&amp;gt; clr.AddReference("StorageClient.dll")
&amp;gt;&amp;gt;&amp;gt; from DataModels import *
&amp;gt;&amp;gt;&amp;gt; from DataHelper import *
&amp;gt;&amp;gt;&amp;gt; from Microsoft.Samples.ServiceHosting.StorageClient import *
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; # create a generic model, using the default account (development server)
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; model = Model[PostDataModel, DataServiceContext](Account(),"PostTable")
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; # now we can add some data rows
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; for i in range(5):
...    post = PostDataModel()
...    post.Name = "Post Name " + str(i)
...    post.Content = "Some content for post" + str(i)
...    model.Insert(post)
...
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; # we can now read them back
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; for post in model.Select():
...    print "Name", post.Name
...
Name Post Name 3
Name Post Name 1
Name Post Name 2
Name Post Name 0
Name Post Name 4
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; # delete them all
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; for post in model.Select():
...    model.Delete(post)
...
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; # and finally ensure they have all been removed
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; for post in model.Select():
...    print "Name", post.Name
...
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I'm pretty excited with how it all worked out, despite the setbacks. In future
posts hopefully I'll have a go creating tables in the cloud with models and
context created on the DLR. I will also try and resolve using Linq Extension
Methods in IronPython which are essential to building complex queries to be
executed in the cloud. I'm also writing a very simple Azure MVC weblog app
which I'll hopefully finish soon.&lt;/p&gt;
&lt;p&gt;The project can be &lt;a href="http://static.sharpthinking.com.au/WindowAzure-TableStorage/AzureTableStorageHelpers.zip"&gt;downloaded here&lt;/a&gt; and I've also uploaded just the
&lt;a href="http://static.sharpthinking.com.au/WindowAzure-TableStorage/DataHelper.dll"&gt;compiled DataHelper assembly&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I found these links useful writing this post:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://soulsolutions.com.au/Blog/tabid/73/EntryId/558/Windows-Azure-Essential-Links.aspx"&gt;Windows Azure Essential Links&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.msdn.com/jnak/archive/2008/10/28/walkthrough-simple-table-storage.aspx"&gt;Walkthrough: Simple Table Storage&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.msdn.com/ploeh/archive/2008/12/04/creating-azure-tables-from-script.aspx"&gt;Creating Azure Tables From Script&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/azure-table-storage-in-ironpython</guid><pubDate>Fri, 20 Feb 2009 14:14:00 +0000</pubDate></item><item><title>Silverlight Dynamic Languages SDK</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Silverlight Dynamic Languages SDK
    &lt;/h1&gt;
    &lt;p&gt;
    Jan 26, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I decided to have a look at creating Silverlight applications with IronPython,
this led me to the &lt;a href="http://www.codeplex.com/sdlsdk"&gt;Silverlight Dynamic Languages SDK&lt;/a&gt;. The main piece of
the SDK is &lt;a href="http://www.codeplex.com/sdlsdk/Wiki/View.aspx?title=Chiron"&gt;Chiron&lt;/a&gt; which is a development utility for creating Silverlight
application packages (XAPs) from dynamic languages.&lt;/p&gt;
&lt;p&gt;Chiron can be used to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create dynamic language Silverlight projects&lt;/li&gt;
&lt;li&gt;Start a local web server which dynamically generates XAPs from the project files&lt;/li&gt;
&lt;li&gt;Write generated XAPs to disk.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I needed something to build on Silverlight with IronPython, so I decided to
write a small part of the UI for an RSS Feed Visualizer I've been wanting to
write. Its only really a demo, the UI could use some more work and I've just
used some sample names as data. I hope to build on this example in the future,
but I'll need to do some refactoring, the code is pretty scrappy as I wrote
most of it after I lost a house poker game. Apparently a stronger
understanding of &lt;a href="http://blog.sharpthinking.com.au/post/2009/01/18/Goats-Cars-and-Babies.aspx"&gt;counter intuitive probability problems&lt;/a&gt; hasn't helped my
game and I don't write my best code after an afternoon of drinking.&lt;/p&gt;
&lt;p&gt;(If there is problems viewing the demo on this page, it can also be viewed
&lt;a href="http://static.sharpthinking.com.au/DataVisualizers/DataVisualizer1.htm"&gt;here&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Here is the IronPython code that drives the demo, I've also uploaded the
&lt;a href="http://static.sharpthinking.com.au/DataVisualizers/DataVisualizer1.zip"&gt;entire project&lt;/a&gt;. I used examples of dragging and inertia from the
excellent &lt;a href="http://channel9.msdn.com/continuum/tutorials/"&gt;Project "Rosetta Stone" Tutorials&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows.Controls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserControl&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows.Controls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TextBlock&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows.Controls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Canvas&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows.Media&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows.Media&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SolidColorBrush&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Visibility&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FontWeights&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Windows.Media.Animation&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Storyboard&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadRootVisual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserControl&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="s2"&gt;"app.xaml"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;spinner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Spinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="s1"&gt;'Tarn'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Leah'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Alex'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Mick'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Rex'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Jack'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Jill'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Elle'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Spinner&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textBlocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout_root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centreClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;createTextBlocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MouseLeftButtonDown&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leftButtonDown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MouseLeftButtonUp&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leftButtonUp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MouseMove&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseMove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Storyboard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"sb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Completed&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enterFrame&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;centreClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Selected"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;createTextBlocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Expanding"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textBlocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;textBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;textBlocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;textBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout_root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centreClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;textBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;textBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;textBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HorizontalAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;textBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;textBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetPosition2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textBlocks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textBox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enterFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;oldAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;oldAngle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fabs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.90&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Expanding"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;expandComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textBlocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;expandComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Selected"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="n"&gt;expandComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedItem&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainText&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Opacity&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textBlocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedItem&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
          &lt;span class="n"&gt;textBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Opacity&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.010&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;expandComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expandComplete&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;createTextBlocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textBlocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedItem&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetPosition2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;leftButtonUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReleaseMouseCapture&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mouseMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;leftButtonDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CaptureMouse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clicked&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clicked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clicked&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TextBlock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Foreground&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SolidColorBrush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;White&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Children&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MouseEnter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseOver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MouseLeave&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseOut&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MouseLeftButtonDown&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseClick&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Children&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FontWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FontWeights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeftProperty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;currentY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TopProperty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;newX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;currentX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;newY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;currentY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;newY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mouseClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mouseOver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FontWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FontWeights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mouseOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FontWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FontWeights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SetPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeftProperty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TopProperty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SetPosition2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Visibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Visibility&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Collapsed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Visibility&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Visible&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I noticed when I uploaded the XAP that it was quite large (&amp;gt; 1Mb) for a very
small demo. I think this is because additional assemblies for dynamic
languages and IronPython are not included in the client Silverlight plug-in
and have to be included in the XAP.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/silverlight-dynamic-languages-sdk</guid><pubDate>Mon, 26 Jan 2009 15:12:00 +0000</pubDate></item><item><title>Goats, Cars and Babies</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Goats, Cars and Babies
    &lt;/h1&gt;
    &lt;p&gt;
    Jan 18, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I watched the movie "21" the other night and although I thought it was
rubbish, I was slightly intrigued in the explanation to a question posed in
the movie, which turns out to be the &lt;a href="http://en.wikipedia.org/wiki/Monty_Hall_problem"&gt;Monty Hall Problem&lt;/a&gt;.&lt;/p&gt;
&lt;div id="demo_container"&gt;&lt;/div&gt;
&lt;p&gt;I was surprised then, while catching up on feeds, to see &lt;a href="http://www.codinghorror.com"&gt;Jeff Atwoods&lt;/a&gt;
post &lt;a href="http://www.codinghorror.com/blog/archives/001203.html"&gt;The Problem of the Unfinished Game&lt;/a&gt; on a similar topic. Jeff asks&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let's say, hypothetically speaking, you met someone who told you they had
two children, and one of them is a girl. &lt;strong&gt;What are the odds that person has a
boy &lt;em&gt;and&lt;/em&gt; a girl?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It turns out that I've always hated this type of math despite generally
enjoying using math to solve engineering problems, having a pretty good
understanding of No-Limit Texas Hold'em and perpetually trying to crack all
those math tricks that make you do a series of seeded numerical operations to
magically reveal some significant number. For me the probability math in
quantum physics was bewildering and in statistical process control it was
incredibly boring as well.&lt;/p&gt;
&lt;p&gt;Asked this question last week and I would probably have meekly replied, "Umm,
maybe 50%?" and mumbled something about "stupid probability questions" then
maybe attempted to draw out some permutations before giving up with absolutely
no confidence in my answer.&lt;/p&gt;
&lt;p&gt;Having no confidence in my answer may have been wise in that case, but in his
following post about the answer to a similar problem, Jeff made a point that
resonated with me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You don't need to be a mathematician to prove this. I'm just a crappy
programmer, and even my crappy code can brute force the answer by simulating
results from thousands of games.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As soon as I read that I decided I was going to give the Monty Hall Problem
the same treatment, at that stage I'd only heard the explanation in the movie
which didn't make much sense to me.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameShow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prizeDoor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Creating Game, Prize Door is "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prizeDoor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;SelectDoor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;door&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;door&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Contestant Selects Door"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstSelection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;HostOpenDoor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prizeDoor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstSelection&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostOffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Host opens door"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostOffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ChangeDoor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Contestant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s2"&gt;"Takes Swap"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;"Keeps Original Choice"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;OpenDoor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alternativeDoor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostOffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstSelection&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedDoor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alternativeDoor&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstSelection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedDoor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prizeDoor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Contestant Selects"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedDoor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"and"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wins&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s2"&gt;"Wins Prize"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;"Looses"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;RunGame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GameShow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectDoor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HostOpenDoor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChangeDoor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenDoor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wins&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;RepeatRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repeats&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;wins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repeats&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RunGame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt; &lt;span class="n"&gt;wins&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wins&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;iterations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;swapWins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RepeatRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;stayWins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RepeatRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Overall Results&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Swap Wins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;swapWins&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s2"&gt;"%"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Stay Wins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;stayWins&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="s2"&gt;"%"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After some fooling around with Python I got my brute force results that proved
to me making the swap was the right choice. Actually I'd proved it to myself
by the time I'd written the code that works out which doors the host can open.
It turns out that if you select a door with a goat behind it first, the host
can only open one door to reveal a goat, the other is the car. And that is the
key; If your first selection is a goat which is a 2/3 chance, then the switch
will always yield a car (this is better than the 1/3 chance first selection).&lt;/p&gt;
&lt;p&gt;I've satisfied my interest in counter intuitive probability problems for now,
which is good because my friends are sick of me talking about them.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="jquery-2.1.3.min.js"&gt;&lt;/script&gt;
&lt;script src="swfobject.js" type="text/javascript" &gt;&lt;/script&gt;
&lt;script&gt;
    $(function() {
        swfobject.embedSWF("GameShow.swf", "demo_container", "302", "192", "9.0.0");
    });
&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/goats--cars-and-babies</guid><pubDate>Sun, 18 Jan 2009 14:36:00 +0000</pubDate></item><item><title>IronPython Asynchronous RSS Reader</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        IronPython Asynchronous RSS Reader
    &lt;/h1&gt;
    &lt;p&gt;
    Jan 13, 2009
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Reading RSS is a common and simple task in Python using the standard library,
unfortunately it uses parts of the standard library that are not implemented
in IronPython. Luckily it isn't very difficult using the .NET framework
either. There is an example &lt;a href="http://www.ironpython.info/index.php?title=Parsing_RSS&amp;amp;oldid=896"&gt;Parsing RSS&lt;/a&gt; on the &lt;a href="http://www.ironpython.info/index.php/Contents"&gt;IronPython Cookbook&lt;/a&gt;
site that demonstrates it.&lt;/p&gt;
&lt;p&gt;While this example was useful, I wanted to make the web request asynchronously
so I could use it in a WPF application without blocking the UI. I was happy to
find implementing it was quite familiar and easy, I've used the WebClient
object asynchronously many times in C# and I've passed event handlers as
parameters in Javascript, both of which are supported by IronPython and are
used in this module.&lt;/p&gt;
&lt;p&gt;In the implementation below I've used all XML processing code from the
&lt;a href="http://www.ironpython.info/index.php?title=Parsing_RSS&amp;amp;oldid=896"&gt;Parsing RSS&lt;/a&gt; example. I removed the class RSSFeedSubscriptions as I didn't
need the functionality and I felt it made the sample more clear. I split the
RSSFeedFetcher into an RSSFeed class for the feed data and an RSSFeedReader
class for handling the downloading and XML processing. I think it is much a
better separation of functionality.&lt;/p&gt;
&lt;p&gt;I found the the exception handling code in the original sample that outputs an
exception message was itself throwing an exception, so I removed it. I don't
think its ideal effectively suppressing exceptions and printing a message but
its good enough for what I'm using it for. If anyone has a suggestion or I
improve the module myself, I'll update this post.&lt;/p&gt;
&lt;p&gt;The example usage is shown in the Main section. I used an AutoResetEvent in
the test to keep the tread alive until the asynchronous event is fired and the
results are output.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;clr&lt;/span&gt;
&lt;span class="n"&gt;clr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'System.Xml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Net&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Xml&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;XmlDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;XmlTextReader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StreamReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MemoryStream&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RSSFeed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FeedURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;URL&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChannelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BadCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;


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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onComplete&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onComplete&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RSSFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;rssBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;xmlDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;XmlDocument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rssBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;xmlDoc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;rssNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmlDoc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"rss"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;channelNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rssNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChildNodes&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;channelNode&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;channelNodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;itemNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;channelNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectNodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChannelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;channelNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerText&lt;/span&gt;

                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;itemNode&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itemNodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;newitem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RSSFeedItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="n"&gt;newitem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;newitem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;newitem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"link"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;itemNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"guid"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                            &lt;span class="n"&gt;newitem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemNode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"guid"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newitem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BadCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BadCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Error Parsing Results"&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onComplete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ReadAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;webClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DownloadDataCompleted&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;
            &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FeedURL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DownloadDataAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Error Starting Read"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;System.Threading&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoResetEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Results!"&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Link&lt;/span&gt;

        &lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;feedReader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RSSFeedReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"http://feeds.feedburner.com/sharpthinking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;feedReader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitOne&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One of the things I like about IronPython 2 is that this code alone can be run
from the command line. Simply saving this code as rssreader.py and running it
from the command line with ipy rssreader.py will run the test code at the
bottom which will output recent posts from this weblog.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/ironpython-asynchronous-rss-reader</guid><pubDate>Tue, 13 Jan 2009 11:25:00 +0000</pubDate></item><item><title>A taste of Python</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        A taste of Python
    &lt;/h1&gt;
    &lt;p&gt;
    Dec 28, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I'm on a bit of a break and generally enjoying a holiday from programming, but
I am spending a couple of hours here and there learning some Python. I think
learning a new programming language is a good way to improve my general
development skills and knowledge. As I'm just learning and not under pressure
to actually build something its quite a lot of fun.&lt;/p&gt;
&lt;p&gt;I decided to learn either Python or Ruby. I understand both are fairly modern
high level languages, both are considered fun languages to use by their
advocates and both been implemented on top of the .NET dynamic runtime.&lt;/p&gt;
&lt;p&gt;I would like to try Ruby on Rails but I felt while I was on break from
programming, learning a new language and a new web framework was a bit much. I
decided on Python, mostly because I like the name, but also as I understand
Python has found use in all sorts of environments and for all sorts of
purposes including scientific computation, robotics, game development, and
business and web applications.&lt;/p&gt;
&lt;p&gt;I found &lt;a href="http://diveintopython.org/"&gt;Dive into Python&lt;/a&gt; to be awesome book for Java, C#
and C++ developers wanting to learn the Python language. And its free online
for browsing and downloading! I think it is very succinct in only explaining
what you need to know about the language, which makes it very readable.&lt;/p&gt;
&lt;p&gt;I was pretty excited about getting into IronPython after watching these two
&lt;a href="http://blogs.msdn.com/ironpython/archive/2007/06/18/videos-of-the-talks-demos-from-teched2007-in-orlando.aspx"&gt;videos&lt;/a&gt; from TechEd 2007 in Orlando. There is lots of interesting
information about IronPython and the Dynamic Language Runtime but there are
also some really cool demos.&lt;/p&gt;
&lt;p&gt;From the interactive console, Merlin (who apparently lives in all recent
versions of windows as a COM control) is summoned and instructed to do all
sorts of stuff, a Windows Form is created and run and then controls and events
are added while its running! There are also demos of Iron Python with Visual
Studio, ASP.NET and Silverlight. They kind of sold me on IronPython for now.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.codeplex.com/IronPython"&gt;IronPython 2&lt;/a&gt; has recently been released on CodePlex and its a pretty
straight forward download and install. Slightly surprisingly it only contains
a command line compiler, an interactive console, some libraries and sample
applications.&lt;/p&gt;
&lt;p&gt;There is also a project called &lt;a href="http://www.codeplex.com/IronPythonStudio"&gt;IronPython Studio&lt;/a&gt; which is a Visual
Studios extension to add support for IronPython development and debugging. I'm
going to start with just the base install and &lt;a href="http://www.flos-freeware.ch/notepad2.html"&gt;Notepad 2&lt;/a&gt; because I know
you can do some pretty cool with it and I'd like to see how it all works.&lt;/p&gt;
&lt;p&gt;Although IronPython is apparently compliant with the Python language, the
standard library, which is considered very powerful and extensive, is not
completely implemented. This basically means most Python code that makes use
of the standard library won't work in IronPython. Fortunately you can use most
of the .NET framework from IronPython meaning there is a pretty rich framework
available.&lt;/p&gt;
&lt;p&gt;I hope it is possible to implement the entire standard library on top of the
.NET framework, this would mean more Python code would work in IronPython and
IronPython code that didn't directly call into the .NET framework would run on
other platforms.&lt;/p&gt;
&lt;p&gt;As I'm happy to use IronPython and the .NET framework there is a great
resource called the &lt;a href="http://www.ironpython.info/index.php/Main_Page"&gt;IronPython Cookbook&lt;/a&gt; which has heaps of examples of
common programming tasks in IronPython and information about using IronPython
in different environments from the Microsoft Robotics kit to the XNA
framework.&lt;/p&gt;
&lt;p&gt;I was hoping to write about some examples in this post, but its become much
longer than I expected and I might wrap it up with some links I have found
useful or interesting along the way.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.devx.com/codemag/Article/39904"&gt;Introducing IronPython&lt;/a&gt; - A good article from DevX&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.resolversystems.com/" title="http://www.resolversystems.com/"&gt;Revolver Systems&lt;/a&gt; - An interesting spread sheet / IronPython mash-up.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/magazine/cc163344.aspx"&gt;IronPython and the Dynamic Language Runtime&lt;/a&gt; - An MSDN article by Bill
Chiles&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.zdnet.com/microsoft/?p=88"&gt;IronPython and ASP.Net: Two tastes that taste great together&lt;/a&gt; - A brief
article from ZDNet&lt;/p&gt;
&lt;p&gt;&lt;a href="http://devhawk.net/2008/11/24/IronPython+And+WPF+Part+5+Interactive+Console.aspx"&gt;IronPython and WPF &lt;/a&gt;- Just one of many great article from DevHawk which
I've since added to my RSS feed reader.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/magazine/cc300810.aspx"&gt;CLR Inside Out - IronPython&lt;/a&gt; - Another good MSDN by James Schementi&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.voidspace.org.uk/ironpython/index.shtml"&gt;The Voidspace IronPython Pages&lt;/a&gt; - A collection of IronPython resources&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.ironpythoninaction.com/"&gt;IronPython in Action&lt;/a&gt; - A book I haven't read and probably won't buy, but
may be of interest to others&lt;/p&gt;
&lt;p&gt;Hope you've found this interesting, I'm having a lot of fun and looking
forward to getting back to hacking some more IronPython.&lt;/p&gt;
&lt;p&gt;Please feel free to add a comment or point out any incorrect information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/a-taste-of-python</guid><pubDate>Sun, 28 Dec 2008 13:51:00 +0000</pubDate></item><item><title>A break I had to have</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        A break I had to have
    &lt;/h1&gt;
    &lt;p&gt;
    Dec 23, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've just finished up working for thinkgroup where I've been writing web based
enterprise software in .Net for the last three years. Despite the fact that I
enjoyed working there, after three years I really felt I needed a change.&lt;/p&gt;
&lt;p&gt;I'm going to take a break before I start looking for new jobs, this may not be
a good idea in the current global economic climate, but its a break I think I
had to have. I have no clear idea yet about where I want to work or even what
I want to do next. And besides Summer in Melbourne is the best time not to
have a job.&lt;/p&gt;
&lt;p&gt;It seems quite logical to get anther job writing web based software in
ASP.NET. I'm very passionate about it, I enjoy coding in C# and I think I am
good at designing and writing software systems to solve real world business
problems. In this area I would be looking for senior developer roles and good
salaries, but there are also other directions I would like to consider.&lt;/p&gt;
&lt;p&gt;I choose to study electronic over software engineering at university even
though I already really liked programming. I was looking for jobs in
electronics when I graduated, but after not finding one due to both a lack of
opportunities and lack of effort on my part, I ended up taking a full time
role developing software.&lt;/p&gt;
&lt;p&gt;I'd still like get a job designing electronics or low level programming but I
think I would probably need to get a more recent qualification and consider
re-locating to increase opportunities.  Another slightly more realistic option
I'm considering is getting a job writing in anther language and framework. I'm
interested in other languages and its a challenge I'd really like to take and
I think I would benefit from in the long term.&lt;/p&gt;
&lt;p&gt;While I'm on holiday I'll probably still do a couple of hours here and there
building a simple weblog with ASP.NET MVC I started and I'll be playing around
with Python and IronPython. I'd like to blog and do a presentation to the
VIC.NET User Group on some of the cool stuff you can do with IronPython but
I'll wait to see how motivated I am during my break before I commit to
anything.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/a-break-i-had-to-have</guid><pubDate>Tue, 23 Dec 2008 17:27:00 +0000</pubDate></item><item><title>Desktop Racer</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Desktop Racer
    &lt;/h1&gt;
    &lt;p&gt;
    Dec 16, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've finally got around to publicly hosting my DevSta entry - Desktop Racer.
Turns out it was pretty easy, I was just too burnt out by it to want to do
anything with it till now.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://static.sharpthinking.com.au/DesktopRacer/DesktopRacer.htm"&gt;Play Desktop Racer Now&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Looking at it now, I'm a little disappointed I didn't just go a little bit
further and add a two player mode as I think it would  have actually made it
fun to play. But honestly after the sixth day of the competition I was so over
it I didn't care.&lt;/p&gt;
&lt;p&gt;I would like to add the two player mode and some performance improvements to
make it a bit more fun to play, but I seriously doubt I'll ever get round to
doing it. I'm already half way into an ASP.NET MVC weblog project and I've
also suddenly become really interested in learning Python.&lt;/p&gt;
&lt;p&gt;I'm going to upload the source as well for anyone who is interested, but I
warn you its probably not a great example Silverlight application as I was
just learning what was a beta technology at the time. As the competition was
only 200 hours I didn't spend much time pondering what would be the best way
to implement it, I pretty much just got in and started coding. Some logic is
nicely packaged but some classes just ended up getting way to large and
unwieldy. I've since seen some good demos and blogs on patterns to help
separate out the presentation and logic.&lt;/p&gt;
&lt;p&gt;There are some cool-ish parts in the code; I like how the level and all the
level elements are defined in XAML so you can visualize the level at design
time in Visual Studios. I also kind of like how the all the level elements the
car can collide with implement an interface that has its geometric
information.  This allowed my to simplify the collision logic to two type of
geometric primitive and basically calculate and collision with all the level
elements in a single loop.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/desktop-racer</guid><pubDate>Tue, 16 Dec 2008 12:10:00 +0000</pubDate></item><item><title>SharpWeblog - Iteration 1</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        SharpWeblog - Iteration 1
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 29, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've finished all the &lt;a href="/journal/sharpweblog-project"&gt;initial requirements&lt;/a&gt; so I'll briefly describe how
its going.&lt;/p&gt;
&lt;h3&gt;The Data Layer&lt;/h3&gt;
&lt;p&gt;The data layer interface has methods to find, create, update and delete
stateless model objects. The data layer is implemented with Linq to Sql. As
the interface uses model data objects, the data layer implements the
repository pattern by mediating these objects with the Linq to Sql classes.&lt;/p&gt;
&lt;p&gt;This is nice as it means the rest of the application doesn't have a dependency
on the data layer implementation. This is good as I don't want to have to deal
with Linq to Sql classes through-out the entire application. There are tests
verifying what I would consider a more sane object model is correctly
implemented by the data layer.&lt;/p&gt;
&lt;p&gt;I personally don't think Linq to Sql should be considered as an ORM for most
web applications; It has no built in way of modeling many to many
relationships and it's much more difficult to use than most other ORM I've
worked with. I'm looking forward to trying the new Entities Framework, and
hope they have finally made a legitimate ORM.&lt;/p&gt;
&lt;h3&gt;Routing&lt;/h3&gt;
&lt;p&gt;I'm a big fan of both being able to write routing logic and routing logic
tests so easily. I highly recommend writing tests verifying your routes. I
briefly discussed how this is done in my previous post about &lt;a href="/journal/sharpweblog-project"&gt;testing this
project&lt;/a&gt;. The requested route is tested against all the routes in sequence
until a match it found. This means its very easy to add a route that
inadvertently catches a route that was supposed to be mapped by another route
further down the sequence.&lt;/p&gt;
&lt;p&gt;I think TDD is really important here as the routing can quickly get quite
complicated. Adding unit tests verifying specified routes are mapped to the
correct controller and actions will protect your routes. It's also generally a
quicker development cycle writing some route tests and then writing the route
logic than writing the route logic and testing it in the browser.&lt;/p&gt;
&lt;p&gt;If you insist on testing your routes in the browser or just want to see how
specific are mapped in action, &lt;a href="http://haacked.com/"&gt;Phil Haack&lt;/a&gt; has a neat little &lt;a href="http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx"&gt;route tester
tool&lt;/a&gt; that renders the route test information in the browser for any URL on
your site you enter.&lt;/p&gt;
&lt;h3&gt;Views&lt;/h3&gt;
&lt;p&gt;So far there has been no work done on the UI and the views are all pretty
basic. I'm using C# with strongly type ViewPages, but I would like to consider
using a dynamic language later in the project.&lt;/p&gt;
&lt;p&gt;I'm a huge fan of the templated ActionLink helper method, I included the
ASP.NET MVC Futures assembly without hesitation when I realised it wasn't
included in the Beta.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%=Html.ActionLink&amp;lt;HomeController&amp;gt;(m =&amp;gt; m.Category(category),category) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'm a little more nervously on the fence with regard to the RenderAction
method which allows you to call an action on the controller from within a view
to render a control. I can see the why its there; It provides a lot of
flexibility for making controls that can be responsible for getting there own
data and can be used by any view. This also means these user control
controller actions can be tested separately. On the other hand it appears to
be way to get around the MVC pattern where in many cases the controller itself
could call off to get the additional information and pass it down to view.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;% Html.RenderAction&amp;lt;HomeController&amp;gt;(m =&amp;gt; m.PostDetail(ViewData.Model.SelectedPost)); %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I could call a view from within another view and pass it a model from the
data already in the calling view I would be a lot more comfortable with it
(You probably can do that, I haven't worked it out yet). That way there would
be a nice way to call controls with model data without calling methods on the
controller from the view.&lt;/p&gt;
&lt;p&gt;EDIT: As soon as I tabbed to Visual Studio after posting this I noticed the
login control from the template renders a user control using RenderPartial
from the view. The user control inherits from ViewUserControl and can be
templated. An overloaded RenderPartial method allows you to pass model data to
the view. I've fallen to the lets get on with coding side of the fence.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;% Html.RenderPartial("LoginUserControl"); %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Membership and Roles&lt;/h3&gt;
&lt;p&gt;It wasn't in my initial requirements, but the MVC VS2008 Beta template comes
with some login in and registration pages using the default SQL Server
Membership provider. I am looking to leverage all the technologies I can to
build this site, so I decided I would use the default provider, if I could use
it and keep the code very testable.&lt;/p&gt;
&lt;p&gt;In the default template a database is generated with the membership and roles
tables and stored procedures used by the default membership provider already
created. As I already had a database, I used a tool called aspnet_regsql to
add the tables and procedures to my database. The tool is part of the .NET
framework and can also be used to generate scripts and works with SQL Server
Express.&lt;/p&gt;
&lt;p&gt;I had mixed emotions when I found this in the controller from the template. I
liked that they had thought about testing the controller (no tests are
actually included).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// This constructor is not used by the MVC framework but is instead provided for ease
// of unit testing this type. See the comments at the end of this file for more
// information.

public AccountController(IFormsAuthentication formsAuth, MembershipProvider provider)
{
    FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
    Provider = provider ?? Membership.Provider;
}

..

// The FormsAuthentication type is sealed and contains static members, so it is difficult to
// unit test code that calls its members. The interface and helper class below demonstrate
// how to create an abstract wrapper around such a type in order to make the AccountController
// code unit testable.

..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That great! Its a pattern I use in the project to avoid dependencies and
improve testability, the only problem is I don't think they've gone far
enough. I'm going to take it further and create a wrapper around Provider so I
can mock it and not have a dependency on System.Web in my controller unit
tests.&lt;/p&gt;
&lt;p&gt;EDIT: After writing this post I found I could reference System.Web in my tests
and not have a development web server fire up when the tests run. I was a
little surprised by this, and now I'm not sure what does fire up the
development web server. I'll post an update on this when I understand it more
clearly.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/sharpweblog--iteration-1</guid><pubDate>Sat, 29 Nov 2008 16:28:00 +0000</pubDate></item><item><title>SharpWeblog - Testing</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        SharpWeblog - Testing
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 24, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I'm right into the coding for this project at the moment, but the iteration
one requirements are all complete so its probably a good time to write some
posts and reflect on what has been implemented so far.&lt;/p&gt;
&lt;p&gt;As specified this project is use TDD for development. There has been a lot of
work put into ASP.NET MVC to make it a most testable framework. I would like
to discuss how various parts of this project have been tested.&lt;/p&gt;
&lt;p&gt;I am using Microsoft Unit Test Framework and &lt;a href="http://code.google.com/p/moq/wiki/QuickStart"&gt;MOQ&lt;/a&gt; for testing this
project. I had never used MOQ before, but I love it. For those that don't know
about MOQ its another mock testing framework, the thing I love about it is it
uses C# 3.5 so you can do a lot with lambda expressions.&lt;/p&gt;
&lt;h3&gt;Controllers&lt;/h3&gt;
&lt;p&gt;In the controller tests a real controller is created with a mocked
IWeblogRepository interface. This means the controller can be tested
independent of an IWeblogRepository implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestInitialize]
public void Setup()
{
    _mockRepository = new Mock&amp;lt;IWeblogRepository&amp;gt;();
    _controller = new HomeController(_mockRepository.Object);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Testing controllers is really testing two things, firstly that the controller
renders the correct view and secondly it sends the right data to that view.
The tests shown here validate the "Post" action on the HomeController.&lt;/p&gt;
&lt;p&gt;In this test the mock repository is setup to return a post when GetPost("Test-
Post-1") is called on it. When we call the controller with these parameters we
expect it to make the specified request to the mock repository. This test
ensure that when a post is found that "Post" view is rendered.&lt;/p&gt;
&lt;p&gt;The sharp eyes will notice that I'm not using the date parameters to find the
post from the repository. That functionality has not been implemented in this
iteration.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestMethod]
public void Post_FindsPost_RendersPostView()
{
    _mockRepository.Expect(m =&amp;gt; m.GetPost(It.Is&amp;lt;string&amp;gt;(i =&amp;gt; i == "Test-Post-1")))
                   .Returns(new PostModel());

    ViewResult result = _controller.Post(8,8,8,"Test-Post-1") as ViewResult;
    Assert.AreEqual("Post", result.ViewName);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also test the correct data is being sent to the view. I personally like
to separate these two tests, this may not be a universal opinion, but I think
its best to keep unit tests short and I think these tests are clearly testing
different expectations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestMethod]
public void Post_FindsPost_ReturnPostViewData()
{
    _mockRepository.Expect(m =&amp;gt; m.GetPost(It.Is&amp;lt;string&amp;gt;(i =&amp;gt; i == "Test-Post-1")))
                   .Returns(new PostModel() {
                                                Title = "Test Post 1",
                                                Content = "Test Post Content 1"
                                            });

    ViewResult result = _controller.Post(8, 8, 8, "Test-Post-1") as ViewResult;
    PostViewData data = result.ViewData.Model as PostViewData;

    Assert.IsNotNull(data);
    Assert.AreEqual("Test Post 1", data.SelectedPost.Title);
    Assert.AreEqual("Test Post Content 1", data.SelectedPost.Content);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two additional test were added to test other expectation of the "Post"
controller action. These test that the "Index" view is rendered when the
action is called with invalid parameters or can't find the post. This
functionality may change to display an error message or alternate posts when
the request is invalid in later iterations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestMethod]
public void Post_PostNotFound_RendersIndexView()
{
    _mockRepository.Expect(m =&amp;gt; m.GetPost(It.Is&amp;lt;string&amp;gt;(i =&amp;gt; i == "Test-Post-1")))
                   .Returns&amp;lt;PostModel&amp;gt;(null);

    ViewResult result = _controller.Post(8, 8, 8, "Non-Existing-Post") as ViewResult;

    Assert.AreEqual("Index", result.ViewName);
}


[TestMethod]
public void Post_InvalidRequest_RendersIndexView()
{
    ViewResult result = _controller.Post(null,null,null,null) as ViewResult;
    Assert.AreEqual("Index", result.ViewName);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Routes&lt;/h3&gt;
&lt;p&gt;Testing routes is testing Http Requests are correctly mapped to the
appropriate controller actions with the appropriate parameters. The below is
testing "~/Post/2008/10/20/Test-Post" request will call the HomeController
with the Post action and will also correctly populate the parameters.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestMethod]
public void PostRoute_TestSuccessfullRoute()
{
    var routes = new RouteCollection();
    var context = new Mock&amp;lt;HttpContextBase&amp;gt;();

    context.ExpectGet(m =&amp;gt; m.Request.AppRelativeCurrentExecutionFilePath)
           .Returns("~/Post/2008/10/20/Test-Post");

    SharpWeblogRouting.RegisterRoutes(routes);
    var routeData = routes.GetRouteData(context.Object);

    Assert.AreEqual("Home", routeData.Values["controller"]);
    Assert.AreEqual("Post", routeData.Values["action"]);
    Assert.AreEqual("20", routeData.Values["day"]);
    Assert.AreEqual("10", routeData.Values["month"]);
    Assert.AreEqual("2008", routeData.Values["year"]);
    Assert.AreEqual("Test-Post", routeData.Values["name"]);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can write a more route tests to test other requests when I have features
saying what should happen.&lt;/p&gt;
&lt;h3&gt;Data Layer&lt;/h3&gt;
&lt;p&gt;While I think its great we can "fake out" the IWeblogRepository to avoid using
an actual implementation while testing other areas of the system, I strongly
believe implementations of the IWeblogRepository should also be tested,
separately.&lt;/p&gt;
&lt;p&gt;The IWeblogRepository interface basically has methods to save, load, update
and delete model objects in a data storage. In this case it is storing the
data in a SQL Express 2005 database using LINQ to SQL.&lt;/p&gt;
&lt;p&gt;NOTE: The model classes are just data objects that have no information about
the data context or data state. This does result in some inefficiencies
updating the database, but the design was chosen as it clearly separates the
implementation of the data layer from the rest of the system.&lt;/p&gt;
&lt;p&gt;Anyway here are some typical tests, no mocking required here.&lt;/p&gt;
&lt;p&gt;NOTE: I think these tests should be written against an interface with our
implementation injected in. This would mean the same tests could be applied to
other IWeblogRepository implementations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestMethod]
public void PostCRUD()
{
    // Create
    PostModel post = CreateTestPost();
    _repository.SavePost(post);

    // Read
    PostModel findPost = _repository.GetPost(post.Id);
    Assert.IsNotNull(findPost);
    ValidatePosts(post, findPost);

    // Update
    findPost.Content = "Updated Content";
    findPost.Title = "Update Content";
    _repository.SavePost(findPost);

    // Read Update
    PostModel findUpdatedPost = _repository.GetPost(post.Id);
    Assert.IsNotNull(findUpdatedPost);

    // Delete
    _repository.DeletePost(findUpdatedPost);
    PostModel findDeletedPost = _repository.GetPost(post.Id);
    Assert.IsNull(findDeletedPost);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While I generally like small tests, I think an objects CRUD tests are best
tied up in one test because otherwise you end up testing "create" four times
to setup for each of the other tests. I would also generally implement the
entire CRUD of an object even if it wasn't all required just so as CRUD tests
can be performed and each operation can be tested.&lt;/p&gt;
&lt;p&gt;I have many more tests for the data layer but they are all pretty standard, so
no point discussing them further here.&lt;/p&gt;
&lt;h3&gt;MetaWeblog&lt;/h3&gt;
&lt;p&gt;The MetaWeblog API has also been (partly, so far) implemented for this
project. I will discuss the implementation further in another post but for now
we'll talk about testing it.&lt;/p&gt;
&lt;p&gt;The MetaWeblog is implemented in two clearly separate components. One part
processes the XML-RPC request and invokes methods on a IMetaWeblog interface
and returns XML-RPC responses to Http Response. The other is an implementation
of the IMetaWeblog, both can be tested separately.&lt;/p&gt;
&lt;p&gt;Testing the IMetaWeblog implementation is pretty straight forward. As the
IMetaWeblog implementation uses the repository through the IWeblogRepository
interface we can mock the repository and test the expected calls are made
against the interface.&lt;/p&gt;
&lt;p&gt;NOTE: Sharp eyes will notice I'm not validating the user name and password.
This isn't yet implemented.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestMethod]
public void NewPost_InvokesSaveMethod()
{
    MWAPost post = TestHelper.CreateTestMWAPost();
    Mock&amp;lt;IWeblogRepository&amp;gt; repository = new Mock&amp;lt;IWeblogRepository&amp;gt;();
    SharpMetaWeblogAPI model = new SharpMetaWeblogAPI(repository.Object);
    model.NewPost("100", "TestUser", "TestPassword", post, false);
    repository.Verify(m =&amp;gt; m.SavePost(It.Is&amp;lt;PostModel&amp;gt;(p =&amp;gt; ComparePosts(p, post))));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also test the return values of the IMetaWeblog methods with a known
responses from the IWeblogRepository mock.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestMethod]
public void GetPost_ReturnsRequestedPost()
{
    Guid id = Guid.NewGuid();

    PostModel post = new PostModel()
    {
         Title = "Test Title",
    };

    Mock&amp;lt;IWeblogRepository&amp;gt; repository = new Mock&amp;lt;IWeblogRepository&amp;gt;();
    repository.Expect&amp;lt;PostModel&amp;gt;(m =&amp;gt; m.GetPost(id)).Returns(post);
    SharpMetaWeblogAPI metaWeblog = new SharpMetaWeblogAPI(repository.Object);
    MWAPost findPost = metaWeblog.GetPost(id.ToString(), "none", "none");

    Assert.AreEqual(post.Id.ToString(), findPost.postID);
    Assert.AreEqual(post.Title, findPost.title);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Being able to unit test the MetaWeblogFramework is cool, but I may have taken
unit testing too far here, but I'll let you decide.&lt;/p&gt;
&lt;p&gt;The MetaWeblogFramework had to be implemented against HttpContextBase, with an
actual context object passed into the ProcessRequest method. An instance of
IMetaWeblogAPI is injected into the constructor to allow the framework to call
the interface methods on a real object after processing the request, kind of
like a call back or an event.&lt;/p&gt;
&lt;p&gt;The structure of most of tests I have written so far is to mock a
HttpContextBase request and send it to a real instance of MetaWeblogFramework
which has been constructed with a mock IMetaWeblog API.&lt;/p&gt;
&lt;p&gt;This way I can verify that with known HTTP requests and payloads the expected
methods on the IMetaWeblog interface are called. The test below creates an
XmlRpcRequest object which the HttpContextBase mock will return when the
MetaWeblogFramework ProcessRequest method requests the
HttpContext.Request.InputStream. The test expects the
IMetaWeblogAPI.NewPost(..) method to be called with known parameters.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TestClass]
public class MetaWeblogFrameworkTest
{
    Mock&amp;lt;HttpContextBase&amp;gt; context;
    Mock&amp;lt;IMetaWeblogAPI&amp;gt; api;
    Byte[] _output;

    [TestInitialize]
    public void Setup()
    {
        context = new Mock&amp;lt;HttpContextBase&amp;gt;();
        api = new Mock&amp;lt;IMetaWeblogAPI&amp;gt;();
        _output = new Byte[1000];
        context.ExpectGet(m =&amp;gt; m.Response.ContentType).Returns("text/xml");
        context.ExpectGet(m =&amp;gt; m.Response.OutputStream).Returns(new MemoryStream(_output));
    }

    [TestMethod]
    public void ProcessRequest_MockRequestNewPost_InvokesNewPostMethod()
    {
        MWAPost sentPost = TestHelper.CreateTestMWAPost();
        XmlRpcRequest mockRequest = new XmlRpcRequest()
        {
            MethodName = "metaWeblog.newPost",
            Params = new List&amp;lt;Param&amp;gt;()
            {
                new StringParam("TestBlogId"),
                new StringParam("TestUserName"),
                new StringParam("TestPassword"),
                new PostParam(sentPost),
                new StringParam("False"),
            }
        };

        context.ExpectGet(m =&amp;gt; m.Request.InputStream).Returns(mockRequest.CreateStream());
        MetaWeblogFramework framework = new MetaWeblogFramework(api.Object);
        framework.ProcessRequest(context.Object);

        api.Verify(m =&amp;gt; m.NewPost("TestBlogId", "TestUserName","TestPassword", It.Is&amp;lt;MWAPost&amp;gt;(a=&amp;gt;sentPost.Match(a)) , true));
    }

    ..
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Testing the MetaWeblogFramework was a little difficult, and I had to create a
whole range of additional objects and methods to perform the tests. I might
re-visit the MetaWeblogFramework implementation later in the project and re-
write it to be more testable.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Well I managed to test a whole range of components in this ASP.NET MVC
project, but my overall coverage is still pretty low (32% over the entire
project). I'll keep you updated with how testing is going though-out the
project and I'll try to get those coverage stats up by the drop of the first
iteration this week.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog.sharpthinking.com.au/image.axd?picture=WindowsLiveWriter/SharpWeblogTesting_7E35/image_2.png"&gt;image&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/sharpweblog--testing</guid><pubDate>Mon, 24 Nov 2008 15:54:00 +0000</pubDate></item><item><title>SharpWeblog Project</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        SharpWeblog Project
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 21, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;I've just started a small project building a simple weblog site using ASP.NET
MVC. I know there are heaps of blog sites out there but I thought it would be
a good project to get familiar with ASP.NET MVC, JQuery and Silverlight 2.&lt;/p&gt;
&lt;p&gt;I don't like writing about projects I'm going to build, but I'm hoping I can
get a couple of the guys from work involved to share the learning and the
work. I'm also going to hassle a few graphic designers I know and try to get
little free consulting and maybe even some design work.&lt;/p&gt;
&lt;p&gt;It will be used it to drive this blog instead of BlogEngine.NET which we are
currently using.&lt;/p&gt;
&lt;p&gt;To keep things simple the project will use the database schema and parts of
the Metaweblog API implementation from the BlogEngine.NET project. LINQ to SQL
will be used to quickly get some ORM mappings to the database schema. It will
also use the standard ASP.NET Membership and Roles providers for SQL Server.
Some Silverlight 2 will be added for some cool menus and navigational
controls.&lt;/p&gt;
&lt;h3&gt;Tools, Technologies and Methodologies&lt;/h3&gt;
&lt;p&gt;The project will use TDD, Agile methodologies and identify design patterns
where appropriate.&lt;/p&gt;
&lt;p&gt;For now the code will be hosted on our internal Subversion server, but I would
like to move it CodePlex eventually. I will move it sooner if anyone else is
interested in checking out the code or contributing to the project. I would
like to use a build machine, but I'm not sure if there is support for this at
CodePlex, but we'll find out.&lt;/p&gt;
&lt;p&gt;The project will be developed with .NET 3.5 SP1 and ASP.NET MVC Beta. The
Microsoft Unit Test framework will initially be used for unit testing but a
Mock Testing framework may be introduced later.&lt;/p&gt;
&lt;p&gt;The project should be able to be run and debugged using the Visual Studios
internal development web server and SQL Server Express, basically by pressing
F5 after checking out the code.&lt;/p&gt;
&lt;h3&gt;Initial Requirements&lt;/h3&gt;
&lt;p&gt;In agile fashion the project will start with some initial requirements,
additional requirements can be added as the project evolves.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A default or index page showing recent posts in reverse chronological
order&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A post page, using the same URI routing as BlogEngine.NET&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A Metaweblog API implementation to support Windows Livewriter for
publishing and managing content on the weblog&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So that's it for now, hopefully we can knock this first iteration together
pretty quickly.&lt;/p&gt;
&lt;h3&gt;Tasks&lt;/h3&gt;
&lt;p&gt;I don't want to get to far into fine grained tasks as I don't have to estimate
the project time or cost, but I do want to cover the basic tasks as it does
give an indication of what I expect will need to be done for this phase.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create the database&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the LINQ to SQL classes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Data Layer and a Data Layer Interface&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write some Data Layer Tests&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Model (tests?)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Controller&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write some Controller Tests&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Setup some routes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the index view&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the post view&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Implement the Metaweblog API&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Lets Rock'n'Roll&lt;/h3&gt;
&lt;p&gt;Hopefully I'll be posting the learning from phase one and the goals of phase
two next week.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/sharpweblog-project</guid><pubDate>Fri, 21 Nov 2008 14:47:00 +0000</pubDate></item><item><title>SQL Sever 2005 Full Text Indexing</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        SQL Sever 2005 Full Text Indexing
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 14, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Till now I've always had problems getting SQL Server 2005 Full
Text Indexing (FTI) to perform well in real world scenarios. Recently I found
it was because I wasn't implementing it correctly, so I'll post this tip which
will hopefully help myself and others get it right next time.&lt;/p&gt;
&lt;p&gt;The query is a user entered keyword search on text columns using the FTI
predicates and T-SQL predicates on other relational columns. The query is over
a moderately large number of and returns paged results.&lt;/p&gt;
&lt;p&gt;FTI is basically the ability to search from text columns where the text has
been indexed with natural language, relevance and ranking considerations built
in. XML and other data type can also be index natively, but I haven't used
that. FTI in SQL Server 2005 uses the Microsoft Search Service. This service
is not actually part of SQL Server itself, it is also used by Exchange and
Sharepoint. I think you can use the service directly with a Sharepoint SDK.&lt;/p&gt;
&lt;p&gt;To use Full Text Indexing in SQL server you must enable full text indexing to
the entire database and the table and columns containing the searchable text.
It can all be setup from SQL Management Studio, but it does create a new
physical folder in addition to the normal database MDF and LDF files. Once
this has been done additional FTI predicates can be used on the selected
columns.&lt;/p&gt;
&lt;p&gt;As SQL Server Express doesn't support FTI, I think it is worth considering
optionally supporting normal T-SQL similar to applications support querying
using the FTI and  functions. Its nice to be able develop or even deploy with
SQL Server Express even though the text searching is not indexed using the
Search Services. But this really depends on circumstances, I just wanted to
note supporting both is an option.&lt;/p&gt;
&lt;p&gt;I found a query similar to this example was taking ages over a large amount of
data. It was unacceptable, we had to render all pages in less than 4 seconds
under moderate load and we expected to get less than two seconds. We we
getting search times alone in double figures for some keywords.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM
(
      SELECT *, ROW_NUMBER() OVER ([DocumentInformation].PublishDate ASC) as RowNumber,
      FROM Document
      JOIN DocumentInformation ON DocumentInformation.DocumentId = Document.DocumentId
      JOIN UserAccount Owner ON Document.OwnerId = UserAccount.UserId
      JOIN UserAccount Publisher ON Document.PublisherId = Publisher.UserId
      WHERE [Document].Published = 1 AND
            [Owner].IsActive = 1 AND
            [Publisher].IsActive = 1 AND
            CONTAINS([Document].DocumentText, "FTI*" )
) x
WHERE RowNumber BETWEEN 1 AND 10
ORDER BY RowNumber
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below has an additional inner query on the document table alone, using just
the CONTAINS predicates on the document text. The additional tables are joined
to the results and the normal T-SQL predicates are added.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT *
FROM
(
    SELECT *, ROW_NUMBER() OVER ([DocumentInformation].PublishDate ASC) as RowNumber,
    FROM
        (SELECT *, DocumentId
        FROM dbo.Document
        WHERE CONTAINS(([Documemnt].ProductKeywords, "FTI*" )) p
        JOIN DocumentInformation ON [DocumentInformation].DocumentId = p.DocumentId
        JOIN UserAccount Owner ON Document.OwnerId = UserAccount.UserId
        JOIN UserAccount Publisher ON Document.PublisherId = Publisher.UserId
        WHERE [Document].Published = 1 AND
              [Owner].IsActive = 1 AND
              [Publisher].IsActive = 1
) x
WHERE RowNumber BETWEEN 1 AND 10
ORDER BY RowNumber
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I gave this a go after reading an &lt;a href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/ftslesld.mspx"&gt;FTI Best Practice article&lt;/a&gt; that
indicated the searchable text rows couldn't be excluded from a search due to
the search service running in a separate process. The result of this subtle
change was amazing, the searches were obviously much quicker and returned the
same result set. Under load test we found this change alone improved the
average execution time of all pages on the website by a massive 30%.&lt;/p&gt;
&lt;p&gt;In addition to this the FTI best practices document also recommends the
following optimizations to improve full text performance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/ftslesld.mspx#Embedding_Filter"&gt;Consider embedding filter conditions as keywords in the indexed text&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/ftslesld.mspx#TextData"&gt;Combine text data to reduce the number of keys returned&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/ftslesld.mspx#CONTAINSTABLE"&gt;The CONTAINSTABLE function can perform better than CONTAINS&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We ended up implementing all the recommendations above to try and get the most
out of the FTI service. After all this we got search times down to about a
second under light load, this was cool but we had grander plans for an in
memory search using of expression trees for search filters and LINQ for
querying the in memory objects. I hope to chronicle this in future posts as
initial performance testing shows much quicker search times and a significant
capacity load increase by removing searching from SQL Server which &lt;strong&gt;was&lt;/strong&gt;
previously our performance bottleneck.&lt;/p&gt;
&lt;p&gt;In SQL Server 2008 they have apparently integrated  FTI into the SQL Server
process which appears to be the cause of my original performance problem. But
so far I've only heard bad things, hopefully these issues will be cleaned up
before I start using it.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://technet.microsoft.com/en-us/library/ms142541(SQL.90).aspx"&gt;2005 Full-Text Search Architecture&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://technet.microsoft.com/en-us/library/ms142541.aspx"&gt;2008 Full-Text Search Architecture&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/sql-sever-2005-full-text-indexing</guid><pubDate>Fri, 14 Nov 2008 09:31:00 +0000</pubDate></item><item><title>Liberation Day - Power To Developers</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Liberation Day - Power To Developers
    &lt;/h1&gt;
    &lt;p&gt;
    Nov 13, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I'm not in anyway formally associated Microsoft other than
that I've been primarily using their tools to develop software for the over
the last few years, so I was a little excited I was invited to Liberation Day
in Sydney after placing 2nd in DevSta. I thought it would be great to see
Steve Ballmer, infamous for this &lt;a href="http://au.youtube.com/watch?v=8To-6VIJZRE&amp;amp;feature=related"&gt;famous clip&lt;/a&gt; and &lt;a href="http://au.youtube.com/watch?v=wvsboPUjrGc&amp;amp;NR=1"&gt;this&lt;/a&gt;. He is, of
course, also the Microsoft CEO and was going to announce Azure the new
Microsoft cloud computing platform. He was sure to be entertaining anyway.&lt;/p&gt;
&lt;p&gt;As I'd never been to Sydney I decided I could spend a couple of days on Bondi
Beach with my girlfriend and drop into the conference and while I was there.
I'm expecting to be looking for a new job from next year, probably not in
Sydney, but I thought it would also be good to see who was there and meet a
few people anyway. I was expecting there would be a couple of DevSta judges
there too.&lt;/p&gt;
&lt;p&gt;There were heaps of people at the event, I wouldn't be surprised if there
actually was the 1000 developers the flyer claimed there would be. It was
cool, I can't image an event with that many developers in Melbourne. It was
streamed live and can be replayed on the &lt;a href="http://www.microsoft.com/australia/powertodevelopers/live_rally.aspx"&gt;Power To Developers&lt;/a&gt; site. I
watched Steve get up and do his thing, and it was fun, not rock'n'roll, but
fun. Unfortunately I wasn't feeling very well, almost feverish, and I had to
duck out to find a chemist.&lt;/p&gt;
&lt;p&gt;Gianpaolo Carraro's presentation attempted to demonstrate how easy developing
cloud solutions for the Azure was with Visual Studios 2008. I thought it was
pretty cool despite most of his demos failing in ways he couldn't have
imagined. The Azure platform sounded pretty awesome, basically allowing custom
.Net assemblies to be uploaded and invoked on Microsoft servers. He also
managed to successfully demonstrate the cloud emulator for locally running and
debugging cloud applications. I also really like the idea of being able to a
write LINQ query joining two data tables from a SQL Server Service in the
cloud.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/gp/pdp/profile/A2D5YJLCHYWN9Q"&gt;Mike Culver&lt;/a&gt; presented the &lt;a href="http://aws.amazon.com/"&gt;Amazon Web Services&lt;/a&gt; cloud solutions to the
Vic.NET user group about a year ago and I think he sold their technology and
the possibilities better. He showed the architecture of a video compression
service that used a message queue service to queue requests and a controller
application that can automatically rent, build and deploy servers capable of
processing the requests. I thought it was pretty cool. I think Microsoft are a
long way behind, but we'll see what the software giant is capable of. I'm
certainly keen to get in and give it a go.&lt;/p&gt;
&lt;p&gt;I still wasn't feeling well and missed most of Tim Sneath which was a shame,
Brodie had seen him in London years ago but said he "knew his stuff". At the
networking drinks I chatted with &lt;a href="http://delicategeniusblog.com/"&gt;Michael Kordahi&lt;/a&gt; which was kind of fun,
he'd judged my entry and he is a Silverlight guy. He introduced me to a couple
of people, one of which was &lt;a href="http://togetheronline.com.au"&gt;Jeremi Kelaher&lt;/a&gt; who does &lt;a href="http://strangedevices.wordpress.com/"&gt;Strange Devices
Podcasts&lt;/a&gt;. I also ran into &lt;a href="http://blog.tatham.oddie.com.au/"&gt;Tatham Oddie&lt;/a&gt; on the way out who I'd seen
present MVC stuff at REMIX and Vic.NET. I would have liked to have chatted
with Andrew Coates who is a great presenter and was also a DevSta judge, but I
didn't end up seeing him this time.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/liberation-day--power-to-developers</guid><pubDate>Thu, 13 Nov 2008 22:40:00 +0000</pubDate></item><item><title>DevSta {Challenge 2008} - 2nd Place!</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        DevSta {Challenge 2008} - 2nd Place!
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 23, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;The winners for &lt;a href="https://web.archive.org/web/20100130005530/http://devsta.microsoft.com.au:80/about.aspx"&gt;DevSta {Challenge 2008}&lt;/a&gt; have been announced: &lt;a href="https://web.archive.org/web/20091130054324/http://devsta.microsoft.com.au:80/winners.aspx"&gt;GPS Tag
wins Microsoft Devsta Challenge&lt;/a&gt;. My &lt;a href="/journal/devsta-challenge-2008"&gt;Desktop Racer&lt;/a&gt; entry came 2nd. I'm
naturally disappointed I didn't win, but definitely happy with 2nd. It was
heaps of fun entering and I'm glad the stress is over ;-)&lt;/p&gt;
&lt;p&gt;Congratulations to all the entrants, particularly the winners &lt;a href="http://jsarge.wordpress.com/"&gt;Jarred
Sarge&lt;/a&gt; and &lt;a href="http://wolfbyte-net.blogspot.com/"&gt;Michael Minutillo&lt;/a&gt; for their GPS Tag Game. Hopefully we'll
compete again next year.  Enjoy Vegas boys.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/devsta-challenge-2008--2nd-place</guid><pubDate>Thu, 23 Oct 2008 13:40:00 +0000</pubDate></item><item><title>Command Line Arguments</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Command Line Arguments
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 21, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I still write heaps of little console applications to do all sorts of things.
Most of the console applications I write are tools for tasks like deployment,
manipulating data and processing images. Usually they are for my own use, if I
find one really useful I might pass it on to the rest of our development team
or publish it on this blog.&lt;/p&gt;
&lt;p&gt;For these little tools, I prefer using console applications over windows
application as I feel they are more useful. Even though its easier to use a
tool that has a little GUI, its not more useful if you ever want to automate
it.&lt;/p&gt;
&lt;p&gt;Ideally I would prefer to write assemblies and use them from Powershell as it
would provide the greatest usage flexibility. But it also requires competence
scripting with Powershell. I was very excited about Windows Powershell when it
was released, but a year on, I still haven't made it the integral part of my
development toolset I thought it would be. This is an area I want to improve
on, but this post is about command line arguments.&lt;/p&gt;
&lt;p&gt;Generally these little non production, internal applications are quite light
weight and fit for purpose. I find I often write simple logging and command
line processing that is also fit for purpose. This is fine, its usually very
simple, quick to write and works. The problem is that I write this same
functionality over and over again for each little application.&lt;/p&gt;
&lt;p&gt;I was toying with the idea of writing a generic command line parsing class. I
figured I could use reflection on a settings class decorated with attributes
describing the specific command line syntax. I figured I could even generate
the help text using reflection too. While this seemed like a great idea, I am
aware of time I've previously spent (wasted?) re-writing code where a
perfectly good open source solution is available.&lt;/p&gt;
&lt;p&gt;So I did a search and found a couple of existing command line solutions. Not
surprisingly, given they were both written in .NET 2.0+, they both used
reflection and attributes. I ended up using the &lt;a href="http://www.codeplex.com/commandline"&gt;Command Line Parser
Library&lt;/a&gt; from CodePlex. I'm not sure if it does everything, but it does
everything I need for 99% of the console applications I write that require
command line processing.&lt;/p&gt;
&lt;p&gt;Anyway here is all the code from my test application:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Options
{
    [Option("r", "run", HelpText = "Run the export", Required = true)]
    public bool Run;

    [Option("n", "Name", HelpText = "Use a specific name")]
    public string Name = String.Empty;

    [HelpOption(HelpText = "Dispaly this help screen.")]
    public string GetUsage()
    {
        HelpText help = new HelpText(new HeadingInfo("Hello WorldExample", "1.0.0"));
        help.Copyright = new CopyrightInfo("sharpthinking", 2008);
        help.AddPreOptionsLine("Usage: Hello --run");
        help.AddOptions(this);
        return help;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Options options = new Options();
        if (Parser.ParseArguments(args, options, Console.Error))
        {
            string name = string.IsNullOrEmpty(options.Name) ? "World" : options.Name;
            Console.WriteLine("Hello {0}!", name);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we get the expected usage from the command line&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog.sharpthinking.com.au/image.axd?picture=WindowsLiveWriter/CommandLineArguments_AF9B/image_2.png"&gt;image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now I have to make sure I bundle the console applications with an external
assembly, but it does make using command line arguments easy.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/command-line-arguments</guid><pubDate>Tue, 21 Oct 2008 11:28:00 +0000</pubDate></item><item><title>Functional Programming</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Functional Programming
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 19, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;h3&gt;&lt;strong&gt;The Problem, The Opportunity&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Brodie and my initiatives in the area of search have been rewarded by
management with a two week project to improve search on our sites. In the two
weeks we also intend to fix one of the darkest corners of the code base, the
Product/Site publishing logic, which is the main reason the search needs
revisiting. We couldn't get the two weeks just to improve infrastructure, so
we promised sub one second search results and better relevance as a carrot.&lt;/p&gt;
&lt;p&gt;The web application powers multiple sites (&amp;gt;100) and has lots of products
(&amp;gt;100,000). There needed to be some way of controlling what products belonged
to what site. As products can appear on multiple sites and new sites are
constantly being added, this is not trivial. The solution implemented is a
site publishing rules system. Products can be &lt;strong&gt;&lt;em&gt;excluded&lt;/em&gt;&lt;/strong&gt; from a site by
Classification, Product or Brand.&lt;/p&gt;
&lt;p&gt;So far the logic is pretty simple, but another type of rule was also added.
Products, Classifications and Brands can also be made &lt;strong&gt;&lt;em&gt;specific&lt;/em&gt;&lt;/strong&gt; to a site
(they can actually be made specific to multiple sites). The logic is that a
Product, Classification or Brand that is specific to a site will appear on
that site, but not on other sites. In my opinion this is kind of crazy, but
the thinking is; If you have a very specific product that should only be
published on one or two sites, its easier to make it specific to those two
sites rather than excluding it from every other site and new sites. Ok, fair
enough.&lt;/p&gt;
&lt;p&gt;The problem is that this logic is scattered through all our layers down to the
database. There is duplication of logic over separate layers causing
maintenance problems and it has been identified as a clear performance bottle-
neck of any product list retrieval operation.&lt;/p&gt;
&lt;p&gt;The scope of this prototype is a quick, robust, maintainable and fully unit
tested solution that implements the product site publish logic.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;These rules are held in 6 SQL joining tables, which I think is interesting
itself. The primary keys are not unique across products, brands and
classifications meaning to enforce referential integrity and not allow nulls
you &lt;strong&gt;do&lt;/strong&gt; need a few tables. While I think this is a very good argument to
have primary keys unique across all your database objects, its not always an
option.&lt;/p&gt;
&lt;p&gt;We could also have used 3 tables by combining the excluded and specific tables
and adding a flag or lookup id. I think less is better, less tables means we
can be more flexible with how we manipulate the data in code. Even in T-SQL I
think I'd prefer less tables, less joins and more where statements - de-
normalized. But that just my opinion in this scenario.&lt;/p&gt;
&lt;p&gt;As I move this logic out of the database and into managed code I'll end up
abstracting the data to one table anyway, for this prototype it doesn't matter
how the rules are created or persisted.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Functional Languages&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now back to the title of this post, I think this problem is really suited to
functional programming. I've become more interested in functional programming
after listening to a &lt;a href="http://herdingcode.com/?p=45"&gt;Heading Code pod cast with Matt Podwysocki&lt;/a&gt;. My
experience with functional programming is limited and I've never found it very
exciting. I learnt a bit of Haskel to help a mate with a computer science
subject he was doing, but I was never really going to get to into it. At that
time I believed all languages were inferior to C++.&lt;/p&gt;
&lt;p&gt;As I've become more involved in software development I think its more
important to use other languages. There is an inevitability that your language
of choice will be relegated to legacy tasks as newer languages emerge better
suited modern computing and user experience. C# code making heavy use of
generics, extension methods, anonymous delegates and LINQ is just not the same
language as the C# that rolled out with .Net 1.0. Dynamic and functional
languages are becoming first class citizens.&lt;/p&gt;
&lt;p&gt;I'm not actually going to write this in F# for now, but I think the way I've
implemented my solution in C# is very functional although I leverage some OO
concepts I might not be able to use in a strictly functional language.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Solution Overview&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Instead of having all these separate lists of rules I've just created one list
which can be queried. It makes heavy use of what Rob Conery described as a
Pipes and Filters pattern in the ASP.NET MVC Storefront series. The pipes and
filters are implemented as extension methods that add filters and data
transformations. A collection of these small and easily testable extension
methods can be used to build very powerful data retrieval and processing
constructs to clearly describe business logic.&lt;/p&gt;
&lt;p&gt;The scope of the prototype is really just a single function that implements
the site publishing logic described earlier. It takes a Product, Site and List
of Rules as parameters and returns the publish rule.&lt;/p&gt;
&lt;p&gt;As soon as I started thinking about a solution to the problem I knew there was
one thing I didn't want to do. I could see there was potentially a lot of
repeated code for calling logic for products, classifications and brands
individually. There is no need as it is the same logic that needs to be
applied to each. I wanted to find a more elegant solution. I used an object
orientated solution; the Rule class itself is abstract and is inherited by
ProductRule, ClassificationRule and BrandRule.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;abstract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Rule&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SiteId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuleType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;abstract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only thing implemented by the specific rule classes is how to bind the
Value to a property on the Product class.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ClassificationRule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassificationId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ProductRule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;..&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I could have implemented this in a more functional way, but it does seem like
a clean solution given I am using C#.&lt;/p&gt;
&lt;h3&gt;Testing the Bits and Pieces&lt;/h3&gt;
&lt;p&gt;Testing the extension methods is easy with such simple domain objects. For
most of the extension methods I only wrote a couple of small tests like this:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;[TestMethod]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;FromSite_ReturnsSiteRule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ProductRule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SiteId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="na"&gt;[TestMethod]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;FromSite_FiltersSiteRule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ProductRule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SiteId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AreEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I used LINQ constructs rather than the trusty delegates I use everywhere else
for the extension methods. I like it, its clear and easy to read. And this is
my point, its changed the language dramatically.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FromSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SiteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SiteId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SiteId&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;&lt;strong&gt;Generic Test Classes&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;I'm going to start using the the Microsoft Visual Studios Unit Test Framework.
It means people who don't use the same testing framework flavour don't get
build errors when they download code download and try to build it. As the
attributes work with NUnit GUI and TestDriven.NET I'm happy to use it for now.
I not sure if there is any mock testing support, but it does allow inheriting
templated test methods.&lt;/p&gt;
&lt;p&gt;For the same reason I didn't want to write code that called product rules,
classification rules and brand rules individually, I don't want to write tests
for them individually either.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;GenericSiteRulesTests&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[TestMethod]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ProductWithNoRules_ProductIsReturned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuleLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;[TestMethod]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ProductIdCanBeExcluded_ExcludedProductIsNotReturned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;RuleType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Excluded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)}};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuleLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The way this is done I think is pretty cool, I did something similar when I
was looking at testing Text Search algorithms recently. All the generic rule
tests are written in a generic test class which must be templated to a type
the derives from Rule.  The class doesn't have a test class attribute. This is
because the test classes we want to run are actually classes that inherit from
the generic test class.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;[TestClass]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ClassificationSiteRulesTests&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GenericSiteRulesTests&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ClassificationRule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="na"&gt;[TestClass]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;ProductSiteRulesTest&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GenericSiteRulesTests&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProductRule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="na"&gt;[TestClass]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;BrandSiteRulesTest&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GenericSiteRulesTests&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BrandRule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These classes do have the test class attributes. Although they are empty, they
expose all the templated methods inherited from the generic test class. These
methods can be executed by a test runner.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="nunit_gui.png"/&gt;&lt;/p&gt;
&lt;p&gt;NUnit GUI has running all the tests.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;The Logic&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Ok, so its not really "clear and easy to read", but it is very compact and
concise.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;RuleLogic&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ProcessRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;siteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// return false if there is any exclusion rule that applies to this product&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;siteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuleType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Excluded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppliesTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// every rule type (ie ProductRule) that any site has a specific rule relating too&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuleType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Specific&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                                   &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppliesTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                                   &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RuleTypes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// this product requires a specific rule of this type&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RuleType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Specific&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTarget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;siteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppliesTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;**Conclusion **&lt;/h3&gt;
&lt;p&gt;It all seems fine, all the 33 tests are running and passing, coverage is at
100%.. still I have some strange feeling there is still a critical bug in
there, somewhere.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image" src="coverage.png"/&gt;&lt;/p&gt;
&lt;p&gt;We'll wait and see Monday morning if the project will go ahead and this
prototype can be extended into part of a production quality product publishing
rules engine. Oh, and the sub 1 second product searching..&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/functional-programming</guid><pubDate>Sun, 19 Oct 2008 19:18:00 +0000</pubDate></item><item><title>Text Searching</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Text Searching
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 14, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Brodie and I recently started presenting code, prototypes and ideas to the
other Devs we work with. This post came about after Brodie presented a search
engine prototype. He was interested to find if we could load about 100,000
pages of text to in-memory collections for fast searching. The prototype used
WCF and LINQ to create a RESTful server that binds to a HTTP endpoint. I
thought it was a really cool prototype, it used less memory than I expected
and was many orders a magnitude faster than the incumbent full-text indexed
SQL query.&lt;/p&gt;
&lt;p&gt;We were discussing how we could make non-cached searches faster and agreed the
String.IndexOf() and String.Contains() methods inside the filtering and
relevance Linq queries would be the best place to start. I probably wouldn't
have done anything more with it but I couldn't stop thinking about how
different indexing full-text methods we discussed might perform, particularly
a tree base algorithm.&lt;/p&gt;
&lt;p&gt;The following week I presented a text search engine interface, some
implementation prototypes, a testing framework prototype and the use of the
&lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt; to inject concrete implementations of a text search engine
interface into Brodies prototype. I'd already taken this way further than I
ever intended, but I decided that since I had gone this far I might as well
wrap it up in a blog post too.&lt;/p&gt;
&lt;p&gt;I started off by creating a search engine interface. I used an interface from
the start as I wanted to try mock testing and dependency injection.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITextSearchEngine&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FindAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The most simple search engine is just using a string and the methods on
System.String. It shows how easy it is to implement the ITextSearchEngine
interface:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SimpleTextSearch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITextSearchEngine&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_contents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#region ITextSeach Members&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_contents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_contents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_contents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_contents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FindAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#endregion&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Search Engine Test Framework&lt;/h3&gt;
&lt;p&gt;The test framework is a custom framework that can load types that implement
ITextSearchEngine, ITestHarness and ITextDataStore from external assemblies at
runtime. ITestHarness instances are created and passed new instances of
ITextSearchEngine and ITextDataStore. The test methods in the test harness are
executed and the results a saved as XML and also transformed to HTML.&lt;/p&gt;
&lt;p&gt;The ITextDataStore was added to make it possible to write tests that are
independent of actual data. This is for performance tests where the data will
effect the results. I intend to verify this by running a tests against
different data.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITextDataStore&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;SearchText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The ITestHarness interface must be implemented by test classes. The Create(..)
method allows the test framework to inject ITextSearchEngine and
ITextDataStore implementations into the test class.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITestHarness&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITextSearchEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITextDataStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datastore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can start implementing some tests before we've even implemented a
search engine. To do this we start by creating a base test class:&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;TestHarnessBase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITestHarness&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#region Data Members&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITextSearchEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_searchEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITextDataStoreTest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_dataStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#endregion&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#region ITestHarness Members&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITextSearchEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ITextDataStoreTestdatastore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_searchEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_dataStore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datastore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;virtual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#endregion&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The test framework will consider public methods with a known return type and
no parameters as test methods.&lt;/p&gt;
&lt;div class="highlight-block highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;SearchEngineSearchTests&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TestHarnessBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#region Test Methods&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Contains_FindExistingWord&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_searchEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The quick brown fox jumps over the lazydog"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_searchEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fox"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Contains_NotFindNonExistingWord&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;_searchEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The quick brown fox jumps over the lazydog"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;_searchEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cat"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;#endregion&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Text Searching Algorithms and Indexing&lt;/h3&gt;
&lt;p&gt;There is lots of documentation on the &lt;a href="http://en.wikipedia.org/wiki/Boyer_%E2%80%93_Moore_string_search_algorithm"&gt;Boyer-Moore algorithm&lt;/a&gt;, I didn't
look into it too deeply as its already been implemented in .NET, but also as
it is designed to work when is only possible to enumerate through the
searchable text. As we have our searchable text in-memory we could leverage
random access to the data.  &lt;a href="http://en.wikipedia.org/wiki/Aho-Corasick_algorithm"&gt;Aho-Corasick&lt;/a&gt; is the same, but it uses a cool
tree to search for multiple words at the same time. It was that tree and later
some influence from the &lt;a href="http://en.wikipedia.org/wiki/Suffix_tree"&gt;Suffix Tree&lt;/a&gt; that led me to think I could store
the entire searchable text in a tree.&lt;/p&gt;
&lt;h3&gt;A Text Tree&lt;/h3&gt;
&lt;p&gt;Consider the string "big bad barry" represented as a tree as below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image_thumb1" src="image1.png"/&gt;&lt;/p&gt;
&lt;p&gt;This tree is great for exact match string searching; Start with the root node
and the first character of the search string. Check if the current node has a
child matching the current character. If it does, move to the node and the
next character and repeat the process. If it doesn't then the word doesn't
exist in the searchable text. If we find a match for every letter, the word
exists in the searchable text.&lt;/p&gt;
&lt;h3&gt;A Text Tree With Indexes&lt;/h3&gt;
&lt;p&gt;The previous tree is only good for the Contains() method as the tree has no
information about where the matches are found or how many times they occur. To
add this information to the tree we could add a new node at the end of every
word with its index in the original text&lt;/p&gt;
&lt;p&gt;&lt;img alt="image_thumb2" src="image2.png"/&gt;&lt;/p&gt;
&lt;p&gt;We follow the same procedure as before to find words, only this time we know
if we have found a complete word (if there is one or more index nodes on the
last node we check). We can also find all instances of the word by traversing
the rest of the tree and returning all the index nodes found. For example we
can see that "ba" is in the string twice by finding all the index nodes after
the 'a' node; In this case at characters 4 and 8.&lt;/p&gt;
&lt;p&gt;From this tree we can also find entire sentences if our search algorithm is
good enough. Basically it just needs to find the words in the sentence
individually and then using the indexes and the word lengths work out which
words combinations were next to each other in the original text.&lt;/p&gt;
&lt;p&gt;The reason this is all possible is the tree has enough information to re-build
the original string.&lt;/p&gt;
&lt;h3&gt;A Text Tree With Indexes and Suffixes&lt;/h3&gt;
&lt;p&gt;In the previous trees we can only search efficiently for words and prefixes.
What if we want to be able to search for substrings? We can do this by also
adding all the suffixes of each word to the tree.&lt;/p&gt;
&lt;p&gt;&lt;img alt="image_thumb3" src="image3.png"/&gt;&lt;/p&gt;
&lt;p&gt;Now we can search for words, substrings and sentences and get all the
occurrences. That's pretty cool, but this tree is getting pretty massive
compared to the original string.&lt;/p&gt;
&lt;h3&gt;Tree Implementation&lt;/h3&gt;
&lt;p&gt;I'm not actually going to describe how to implement this tree, its easier just
to check the code and step through it if you have too.&lt;/p&gt;
&lt;p&gt;The search engines I have implemented are&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SimpleTextSearch&lt;/strong&gt; Using IndexOf() and Contains() methods on System.String&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RegexTextSearch&lt;/strong&gt; Quick and dirty Regex implementation&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SimpleTreeTextSearch&lt;/strong&gt; The basic tree with no index information. Only implements the Contains() method&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IndexTreeTextSearch&lt;/strong&gt; The basic tree with index information. Search algorithm only looks for a single word&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ExtendedIndexTextTreeSearch&lt;/strong&gt; Extends the IndexedTreeTextSearch search algorithm to support multiple word search phrases&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SuffixIndexTreeTextSearch&lt;/strong&gt; Extends the IndexedTreeTextSearch to include suffixes in the tree.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ExtendedSuffixIndexTreeTextSearch&lt;/strong&gt; Includes suffixes and the search algorithm from above that supports multiple word search phrases.&lt;/p&gt;
&lt;p&gt;Some things I didn't do but would look into if I was actually going to use
this anywhere:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Hash tables for each nodes children (currently using List&amp;lt;&amp;gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Split text into words more effectively and maybe remove stop words (&lt;a href="http://nayyeri.net/blog/how-to-split-a-text-into-words/"&gt;How to Split a Text into Words&lt;/a&gt; might be worth reading)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Case insensitive search algorithm (with case information still in the tree)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Removing casing from searchable text&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;To be continued...&lt;/h3&gt;
&lt;p&gt;To be honest, I got distracted away from this by getting into the new
Silverlight 2 SDK for my DevSta entry, &lt;a href="/journal/devsta-challenge-2008/"&gt;Desktop Racer&lt;/a&gt;. DevStar is an
Australian Microsoft Visual Studios development competition and I figured
Silverlight 2 is a little more exciting than a text searching algorithm by
someone with little to no background text search theory. Never-the-less I've
got the ExtendedSuffixIndexTreeTextSearch passing all the functional search
test I've written. I've uploaded a copy of the results and the project in its
current state.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://static.sharpthinking.com.au/TextSearchPost1/Results/results.html"&gt;HTML Functional Test Results&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://static.sharpthinking.com.au/TextSearchPost1/TextSearchProject.zip"&gt;TextSearch VS2008 Project&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I haven't yet written any performance tests to establish each engines setup
speed, search speed, CPU usage and memory usage. I think this will be very
interesting, while I'm pretty confident the tree search methods will be quick,
I suspect they will require significantly more memory than storing plain text.
I would not be surprised to find that the memory usage and setup times for my
implementations of tree searches make them impractical to use for most real
world search problems. Regardless, it has been lots of fun just getting the
tree search functional but I'd be surprised if I've added any value to one of
the most researched topics in computer science.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/text-searching</guid><pubDate>Tue, 14 Oct 2008 21:11:00 +0000</pubDate></item><item><title>DevSta {Challenge 2008}</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        DevSta {Challenge 2008}
    &lt;/h1&gt;
    &lt;p&gt;
    Oct 07, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;200 hours and 8 minutes later, my &lt;a href="https://web.archive.org/web/20100130005530/http://devsta.microsoft.com.au:80/about.aspx"&gt;devsta&lt;/a&gt; entry is finally complete and
submitted! Over the next week or so I'll write a post about writing it, how it
turned out, what I learned and hopefully provide the code for download. For
now, here is a brief description and some screen shots from in the game.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;"Micro Machines was one of my favourite old school games and I thought it
would be cool to race a Micro Machine around a fictional devstas desktop. Who
wouldnt want to race around those icons and windows, trashing the desktop with
tire marks?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Desktop Racer is a Silverlight 2 game. It has a Micro Machine like zoomed
in mode, and also a mode where the whole desktop is visible. Players can play
1 or 3 laps time trails."&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="The game starts with an intro and some jokes in a command window" src="devsta2008.tarn.desktopracer.screenshot0.png"/&gt;&lt;/p&gt;
&lt;p&gt;The game starts with an intro and some jokes in a command window&lt;/p&gt;
&lt;p&gt;&lt;img alt="I used a cruise control window for the start lights" src="devsta2008.tarn.desktopracer.screenshot2.png"/&gt;&lt;/p&gt;
&lt;p&gt;I used a cruise control window for the start lights&lt;/p&gt;
&lt;p&gt;&lt;img alt="You can watch the devsta video while playing" src="devsta2008.tarn.desktopracer.screenshot1.png"/&gt;&lt;/p&gt;
&lt;p&gt;You can watch the devsta video while playing!&lt;/p&gt;
&lt;p&gt;&lt;img alt="Finally a trashed desktop and the finish flag toaster." src="devsta2008.tarn.desktopracer.screenshot3.png"/&gt;&lt;/p&gt;
&lt;p&gt;Finally a trashed desktop and the finish flag toaster.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/devsta-challenge-2008</guid><pubDate>Tue, 07 Oct 2008 11:00:00 +0000</pubDate></item><item><title>Amazon S3</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Amazon S3
    &lt;/h1&gt;
    &lt;p&gt;
    Sep 09, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;As this site is currently hosted on a shared hosting environment with limited
bandwidth and disk space we decided we would start hosting some content on
&lt;a href="http://aws.amazon.com/s3"&gt;Amazon S3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I first saw S3 when a guy from Amazon presented it at a VIC.NET user group
meeting. I thought it sounded pretty cool and signed up for an account as soon
as I got home. I quickly found a use for S3 in a web application I was
building at the time. I used S3 for all the files users uploaded to the site.
I also used it for hosting some web components like static CSS, JavaScript and
image files.&lt;/p&gt;
&lt;p&gt;This works fine when you just want to reference static content on a web page.
To do this you just need to upload the components to S3, make them public and
modify the URLs in your HTML output to point to the S3 resource.&lt;/p&gt;
&lt;p&gt;I did run into cross-domain browser security restrictions when I tried to host
the entire site on S3 and use web services to get dynamic content. In this
case I persisted and got dynamic behavior by &lt;a href="/post/2008/06/03/Dynamically-inserting-dynamic-scripts.aspx"&gt;dynamically inserting dynamic
script elements into the DOM&lt;/a&gt;.  While Google Maps does this very well to
dynamically get map tiles, it doesn't work so well for scripts as the browsers
blocks the page while a script is downloading. I would defiantly be looking at
the alternatives before doing that again. In this case we just want some of
the larger downloadable files off the web server and onto S3, which shouldn't
be a problem.&lt;/p&gt;
&lt;h3&gt;Tools&lt;/h3&gt;
&lt;p&gt;I've always used &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/3247"&gt;S3Fox&lt;/a&gt; which is a Firefox plug-in to manage files on S3.
But when I fired it up this morning it isn't accepting my credentials.&lt;/p&gt;
&lt;p&gt;I tried using &lt;a href="http://www.s3drive.net/"&gt;S3Drive&lt;/a&gt; which is a Windows Desktop application. This
defiantly wasn't what I wanted. It makes S3 look like a "drive" in Windows
that has files and directories. This hides the fact the S3 is actually a flat
file structure. It seems to use a database to manage the virtual file
structure (like a file allocation table). Anyway this would be really good for
backing stuff up as you can treat it like a normal drive and drag and drop
entire folders. Its no good for us as our content needs to be accessed by HTTP
GET Requests.&lt;/p&gt;
&lt;p&gt;I ended up using &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/6955"&gt;s3:///&lt;/a&gt; which is an "experimental" Firefox plug-in. It
works fine but doesn't seem to have the tools to manage the access control
list (ACL) that S3Fox has. Anyway its enough to create a bucket and upload
some files (Note: The files were already public and didn't require me allow
public access. This is fine for me now, but might be a problem if you were
uploading sensitive information you didn't want to share)&lt;/p&gt;
&lt;h3&gt;Virtual Sub Domains with CNAME records&lt;/h3&gt;
&lt;p&gt;One of the many things I like about S3 is that you can add a CNAME record on
your domain and point it to an S3 bucket on account at amazon. So while you
can still download your public content from the amazonaws.com URL:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://static.sharpthinking.com.au.s3.amazonaws.com/sharpthinking.jpg"&gt;http://static.sharpthinking.com.au.s3.amazonaws.com/sharpthinking.jpg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also download the same resource from a sub domain of your own URL:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://static.sharpthinking.com.au/sharpthinking.jpg"&gt;http://static.sharpthinking.com.au/sharpthinking.jpg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But as you can see with trusty Firebug the image is still hosted from Amazon
servers&lt;/p&gt;
&lt;h3&gt;Reading and Writing to S3 with .NET&lt;/h3&gt;
&lt;p&gt;I have an S3 project I used previously to read and write files to S3. I wrote
it ages ago, copying large parts of Amazon examples from memory. Although it
looks a little shabby, the one and only test still works, so I'm confident its
still working OK. I'm sure there a better .NET wrappers around these days if
you did a quick Google search.&lt;/p&gt;
&lt;p&gt;Here is the &lt;a href="http://static.sharpthinking.com.au/MenuMan.Storage.2008.09.09.zip"&gt;source code&lt;/a&gt;. You'll notice it still has the namespaces from
the original project, but hopefully my S3 credentials have been removed.
You'll also need to add a reference to an &lt;a href="http://www.nunit.org"&gt;NUnit&lt;/a&gt; assembly to run the
test.&lt;/p&gt;
&lt;h3&gt;BlogEngine.NET, Live Writer and S3&lt;/h3&gt;
&lt;p&gt;As we use Live Writer to publish content to this blog, I am going to update
the BlogEngine.NET Metablog API to upload the media elements to S3 instead of
the file system. I've looked into it and it should be pretty straight forward.
I'm pretty sure you can already get WordPress plug-ins that do this. I'll post
the code when I get it working.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/amazon-s3</guid><pubDate>Tue, 09 Sep 2008 16:58:00 +0000</pubDate></item><item><title>Flex Builder Linux</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Flex Builder Linux
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 31, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I read in a Linux magazine that Adobe were releasing Flex Builder for Linux. I
thought this was really exciting as it was a great chance to use &lt;a href="http://blog.sharpthinking.com.au/post/2008/08/25/Using-Linux-on-a-Media-Box.aspx"&gt;my new Linux
box&lt;/a&gt; for some development and write a basic Flash application. It regularly
annoyed me that I had never developed a Flash application.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://labs.adobe.com/technologies/flex/flexbuilder_linux/"&gt;Adobe Flex Builder Linux Public Alpha&lt;/a&gt; is currently up to alpha 4. It is a
plug-in for Eclipse, a very popular, extendable development IDE build on Java.
I had to install Eclipse 3.3.2 because earlier version weren't working on my
machine and later version have problems with the plug in. I wanted to use
OpenJava but I couldn't get Eclipse working on it, so I decided it would just
be easier to use Sun Java.&lt;/p&gt;
&lt;p&gt;Once I got Eclipse and Flex Builder working it was pretty fun. I was surprised
how much of the stuff I learned for Silverlight could be applied to Flex
Builder. In fact quite a few things I thought were unique to Silverlight were
already features in Flex Builder.&lt;/p&gt;
&lt;p&gt;I found ActionScript was quite easy to get into, it looks similar to
JavaScript but with extra type information. The intellisense for ActionScript
with Eclipse and Flex Builder is really good and really helped.&lt;/p&gt;
&lt;p&gt;Anyway here is my first Flash application!&lt;/p&gt;
&lt;div id="demo_container"&gt;&lt;/div&gt;
&lt;p&gt;It was great from me to solve the mystery behind Flash files and find it is
just source code. In this application there are some ActionScript classes, the
main MXML and HTML/JavaScript to host it. Get the source code &lt;a href="http://blog.sharpthinking.com.au/file.axd?file=2008%2f8%2fFirstFlashApplication.zip"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Well that was pretty fun but there is so much more. I'd like to write an
application that interacts with a server and try some of the cool data binding
Flex Builder supports.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    &lt;script src="jquery-2.1.3.min.js"&gt;&lt;/script&gt;
&lt;script src="swfobject.js" type="text/javascript" &gt;&lt;/script&gt;
&lt;script&gt;
    $(function() {
        swfobject.embedSWF("application.swf", "demo_container", "500", "50", "9.0.0");
    });
&lt;/script&gt;


    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/flex-builder-linux</guid><pubDate>Sun, 31 Aug 2008 22:36:00 +0000</pubDate></item><item><title>Using Linux on a Media Box</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Using Linux on a Media Box
    &lt;/h1&gt;
    &lt;p&gt;
    Aug 25, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;h3&gt;The existing system&lt;/h3&gt;
&lt;p&gt;We have a moderate to low spec'd computer in our lounge room running Windows
XP. We built it about a year ago with spare parts lying round the house. We
use the box for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Sharing files for everyone on our network&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Playing media through the TV and stereo system&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hosting Subversion&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The box usually doesn't have a monitor, keyboard or mouse attached. As it has
no input devices we use VNC server. This works out ok as we have several other
desktops and laptops around the house we use to connect to it and control it.&lt;/p&gt;
&lt;h3&gt;Why change&lt;/h3&gt;
&lt;p&gt;The box was working fine so there was no real reason to change to Linux except
that I really wanted some exposure to it. Eventually I want to try out setting
up a LAMP web server and maybe try Struts and JSP.&lt;/p&gt;
&lt;p&gt;I knew I needed to get the box doing everything it was doing before or else my
house mates would be pushing to revert back to the XP system. I haven't used
Linux much, but I figured if I spend some time with it I could probably get it
doing all the things the existing box is doing. I wrote a list of the things I
need the Linux box to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use the TV as a primary display&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Share files with our Microsoft boxes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Boot to an IDE without user input&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Host Subversion&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allow remote access&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Play media (DVD, CD's, MP3, DivX etc)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing to difficult sounding there you would hope..&lt;/p&gt;
&lt;h3&gt;Choose a distro&lt;/h3&gt;
&lt;p&gt;I went into this with fairly limited knowledge of Linux. I'd installed
Mandrake years ago as a dual boot on my personal PC but never really spent any
time actually using it. I'd also used openSUSE while I was in Ireland getting
training with OpenJaws travel software. Recently Brodie tried the Ubuntu live
CD at work, but we didn't really get passed the gimmicky UI features.&lt;/p&gt;
&lt;p&gt;After doing a bit of reading I still had no idea which one I wanted to use.&lt;/p&gt;
&lt;h3&gt;Trials and tribulations&lt;/h3&gt;
&lt;p&gt;I started with Mandriva for no real reason. On the first install it didn't
detect my graphics card correctly and crashed horribly every time I went
anywhere near the GUI graphic settings. I wasn't ready to start screwing with
my xorg.conf yet, so I tried installing it again. I didn't have any luck this
time either, I couldn't even finish the install because after I tried testing
a graphic mode it returned me to a mode I couldn't read the text. (That mode
reminded me of writing little real mode VGA graphics demos in assembly, it was
easy to leave the console unreadable if you messed with the graphics mode
using the BIOS interrupts and didn't return it to the correct mode, 0x80 I
think I remember). I decided to try another distro in hope the install might
do a better job detecting my graphics card.&lt;/p&gt;
&lt;p&gt;openSUSE was much quicker to install but comes with very few extras. openSUSE
did choose the correct open source drivers from my graphics card and installed
perfectly. There were tools to configure a dual head display but after a
couple of hours reading endless forums, manually editing my xorg.conf file,
restarting the X system and unsuccessfully attempting to install the ATI
binary drivers, I gave up. It wasn't a success yet but I did learn heaps more
about Linux and was confident I could get the TV working if I persisted.&lt;/p&gt;
&lt;p&gt;Finally I tried Ubuntu after I read that installing the ATI drivers was easy.
Installing the ATI drivers was simple but it didn't immediately solve my
problem. I still wasn't getting anything on the TV. After another couple of
hours many more "atp-gets" I finally got the TV to clone the primary display.&lt;/p&gt;
&lt;h3&gt;Success!&lt;/h3&gt;
&lt;p&gt;Everything else was pretty easy;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I installed the proprietary stuff needed to play proprietary media formats&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I used "apt-get" to get most of the additional software I needed which
made things pretty easy&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I could share a folder using the GUI&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I could read my external NTFS drive, but I did have to force mount it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There was an option in the administration to automatically login a user on
startup&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remote access to VNC clients was build-in and I just needed to enable it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Subversion was easy to configure as it used the same command line
functionality as the Windows version.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It was quite easy to get the Subversion server automatically starting and
I learnt how run scripts on startup&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Can't turn water into wine&lt;/h3&gt;
&lt;p&gt;Everything went so well I was inspired to try using a crappy DVB TV tuner card
I bought, hated and never used to watch television. Even with a fast computer
and perfect reception it would still start losing frames when the video was
panning. What frustrated me even more was I could only use the software that
came with it to use it, when I started using Vista I couldn't use it at all.&lt;/p&gt;
&lt;p&gt;In about 20 minutes I able to add it, tune it and get some video out it. The
only problem was it looked even worse than when it was using its own software
on XP. I know there is plenty more I could do to get it working better, I was
happy I got it working at all and probably won't use it again.&lt;/p&gt;
&lt;h3&gt;The response&lt;/h3&gt;
&lt;p&gt;After two weekends I've finally got the box back where it belongs, doing
everything it was before except now running ubuntu. I can still boot into the
old XP system if I boot off the secondary hard disk, but I'm happy leaving
ubuntu as the default boot and not even adding XP as an option in the boot
loader. My house mates are happy too, apparently the VNC is much better that
it was with windows but they want a music player that supports play lists.&lt;/p&gt;
&lt;h3&gt;That's it&lt;/h3&gt;
&lt;p&gt;I'll report back on how the Linux box handles over time and what I do with it.
I've also created a VMWare Linux instance on my personal computer which I'll
hopefully use to try out running web servers and maybe get into a little bit
of programming.&lt;/p&gt;
&lt;p&gt;Its been frustrating at times but over all its been lots of fun getting this
Linux box up and running, I've learned lots about Linux and I want to learn
more.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/using-linux-on-a-media-box</guid><pubDate>Mon, 25 Aug 2008 22:24:00 +0000</pubDate></item><item><title>Network Checks</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Network Checks
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 22, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I work with an IT manager who is very into knowing about how all the servers
on the network are performing, and rightfully so. But the process isn't great;
he gets an IT person to remote into all the servers daily and manually record
information in a spreadsheet. These include things like disk usage, logs, log
sizes, databases, web servers, processes, memory and CPU usage.&lt;/p&gt;
&lt;p&gt;A while back I wrote a Powershell Script that recorded heaps of the results
for him. Unfortunately it didn't take many server changes before the script
had more errors than results, I guess he could do stuff with all that extra
time. I would fix stuff from time to time, but more often than not I was too
busy with things I had to do or things I wanted to do.&lt;/p&gt;
&lt;p&gt;The other day I decided it would be cool if I could write all the checks as
NUnit tests. That way he could just run NUnit, point it towards the assembly
and get cool Red/Green light testing of the network status. I could write test
like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Test]
public void Server1_DiskUsage_CDrive_LessThan80Percent().
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also found this article: &lt;a href="http://www.codeproject.com/KB/dotnet/cruisecontrol_continuous.aspx"&gt;Setting up CruiseControl.NET to be a Continuous
Monitoring Server&lt;/a&gt;. I haven't read it in detail, but I think its a pretty
cool idea.&lt;/p&gt;
&lt;p&gt;I don't have time to implement this yet, but I have started writing some of
the tests from the Powershell script in C#. I started some code to get the
memory usage from a server and I was immediately reminded how annoying it was
trying to work out how values in WMI related to the items on the Performance
tab in task manager. So marked up an image of the task manager window with the
corresponding properties of the WMI.&lt;/p&gt;
&lt;p&gt;&lt;a href="/image.axd?picture=WindowsLiveWriter/NetworkChecksOperatingSystemPerformance_10294/Performance_2.jpg"&gt;Performance&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See &lt;a href="http://msdn.microsoft.com/en-us/library/aa394239(VS.85).aspx"&gt;Win32_OperatingSystem_Class&lt;/a&gt; on MSDN for all the properties and
descriptions&lt;/p&gt;
&lt;p&gt;Anyway you might now be asking to see the C# code, well here it is. There is
no exception handling as I was running it as an NUnit test which handled them
for me.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private OperatingSystemInfo GetOperatingSystemInfo(string serverName)
{
    string wmiObjectName = "Win32_OperatingSystem";
    string connectionString = string.Format("\\\\{0}\\root\\cimv2:{1}",serverName, wmiObjectName);
    ManagementClass os = new ManagementClass(connectionString);

    foreach (ManagementObject obj in os.GetInstances())
    {
        OperatingSystemInfo osInfo = new OperatingSystemInfo();
        osInfo.NumberOfProcesses = (uint)obj.GetPropertyValue("NumberOfProcesses");
        osInfo.TotalVisibleMemorySize = (ulong)obj.GetPropertyValue("TotalVisibleMemorySize");
        osInfo.TotalVirtualMemorySize = (ulong)obj.GetPropertyValue("TotalVirtualMemorySize");
        osInfo.FreeVirtualMemory = (ulong)obj.GetPropertyValue("FreeVirtualMemory");
        osInfo.FreeSpaceInPagingFiles = (ulong)obj.GetPropertyValue("FreeSpaceInPagingFiles");
        osInfo.FreePhysicalMemory = (ulong)obj.GetPropertyValue("FreePhysicalMemory");
        return osInfo;
    }
    return null;
}

public class OperatingSystemInfo
{
    public uint NumberOfProcesses { get; set; }
    public ulong TotalVisibleMemorySize { get; set; }
    public ulong TotalVirtualMemorySize { get; set; }
    public ulong FreeVirtualMemory { get; set; }
    public ulong FreeSpaceInPagingFiles { get; set; }
    public ulong FreePhysicalMemory { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope it helps. I'm getting back to playing with the Microsoft MVC Preview 3
which is really cool and lots of fun. I hope to get a post out about what I
like and don't like about it soon.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/network-checks</guid><pubDate>Tue, 22 Jul 2008 19:12:00 +0000</pubDate></item><item><title>Microsoft Robotics Studio</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Microsoft Robotics Studio
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 17, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I finally decided to download &lt;a href="http://msdn.microsoft.com/en-us/robotics"&gt;Microsoft Robotics Studio (MSRS)&lt;/a&gt; and have a
look at it. I had no idea what I would get, but I was hoping I could use it to
simulate autonomous robot soccer matches and I wanted to see what else it
could do. I spent an evening having a play with it, and here are my first
impressions.&lt;/p&gt;
&lt;h3&gt;The SDK&lt;/h3&gt;
&lt;p&gt;The download was much smaller than I expected (approximately 87Mb), for some
reason I thought it was going to be a plug-in for the Visual Studios IDE. It's
just a collection a projects and some tools. I haven't yet download the April
CTP which must have a bit more; its around 400Mb.&lt;/p&gt;
&lt;p&gt;I looked at a few projects and most appear to be based around a Decentralized
System Services (DSS) application model, or framework. The application model
provides a framework for concurrent distributed services to interact. These
could be sensors and actuators, controllers or processors. The services use a
light-weight REST messages to communicate and interact. During simulation your
services run normally, but instead of getting real images from the camera and
really actuating motors you communicate with a simulated environment. The DSS
is setup and linked to relevant service assemblies by XML files. There is a
visual editor to assist configuring these files.&lt;/p&gt;
&lt;p&gt;The simulations uses the AGEIA physics engine and DirectX 3d. I don't know
much about the physics engine, except its made by NVidia and it can use
dedicated physics hardware if you have it, which I don't. I have a mate who
writes low level tests for graphics cards as a job and writes his own physics
engine for fun. I'm sure he'll have plenty to tell me about it. Actually -
Bryce you know we want you to post on this blog&lt;/p&gt;
&lt;p&gt;A visual programming language is provided. Its a visual data flow designer
that can connect services that fit into the DSS framework. It seems similar to
event based circuit simulation tools I've used, but with a very simple user
interface and debugging tools. I did some of the tutorials but the only thing
that really kept me interested was that I could send text to the Text to
Speech convertor. I don't have a robot that I want to work right now, maybe
then I would rate this tool more highly.&lt;/p&gt;
&lt;h3&gt;The Robots&lt;/h3&gt;
&lt;p&gt;There is a fairly wide price range of off-the-shelf robots and robot kits that
the Microsoft Robotics Studio that have been can be simulated (I found a list
of these in this article). I was immediately drawn to a robot described as a
"fighting Japanese robot" (for around $1500). When I looked him up on
&lt;a href="http://www.youtube.com/watch?v=sMKjPJbAlWE"&gt;YouTube&lt;/a&gt; I was surprised to find that I'd seen him before, but he was
dancing! Lego also has a more reasonably priced Robotics kit ($120). There is
also the &lt;a href="http://www.aldebaran-robotics.com/eng/Nao.php"&gt;Nao&lt;/a&gt; which will apparently be used in RoboCup 2008! (I'm not sure
if those Sony dogs compete in a different category, or are obsolete)&lt;/p&gt;
&lt;p&gt;I haven't yet decided how much, if any, I am willing to fork out for a robotic
toy. Can I write it off as tax deduction?&lt;/p&gt;
&lt;h3&gt;RoboCup and RoboChamps&lt;/h3&gt;
&lt;p&gt;The RoboChamps site was disappointing for me, it seems to be Silverlight 2. It
caused javascript errors didn't rendering anything for me. I didn't want to
spend the evening updating my Silverlight installation so I moved on.&lt;/p&gt;
&lt;p&gt;Things started looking up when I found the &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=AFA19EE3-F959-4EBC-A25F-4A2F32FB8FB2&amp;amp;displaylang=en"&gt;Microsoft Soccer Simulation
download&lt;/a&gt;. This download has the soccer simulation framework and a demo
project with a two-on-two autonomous robot soccer match setup. The robots
aren't humanoid, they're two wheeled things with a kicker and a camera. The
robots have only been setup for two tasks; Find Ball and Approach Ball. Hence
its not really a great spectacle, but a fantastic starting point.&lt;/p&gt;
&lt;p&gt;There is a very basic vision processing method the robots use in the demo. The
purpose of the method is simple; it takes a 320x200 array of colours from the
camera and determines whether or not the ball is in the image. I had a look at
the code and quickly realised I'd need to read up on some vision processing
theory to really understand how it was working.&lt;/p&gt;
&lt;p&gt;I then looked into an announcement: &lt;a href="http://blogs.msdn.com/msroboticsstudio/archive/2008/05/09/the-msrs-nao-simulation-competition-for-robocup-2008-is-now-available.aspx"&gt;MSRS Nao Simulation Competition for
RoboCup 2008 is now available&lt;/a&gt;. The post has everything you need to get
started writing code for the humanoid Nao robots and simulating 4x4 or 8x8
soccer competitions. The RoboCup simulations uses the Microsoft Soccer
Simulation. While these guys are much cooler than the Microsoft robots, they
also take a lot more grunt to simulate and there are more of them on the
pitch. My quad core processor and low range graphics didn't simulate them very
well. (I've never been into computer games, I spend too much timing working
and developing on computers and prefer board games, till now, my low range
graphics card has always done enough for me).&lt;/p&gt;
&lt;h3&gt;Conclusion?&lt;/h3&gt;
&lt;p&gt;I only scratched the surface but I'm glad I checked it out. I'm wrapped
there's a framework I can write C# services for autonomous robotic soccer
players. I just need a better graphics card, more time and some mates to
contribute.&lt;/p&gt;
&lt;p&gt;It seems the framework can only be used to make truly autonomous that have a
processor running Windows and the .NET framework. This is a big step from the
8-bit microcontrollers at university. Robots have got cooler since then, but
I'd look further into other operating systems, languages and tools before I
started developing code for a real robot in MSRS.&lt;/p&gt;
&lt;p&gt;If you just want to write some cool bot code and battle you mates, checkout
this Scott Hanselman article, &lt;a href="http://www.hanselman.com/blog/LearningOpportunityNETTerrariumIsBack.aspx"&gt;Learning Opportunity - .NET Terrarium is
back!&lt;/a&gt; I haven't tried the original or the new, but I'm looking forward to
it.&lt;/p&gt;
&lt;p&gt;Finally, if your feeling a little concerned that the rise of the machines is
fast approaching, &lt;a href="http://www.youtube.com/watch?v=b2bExqhhWRI"&gt;this video&lt;/a&gt; won't give you any comfort. But it's also
the topic of one of &lt;a href="http://xkcd.com/251/"&gt;my favourite XKCD&lt;/a&gt; cartoons that doesn't involve
velociraptors.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/microsoft-robotics-studio</guid><pubDate>Thu, 17 Jul 2008 20:50:00 +0000</pubDate></item><item><title>Web Development Frameworks</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Web Development Frameworks
    &lt;/h1&gt;
    &lt;p&gt;
    Jul 17, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I've just started getting into the Microsoft MVC Preview 3. Its cool and I'm
having heaps of fun with it. Who thought you could have multiple form tags on
a page ;-)&lt;/p&gt;
&lt;p&gt;I wrote a post earlier this year after spending a couple of weeks in Ireland
with some Java developers. I originally posted it internally at work with the
intention of stirring up some .NET developers in my team. Today I decided to
check out what I'd written to see if added any value to Microsoft MVC
framework I am using now. It doesn't, but I decided to post it publicly
anyway.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Its taken a while but you knew it was coming. Having come in contact with
some Java development from a .Net world I wanted post some observations and
opinions. Ive really only had one day of exposure to server side Java, so Im
sure this article wont be completely technically correct and Im happy for
people to comment on my errors or oversights.&lt;/p&gt;
&lt;p&gt;Firstly Ill start with the background. These boys are using Java SE (Standard
Edition I guess) and not EE (Enterprise Edition). My understanding is that
Java EE comes with some additional web framework elements, but I stand to be
corrected on this.&lt;/p&gt;
&lt;p&gt;Java SE at first glance actually looks like classic ASP with Java code. Just
to clarify, both a server side languages and not VBScript and Javascript
respectively. Both use the &amp;lt;% %&amp;gt; characters to separate the code from the
response. Both can just write to the output directly. Both .jsp and .asp files
can be called directly by URI and can respond (provided they are hosted on
suitable web servers). So right now this looks like I should be comparing
Classic ASP to Java, but thats no brainer. Java is a native OO language,
platform independent and also has native support for threading; game over
classic ASP.&lt;/p&gt;
&lt;p&gt;There still is the issue that Java server side code can very quickly
degenerate to the spaghetti code we have seen in classic ASP code. And it can,
and would if written badly. But to be fair, any language can. In some ways
being a native OO language you have the tools to protect you against this.
Really its still pretty primitive for a web framework.&lt;/p&gt;
&lt;p&gt;The comparison with the ASP.Net framework becomes a much more interesting when
you add the Apache Struts framework to Java. Basically the Struts framework is
an implementation of the Model View Controller (MVC) pattern. The MVC pattern
has is roots in an Apple SmallTalk framework. It wasnt a framework that was
used for web applications, but it did have clearly defined model, view and
controller components.&lt;/p&gt;
&lt;p&gt;MVC is a pattern to implement a system that takes requests and deliver
responses. This is what HTTP is all about, all web servers do it.&lt;/p&gt;
&lt;p&gt;The MVC pattern consists of three components. There is the Model which is the
domain logic (a.k.a business logic or middle tier) and it also includes the
data persistence the domain logic works with. There is also the Controller
which is responsible for taking the request and routing them to the correct
business logic, it then routs the output of the logic to a View. The View
selected by the Controller takes the output from the domain logic and presents
it to the user. These guys weren't using the standard "View" from Apache
struts they used XSLT to transform XML data from the model to HTML.&lt;/p&gt;
&lt;p&gt;ASP.Net uses a page life cycle pattern to handle the same problem. We know
this well; An .aspx page is requested, the aspx page points to some page
logic, which can then calls some domain logic. The page then binds data to
objects capable of rendering a view of that data. It seems normal that events
on the rendered page are posted back and handled by that same page code. This
is vastly different to how the MVC pattern works. &lt;/p&gt;
&lt;p&gt;_This has already got to long, so Ill post another blog soon to continue
this discussion and hopefully get stuck into the inevitable debates about the
merit of both.&lt;/p&gt;
&lt;p&gt;Feel free to comment corrections, additional information or opinions.&lt;/p&gt;
&lt;p&gt;Tarn  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I hope to write some more relevant posts about my recent experiences with the
Microsoft MVC framework soon. At the moment I'm having too much fun coding
with it, and don't yet have much to say that Rob Connery, Scott Hanselman and
Scott Gu haven't covered.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/web-development-frameworks</guid><pubDate>Thu, 17 Jul 2008 10:40:00 +0000</pubDate></item><item><title>Page summary in Google search results</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Page summary in Google search results
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 23, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I just realised the summary of some of my pages in Google search results were
terrible. So I looked into sites that had good descriptions and discovered
Google will use the "description" meta tag for the summary when it's present.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta name="description" content="Your Page Summary Here!"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is easy to do for static pages, and is not the hard to do for dynamic
pages. If you use Master Pages its common to define a place holder inside the
head tag. This means any dynamic pages can implement a content server control
for that place holder and insert a meta tag with relevant information to be
displayed in the summary of the Google search results.&lt;/p&gt;
&lt;p&gt;You could also give the head tag ID and runat="server" attributes. This means
you can find it when your processing the page and dynamically append a meta
tag element to its children property.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/page-summary-in-google-search-results</guid><pubDate>Mon, 23 Jun 2008 15:46:00 +0000</pubDate></item><item><title>Minify Javascript</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Minify Javascript
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 22, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;One of my favourite books on web development is &lt;a href="http://oreilly.com/catalog/9780596529307/"&gt;High Performance Web Sites:
Essential Knowledge for Front-End Engineers&lt;/a&gt; by &lt;a href="http://www.oreillynet.com/pub/au/2951"&gt;Steve Souders&lt;/a&gt;. From
the authors role as Chief Performance Yahoo! he describes 14 surprisingly
simple rules for improving front end performance of web sites. Most
professional web developers should know most of the best practices, but might
also pick up a tip or two. The rules are justified with quantifiable test
results, which is great when you're trying to convince a manager its worth
while implementing front end improvements.&lt;/p&gt;
&lt;p&gt;There also is 34 categorized &lt;a href="http://developer.yahoo.com/performance/rules.html"&gt;rules&lt;/a&gt; for best practice on the Yahoo! web
site. Yahoo! provides &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; a plug-in for Firefox which analyses pages
and explains rules that would improve front end performance.&lt;/p&gt;
&lt;p&gt;This post is about implementing rule 10, Minify JavaScript. I have known about
minifying Javascript for a long time but had never implemented it, today I
decided to give it a go on a site I run. I had a look at a few minifying
tools:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.crockford.com/javascript/jsmin.html"&gt;JSMin&lt;/a&gt; - Created by Douglas Crockford, JSMin is the most basic of the
minifying tools I've seen out there. It has been ported to .Net, but its not
really a very sophisticated tool. It removes line breaks, white space and
doesn't do any variable renaming. There is a &lt;a href="http://www.jscompress.com/"&gt;site&lt;/a&gt; that you can paste some
JavaScript in a compress it with JSMin or Packer to have a look at the output.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://dean.edwards.name/packer/"&gt;Packer&lt;/a&gt; - Created by Dean Edwards, Packer uses a different approach to
minifying JavaScript. Packer compresses the code into an expression that can
be evaluated to its original form by the &lt;a href="http://www.w3schools.com/jsref/jsref_eval.asp"&gt;Javascript eval&lt;/a&gt; function. I
think this is cool and I'm not influenced by people out there saying you
should never use the eval function. Packer is used and the eval function has
been around since Javascript 1.0, it will be supported by modern browsers I'm
supporting. The two main criticisms of Packer I am a little concerned about
are;  There is a delay after the script is received by the browser before it
evaluates to your actual Javascript code. With that in mind, unless you design
the page well, you may intermittently cause Javascript errors by referencing
code that doesn't yet exist. The second criticism that while packers
compression is very good, it doesn't compress much more when gziped before
being sent to the browser. Packer has been ported to .Net, Perl and PHP.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUI Compressor&lt;/a&gt; - Created by Yahoo!, this is what I wanted in a minifying
tool. It removes all unnecessary characters and does variable renaming. The
YUI Compressor is implemented in Java, there is an &lt;a href="http://www.codeplex.com/YUICompressor"&gt;open source project&lt;/a&gt;
on &lt;a href="http://www.codeplex.com"&gt;CodePlex&lt;/a&gt; to port it to .Net but at the time or writing this, it has
only ported the CSS compression algorithm. &lt;a href="http://www.julienlecomte.net/blog/author/admin/"&gt;Julien Lecomte&lt;/a&gt; an architect
at Yahoo talks about the YUI Compressors performance in his &lt;a href="http://www.julienlecomte.net/blog/2007/08/13/"&gt;blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://dojotoolkit.org/docs/shrinksafe"&gt;Dojo ShrinkSafe&lt;/a&gt; - Create by the Dojo people, it seems to be doing the
same thing as the YUI Compressor. I didn't look very hard, but I didn't find a
.Net port for this one either.&lt;/p&gt;
&lt;p&gt;There is also &lt;a href="http://code.google.com/p/minify/"&gt;Minify&lt;/a&gt; a PHP library on &lt;a href="http://code.google.com/"&gt;Google Code&lt;/a&gt; for creating
servers that can combine and minify script files on the fly before they are
sent to the browser. The project I'm working on is hosted by IIS and S3 so I
didn't look into this any further.&lt;/p&gt;
&lt;p&gt;I decided to try the Yahoo YUI Compressor and Dojo ShrinkSafe even though
there was no .Net port for either. JSMin didn't provide the compression I
wanted to make it worth while. I outlined some of my concerns about Packer,
and I decided I would just prefer plain compacted Javascript.&lt;/p&gt;
&lt;p&gt;Then the was the question of whether its ok to use a Java application in my
.Net build process? Java is a free and widely used platform, and in this case
the best tools for are built on it. I have the JRE on all the computers I use
anyway, so for me its no problem. If you need to compress scripts at runtime
in .Net it may be worth looking a bit further into other .Net solutions and
ports.&lt;/p&gt;
&lt;p&gt;Both the Yahoo! and Dojo compressors were easy to use from the command line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -jar yuicompressor-2.3.5.jar scripts\combined.js -o scripts\yui_compressed.js

java -jar custom_rhino.jar -c scripts\combined.js &amp;gt; scripts\dojo_compress.js 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only thing I had to do to get them working was change the UFT-8 encoding
of some of my script files to ANSI. After that everything on my site worked as
is should with both libraries. If I had any problems I was going to give &lt;a href="http://www.jslint.com/lint.html"&gt;JS
Lint&lt;/a&gt; a try. It is recommended to use JS Lint to verify your Javascript
before compressing it.&lt;/p&gt;
&lt;p&gt;After using a tool to combine my uncompressed script files, the final script
was 16,042 bytes. Below are the result of using ShrinkSafe and YUI Compressor
on that code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dojo ShrinkSafe, 11,543 bytes, 71%

YUI Compressor, 10,448 bytes, 65%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So what did I discover? I was really happy with both tools, they both
compressed and didn't break my code. Because both can be run from the command
line they should be easy to integrate into my build process, provided my build
and development machines have Java installed.&lt;/p&gt;
&lt;p&gt;At this stage I'm not gziping my scripts, but that has more to do with my
applications architecture, the scripts are being served by a storage in the
cloud solution. I may look into providing the correct meta data to statically
host gziped scripts, but that will be the topic for another post.&lt;/p&gt;
&lt;p&gt;As for getting it into my build process I just used the post build tasks hook
in Visual Studios. It could just as easily be part of your NAnt script.&lt;/p&gt;
&lt;p&gt;I hope this helps the front end performance of your web pages too.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/minify-javascript</guid><pubDate>Sun, 22 Jun 2008 22:04:00 +0000</pubDate></item><item><title>Anonymous Methods - Understanding Scope</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Anonymous Methods - Understanding Scope
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 19, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Anonymous Methods were introduced in .net 2.0. If you haven't used or seen
them before I suggest reading some of the many blogs and MSDN articles about
them. I'm not going to introduce them here, I'm going to briefly look into how
the scope works when they are invoked from outside the scope they were created
in.&lt;/p&gt;
&lt;p&gt;Below I have a simple example of a class that has a member variable and a
method that returns an anonymous method. The only purpose of this little class
is to investigate some scope issues.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public delegate string FooDelegate();

public class Foo
{
    public string Bar { get; set; }

    public FooDelegate GetDelegate()
    {
        string bar = Bar;
        return delegate()
            {
                return string.Format("Member: {0}; Local: {1}", Bar, bar);
            };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that in this class the anonymous method we are creating references
a local variable and a member variable of the class. This is completely
legitimate code in .net 2.0, 3.0 and 3.5. Now lets see how it works, below is
a simple example.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Foo foo = new Foo();
foo.Bar = "test";

FooDelegate fooDelegate = foo.GetDelegate();

string result = fooDelegate.Invoke();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is "Member: test; Local: test"&lt;/p&gt;
&lt;p&gt;You can see that when we invoked the anonymous method both the local and
member variables evaluated  to "test". Once you accept its ok to reference
variables from the scope the anonymous method was created in, this behavior
makes sense.&lt;/p&gt;
&lt;p&gt;But what happens if we change the property after we create the anonymous
method? What would we expect the values to be when we invoke the method? If
you've used anonymous methods in other languages it might be quite obvious. If
you don't know, or want to validate that you do, the example below will do
just that.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Foo foo = new Foo();
foo.Bar = "test";

FooDelegate fooDelegate = foo.GetDelegate();
foo.Bar = "update";

string result = fooDelegate.Invoke();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is "Member: update; Local: test"&lt;/p&gt;
&lt;p&gt;As you can the see the method did use the updated property, but only for the
class property. The value of the local variable it was referring to when the
anonymous method was created has not been changed.&lt;/p&gt;
&lt;p&gt;Normally a local variable would be discarded and later collected by the GC
once execution of the block is complete. In this case it's still in the scope
of the anonymous method and is therefore not discarded while the anonymous
method is still referenced.&lt;/p&gt;
&lt;p&gt;The local variable is not updated with the new value as we set the local
variable when we created the delegate, and we changed the class property after
that. In this case we can't change the value of that variable as we no longer
have a reference to it once we leave the GetDelegate() method. Its only
reference by the delegate itself.&lt;/p&gt;
&lt;p&gt;This example works because strings create a new instance of a string when they
are assigned. The example would behave differently if the local variable was a
reference to the same object as the class property.&lt;/p&gt;
&lt;p&gt;Using the local variable in an anonymous method that is called from outside
scope it was created might not be something you ever do in .Net, but its a
concept .Net developers should understand. I've haven't been able to think of
a simple, relevant, real world .net example for this, but I do use the same
concept in some Javascript I write about in &lt;a href="/journal/adrotator-webcontrol-example"&gt;another post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;EDIT: This is known as closure. This article I found on the VS2008 Start Page
talks about it and discusses the MSIL generated :  &lt;a href="http://www.managed-world.com/2008/06/13/LambdasKnowYourClosures.aspx"&gt;Lambdas - Know Your
Closures&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/anonymous-methods--understanding-scope</guid><pubDate>Thu, 19 Jun 2008 11:42:00 +0000</pubDate></item><item><title>AdRotator: Injecting scripts</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        AdRotator: Injecting scripts
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 15, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This article is part of a series of posts about various aspects of writing web
controls for ASP.Net using an ad rotator as an example. The &lt;a href="/journal/adrotator-webcontrol-example"&gt;AdRotator
WebControl Example&lt;/a&gt; post has links to related posts and downloads.&lt;/p&gt;
&lt;p&gt;The AdRotator needs to insert a class definition script in the output once,
even if multiple AdRotator controls are declared on the page. It also needs to
insert a script that instantiates that class prototype for every AdRotator
control on the page. Fortunately there are static methods on the
System.Web.UI.Page object that will do this for us.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Page.ClientScript.RegisterStartupScript(Type type, string key, string script)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The keys here a crucial, there is only one script rendered for each key. If we
inject the definition script with a static key it means no matter how many
instances control try to register it, only one instance of script will be sent
to the output.&lt;/p&gt;
&lt;p&gt;To to insert a unique script for every instance of the AdRotator we need to
use a unique key. I use the server side controls ClientId property which will
be difference for every instance of the control on any page. You could also
use a GUID.&lt;/p&gt;
&lt;p&gt;This is clearly the weakest post of all the posts about the AdRotator. I just
kind of got this working an never really got very inspired to learn anything
else about it. I posted it anyway as I felt I did need to cover this topic for
completeness of the series. I'll try to update it soon.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/adrotator-injecting-scripts</guid><pubDate>Sun, 15 Jun 2008 18:55:00 +0000</pubDate></item><item><title>AdRotator: Json serialization</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        AdRotator: Json serialization
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 15, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This article is part of a series of posts about various aspects of writing
web controls for ASP.Net using an ad rotator as an example. The &lt;a href="/journal/adrotator-webcontrol-example"&gt;AdRotator
WebControl Example&lt;/a&gt; post has links to related posts and downloads.&lt;/p&gt;
&lt;p&gt;The AdRotator needs to pass some data to the client side code so I use a Json
serializer. I use the same class we use on the server side. The Json
serializer converts an instance of it, or in our case a templated list of it,
to a sting on JSON we can emit in the output. Json is JavaScript Object
Notation and literally describes an object in Javascript that we can use in
our client side Javascript code.&lt;/p&gt;
&lt;p&gt;The object is very simple and only contains three strings and constructors. I
have removed some of the attributes described in other articles as they are
not relevant in this context.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ImageItem
{
    public ImageItem()
        : this(string.Empty, string.Empty, string.Empty)
    {
    }

    public ImageItem(string linkUrl, string imageUrl, string displayTime)
    {
        LinkUrl = linkUrl;
        ImageUrl = imageUrl;
        DisplayTime = displayTime;
    }

    public string LinkUrl { get; set; }
    public string ImageUrl { get; set; }
    public string DisplayTime { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The AdRotator has a public property Images that is List of the ImageItem type
above.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public List&amp;lt;ImageItem&amp;gt; Images;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;During the RenderContents event of the AdRotator we can serialize this list
into a string of Json.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = serializer.Serialize(Images);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Json will look something like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[{"ImageUrl":"Images/Winter.jpg","LinkUrl":"#","DisplayTime":"1000"},
 {"ImageUrl":"Images/Sunset.jpg","LinkUrl":"#","DisplayTime":"4000"}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following script and corresponding output will hopefully demonstrate how
we can use the Json on the client side as data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var imageList = [{"ImageUrl":"Images/Winter.jpg","LinkUrl":"#","DisplayTime":"1000"},
                 {"ImageUrl":"Images/Sunset.jpg","LinkUrl":"#","DisplayTime":"4000"}];

for(var i=0; i&amp;lt;imageList.length; i++)
{
    document.writeln(imageList[0].ImageUrl);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Images/Winter.jpg
Images/Winter.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well that's all the information I'm going to include in this article. I'd like
to go further into how the Json is used in the AdRotator but I've rambled on
long enough. I think there is enough here to see that Json is a pretty cool
language for communicating data between the server and client side Javascript.&lt;/p&gt;
&lt;p&gt;I should also note that the JavaScriptSerializer is marked as depreciated and
points towards the DataContractJsonSerializer. I briefly tried to get this
working, but I couldn't seem to find the object in my system assemblies. To
read more about that, check out &lt;a href="http://www.west-wind.com/WebLog/posts/218001.aspx"&gt;DataContractJsonSerializer in .NET 3.5&lt;/a&gt;
that discusses using the DataContractJsonSerializer.&lt;/p&gt;
&lt;p&gt;I hope you've found the article interesting, if you have you might be
interested in reading more article about the AdRotator example or you might
want to download and checkout the example project.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/adrotator-json-serialization</guid><pubDate>Sun, 15 Jun 2008 18:46:00 +0000</pubDate></item><item><title>AdRotator: Client side code</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        AdRotator: Client side code
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 15, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This article is part of a series of posts about various aspects of writing web
controls for ASP.Net using an ad rotator as an example. The &lt;a href="/journal/adrotator-webcontrol-example"&gt;AdRotator
WebControl Example&lt;/a&gt; post has links to related posts and downloads.&lt;/p&gt;
&lt;h3&gt;The Prototype&lt;/h3&gt;
&lt;p&gt;I wanted the client side code to be as object orientated as possible.  I
implemented the behaviour of our AdRotator as  a prototyped Javascript object,
equivalent to a class definition in other OO languages. This means every
instance of the AdRotator on a page only needs to be an instance of the ad
rotator prototype.&lt;/p&gt;
&lt;p&gt;I create the anchor and image elements on the fly. I need the client side
object to know about these elements to change them when the image changes. I
could have created them in the server-side and passed their ids to find them
in the DOM, or found them by looking through our containers child controls. In
the end creating them on client side was just slightly easier.&lt;/p&gt;
&lt;p&gt;The images parameter is an array of objects that have ImageUrl, LinkUrl and
DisplayTime properties. I pass it in as Json (Javascript object notion) that I
generated on the server using a Json serializer. You'll see the Json later in
this article, and I've also added an article about generating Json script on
the server side.&lt;/p&gt;
&lt;p&gt;You notice the strange way setTimeout is called in the RotateAd function. It
is all about Javascript scope and is far to big an issue to go into here. All
I will say is that if I had used "this" instead on the "thisObject" which is
set to "this" anyway, it might not point to this instance of this class, it
might actually point to the document, window or whatever the current "this"
actually is when the timeout event occurs.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function AdRotator(id, ads, height, width)
{
   this.id = id;
   this.ads = ads;
   this.index = 0;
   this.container = document.getElementById(id);
   this.anchorElement = document.createElement('a');
   this.imageElement = document.createElement('img');
   this.imageElement.setAttribute('height', height);
   this.imageElement.setAttribute('width', height);
   this.anchorElement.appendChild(this.imageElement);
   this.container.appendChild(this.anchorElement);
   this.RotateAd();
}


AdRotator.prototype.RotateAd = function()
{
    var currentAd = this.NextAd();
    this.imageElement.setAttribute('src', currentAd.ImageUrl);
    this.anchorElement.setAttribute('href', currentAd.LinkUrl);
    var thisObject = this;
    setTimeout(function() { thisObject.RotateAd(); }, currentAd.DisplayTime);
}

AdRotator.prototype.NextAd = function()
{
    var ad = this.ads[this.index];
    this.index ++;
    if (this.index == this.ads.length) this.index = 0;
    return ad;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The Instance&lt;/h3&gt;
&lt;p&gt;For each instance of the ad rotator we simply use this script. You'll
immediately notice that the code is filled with place holders. I replace these
placed holders on the server-side before I inject it into the output. I'll
describe these in more detail and finally I'll include the source of a
rendered page to see this as the browser sees.&lt;/p&gt;
&lt;p&gt;You might also notice that we are not actually assigning this object to
anything. Hopefully because it will always be referenced by a setTimeout event
it will always stay in scope. If I ever do have any problem with this I could
easily create and array in the definition and push each instance into the
array.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(new AdRotator('$ElementId', $Images, $Height, $Width));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The $ElementId placeholder is replaced with server control the server controls
ClientId property. This means we can reference the DOM object (a span element)
that the server outputs as a container for the control. The ClientId property
is very importing when creating elements on the server and interacting with
them on the client side. Often elements are not output with the ID of the
server control. One of the reasons is that its possible in ASP.Net to have two
control with the same ID in different placeholders. Or the same ID in a master
page and in the content page. I won't go into detail about how this naming
works, but its pretty clear when you view the source of the pages rendered by
an ASP.Net server. It is a string as it's used by definition to find an
element with the getElementById function.&lt;/p&gt;
&lt;p&gt;The $Images place holder is replaced with Json. Effectively a Javascript
object describing an object which is a an array of objects containing
ImageUrl, LinkUrl and DisplayTime properties. I have written an article about
how this Json is generated on the server side.&lt;/p&gt;
&lt;p&gt;The $Height and $Width place holders are replaced by the corresponding values
on the AdRotator server control.&lt;/p&gt;
&lt;p&gt;An example of how this script is actually output by the server should
hopefully demonstrates how this client side script actually works, and
hopefully how it works for multiple controls.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type="text/javascript"&amp;gt;
(new AdRotator('ctl00_MainContentPlaceHolder_rotator1', 
               [{"ImageUrl":"Images/Winter.jpg",
                 "LinkUrl":"Winter.aspx",
                 "DisplayTime":"1000"
                },
                {"ImageUrl":"Images/Sunset.jpg",
                 "LinkUrl":"Sunset.aspxd",
                  "DisplayTime":"4000"}],
                200, 200))
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just one article in a series describing various aspects of writing
ASP.Net server side controls. You can download the example code or assembly
and see how it works.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/adrotator-client-side-code</guid><pubDate>Sun, 15 Jun 2008 18:23:00 +0000</pubDate></item><item><title>AdRotator: Declaratively setting a collection property</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        AdRotator: Declaratively setting a collection property
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 15, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This article is part of a series of posts about various aspects of writing web
controls for ASP.Net using an ad rotator as an example. The &lt;a href="/journal/adrotator-webcontrol-example"&gt;AdRotator
WebControl Example&lt;/a&gt; post has links to related posts and downloads.&lt;/p&gt;
&lt;p&gt;I really wanted to make it possible for the control to be added completely
declaratively. Much the same way as you can create a list box with list items
declaratively.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;asp:ListBox ID="2" runat="server"&amp;gt;
    &amp;lt;asp:ListItem&amp;gt;Option1&amp;lt;/asp:ListItem&amp;gt;
    &amp;lt;asp:ListItem&amp;gt;Option1&amp;lt;/asp:ListItem&amp;gt;
&amp;lt;/asp:ListBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This actually turned out to be really easy after I found a great example from
Microsoft, &lt;a href="http://msdn.microsoft.com/en-us/library/9txe1d4x.aspx"&gt;Web Control Collection Property Example&lt;/a&gt;. I'd advise getting
straight into that if you are implementing this and need more detail. I will
just briefly describe what I had to implement for the AdRotator Control.&lt;/p&gt;
&lt;h3&gt;Attributes&lt;/h3&gt;
&lt;p&gt;I regularly set public properties on a web control with the appropriate type
converters declaratively. For example where the public properties Height and
Width declared as below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class AdRotator : WebControl
{
    public new string Width { get; set; }
    public new string Height { get; set; }
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They can be set declaratively in ASP.Net web files&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;x:AdRotator ID="AdRotator1" runat="server" Height="100" Width="100" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Children&lt;/h3&gt;
&lt;p&gt;When it came to adding the child data, I had already implemented a strongly
typed public data source property on the AdRotator control, so all I really
needed to do to get it working was add attribute meta data to some classes and
properties.&lt;/p&gt;
&lt;p&gt;On the actual control I had to add meta data about the type of child elements
we the control will support.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[DefaultProperty("Images")]
[ParseChildren(true, "Images")]
...
public class AdRotator : WebControl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the property "Images" I added the following metadata. I didn't need it all,
the "Category" for example is used when the object is displayed in a property
list control. The attribute classes and CollectionEditor are all in the
System.ComponentModel.Design namespace and the UITypeEditor is in
System.Drawing.Design namespace. As the collection we are exposing is strongly
typed we don't need to extend the CollectionEditor class.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Category("Behavior")]
[Description("Image URL collection")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List&amp;lt;ImageItem&amp;gt; Images
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ImageItem class also need some additional meta data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[TypeConverter(typeof(ExpandableObjectConverter))]
public ImageItem() : this(string.Empty, string.Empty, string.Empty)
{

    public ImageItem(string linkUrl, string imageUrl, string displayTime)
    {
        LinkUrl = linkUrl;
        ImageUrl = imageUrl;
        DisplayTime = displayTime;
    }

    [Category("Behavior")]
    [DefaultValue("")]
    [Description("Anchor Href Url")]
    [NotifyParentProperty(true)]
    public string LinkUrl { get; set; }

    ...

}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Finally&lt;/h3&gt;
&lt;p&gt;By the time the RenderContents method is fired, the Images property is fully
populated with all the items we declaratively added. When we are declaring the
image items we are getting nice intellisense. That's everything we set out to
do.&lt;/p&gt;
&lt;p&gt;&lt;a href="/image.axd?picture=WindowsLiveWriter/AdRotatorParsingpropertiesdeclaratively_E679/ad_rotator_intellisense_example1_2.jpg"&gt;ad_rotator_intellisense_example1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Although I didn't go into much detail here we did get everything I wanted in
the AdRotator example. I find this quite interesting as the concept of XML
based object declarations was build into the XAML SilverLight and WPF
technologies.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/adrotator-declaratively-setting-a-collection-property</guid><pubDate>Sun, 15 Jun 2008 18:22:00 +0000</pubDate></item><item><title>AdRotator: Embedding resources in an assembly</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        AdRotator: Embedding resources in an assembly
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 15, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;This article is part of a series of posts about various aspects of writing web
controls for ASP.Net using an ad rotator as an example. The &lt;a href="/journal/adrotator-webcontrol-example"&gt;AdRotator
WebControl Example&lt;/a&gt; post has links to related posts and downloads.&lt;/p&gt;
&lt;p&gt;The AdRotator has several scripts which it emits in the output. I wanted the
AdRotator completely encapsulated in a single "dll" file. I choose to embed
the scripts in the assembly and read them back at run time. I could have
included the scripts in the code as a string but I feel having escaped script
declarations in code is more difficult to read and maintain. It also makes it
difficult for the IDE to provide intellisense.&lt;/p&gt;
&lt;p&gt;Its easy to embed a file included in a project into the projects output
assembly. The setting can be found by selecting properties of the file you
want embed and selecting "Embedded Resource" as the "Build Action".&lt;/p&gt;
&lt;p&gt;&lt;a href="/journal/adrotator-webcontrol-example"&gt;embed_resource_example&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The file can be read back as a stream by reflecting the assembly and getting
the resource by name. Below is an example of reading a resource by name into a
string.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private string GetResourceString(string resourceName)
{
    Assembly assembly = GetType().Assembly;
    string resourceFullName = string.Format("{0}.{1}", assembly.GetName().Name, resourceName);
    string resourceString;

    using (Stream stream = assembly.GetManifestResourceStream(resourceFullName))
    {
        TextReader reader = new StreamReader(stream);
        resourceString = reader.ReadToEnd();
        reader.Close();
    }

    return resourceString;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As far as the code above goes, if it was a more professional application the
method would be protected by a unit tested wrapper with application specific
exception handling.&lt;/p&gt;
&lt;p&gt;There is a &lt;a href="http://www.sellsbrothers.com/tools/#resourceE"&gt;free tool&lt;/a&gt; created by the Sells Brothers you can use to view
the resources embedded in an assembly that may be useful.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/adrotator-emedding-resources-in-an-assembly</guid><pubDate>Sun, 15 Jun 2008 18:20:00 +0000</pubDate></item><item><title>AdRotator WebControl Example</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        AdRotator WebControl Example
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 15, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I didn't really actually want to write another ad rotator, but I did think it
would be a good example control to implement. It has the scope to cover some
aspects of writing web controls I wanted to learn more about and discuss. I
decided to build an ASP.Net web control that cycles through a list of images
on the client side. There are a few areas I want to discuss.&lt;/p&gt;
&lt;p&gt;I wanted the control completely encapsulated within an assembly. So all you'd
need to use the control would be a copy of the assembly. To do this I embedded
some resources in the assembly and read them out at runtime. I discuss this in
the article.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/journal/adrotator-embedding-resources-in-an-assembly"&gt;AdRotator: Embedding resources in an assembly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wanted the control to be able to be created completely declaratively, and I
thought providing the sort of intellisense you get when using standard ASP.Net
controls would be nice.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/journal/adrotator-declaratively-setting-a-collection-property"&gt;AdRotator: Declaratively setting a collection property&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wanted the server-side control to inject Javascript into the output. I
wanted the control to be smart enough to only output the Javascript prototype,
or class definition once, even if there were multiple controls on the page. I
wanted the client side code to be as object orientated as possible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/journal/adrotator-injecting-scripts"&gt;AdRotator: Injecting client scripts&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/journal/adrotator-client-side-code"&gt;AdRotator: Client-side code&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="/journal/adrotator-json-serialization"&gt;AdRotator: Json serialization&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can download the source code which includes an example website in the
solution.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/downloads/AdRotatorExample.zip"&gt;Source Download&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I would like to add a post about supporting difference storage mechanisms and
using the control, but I have other projects demanding my attention. Hopefully
I'll get back to it sometime.&lt;/p&gt;
&lt;p&gt;I Hope you've found some of these articles useful or interesting. I'll keep
and eye on the comments, contribute to further discussion, and update the
posts where errors or omissions are noted.&lt;/p&gt;
&lt;p&gt;Cheers&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/adrotator-webcontrol-example</guid><pubDate>Sun, 15 Jun 2008 18:01:00 +0000</pubDate></item><item><title>String.Format style parameters</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        String.Format style parameters
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 05, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Just a reminder that the String.Format parameters aren't magic, we can make
methods with these parameters. Here is an example.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public string StringFormat(string format, params object [] parameters)
{
      return string.Format(format, parameters);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I actually can't think of any great uses for it now, but I do use it to expose
string.Format style parameters on a LogMessage method in a custom logging
tool. This makes the code cleaner where I was using string.Format before
sending it to a LogMessage method as a string.&lt;/p&gt;
&lt;p&gt;Next week, Named Parameters! (well probably not, but if I ever use them.. )&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/string-format-style-parameters</guid><pubDate>Thu, 05 Jun 2008 14:34:00 +0000</pubDate></item><item><title>Dynamically inserting dynamic scripts</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Dynamically inserting dynamic scripts
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 03, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;I came across this technique when I was trying to get around the cross-domain
restrictions applied by modern browsers to the XMLHttpRequest object.
Basically you can't do it.&lt;/p&gt;
&lt;p&gt;This got me thinking, how does Google Maps do it? I have Google Map code in
pages hosted on my domain. Somehow it's going to the Google server, from the
browser, to get images. It can't be using XMLHttpRequest to do it, so how does
it work?&lt;/p&gt;
&lt;p&gt;Its seems ridiculously simple. You can reference scripts from another domain.
You can use javascript to manipulate the DOM and web servers can deliver
dynamic content based on a request.&lt;/p&gt;
&lt;p&gt;So all we have to do to dynamically request a dynamic script is run some
javascript that will insert a script tag with the source attribute pointing to
the desired URL in the document head tag. The browser will then request the
script from that URL. The server can return a script based on the request
parameters provided. When the browser receives the script it will add it to
the DOM.&lt;/p&gt;
&lt;p&gt;My example here doesn't actually get the scripts off an external server or
dynamically generate the script. The example has buttons that insert a script
element into the head element when clicked. The script elements source
attribute depends on which button is clicked, but both local script files
display an alert message when loaded.&lt;/p&gt;
&lt;p&gt;&lt;a href="DynamicScriptDemo1.zip"&gt;Download Demo (&amp;gt; 1k)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To see this example running, just open the folder as a Website in Visual
Studios and run it. I'll add a link to a live demo soon.&lt;/p&gt;
&lt;p&gt;While this works really well, I will go into a light-weight Javascript
framework I wrote that wraps round this concept and provides response, timeout
and some error handling events as you would expect in most XMLHttpRequest
wrappers.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/dynamically-inserting-dynamic-scripts</guid><pubDate>Tue, 03 Jun 2008 15:40:00 +0000</pubDate></item><item><title>Client-side onload event and Master pages</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Client-side onload event and Master pages
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 02, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;First blog ever for me, YAY!  I'll add to it regularly (yeah right).&lt;/p&gt;
&lt;p&gt;Anyways, you've just added some cool JavaScript to your page and you want to
call it when your page loads. Usually you'd just add an attribute to your body
tag, something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;body onload="MyLoadFunction()"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But you find that your body element is defined in your Master page and you
can't just add it to the Master as not all the pages that use the master will
implement MyLoadFunction(). There are heaps of ways of getting round this
problem, but I've found the following solution to be quite elegant.&lt;/p&gt;
&lt;p&gt;The solution basically involves creating a method on your Master page to add
an onload attribute to the body tag. So in the content page you want to add an
onload method you would call something like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Master.SetBodyOnloadAttribute("MyLoadFunction")
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To implement this method we need to be able to find the body tag and add the
attribute. We can find the body tag if we put id and runat="server" attributes
it when we define it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;body id="masterBody" runat="server"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can implement our SetBodyOnloadAttribute method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void SetBodyOnloadAttribute(string value)
{
    HtmlGenericControl bodyControl = FindControl("masterBody") as HtmlGenericControl;
    if (bodyControl != null)
    {
        bodyControl.Attributes.Add("onload", value);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now all you need to do is call the method on the master from your content
page and make sure the content page has the JavaScript function to handle the
event. Thats it.&lt;/p&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;p&gt;To use specialized methods on Master, you will have to add a Master Type
directive in the content aspx page, this will inform Visual Studios to use
your implementation of MasterPage. This is added after the Page directive.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%@ MasterType VirtualPath="~/MyMaster.Master" %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hope this helps&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/clientside-onload-event-and-master-pages</guid><pubDate>Mon, 02 Jun 2008 10:03:00 +0000</pubDate></item><item><title>Copy to Output Directory</title><description>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Tarn Barford&lt;/title&gt;
        &lt;meta charset="utf-8"/&gt;
        &lt;link rel="icon" type="image/x-icon" href="/favicon.ico"&gt;
        &lt;link href="/style.css" media="screen" rel="stylesheet" type="text/css" /&gt;
        &lt;link rel="alternate" type="application/atom+xml" title="Journals of Tarn Barford" href="/atom" /&gt;
        
    &lt;link href="/highlight.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    &lt;link href="/highlight-console.css" media="screen" rel="stylesheet" type="text/css" /&gt;
    

    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="container"&gt;
            &lt;div id="header"&gt;
                
&lt;div id="header"&gt;
    &lt;p&gt;From the &lt;a href="/journal"&gt;Journals&lt;/a&gt; of &lt;a href="/"&gt;Tarn Barford&lt;/a&gt;&lt;/p&gt;
    &lt;h1&gt;
        Copy to Output Directory
    &lt;/h1&gt;
    &lt;p&gt;
    Jun 02, 2008
    &lt;/p&gt;
&lt;/div&gt;

            &lt;/div&gt;

            
&lt;div id="post_content"&gt;
    &lt;html&gt;&lt;body&gt;&lt;p&gt;Far to often I've had multi-project solutions in Visual Studio not build
because of post build copy tasks. Far too often I've seen references to the
parent directory of a project to get files. No doubt both these things have a
time and place; you might have a Javascript minify task in your post build or
be writing an application that needs to access data or configuration from a
parent directory.&lt;/p&gt;
&lt;p&gt;Most of the time this is not the case and I often see people overlooking the
"copy to output directory" property on items in the solution explorer. This
property is really useful to solve the common problem of needing resources
from your project in the output, and it works in multi-project solutions!&lt;/p&gt;
&lt;p&gt;An example scenario would be if you had a solution with an application
project, and assembly project and a test assembly project. In this scenario
the assembly project has an XSL transform file, it makes sense to have this
file with the code in the project. Both the test project and the application
project will need to know about this file.&lt;/p&gt;
&lt;p&gt;I've actually seen code that references the file in project location, using
lots of ".." and "\" in the path to reference the source file. This will cause
problems if your application actually modifies the source code. It will cause
problems if you restructure your solution. And if you use absolute paths it
will prevent the solution from building unless its in a specific location.&lt;/p&gt;
&lt;p&gt;I've also seen extensive post build tasks that copy the required files to all
the places that might need them. This is means that every new project the
references the assembly will needed to be added in the post-build copy steps.&lt;/p&gt;
&lt;p&gt;Both these solutions have maintenance problems and are excessively difficult
to implement. The best solution here is to use the "copy to output directory"
property on the XSL file in the solution explorer. This will mean that when
you build your solution it will be copied to the output of all projects in the
solution that reference the project.&lt;/p&gt;
&lt;p&gt;There is a "copy always" and a "copy if newer" option. As I said earlier there
is a time and place for the other methods so it's best to think through what
makes sense for your solution.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
&lt;/div&gt;
&lt;div id="comments"&gt;
    
&lt;/div&gt;


        &lt;/div&gt;
        &lt;div id="footer"&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
            &lt;p&gt;Questions, comments, suggestions? Email me, &lt;a href="mailto:tarn@tarnbarford.net"&gt;tarn@tarnbarford.net&lt;/a&gt; (&lt;a href="/pgp.txt"&gt;public key&lt;/a&gt;)&lt;/p&gt;
            &lt;p&gt;&amp;nbsp;&lt;/p&gt;
        &lt;/div&gt;
        
    

    &lt;/body&gt;
&lt;/html&gt;</description><author>tarn@tarnbarford.net (tarn)</author><guid isPermaLink="false">https://tarnbarford.net/journal/copy-to-output-directory</guid><pubDate>Mon, 02 Jun 2008 09:53:00 +0000</pubDate></item></channel></rss>