<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1397432135341343773</id><updated>2020-02-29T08:07:08.945+00:00</updated><category term="iPhone"/><category term="scala"/><category term=".net"/><category term="MarkdownPreview"/><category term="dependency injection"/><category term="functional programming"/><category term="jailbreak"/><category term="markdown"/><category term="software engineering"/><category term="tdd"/><category term="20% time"/><category term="App Store"/><category term="C#"/><category term="Cydia"/><category term="F#"/><category term="Facebook"/><category term="NetShare"/><category term="PdaNet"/><category term="QuickPwn"/><category term="amazon"/><category term="aws"/><category term="clojure"/><category term="datamapper"/><category term="debugging"/><category term="dm-paperclip"/><category term="eclipse"/><category term="innovation"/><category term="inversion of control"/><category term="management"/><category term="maven"/><category term="merb"/><category term="motivation"/><category term="msbuild"/><category term="open-source"/><category term="optimisation"/><category term="paperclip"/><category term="programming"/><category term="project"/><category term="ruby"/><category term="s3"/><category term="slipup"/><category term="softwareengineering"/><category term="tail recursion"/><category term="teaching"/><category term="tethering"/><category term="visual studio"/><category term="wpf"/><title type='text'>Geoff on..</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://tech.geoff.me/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default?start-index=26&amp;max-results=25'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>32</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-3511616347971062996</id><published>2012-09-30T12:52:00.000+01:00</published><updated>2012-09-30T12:52:22.140+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="functional programming"/><category scheme="http://www.blogger.com/atom/ns#" term="optimisation"/><category scheme="http://www.blogger.com/atom/ns#" term="scala"/><category scheme="http://www.blogger.com/atom/ns#" term="tail recursion"/><title type='text'>Tailrec in Scala</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-mryY5t130bc/UGgxROn0pNI/AAAAAAAAAt0/Z6d3qUbsmbY/s1600/tail-recursion.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;http://3.bp.blogspot.com/-mryY5t130bc/UGgxROn0pNI/AAAAAAAAAt0/Z6d3qUbsmbY/s320/tail-recursion.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;One of the more tricky aspects to &lt;a href=&quot;http://en.wikipedia.org/wiki/Functional_programming&quot;&gt;functional programming&lt;/a&gt; is optimising your code. This is because you are typically working at a higher level of abstraction than &lt;a href=&quot;http://en.wikipedia.org/wiki/Procedural_programming&quot;&gt;procedural languages&lt;/a&gt;. The code you write isn&#39;t necessarily a natural fit to the way the processor works. For example, if you want to compare the factorial function in C# vs Scala:&lt;/p&gt;&lt;p&gt;&lt;em&gt;C#:&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-cs&quot;&gt;public void Factorial(int n) &lt;br /&gt;{&lt;br /&gt;    var result = 0;&lt;br /&gt;    for (int i = n; i &amp;gt; 0; i--)&lt;br /&gt;    {&lt;br /&gt;        result *= i;&lt;br /&gt;    }&lt;br /&gt;    return result;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Scala:&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-scala&quot;&gt;def factorial(n: Int): Int = &lt;br /&gt;    if (n == 0) 1&lt;br /&gt;    else n * factorial(n - 1)&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The big difference here is that in a procedural language you&#39;ll tend to write a loop that side effects on a variable where as in a functional language you&#39;ll tend to use recursion or a higher order function to write side-effect free code. Processors are &lt;em&gt;really&lt;/em&gt; good at the former.&lt;/p&gt;&lt;p&gt;What we see here is a compromise between performance and readability. The C# code will be quicker because the code will do all its work in the same stack frame. The Scala version will create &lt;code&gt;n&lt;/code&gt; stack frames while calling itself recursively. However, the Scala version is easier to reason about because there are no side effects, i.e. you&#39;re not rewriting &lt;code&gt;result&lt;/code&gt; in a loop. You may disagree for such a simple example, but once you have a couple of loops and several variables, procedural code becomes very difficult to follow.&lt;/p&gt;&lt;p&gt;Most functional languages address this issue by performing an optimisation called tail-call optimisation. However, some languages are better at it than others. In Scala you need to make sure that your recursive call is in the tail position (i.e. the recursive call is the last expression). In the factorial example above, it isn&#39;t the tail expression is actually &lt;code&gt;n * factorial(n - 1)&lt;/code&gt;. (You can read about this in more detail &lt;a href=&quot;http://blog.richdougherty.com/2009/04/tail-calls-tailrec-and-trampolines.html&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;http://stronglytypedblog.blogspot.co.uk/2009/08/scala-tail-recursion.html&quot;&gt;here&lt;/a&gt;.)&lt;/p&gt;&lt;p&gt;Reasoning about whether recursive functions will or won&#39;t be optimised is a problem for beginners because it isn&#39;t always obvious what qualifies for optimisation. There are other criteria to take into account such as the call must be a local function and the function must be final. A great feature of Scala is an annotation, &lt;code&gt;@tailrec&lt;/code&gt;, that raises a compile error you if your function won&#39;t be optimised:&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-scala&quot;&gt;import scala.annotation.tailrec&lt;br /&gt;&lt;br /&gt;@tailrec&lt;br /&gt;def factorial(n: Int): Int = &lt;br /&gt;    if (n == 0) 1&lt;br /&gt;    else n * factorial(n - 1)&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;This will generate a compile error in this case because the recursive function can&#39;t be tail-call optimised.&lt;/p&gt;&lt;p&gt;To allow the compile to optimise this, you will need to find a way to have the tail call be the recursive call, for example:&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-scala&quot;&gt;def factorial(n: Int): Int = {&lt;br /&gt;    @tailrec&lt;br /&gt;    def recFac(n: Int, acc: Int): Int = &lt;br /&gt;        if (n == 0) acc&lt;br /&gt;        else recFac(n - 1, acc * n)&lt;br /&gt;    recFac(n, 1)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;I learnt about &lt;code&gt;@tailrec&lt;/code&gt; through Martin Odersky&#39;s Scala course on &lt;a href=&quot;https://class.coursera.org/progfun-2012-001&quot;&gt;Coursera&lt;/a&gt;. The course is fantastic, Odersky&#39;s lectures are very interesting and tie theory into practical applications very neatly. The practical assignments really take me back to my first year of university.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/3511616347971062996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=3511616347971062996' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/3511616347971062996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/3511616347971062996'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/09/tailrec-in-scala.html' title='Tailrec in Scala'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-mryY5t130bc/UGgxROn0pNI/AAAAAAAAAt0/Z6d3qUbsmbY/s72-c/tail-recursion.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Wimbledon, London, UK</georss:featurename><georss:point>51.4347832 -0.213428</georss:point><georss:box>51.429833699999996 -0.2232985 51.4397327 -0.2035575</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-2571142373950206372</id><published>2012-09-15T14:10:00.000+01:00</published><updated>2012-09-15T14:10:53.140+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".net"/><category scheme="http://www.blogger.com/atom/ns#" term="msbuild"/><category scheme="http://www.blogger.com/atom/ns#" term="visual studio"/><title type='text'>Odd MSBuild platform behaviour</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-3UuJSvu6yV0/UFR-GxfaGkI/AAAAAAAAAtQ/SSA-J4i46Ow/s1600/weird-building-4.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear:left; float:left;margin-right:1em; margin-bottom:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;206&quot; width=&quot;320&quot; src=&quot;http://4.bp.blogspot.com/-3UuJSvu6yV0/UFR-GxfaGkI/AAAAAAAAAtQ/SSA-J4i46Ow/s320/weird-building-4.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;I came across some odd behaviour with msbuild this week. I was building a solution from the command line and setting the platform to &lt;code&gt;Any CPU&lt;/code&gt;. This worked find and the whole solution built. I then built one of the projects in the solution on its own (to create a clickonce installer), again with the platform set to &lt;code&gt;Any CPU&lt;/code&gt;, and this time it failed. The error I got was that I was trying to build for an unknown platform.&lt;/p&gt; &lt;p&gt;It turns out that the actual platform described in the .csproj is &lt;code&gt;AnyCPU&lt;/code&gt; (i.e. without a space) and that when building a solution &lt;code&gt;Any CPU&lt;/code&gt; gets translated to &lt;code&gt;Any CPU&lt;/code&gt;. So to build a solution you need to use &lt;code&gt;Any CPU&lt;/code&gt; and to build a project you need to use &lt;code&gt;AnyCPU&lt;/code&gt;. How bizarre.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/2571142373950206372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=2571142373950206372' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/2571142373950206372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/2571142373950206372'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/09/odd-msbuild-platform-behaviour.html' title='Odd MSBuild platform behaviour'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-3UuJSvu6yV0/UFR-GxfaGkI/AAAAAAAAAtQ/SSA-J4i46Ow/s72-c/weird-building-4.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Wimbledon, London, UK</georss:featurename><georss:point>51.4200159 -0.2188156</georss:point><georss:box>51.4175404 -0.2237511 51.422491400000006 -0.2138801</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-979038159476316147</id><published>2012-09-08T14:40:00.000+01:00</published><updated>2012-09-08T14:41:05.836+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dependency injection"/><category scheme="http://www.blogger.com/atom/ns#" term="F#"/><title type='text'>Alternative dependency injection in F#</title><content type='html'> &lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Dependency_injection&quot;&gt;Dependency injection&lt;/a&gt; is a pattern where a dependency of a class is passed to an instance of the class at runtime and is not created by the class itself. This is very useful for decoupling code, especially when you are unit testing and want to mock a dependency.&lt;/p&gt;&lt;p&gt;In OO languages like C# and Java, this is mostly often done with constructor injection, i.e.: &lt;/p&gt;&lt;pre class=&quot;prettyprint lang-cs&quot;&gt;interface IB&lt;br /&gt;{&lt;br /&gt;        void DoB();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class A&lt;br /&gt;{&lt;br /&gt;        public A(IB b)&lt;br /&gt;        {&lt;br /&gt;                this.b = b;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public DoA()&lt;br /&gt;        {&lt;br /&gt;                this.b.DoB();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private IB b;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The same thing can be done in F#, but there&#39;s an alternative. You could have your dependency be an abstract member and use an &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/dd233237.aspx&quot;&gt;object expression&lt;/a&gt; to create a concrete instance that has the correct dependency.&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-ml&quot;&gt;type IB =&lt;br /&gt;    abstract DoB : unit -&amp;gt; unit&lt;br /&gt;&lt;br /&gt;type B() =&lt;br /&gt;    interface IB with&lt;br /&gt;        member this.DoB() = ()&lt;br /&gt;&lt;br /&gt;[&amp;lt;AbstractClass&amp;gt;]&lt;br /&gt;type A() = &lt;br /&gt;    abstract b : IB&lt;br /&gt;    member this.doA() = this.b.DoB()&lt;br /&gt;&lt;br /&gt;let implA = &lt;br /&gt;    { new A() with &lt;br /&gt;        member x.b = B() :&amp;gt; IB&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;This isn&#39;t a better way of doing dependency injection. It&#39;s a more complicated way of achieving pretty much the same thing, but it does remind me a little of the Scala &lt;a href=&quot;http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di/&quot;&gt;cake pattern&lt;/a&gt;. &lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/979038159476316147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=979038159476316147' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/979038159476316147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/979038159476316147'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/09/alternative-dependency-injection-in-f.html' title='Alternative dependency injection in F#'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total><georss:featurename>Train to Penrith</georss:featurename><georss:point>54.664097 -2.752708</georss:point><georss:box>54.6457295 -2.79219 54.682464499999995 -2.713226</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-2294037304233604937</id><published>2012-09-02T18:54:00.001+01:00</published><updated>2012-09-02T22:33:22.247+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="markdown"/><category scheme="http://www.blogger.com/atom/ns#" term="MarkdownPreview"/><title type='text'>MarkdownPreview 0.2.0</title><content type='html'>&lt;p&gt;I&#39;ve uploaded a new version of MarkdownPreview. You can pick it up &lt;a href=&quot;https://bitbucket.org/hotfigs/markdownpreview/downloads/MarkdownPreview-0.2.0.exe&quot;&gt;here&lt;/a&gt;. This includes:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A better HTML render. The main benefit is that scrolling doesn&#39;t go back to the top when the Markdown source is reloaded.&lt;/li&gt;&lt;li&gt;Syntax highlighting. This uses Google prettify. You can add syntax to a code block by putting &lt;code&gt;{{cs}}&lt;/code&gt; (for csharp, see &lt;a href=&quot;http://google-code-prettify.googlecode.com/svn/trunk/README.html&quot;&gt;here&lt;/a&gt;) at the top of your code block.&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/2294037304233604937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=2294037304233604937' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/2294037304233604937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/2294037304233604937'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/09/markdownpreview-020.html' title='MarkdownPreview 0.2.0'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-6245475649489555603</id><published>2012-09-01T21:11:00.000+01:00</published><updated>2012-09-02T22:33:43.949+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".net"/><category scheme="http://www.blogger.com/atom/ns#" term="markdown"/><category scheme="http://www.blogger.com/atom/ns#" term="MarkdownPreview"/><category scheme="http://www.blogger.com/atom/ns#" term="open-source"/><category scheme="http://www.blogger.com/atom/ns#" term="project"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><title type='text'>MarkdownPreview</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;/div&gt;MarkdownPreview is a small Windows utility to watch a text file written in &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; for changes and to render it to HTML. You can &lt;a href=&quot;https://bitbucket.org/hotfigs/markdownpreview/downloads/MarkdownPreview-0.2.0.exe&quot;&gt;download&lt;/a&gt; it or visit the &lt;a href=&quot;https://bitbucket.org/hotfigs/markdownpreview&quot;&gt;project page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-sN6D9pAj66E/UEJqeO8zdcI/AAAAAAAAAs8/_g2jVYuuJSE/s1600/Markdown-0.1.0.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;MarkdownPreview 0.1.0&quot; border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-sN6D9pAj66E/UEJqeO8zdcI/AAAAAAAAAs8/_g2jVYuuJSE/s320/Markdown-0.1.0.PNG&quot; title=&quot;&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;I write my blog posts in Markdown using &lt;a href=&quot;http://www.vim.org/&quot;&gt;Vim&lt;/a&gt;. I wanted to be able to preview what I was writing as I was writing it. While there are some Markdown enabled editors out there, I wanted to stick to Vim, so I wrote a small utility to watch my markdown file for changes and re-render to HTML whenever I save it.&lt;br /&gt;&lt;br /&gt;I think this is my first open-source effort (MIT license)! It&#39;s only about an hours worth of work, so there may well be problems. Let me know if you find any. The main problems now are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The page scrolls back to the top when it reloads&lt;/li&gt;&lt;li&gt;The links are followed in MarkdownPreview, not externally&lt;/li&gt;&lt;li&gt;There is annoying sound-effect when the page reloads&lt;/li&gt;&lt;li&gt;No syntax highlighting&lt;/li&gt;&lt;/ul&gt;I&#39;ll take a look at these soon.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/6245475649489555603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=6245475649489555603' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/6245475649489555603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/6245475649489555603'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/09/markdownpreview.html' title='MarkdownPreview'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-sN6D9pAj66E/UEJqeO8zdcI/AAAAAAAAAs8/_g2jVYuuJSE/s72-c/Markdown-0.1.0.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-4249813808194444817</id><published>2012-08-27T18:11:00.000+01:00</published><updated>2012-09-02T17:45:09.704+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="dependency injection"/><category scheme="http://www.blogger.com/atom/ns#" term="inversion of control"/><category scheme="http://www.blogger.com/atom/ns#" term="software engineering"/><category scheme="http://www.blogger.com/atom/ns#" term="tdd"/><title type='text'>The object-oriected TDD journey</title><content type='html'>  &lt;p&gt;When you start practicing TDD for real, one of the first problems you&#39;ll come across is dependency management. Chances are, that before you were TDDing, whenever you needed to use class &lt;code&gt;A&lt;/code&gt; from class &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt; would create it&#39;s own instance of &lt;code&gt;A&lt;/code&gt; or &lt;code&gt;A&lt;/code&gt; might be a singleton. &lt;/p&gt;&lt;pre class=&quot;prettyprint lang-cs&quot;&gt;class B&lt;br /&gt;{&lt;br /&gt;    private A instanceA = new A();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;You soon find that you want to mock &lt;code&gt;A&lt;/code&gt; and the best way to get the mock of &lt;code&gt;A&lt;/code&gt; into an instance of &lt;code&gt;B&lt;/code&gt; (your &lt;a href=&quot;http://en.wikipedia.org/wiki/System_under_test,&quot; title=&quot;Wikipedia: System Under Test&quot;&gt;SUT&lt;/a&gt;), is to pass it into the constructor. This is called &lt;a href=&quot;http://en.wikipedia.org/wiki/System_under_test&quot;&gt;Dependency Injection&lt;/a&gt;:&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-cs&quot;&gt;class B&lt;br /&gt;{&lt;br /&gt;    public B(A a)&lt;br /&gt;    {&lt;br /&gt;        instanceA = a;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private readonly A instanceA;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;You also find that you don&#39;t want a dependency on &lt;code&gt;A&lt;/code&gt; because creating an instance of the mock of &lt;code&gt;A&lt;/code&gt; is going to call the constructor of &lt;code&gt;A&lt;/code&gt;. So you solve that by having &lt;code&gt;A&lt;/code&gt; implement an interface &lt;code&gt;IA&lt;/code&gt; and pass &lt;code&gt;IA&lt;/code&gt; to &lt;code&gt;B&lt;/code&gt; instead.&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-cs&quot;&gt;class B&lt;br /&gt;{&lt;br /&gt;    public B(IA a)&lt;br /&gt;    {&lt;br /&gt;        instanceA = a;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private readonly IA instanceA;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Your next problem comes along when you actually want to create an instance of &lt;code&gt;B&lt;/code&gt;. Say the constructor of &lt;code&gt;A&lt;/code&gt; takes an instance of &lt;code&gt;IC&lt;/code&gt;, you will need to do the following when you want a new &lt;code&gt;B&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-cs&quot;&gt;B instanceB = new B(new A(new C))&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;For a large application, this step will be a horror of nested &lt;code&gt;new&lt;/code&gt;s. So you will then learn about &lt;a href=&quot;http://en.wikipedia.org/wiki/Inversion_of_control&quot;&gt;Inversion Of Control&lt;/a&gt; and this will help you manage your dependencies:&lt;/p&gt;&lt;pre class=&quot;prettyprint lang-cs&quot;&gt;IocContainer container = new IocContainer();&lt;br /&gt;container.Register&amp;lt;B&amp;gt;();&lt;br /&gt;container.Register&amp;lt;IA&amp;gt;().ImplementedBy&amp;lt;A&amp;gt;();&lt;br /&gt;container.Register&amp;lt;IC&amp;gt;().ImplementedBy&amp;lt;C&amp;gt;();&lt;br /&gt;B instanceB = container.Resolve&amp;lt;B&amp;gt;();&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Here the IOC framework is responsible for figuring out the dependencies of &lt;code&gt;B&lt;/code&gt; and creating the required instances recursively.&lt;/p&gt;&lt;p&gt;Now you find that you&#39;re making a mess becase your IOC framework is letting you create a dependency from any class in your system to any other. You now pay attention to group your classes into modules with small, well-defined interfaces between them.&lt;/p&gt;&lt;p&gt;When working on a piece of code you will try and break it down into small coherent modules that look like stand-alone libraries. They will ideally have a very small set of public interfaces. The IOC registration will be done by the module itself and classes external to the module should only use the public interfaces. This prevents a spaghetti of inter-class dependencies.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/4249813808194444817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=4249813808194444817' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4249813808194444817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4249813808194444817'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/08/the-object-oriected-tdd-journey.html' title='The object-oriected TDD journey'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-5191710447363658670</id><published>2012-08-26T14:19:00.001+01:00</published><updated>2012-08-26T14:19:46.155+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="clojure"/><category scheme="http://www.blogger.com/atom/ns#" term="debugging"/><title type='text'>Rich Hickey on Debugging</title><content type='html'>I was reading Micheal Fogus&#39; &lt;a href=&quot;http://www.codequarterly.com/2011/rich-hickey/&quot;&gt;interview &lt;/a&gt;with Rich Hickey, the man behind &lt;a href=&quot;http://clojure.org/&quot;&gt;Clojure&lt;/a&gt;. When asked why he is considered an excellent debugger, his answer was fantastic. It often surprises me that this isn&#39;t how many programmers approach debugging.&lt;br /&gt;&lt;br /&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;&lt;i&gt;&lt;b&gt;Fogus&lt;/b&gt;&lt;/i&gt;: I’ve spoken with a few of your former co-workers, and they described you as a trouble-shooting and debugging master. How do you debug?&amp;nbsp;&lt;/blockquote&gt;&lt;blockquote class=&quot;tr_bq&quot;&gt;&lt;i&gt;&lt;b&gt;Hickey&lt;/b&gt;&lt;/i&gt;: I guess I use the scientific method. Analyze the situation given the available information, possibly gathering more facts. Formulate a hypothesis about what is wrong that fits the known facts. Find the smallest possible thing that could test the hypothesis. Try that. Often this will involve constructing an isolated reproducing case, if possible. If and only if the hypothesis is confirmed by the small test, look for that problem in the bigger application. If not, get more or better facts and come up with a different idea. I try to avoid attempting to solve the problem in the larger context, running in the debugger, just changing things to see effects, etc.&lt;br /&gt;Ideally, you know you have solved the problem before you touch the computer, because you have a hypothesis that uniquely fits the facts.&lt;/blockquote&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/5191710447363658670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=5191710447363658670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5191710447363658670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5191710447363658670'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/08/rich-hickey-on-debugging.html' title='Rich Hickey on Debugging'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-4502535018036758337</id><published>2012-08-04T14:31:00.000+01:00</published><updated>2012-08-04T15:13:49.557+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="functional programming"/><category scheme="http://www.blogger.com/atom/ns#" term="scala"/><title type='text'>Scala first impressions</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-c2c0o1wICEQ/UBWUeTXGMpI/AAAAAAAAAE4/CimtBALh7Hk/s1600/newsflash_logo.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;91&quot; src=&quot;http://4.bp.blogspot.com/-c2c0o1wICEQ/UBWUeTXGMpI/AAAAAAAAAE4/CimtBALh7Hk/s320/newsflash_logo.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;In the last year or two I&#39;ve been practicing TDD and the things that come naturally from it: dependency injection, inversion of control and data / behaviour separation. All these techniques complement each other and encourage you to write code that is actually quite functional. This lead me to start looking at functional languages to see if there was a better way to build software; OO paradigms were starting to look a little unnecessary. I started with Haskell, it being one of the most pure functional languages. I then tried F# and now I&#39;m playing with Scala.&lt;/p&gt; &lt;p&gt;Scala is interesting because it is a full object-oriented language, in some ways going further than Java, and is also a functional language providing most of the things you&#39;d expect from a functional language. For example:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;Pattern matching&lt;/li&gt;&lt;li&gt;Higher order functions&lt;/li&gt;&lt;li&gt;Anonymous functions / lambdas&lt;/li&gt;&lt;li&gt;Type inference&lt;/li&gt;&lt;li&gt;Currying&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;Traits&lt;/h2&gt; &lt;p&gt;One of the more interesting features of Scala is &lt;a href=&quot;http://www.scala-lang.org/node/126&quot;&gt;traits&lt;/a&gt;. These are similar to interfaces in Java and C# except that they allow implementations of the methods they describe. I find this a bit scary but also fascinating because a very common &lt;a href=&quot;http://en.wikipedia.org/wiki/Composition_over_inheritance&quot;&gt;code smell&lt;/a&gt; I see is people deriving from base classes in order to re-use code between classes and not because of an inherent &#39;is-a&#39; relationship. Traits let you re-use code between classes without a cumbersome inheritance relationship. I&#39;m only starting to get the hang of them though, for example they can be &lt;a href=&quot;http://www.artima.com/scalazine/articles/stackable_trait_pattern.html&quot;&gt;stacked&lt;/a&gt;.&lt;/p&gt; &lt;h2&gt;Pattern matching&lt;/h2&gt; &lt;p&gt;Pattern matching in Scala is very similar to F# and Haskell. It&#39;s one of those features that you find yourself missing from other languages once you&#39;ve learnt about it.&lt;/p&gt; &lt;h2&gt;Type inference&lt;/h2&gt; &lt;p&gt;The type inference in Scala is not as good as in F#, this is apparently because of Scala&#39;s &lt;a href=&quot;http://stackoverflow.com/questions/168428/f-and-scala-comparison&quot;&gt;OO features&lt;/a&gt;. That said it&#39;s still much better than Java or C#, although that&#39;s probably an unfair judgement).&lt;/p&gt; &lt;h2&gt;Anonymous functions&lt;/h2&gt; &lt;p&gt;This isn&#39;t really exclusively the domain of functional languages anymore, C# and even C++ support it now. I think the Java committee have really let themselves down by not including this in &lt;a href=&quot;http://en.wikipedia.org/wiki/Java_version_history#Java_SE_7_.28July_28.2C_2011.29&quot;&gt;Java 7&lt;/a&gt;. They are extremely useful and pop up in all sorts of places. They also allow library writers to design great APIs.&lt;/p&gt; &lt;h2&gt;JVM&lt;/h2&gt; &lt;p&gt;I really like that Scala compiles to bytecode and can be run on lots of platforms. I love C# and the .NET framework, but I hate that it doesn&#39;t have first class support on non-windows platforms. The &lt;a href=&quot;(http://www.mono-project.com/Main_Page&quot;&gt;mono&lt;/a&gt; framework is an amazing piece of kit, but I&#39;m always going to trust the JVM on Linux over Mono. That&#39;s perhaps unfair, but you really don&#39;t want to worry about bugs in your runtime environment and there&#39;s always a manager you&#39;re going to need to convince.&lt;/p&gt; &lt;h2&gt;Tooling&lt;/h2&gt; &lt;p&gt;One of the things that I found a little disappointing about the Haskell eco-system is that there is a distinct lack of decent tools. The only proper Haskell IDE is &lt;a href=&quot;http://leksah.org/&quot;&gt;Leksah&lt;/a&gt; which is a step in the right direction, but is still in its infancy. Haskell is the perfect target for auto-refactoring tools because it has proper strong typing, but there isn&#39;t anything serious, no re-sharper or eclipse for Haskell. F# is better but still limited (you can&#39;t organise your code in folders in Visual Studio for example).&lt;/p&gt; &lt;p&gt;I&#39;ve been really impressed with the tools available for Scala. There is decent support available in &lt;a href=&quot;http://confluence.jetbrains.net/display/SCA/Scala+Plugin+for+IntelliJ+IDEA&quot;&gt;Intellij&lt;/a&gt; and &lt;a href=&quot;http://scala-ide.org/&quot;&gt;Eclipse&lt;/a&gt; (through plugins). They&#39;re still a little rough, but are definitely usable. &lt;/p&gt; &lt;p&gt;I think &lt;a href=&quot;http://maven.apache.org/&quot;&gt;Maven&lt;/a&gt; is fantastic and being able to use it to &lt;a href=&quot;http://scala-tools.org/mvnsites/maven-scala-plugin/&quot;&gt;build Scala&lt;/a&gt; and pull in maven dependencies is wonderful. Also, &lt;a href=&quot;http://www.scala-sbt.org/&quot;&gt;SBT&lt;/a&gt; is an alternative to Maven for Scala which is easier to get started with (not being XML based helps), which I also quite like. SBT can pull in maven dependencies which means getting an SBT project started is very quick.&lt;/p&gt; &lt;h2&gt;Conclusion&lt;/h2&gt; &lt;p&gt;I&#39;m clearly becoming a fan of Scala, but I can&#39;t help worry that the reason I&#39;m getting on with it is because it&#39;s not actually very different from the languages I&#39;m comfortable with (C#, C++, Python, Java etc). I think I still need to keep learning Haskell to try and get around some of my problems with it (like how to build a large system TDDing as I go along - I can&#39;t rely on IOC to help me out like I do with C# and Java). However, right now I would be happy to start a production project using Scala. Though I would love to do one in Haskell as an experiment, it would be a risky proposition.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/4502535018036758337/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=4502535018036758337' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4502535018036758337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4502535018036758337'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/08/scala-first-impressions.html' title='Scala first impressions'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-c2c0o1wICEQ/UBWUeTXGMpI/AAAAAAAAAE4/CimtBALh7Hk/s72-c/newsflash_logo.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Wimbledon, London SW19, UK</georss:featurename><georss:point>51.4347832 -0.213428</georss:point><georss:box>51.429833699999996 -0.2232985 51.4397327 -0.2035575</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-3581725469432024787</id><published>2012-07-29T20:54:00.001+01:00</published><updated>2012-07-29T20:55:40.910+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="eclipse"/><category scheme="http://www.blogger.com/atom/ns#" term="maven"/><category scheme="http://www.blogger.com/atom/ns#" term="scala"/><title type='text'>Scala, Maven and Eclipse</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/-c2c0o1wICEQ/UBWUeTXGMpI/AAAAAAAAAE4/CimtBALh7Hk/s1600/newsflash_logo.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;91&quot; src=&quot;http://4.bp.blogspot.com/-c2c0o1wICEQ/UBWUeTXGMpI/AAAAAAAAAE4/CimtBALh7Hk/s320/newsflash_logo.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;You might be having trouble getting &lt;a href=&quot;http://scala-ide.org/&quot;&gt;Scala IDE&lt;/a&gt;, Maven (&lt;a href=&quot;http://www.sonatype.org/m2eclipse/&quot;&gt;m2e&lt;/a&gt;) and Eclipse to play nicely. In particular the &lt;code&gt;More than one scala library found in the build path [...]&amp;nbsp;At least one has an incompatible version. Please update the project build path so it contains only compatible scala libraries.&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Try the following:&lt;br /&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Generate a scala project using the maven archetype &lt;code&gt;scala-archetype-simple&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Edit the &lt;code&gt;pom.xml&lt;/code&gt; with the following:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Change the &lt;code&gt;scala.version&lt;/code&gt; to 2.9.2&lt;/li&gt;&lt;li&gt;Remove the unit testing dependencies (add the ones you need back later once you have the other stuff working - the default versions won&#39;t be compatible with 2.9.2)&lt;/li&gt;&lt;li&gt;Change the &lt;code&gt;maven-surefire-plugin&lt;/code&gt; to 2.12&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Remove the sample test classes in &lt;code&gt;src/test/scala/sample&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Import the maven project into eclipse: &lt;code&gt;Package Explorer -&amp;gt; Import... -&amp;gt; Existing Maven Projects&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;This should now give you a mavenised Scala project loaded into Eclipse. If you have trouble with eclipse not updating with your pom changes, delete the project, clear out the eclipse stuff from the project dir (.project, .settings, .cache, etc) then re-import the maven project.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Also note that I&#39;m using:&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Eclipse Indigo&lt;/li&gt;&lt;li&gt;The &lt;a href=&quot;http://scala-ide.org/download/nightly.html&quot;&gt;nightly builds&lt;/a&gt; of Scala IDE&lt;/li&gt;&lt;li&gt;The normal m2e&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/3581725469432024787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=3581725469432024787' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/3581725469432024787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/3581725469432024787'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/07/scala-maven-and-eclipse.html' title='Scala, Maven and Eclipse'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-c2c0o1wICEQ/UBWUeTXGMpI/AAAAAAAAAE4/CimtBALh7Hk/s72-c/newsflash_logo.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Wimbledon, London SW19, UK</georss:featurename><georss:point>51.4347832 -0.213428</georss:point><georss:box>51.429833699999996 -0.2232985 51.4397327 -0.2035575</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-1384045658558914145</id><published>2012-07-28T15:27:00.000+01:00</published><updated>2012-07-28T15:28:36.782+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="20% time"/><category scheme="http://www.blogger.com/atom/ns#" term="innovation"/><category scheme="http://www.blogger.com/atom/ns#" term="management"/><category scheme="http://www.blogger.com/atom/ns#" term="motivation"/><category scheme="http://www.blogger.com/atom/ns#" term="software engineering"/><title type='text'>Give your people time to innovate</title><content type='html'>&lt;br /&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2.bp.blogspot.com/-XV96WcxYYWQ/UBP2XtxzSOI/AAAAAAAAAEg/NUCFdVtw85M/s1600/post-it.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;http://2.bp.blogspot.com/-XV96WcxYYWQ/UBP2XtxzSOI/AAAAAAAAAEg/NUCFdVtw85M/s200/post-it.jpg&quot; width=&quot;188&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Developers who are too busy to experiment are wasted talent. Software developers are smart people who need to be creative to be good at their jobs. In all but the most lax environments, team members will have their work set out for them. Even in Agile environments, there might be flexibility in who does what work and developers will be involved in the work estimation process. Ultimately, it is management and customers who decide&amp;nbsp;&lt;i&gt;what&lt;/i&gt;&amp;nbsp;work is done.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Google has made the concept of 20% time famous. However, 3M have been encouraging their employees to experiment for 15% of their time since 1948:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;i&gt;&quot;[..] workers often use 15 percent time to pursue something they discovered through the usual course of work but didn&#39;t have time to follow up on.&quot;&amp;nbsp;&lt;/i&gt;Kaomi Goetz -&amp;nbsp;&lt;a href=&quot;http://www.fastcodesign.com/1663137/how-3m-gave-everyone-days-off-and-created-an-innovation-dynamo&quot;&gt;www.fastcodesign.com&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;The idea is very simple. Give your teams a little slack so that they can try out new things. For example, a programmer might want to try out a new library, learn a new language or build an app. There are some solid benefits:&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;They can work on an idea that they think will be great, but would never be able to convince management without a prototype. Products like &lt;a href=&quot;http://googlepress.blogspot.co.uk/2004/04/google-gets-message-launches-gmail.html&quot;&gt;Google Mail&lt;/a&gt;&amp;nbsp;and&amp;nbsp;the &lt;a href=&quot;http://www.fastcodesign.com/1663137/how-3m-gave-everyone-days-off-and-created-an-innovation-dynamo&quot;&gt;Post-It note&lt;/a&gt;&amp;nbsp;came out of these kinds of schemes.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;Fix something in the product won&#39;t ever be a high priority, but these kinds of things tend to add quality to the product over time.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;On the smaller scale improvements to processes and smaller innovations that can be added to existing products.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;It is great motivation for employees. All projects will have periods of grind where developers are just cranking out repetitive work. A bit of a break when they can let their hair down does wonders for morale.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: white;&quot;&gt;It&#39;s a great form of training. No matter what people work on in this time, they&#39;ll be learning while doing it.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Ok, so assuming that you&#39;re now convinced, how would you implement a scheme like this? Mike Cannon-Brookes of Atlassian &lt;a href=&quot;http://blogs.atlassian.com/2008/03/20_time_experiment/&quot;&gt;says&lt;/a&gt;:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;i&gt;&quot;You see, while everyone knows about Google’s 20% time and we’ve heard about all the neat products born from it (Google News, GMail etc) – we’ve found it extremely difficult to get any hard facts about how it actually works in practice.&quot;&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;It will ultimately depend on your organisation. I think the key is to keep it as open as possible. Any constraints on the developers are going to limit any possibly innovation. However, a few constraints will help keep everyone&#39;s goals aligned:&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;i&gt;&quot;Self-organizing systems are able to create their own rules. All is needed&amp;nbsp;&lt;/i&gt;&lt;span style=&quot;background-color: white; font-style: italic;&quot;&gt;for such a system to work is a set of simple constraints, sometimes called a&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: white; font-style: italic;&quot;&gt;boundary. It is important for managers to tune these constraints, and not&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: white; font-style: italic;&quot;&gt;to try and design all the rules. This means the job of a manager is to manage&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&lt;i&gt;the system, and not the people in it.&quot; &lt;/i&gt;Management 3.0 - Jurgen Appelo&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: white;&quot;&gt;For example:&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;ul&gt;&lt;li&gt;Your project should be something that would improve the company&lt;/li&gt;&lt;li&gt;Your project should have short, achievable goals&lt;/li&gt;&lt;li&gt;Your project must be visible to everyone&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;The first point is designed to steer people to work on something relevant to the company but without limiting them so much that they play it safe.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The typical scheme will be between 10% - 20% of the employee&#39;s time. Any less and they won&#39;t have time to produce anything of substance, any more and it will interfere with their jobs. However, this is still not much time, so it&#39;s important to make sure people aren&#39;t being too ambitious and setting themselves unachievable goals (e.g. write an operating system in 4 hours). This also helps with the other two goals.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Lastly, people should be talking about what they are working on. This is important for stakeholders to see that people aren&#39;t just wasting time. More importantly, however, it means that people are sharing ideas which is going to generate more ideas and collaboration. A wiki and small demos might be a nice way to kick start this.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So why not try it and see what happens. Friday afternoons are typically productivity sinks anyway. Why not use that slow time to stir things up a bit?&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/1384045658558914145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=1384045658558914145' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/1384045658558914145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/1384045658558914145'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/07/give-your-people-time-to-innovate.html' title='Give your people time to innovate'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-XV96WcxYYWQ/UBP2XtxzSOI/AAAAAAAAAEg/NUCFdVtw85M/s72-c/post-it.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Wimbledon, London SW19, UK</georss:featurename><georss:point>51.425437111187236 -0.21895408630371094</georss:point><georss:box>51.420486611187236 -0.22882458630371094 51.430387611187236 -0.20908358630371093</georss:box></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-5997767616836609468</id><published>2012-07-20T21:52:00.000+01:00</published><updated>2012-07-28T15:29:11.943+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="softwareengineering"/><category scheme="http://www.blogger.com/atom/ns#" term="tdd"/><category scheme="http://www.blogger.com/atom/ns#" term="teaching"/><title type='text'>Teaching TDD</title><content type='html'>&lt;div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://4.bp.blogspot.com/_M5CxIdbNdyk/R0pFEYdeg_I/AAAAAAAAA4s/WWZtdHQP5F0/s320/boy-at-blackboard.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/_M5CxIdbNdyk/R0pFEYdeg_I/AAAAAAAAA4s/WWZtdHQP5F0/s320/boy-at-blackboard.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;I&#39;ve been evangelising TDD at work and have been quite surprised by the how it has been taken up. In general, people are excited to learn about TDD and there was an excellent turnout to talks a colleague and I have given. We ran through the full test driven cycle:&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Write a test&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Watch it fail to compile&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Fix the compile errors&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Run the test and watch it fail&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Write just enough code to make the test pass&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Watch the test pass&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Refactor&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Go to step 1&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;We demonstrated each one of these steps with 3 or 4 unit tests for an example class.&amp;nbsp;However, no matter how much I repeat that you shouldn&#39;t write code before a test and that you should only write enough code to make your test pass, people struggle to get their heads around that idea. I still often come across people who are trying to learn TDD, but are not following this pattern.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;border-collapse: separate; border-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;I was prompted to start running the mini TDD courses when I saw a piece of code a junior member of my team was working on. He was still not used to the idea of writing tests first, so I sat with him and helped him write a bit of code in the TDD style. While he coded in front of me, I was able to point out when he&#39;d skipped too far ahead and had written code that he didn&#39;t have a test for yet and it was only after a few round of this that he finally&amp;nbsp;&lt;i&gt;got&amp;nbsp;&lt;/i&gt;what TDD was all about.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;I think that a practical one-on-one session like this is the only way to really show someone how to do TDD. Sit with them and have them start writing code and just gently nudge them when they&#39;ve skipped a step. I&#39;ve done this a couple of times now and it only takes a couple of tests before it sinks in.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/5997767616836609468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=5997767616836609468' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5997767616836609468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5997767616836609468'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2012/07/teaching-tdd.html' title='Teaching TDD'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_M5CxIdbNdyk/R0pFEYdeg_I/AAAAAAAAA4s/WWZtdHQP5F0/s72-c/boy-at-blackboard.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-4622121780786574218</id><published>2008-12-18T15:51:00.000+00:00</published><updated>2008-12-18T16:10:59.605+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="amazon"/><category scheme="http://www.blogger.com/atom/ns#" term="aws"/><category scheme="http://www.blogger.com/atom/ns#" term="datamapper"/><category scheme="http://www.blogger.com/atom/ns#" term="dm-paperclip"/><category scheme="http://www.blogger.com/atom/ns#" term="merb"/><category scheme="http://www.blogger.com/atom/ns#" term="paperclip"/><category scheme="http://www.blogger.com/atom/ns#" term="programming"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><category scheme="http://www.blogger.com/atom/ns#" term="s3"/><title type='text'>Using S3 With dm-paperclip</title><content type='html'>&lt;a href=&quot;http://github.com/krobertson/dm-paperclip/tree/master&quot;&gt;dm-paperclip&lt;/a&gt; is a port of &lt;a href=&quot;http://www.thoughtbot.com/projects/paperclip/&quot;&gt;Paperclip&lt;/a&gt; to &lt;a href=&quot;http://datamapper.org/doku.php&quot;&gt;Datamapper&lt;/a&gt;. It&#39;s a pretty straightforward port so most of the Paperclip documentation is valid for dm-paperclip. One of the most attractive features of Paperclip is it&#39;s ability to host your files on Amazon S3 instead of locally. Here&#39;s how you do it:&lt;br /&gt;&lt;br /&gt;You will need 3 extra parameters on your &lt;code&gt;has_attached_file&lt;/code&gt; declaration in your model:&lt;br /&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;br /&gt;has_attached_file :photo,&lt;br /&gt;:storage =&gt; :s3,&lt;br /&gt;:s3_credentials =&gt; Merb.root / &quot;config&quot; / &quot;s3.yml&quot;,&lt;br /&gt;:path =&gt; &#39;:attachment/:id/:style.:extension&#39;&lt;br /&gt;validates_attachment_presence :photo&lt;br /&gt;validates_attachment_size :photo, :in =&gt; 1..200000&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;code&gt;config/s3.yml&lt;/code&gt; file stores your S3 credentials and bucket name. This is very handy because you can have different configs and inheritance, just like database.yml:&lt;br /&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;br /&gt;development: &amp;amp;defaults&lt;br /&gt;access_key_id: ...&lt;br /&gt;secret_access_key: ...&lt;br /&gt;bucket: project_images_development&lt;br /&gt;&lt;br /&gt;production:&lt;br /&gt;&lt;&lt;:       *defaults      bucket: project_images_production &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That&#39;s it!</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/4622121780786574218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=4622121780786574218' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4622121780786574218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4622121780786574218'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/12/using-s3-with-dm-paperclip.html' title='Using S3 With dm-paperclip'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-62695063254785416</id><published>2008-11-30T21:08:00.000+00:00</published><updated>2008-11-30T21:23:15.947+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="App Store"/><category scheme="http://www.blogger.com/atom/ns#" term="iPhone"/><title type='text'>&quot;Update All&quot; Has Returned To The App Store</title><content type='html'>&lt;img style=&quot;margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 226px; height: 320px;&quot; src=&quot;http://2.bp.blogspot.com/_VcWdtebG1dM/STKvHCUh3fI/AAAAAAAAABE/mIppwLqWhFI/s320/hero20081004.jpg&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5274470649021193714&quot; border=&quot;0&quot; /&gt;I updated my iPhone to 2.2 today and was very pleasantly surprised to see that the ability to update all the applications at once has returned.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/62695063254785416/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=62695063254785416' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/62695063254785416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/62695063254785416'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/11/update-all-has-returned-to-app-store.html' title='&quot;Update All&quot; Has Returned To The App Store'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_VcWdtebG1dM/STKvHCUh3fI/AAAAAAAAABE/mIppwLqWhFI/s72-c/hero20081004.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-5839824886901385218</id><published>2008-11-30T17:03:00.000+00:00</published><updated>2008-11-30T17:16:45.920+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cydia"/><category scheme="http://www.blogger.com/atom/ns#" term="iPhone"/><category scheme="http://www.blogger.com/atom/ns#" term="jailbreak"/><category scheme="http://www.blogger.com/atom/ns#" term="QuickPwn"/><title type='text'>Upgrading A Jailbroken iPhone From 2.1 to 2.2</title><content type='html'>To upgrade an iPhone that has been jailbroken on version 2.1 to version 2.2:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Open up iTunes and plug in your iPhone&lt;/li&gt;&lt;li&gt;Sync and backup your iPhone&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Open up iTunes and click &lt;span style=&quot;font-weight: bold;&quot;&gt;Restore&lt;/span&gt; to restore the factory default, this should also ask you to upgrade to 2.2&lt;/li&gt;&lt;li&gt;Use &lt;a href=&quot;http://www.quickpwn.com/&quot;&gt;QuickPwn&lt;/a&gt; to jailbreak your fresh 2.2 install&lt;/li&gt;&lt;li&gt;Restore your backup&lt;/li&gt;&lt;/ul&gt;NOTE: You can also use &lt;a href=&quot;http://www.ismashphone.com/2008/09/appbackup-aptba.html&quot;&gt;AptBackup&lt;/a&gt; from Cydia to backup your jailbroken apps.&lt;br /&gt;&lt;center&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://1.bp.blogspot.com/_VcWdtebG1dM/STLITvnedJI/AAAAAAAAABM/myiLC-cyBvU/s1600-h/2691665454_684ca8b1f7.jpg&quot;&gt;&lt;img style=&quot;margin: 20px 0px 10px 0pt; cursor: pointer;&quot; width=&quot;200px&quot; src=&quot;http://1.bp.blogspot.com/_VcWdtebG1dM/STLITvnedJI/AAAAAAAAABM/myiLC-cyBvU/s320/2691665454_684ca8b1f7.jpg&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5274498355129382034&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/center&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/5839824886901385218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=5839824886901385218' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5839824886901385218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5839824886901385218'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/11/upgrading-jailbroken-iphone-from-21-to.html' title='Upgrading A Jailbroken iPhone From 2.1 to 2.2'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_VcWdtebG1dM/STLITvnedJI/AAAAAAAAABM/myiLC-cyBvU/s72-c/2691665454_684ca8b1f7.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-5581368804137316456</id><published>2008-11-30T14:39:00.000+00:00</published><updated>2008-11-30T15:20:07.607+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="iPhone"/><category scheme="http://www.blogger.com/atom/ns#" term="jailbreak"/><category scheme="http://www.blogger.com/atom/ns#" term="NetShare"/><category scheme="http://www.blogger.com/atom/ns#" term="PdaNet"/><category scheme="http://www.blogger.com/atom/ns#" term="tethering"/><title type='text'>iPhone Tethering Done Right: PdaNet</title><content type='html'>&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://2.bp.blogspot.com/_VcWdtebG1dM/STKvHCUh3fI/AAAAAAAAABE/mIppwLqWhFI/s1600-h/hero20081004.jpg&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 226px; height: 320px;&quot; src=&quot;http://2.bp.blogspot.com/_VcWdtebG1dM/STKvHCUh3fI/AAAAAAAAABE/mIppwLqWhFI/s320/hero20081004.jpg&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5274470649021193714&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;Tethering an iPhone to another device, usually a laptop, is not allowed. AT&amp;amp;T contracts ban the practice (though this is &lt;a href=&quot;http://gizmodo.com/5078562/confirmed-3g-tethering-coming-to-iphone&quot;&gt;rumoured to be changing soon&lt;/a&gt;). However, when you&#39;re stuck without a decent WiFi connection, an iPhone tether is a tempting proposition. Sadly, since it is banned, Apple have not made it easy.&lt;br /&gt;&lt;br /&gt;Most solutions revolved around something called a &lt;a href=&quot;http://en.wikipedia.org/wiki/SOCKS&quot;&gt;SOCKS&lt;/a&gt; proxy. A proxy is just an application that runs on the iPhone that the tethered device communicates with in order to retreive data from the web. Despite it&#39;s simplicity, there is a major drawback: every application that needs to use the internet must have SOCKS compatibility built in. Many applications do, but the implementations are usually hasily added and tend to be buggy (even Firefox has issues).&lt;br /&gt;&lt;br /&gt;If you were lucky enough to acquire the &lt;a href=&quot;http://www.theregister.co.uk/2008/08/04/netshare_in_out_in_out/&quot;&gt;NetShare&lt;/a&gt; iPhone application (a SOCKS proxy that was available on the App Store for a very short period before being removed by Apple) you do not need to jailbreak your iPhone to be able to tether a device. However, most people will need to do this. However, this is really easy these days and quite safe. A tool such as &lt;a href=&quot;http://www.quickpwn.com/&quot;&gt;QuickPwn&lt;/a&gt; will do the job with very little fuss.&lt;br /&gt;&lt;br /&gt;Once your iPhone is jailbroken, you have a number of proxy server choices. From the NetShare style &lt;a href=&quot;http://www.mydigitallife.info/2008/07/27/how-to-tether-and-connect-iphone-1g-2g-and-3g-as-modem-for-internet-gateaway/&quot;&gt;3proxy&lt;/a&gt; to the old school SSH server. However, there is a much better solution: &lt;a href=&quot;http://www.junefabrics.com/iphone/index.php&quot;&gt;PdaNet&lt;/a&gt;. This is &lt;span style=&quot;font-style: italic;&quot;&gt;not&lt;/span&gt; a SOCKS proxy, it acts more like a software router, passing all your network traffic over the iPhone&#39;s internet connection. This means that your applications don&#39;t need to have SOCKS built in.&lt;br /&gt;&lt;br /&gt;There are a number of issues with NetShare. Sometimes the DNS setup doesn&#39;t work very well (especially using an Ad-Hoc network on a Mac). It will also heavily drain your battery. You will need to plug it in for it to last for any long period (i.e. over an hour). You also have to pay to use it with encrypted websites (i.e. HTTPS) - but there is a way around that. Just visit the link printed at the bottom of the PdaNet screen on the iPhone, pretend to buy the software, but stop when you hit the payment screen. You can then browse any site you want without paying!&lt;br /&gt;&lt;br /&gt;However, it&#39;s definitely the best tethering solution at the moment. Though you might also like to try solutions like &lt;a href=&quot;http://www.proxifier.com/&quot;&gt;proxifier&lt;/a&gt; for making SOCKS proxies easier (and more reliable).</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/5581368804137316456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=5581368804137316456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5581368804137316456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5581368804137316456'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/11/iphone-tethering-done-right-pdanet.html' title='iPhone Tethering Done Right: PdaNet'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_VcWdtebG1dM/STKvHCUh3fI/AAAAAAAAABE/mIppwLqWhFI/s72-c/hero20081004.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-4779838294654659954</id><published>2008-11-30T14:22:00.000+00:00</published><updated>2008-11-30T14:37:28.905+00:00</updated><title type='text'>Facebook Group Email Limits</title><content type='html'>&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://1.bp.blogspot.com/_VcWdtebG1dM/STKk4Fn4EYI/AAAAAAAAAA8/lXXKOALHLkc/s1600-h/logo_facebook.jpg&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 320px; height: 120px;&quot; src=&quot;http://1.bp.blogspot.com/_VcWdtebG1dM/STKk4Fn4EYI/AAAAAAAAAA8/lXXKOALHLkc/s320/logo_facebook.jpg&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5274459397093331330&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;Facebook do not allow group owners with more than 5000 members to message all their members at once. Facebook have made this decision to stop people from acquiring large Facebook groups in order to spam the members. A group with over a million members would be incredibly valuable as there is a very direct and clutter free communication channel with the members.&lt;br /&gt;&lt;br /&gt;However, I think that Facebook have missed a trick here. They have been struggling to find a revenue model for a while now and allowing this kind of marketing might give them a stready income stream by charging companies who want access to their group members. For example, say a million people sign up to the &#39;I Love Sprite&#39; group. Sprite would have no way of sending a message to these users, despite them all having expressly shown their interest in Sprite.&lt;br /&gt;&lt;br /&gt;Facebook could charge Sprite to message their users. This fee could also help to pay for decent quality control and to enforce any rules they might have. Facebook is already proficient at rejecting low quality ads (though they do have a inventory problem), this shouldn&#39;t be difficult for them.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/4779838294654659954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=4779838294654659954' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4779838294654659954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4779838294654659954'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/11/facebook-group-email-limits.html' title='Facebook Group Email Limits'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_VcWdtebG1dM/STKk4Fn4EYI/AAAAAAAAAA8/lXXKOALHLkc/s72-c/logo_facebook.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-1061958676006051248</id><published>2008-11-29T20:31:00.000+00:00</published><updated>2008-11-29T20:34:09.359+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Facebook"/><category scheme="http://www.blogger.com/atom/ns#" term="slipup"/><title type='text'>Facebook Loses Data</title><content type='html'>A schoolboy mistake from the king of social networks today. They sent me this email:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(51, 51, 255);&quot;&gt;Please reset your email notification settings&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(51, 51, 255);&quot;&gt;Unfortunately, the settings that control which email notifications get sent to you were lost. We&#39;re sorry for the inconvenience.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(51, 51, 255);&quot;&gt;To reset your email notification settings, go to:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(51, 51, 255);&quot;&gt;http://www.facebook.com/editaccount.php?notifications&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(51, 51, 255);&quot;&gt;Thanks,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(51, 51, 255);&quot;&gt;The Facebook Team&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(0, 0, 0);&quot;&gt;Made me chuckle&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/1061958676006051248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=1061958676006051248' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/1061958676006051248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/1061958676006051248'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/11/facebook-loses-data.html' title='Facebook Loses Data'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-7365781048840563858</id><published>2008-11-23T11:40:00.000+00:00</published><updated>2008-11-28T18:40:59.301+00:00</updated><title type='text'>Spotify Rocks</title><content type='html'>&lt;a href=&quot;http://www.geoffon.com/wp-content/uploads/2008/11/logo.png&quot;&gt;&lt;img class=&quot;alignleft size-medium wp-image-51&quot; style=&quot;float: left; margin-right: 20px;&quot; title=&quot;Spotify&quot; src=&quot;http://www.geoffon.com/wp-content/uploads/2008/11/logo.png&quot; alt=&quot;&quot; width=&quot;108&quot; height=&quot;116&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://www.spotify.com/&quot;&gt;Spotify&lt;/a&gt; is a new music service that lets you listen to unlimited amounts of music for free. They have an impressively complete collection and you have complete control over what you listen to. The only caveat: it&#39;s ad supported. However, the ads are sparse (about one 30 second add for every album you listen to).&lt;br /&gt;&lt;br /&gt;I was initially skeptical about having ads. I hate radio stations that will have you listening to adverts all day long or TV stations that play a few minutes of ads every 20 minutes. But adverts in Spotify are rare enough that it doesn&#39;t bother me. If you don&#39;t like the adverts, you can pay a subscription at £9.99 a month or a day pass for £0.99 (for parties perhaps?). I think the £9.99 a month is a little steep, but luckily I&#39;m happy with the ad-supported version.&lt;br /&gt;&lt;br /&gt;They have most of the music I like to listen to. However, they often don&#39;t have the obscure first albums (produced by tiny labels), which is a real shame. I expect this will change if Spotify gets big. There are a couple of big names who are not available. The only one I have really missed is Metallica&#39;s &lt;a href=&quot;http://en.wikipedia.org/wiki/Metallica%27s_ninth_studio_album&quot;&gt;new album&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Each artist has links to similar artists. This is nice for finding new music, but doesn&#39;t appear to be particularly clever. That said, exploring is effortless because songs are played instantly and the browsing interface is very well designed. Songs and playlists can be sent to friends using special URLs, but these have to be emailed or IMed, it would be much better to have a more tightly integrated social network. I&#39;m sure this will follow later on.&lt;br /&gt;&lt;br /&gt;At the moment, Spotify is invitation only. I have a few spare, leave me your email and I&#39;ll send you one if I have one left.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/7365781048840563858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=7365781048840563858' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/7365781048840563858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/7365781048840563858'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/11/spotify-rocks.html' title='Spotify Rocks'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-5238362037854280731</id><published>2008-10-10T22:00:00.000+01:00</published><updated>2008-11-28T18:42:06.271+00:00</updated><title type='text'>Doing EC2 Without Scalr</title><content type='html'>&lt;a href=&quot;http://www.geoffon.com/wp-content/uploads/2008/10/image-server-farm15752254_std148155151_std.jpeg&quot;&gt;&lt;img class=&quot;alignleft size-full wp-image-37&quot; style=&quot;padding-right: 10px; float: left;&quot; title=&quot;Server Farm&quot; src=&quot;http://www.geoffon.com/wp-content/uploads/2008/10/image-server-farm15752254_std148155151_std.jpeg&quot; alt=&quot;&quot; width=&quot;234&quot; height=&quot;176&quot; /&gt;&lt;/a&gt;It is important that your website can scale. You can spend all your energy promoting it and adding features, but that is all wasted if it cannot deal with all those users who are desperate to use your site. The architecture is at least half the battle. We are spoilt these days with affordable options to be able to deal with scale. Cloud computing has become a buzz word and big tech companies are scrambling to carve out their chunk of the market. So far we have &lt;a href=&quot;http://aws.amazon.com/&quot;&gt;EC2&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/appengine/&quot;&gt;Google App Engine&lt;/a&gt;, &lt;a href=&quot;http://joyent.com/&quot;&gt;Joyent&lt;/a&gt;, VPS services (such as &lt;a href=&quot;http://www.slicehost.com/&quot;&gt;Slicehost&lt;/a&gt;) and a number of others.&lt;br /&gt;&lt;br /&gt;EC2 was one of the first and most mature of the offerings. Though it does not stray far from the traditional concept of a machine (with fixed CPU and memory), what it does do is give you access to as many of these as you need at any one point and the (very) basic tools to manage them. They provide the basic infrastructure: machines, network connections and data storage. It is up to you to do the rest: load balancing, application servers, relational databases, backups, replication, redundancy, fail-over, etc.&lt;br /&gt;&lt;br /&gt;Scalr promises to help with some of these issues, notably load balancing, and backups / replication / fail-over for MySQL. The idea is great but the project is not mature (despite the claim to v1.0) and the architecture is not very solid. To be fair, this is acknowledged to some degree in that v2 is to be a complete redesign. After a period of two months using Scalr, I decided to move on because of these issues: &lt;ul&gt; &lt;li&gt;Ad-hoc design: a strange combination of PHP, bash, MySQL and CRON. The interaction between these components was overly complicated, which means debugging was tricky and the system itself was prone to getting mixed up (for example: CRON calling PHP to do a backup but the MySQL state was stuck so backups were being skipped, silently, for weeks). V2 is to be Java.&lt;/li&gt; &lt;li&gt;Buggy: rebundling an instance would break the load balancer. It would get confused about the new instances and refuse to update. This was only solved by restarting the whole farm.&lt;/li&gt; &lt;li&gt;Scalr makes the decision to start new instances based on the load average. This is over simplistic really and can mean that your machines are under heavy load for a little too long which new instances are being brought up.&lt;/li&gt; &lt;li&gt;Scalr is built on Ubuntu 7.04 AMIs, upgrading the distro breaks it.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;I do like what Scalr are trying to do. Just having pre-built load balancing servers, application servers and MySQL servers that are able to replicate and reconfigure themselves based on load is great. However, Scalr v1 is experimental really, I&#39;m very keen to see how the next version evolves.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One alternative is to build the architecture yourself. It&#39;s actually not very hard (and quite quick). All you need is a decent scripting language that has good SSH and EC2 libraries and some experience with MySQL and linux admin. A few days work (4 in my case) will get you:&lt;/div&gt;&lt;div&gt;&lt;ul&gt; &lt;li&gt;Run and stop instances with a single command, including application servers, master and slave MySQL instances.&lt;/li&gt; &lt;li&gt;MySQL slave replication (automatically configured against the current master)&lt;/li&gt; &lt;li&gt;NGINX load balancer, always up to date with the application servers.&lt;/li&gt; &lt;li&gt;Automatic backup and restore facilities.&lt;/li&gt; &lt;li&gt;Various other tricks that your setup might benefit from.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;All that would be needed to catch up with Scalr&#39;s features (albeit not generically) is to automatically bring up the required instances as demand changes and automatically fail over MySQL instances. Not much more work and with the added benefit that you can tailor your load algorithm to your site (i.e. use response times instead of load averages). I would recommend getting your hands dirty!&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/5238362037854280731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=5238362037854280731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5238362037854280731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/5238362037854280731'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/10/doing-ec2-without-scalr.html' title='Doing EC2 Without Scalr'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-8614515207303706207</id><published>2008-09-05T07:53:00.000+01:00</published><updated>2008-11-28T18:44:50.814+00:00</updated><title type='text'>Spotlight Goes Dotty</title><content type='html'>&lt;a href=&quot;http://www.geoffon.com/wp-content/uploads/2008/09/spotlight.jpg&quot;&gt;&lt;img class=&quot;alignleft size-medium wp-image-29&quot; style=&quot;float: left; margin-right: 10px;&quot; title=&quot;Spotlight&quot; src=&quot;http://www.geoffon.com/wp-content/uploads/2008/09/spotlight.jpg&quot; alt=&quot;&quot; width=&quot;188&quot; height=&quot;188&quot; /&gt;&lt;/a&gt;The last 3 times I have had a new install of OS X, I&#39;ve noticed that it gets sluggish after a few weeks. Loading up the monitor app shows a process called mdworker being heavy on both CPU and memory.&lt;br /&gt;&lt;br /&gt;My first reaction was that it was some sort of virus or at best a rogue app, but googling mdworker revealed that it was part of OS X. More precisely it is the Spotlight indexing daemon (keeps track of all your files to make searching faster).&lt;br /&gt;&lt;br /&gt;My first reaction was to look for a way to turn off spotlight. I don&#39;t really use it very much and &lt;a title=&quot;Quicksilver&quot; href=&quot;http://www.blacktree.com/&quot;&gt;Quicksilver&lt;/a&gt; covers the needs I do have. However, Spotlight is also needed to search your mail, which I can&#39;t do without.&lt;br /&gt;&lt;br /&gt;The problem appears when Spotlight is trying to keep up with a large number of new files on the system. In my case copying some music onto a new machine. The most straightforward solution is to tell Spotlight to ignore those files. This is easily done in &lt;strong&gt;System Preferences&lt;/strong&gt; -&gt; &lt;strong&gt;Spotlight&lt;/strong&gt; -&gt; &lt;strong&gt;Privacy&lt;/strong&gt; -&gt; &lt;strong&gt;Add ( + )&lt;/strong&gt;. I added my Music directory which is where iTunes stores it&#39;s library.&lt;a href=&quot;http://www.geoffon.com/wp-content/uploads/2008/09/picture-10.png&quot;&gt;&lt;img class=&quot;aligncenter size-full wp-image-31&quot; title=&quot;Spotlight Privacy&quot; src=&quot;http://www.geoffon.com/wp-content/uploads/2008/09/picture-10.png&quot; alt=&quot;&quot; width=&quot;350&quot;/&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It is odd that this should be a problem. Indexing is something that should happen in the background. There is no reason for it to affect the user. It appears that Apple decided that it was more important for files to be searchable than for the operating system to be responsive.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/8614515207303706207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=8614515207303706207' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/8614515207303706207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/8614515207303706207'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/09/spotlight-goes-dotty.html' title='Spotlight Goes Dotty'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-594129889459063580</id><published>2008-07-19T18:04:00.000+01:00</published><updated>2008-11-28T18:45:36.151+00:00</updated><title type='text'>A Good Reason To Use IFrames For Facebook Apps</title><content type='html'>&lt;img style=&quot;float: left; margin-right: 10px;&quot; src=&quot;http://www.geoffon.com/wp-content/uploads/2008/03/logo_facebook_small.jpg&quot; alt=&quot;&quot; /&gt;If you use canvas pages to display your Facebook apps, dealing with scale suddenly becomes quite a bit harder. A request must be served in less than 4 seconds otherwise it fails completely. This is fine when your traffic is constant, but can catch you off your guard if you get a sudden spike.&lt;br /&gt;&lt;br /&gt;I&#39;m currently building a Facebook app using Scalr to deal with scaling. This automatically creates new Amazon EC2 instances to deal with increases in load and kills them again when traffic dies down. The problem arises in deciding when to start a new instance.&lt;br /&gt;&lt;br /&gt;Scalr keeps track of the server load average.  This is a moving average over 15 minutes. It&#39;s a good idea to use a slow moving average like this so as to not start instances prematurely. It will only do so if the existing instances do actually need some help (especially considering Amazon charge by the hour). However, this introduces lag in starting a new server, say about 5 minutes.&lt;br /&gt;&lt;br /&gt;If I weren&#39;t serving pages to users via Facebook this wouldn&#39;t be such a problem. There would be small window when the site would be a little sluggish. For a canvas page this means pages are considered to have timed out, not good!&lt;br /&gt;&lt;br /&gt;Of course, using IFrame&#39;s is generally faster because it doesn&#39;t have the proxy overhead (and sometimes sluggish Facebook servers). There are some drawbacks in losing some cool FBML functionality (e.g. fb:can-see) and you will have to make a lot more calls to the API, but I now think IFrames are the way to go.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/594129889459063580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=594129889459063580' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/594129889459063580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/594129889459063580'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/07/good-reason-to-use-iframes-for-facebook.html' title='A Good Reason To Use IFrames For Facebook Apps'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-8908591254867695101</id><published>2008-05-28T20:02:00.000+01:00</published><updated>2008-11-28T18:47:20.272+00:00</updated><title type='text'>Is SQLAlchemy Ready For Production?</title><content type='html'>&lt;img src=&quot;http://www.geoffon.com/wp-content/uploads/2008/05/sqla-logo6.gif&quot; alt=&quot;SQLAlchemy&quot; title=&quot;sqla-logo&quot; class=&quot;alignleft size-medium wp-image-24&quot; style=&quot;float: left; margin-right: 10px;&quot; width=&quot;188&quot; height=&quot;52&quot; /&gt;I have built my most recent project using &lt;a href=&quot;http://www.sqlalchemy.org/&quot;&gt;SQLAlchemy&lt;/a&gt; (SQLA) 0.4. It is a reasonably young library, so I thought others considering using SQLAlchemy might benefit from my experiences.&lt;br /&gt;&lt;br /&gt;The most recent version is 0.4, so it obviously does not claim to be &#39;finished&#39; (can software ever be?) However, there are plenty of benefits in using it already:&lt;ul&gt; &lt;li&gt;Ridding your beautiful Python code of any SQL. It&#39;s untidy and pain to work with (which means bugs).&lt;/li&gt;        &lt;li&gt;ORM (Object Relational Model) to fit in with a nice OO architecture.&lt;/li&gt; &lt;li&gt;Free sharding to split your database horizontally.&lt;/li&gt;        &lt;li&gt;DB independant code, so you can switch if you feel the need.&lt;/li&gt;        &lt;li&gt;Hassle free transactions and DB connection management.&lt;/li&gt;&lt;/ul&gt;Sounds great doesn&#39;t it? In general this is exactly what you get. However, there are a few points which are very important to consider before committing yourself to using SQLA.&lt;b&gt;&lt;i&gt;&lt;br /&gt;&lt;br /&gt;Forcing Indices&lt;br /&gt;&lt;br /&gt;&lt;/i&gt;&lt;/b&gt;Anyone who has had to do something reasonably involved in MySQL has spent time optimising their indices. Sadly, MySQL is not very clever about how it chooses the index to use for a particular query. In some cases the only option is to force it with &lt;code&gt;FORCE&lt;/code&gt;. This is &lt;i&gt;not&lt;/i&gt; available in SQLA, so anytime you need to do this, you&#39;ll have to manually specify the SQL. Not only does this partly defeat the point of SQLA, but it also happens that the place you most need the SQL abstraction is often the same place you need to force an index. Consider a complicated search page. You are searching over a large data set using a number of filters. MySQL is probably going to get the index wrong and you&#39;re going to have to generate a complicated SQL query without SQLAlchemy&#39;s help.&lt;b&gt;&lt;i&gt;&lt;br /&gt;&lt;br /&gt;Commit ORM Objects&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;SQLAlchemy can be a little silly about detecting a change in an ORM object. If you assign to a member variable which is part of the schema, it will be marked as dirty even if the value doesn&#39;t actually change. The solution is to check for a change before assigning, which does not make for neat code!&lt;b&gt;&lt;i&gt;&lt;br /&gt;&lt;br /&gt;Sharding&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;It&#39;s a good idea to plan for scaling as early as possible. It can be &lt;i&gt;very&lt;/i&gt; difficult to build the necessary bits in later when you need it (and will also under pressure to fix things fast). One common way to deal with scale is to split large tables over several machines, this is known as sharding (each independant chunk of table is known as a shard).&lt;br /&gt;&lt;br /&gt;SQLAlchemy has some code to help you there. All you need to do is to define 3 functions which tell SQLA which shard a particular row is in. Really simple. Sadly, this part of the code is not very mature at all:&lt;ul&gt;&lt;li&gt;query.count() doesn&#39;t work (nor any scalar query). I had to write a function to query each shard in turn and sum the result. The real problem here is that it was not clear at all which bits of functionality will or won&#39;t work with sharding (expect long debugging sessions, digging into the SQLA code).&lt;/li&gt;&lt;li&gt;The ORM caches objects and identifies them by their primary key. However, a common MySQL trick when sharding is to have an auto_incremented INT as the primary key of each shard but use something like a UUID as the &#39;real&#39; primary key recognised by the code (this speeds things up quite a bit). Of course, the auto_incremented key will not be unique across shards and this will confuse SQLAlchemy. I think the best solution here (suggested by someone on the SQLAlchemy group) is to have a 2 column primary key with an INT and another integer shard identifier, making it unique.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;&lt;i&gt;No Server Side Cursors&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A minor point, but might be important for some. Server side cursors come in handy sometimes when you are dealing with large amounts of data.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Tips For Optimising&lt;/i&gt;&lt;/b&gt;&lt;ul&gt;&lt;li&gt;Periodically check the SQL queries being made with the &lt;code&gt;echo&lt;/code&gt; option. There might be some surprises in there (though usually easily fixed). This kind of thing usually pops up because you will use the ORM instance and forget / not realise that it will result in a query (from a software design point of view, great. From an optimisation point of view, awful).&lt;/li&gt;&lt;li&gt;Use &lt;code&gt;set_shard&lt;/code&gt; on a query whenever you are able. If you know which shard the column you want is, no need to go checking the other ones. A common example is when the shard identitifier is in one of the query parameters.&lt;/li&gt;&lt;li&gt;Design for scale right from the beginning and develop / test on a distributed architecture (i.e. have at least 2 shards). This doesn&#39;t need to be difficult, for example: just create two databases on your developement box to simulate two machines.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;&lt;i&gt;Conclusion&lt;br /&gt;&lt;br /&gt;&lt;/i&gt;&lt;/b&gt;I still think that SQLAlchemy is worth using. Overall it will save time and effort as long as you are careful and not afraid to get your hands dirty when the going gets tough. I expect most of these problems will be addressed in (near) future releases.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/8908591254867695101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=8908591254867695101' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/8908591254867695101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/8908591254867695101'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/05/is-sqlalchemy-ready-for-production.html' title='Is SQLAlchemy Ready For Production?'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-6881320573675646918</id><published>2008-05-27T23:12:00.000+01:00</published><updated>2008-11-28T17:50:44.917+00:00</updated><title type='text'>C# BHO Tutorial</title><content type='html'>&lt;img src=&#39;http://www.geoffon.com/wp-content/uploads/2008/05/ie7.jpg&#39; alt=&#39;ie7&#39; width=&quot;100px&quot; style=&quot;float:left; margin-right:10px;&quot;/&gt;I get a fair bit of traffic from people looking for help with C# and BHOs (my &lt;a href=&quot;http://www.geoffon.com/2007/12/21/c-browser-helper-object-event-handling/&quot;&gt;event handling&lt;/a&gt; post). There isn&#39;t much information about and the only beginners tutorial went missing a few months back. A &lt;a href=&quot;http://www.codeproject.com/kb/cs/attach_bho_with_c_.aspx&quot;&gt;new one&lt;/a&gt; has appeared, anyone wanting to get going should check it out.</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/6881320573675646918/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=6881320573675646918' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/6881320573675646918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/6881320573675646918'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/05/c-bho-tutorial.html' title='C# BHO Tutorial'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-3144263526895101111</id><published>2008-05-14T21:31:00.000+01:00</published><updated>2012-08-04T17:08:27.074+01:00</updated><title type='text'>Vim: The Word Processor</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://3.bp.blogspot.com/-uoBh9TSpk1M/UB1I5vNZNAI/AAAAAAAAAFc/OjFrYbkTDsE/s1600/vim.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;http://3.bp.blogspot.com/-uoBh9TSpk1M/UB1I5vNZNAI/AAAAAAAAAFc/OjFrYbkTDsE/s200/vim.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;I love &lt;a href=&quot;http://www.vim.org/&quot;&gt;Vim&lt;/a&gt;. It took me a while to get there, but I&#39;ve been using it exclusively for coding for a couple of years now and it has become second nature. I first learnt to use it during a practical exercise for an operating systems course at uni. We had to write a &lt;a href=&quot;http://www.minix3.org/&quot;&gt;Minix&lt;/a&gt; driver so all the work had to be done on the command line. A powerful text editor was a must. I think I used Vi (rather than Emacs) because it was available and I had been told it was great. It was a steep learning curve, but I got a hang of the basics after a few days.&lt;br /&gt;&lt;br /&gt;It was a couple of years before I started to use it again. In the meantime I&#39;d mostly been working with .NET and Java so I&#39;d been using some pretty decent development environments and it didn&#39;t seem necessary to use anything else (especially considering the power of their debuggers). But I moved into the world of the web and started writing PHP and then Python, these did not really have especially good IDEs and so it was back to a text editor and my choice was Vim. It also coincided with worsening RSI, for which Vim is great.I tend to learn a couple of features in a burst every few months when something really bugs me. This is probably not ideal, but Vim is &lt;i&gt;so&lt;/i&gt; powerful I will never learn it all. I&#39;ve been keeping a to do list in a text file recently (rather than on paper - go planet!) but Vim&#39;s defaults are not great for editing prose:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Vim&#39;s word wrap is by character, not word.&lt;/li&gt;&lt;li&gt;k and j (up and down) work on a line basis. If you have a wrapped line, you cannot move inside it with  j and k (like you would with a normal text editor).&lt;/li&gt;&lt;/ul&gt;Of course, Vim is hugely powerful and can be tweaked to be much more useable when writing prose. These &lt;code&gt;.vimrc&lt;/code&gt; commands:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Wrap lines by cutting lines off at word boundaries.&lt;/li&gt;&lt;li&gt;The word wrap is virtual, no extra line break is inserted (so that it&#39;s easy to edit afterwards).&lt;/li&gt;&lt;li&gt;j and k are replaced with gj and gk which allow you to move up and down &lt;i&gt;inside&lt;/i&gt; a wrapped line.&lt;/li&gt;&lt;li&gt;I&#39;ve also added the spell checker (I&#39;ve not played around with it properly yet, but it looks a little weak).&lt;/li&gt;&lt;li&gt;smartindent for bullet points.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;pre class=&quot;prettyprint&quot;&gt;&lt;br /&gt; autocmd BufRead *\.txt setlocal formatoptions=l&lt;br /&gt; autocmd BufRead *\.txt setlocal lbr&lt;br /&gt; autocmd BufRead *\.txt map &lt;buffer&gt; j gj&lt;br /&gt; autocmd BufRead *\.txt  map &lt;buffer&gt; k gk&lt;br /&gt; autocmd BufRead *\.txt setlocal smartindent&lt;br /&gt; autocmd BufRead *\.txt setlocal spell spelllang=en_us&lt;/buffer&gt;&lt;/buffer&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;On a slightly different note: if you&#39;re using OS X, get &lt;a href=&quot;http://code.google.com/p/macvim/&quot;&gt;this&lt;/a&gt; port of Vim. It&#39;s aim (and it does) is to integrate better into Macs. It&#39;s worth getting just because it has pretty Carbon tabs ;-)</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/3144263526895101111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=3144263526895101111' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/3144263526895101111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/3144263526895101111'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/05/vim-word-processor.html' title='Vim: The Word Processor'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-uoBh9TSpk1M/UB1I5vNZNAI/AAAAAAAAAFc/OjFrYbkTDsE/s72-c/vim.jpg" height="72" width="72"/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1397432135341343773.post-4947472679528557546</id><published>2008-04-23T21:53:00.000+01:00</published><updated>2008-11-28T18:53:21.419+00:00</updated><title type='text'>Facebook Chat Firefox Plugin</title><content type='html'>&lt;img style=&quot;float: left;&quot; src=&quot;http://www.geoffon.com/wp-content/uploads/2008/03/logo_facebook_small.jpg&quot; alt=&quot;Facebook&quot; /&gt;&lt;a href=&quot;http://www.facebook.com/&quot;&gt;Facebook&lt;/a&gt; finally &lt;a href=&quot;http://www.techcrunch.com/2008/04/22/facebook-finishes-chat-integration-makes-this-bloggers-life-miserable/&quot;&gt;released&lt;/a&gt; their chat functionality to all their users today. It&#39;s fantastic! I quickly came to the conclusion that it did have one major flaw: you have to be on the Facebook page to see any incoming messages. So to solve this, I quickly knocked up a Firefox add-on to alert you if you have any new chat messages. You can download it &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/7099&quot;&gt;here&lt;/a&gt;.&lt;img src=&quot;http://www.geoffon.com/wp-content/uploads/2008/04/picture-6.png&quot; alt=&quot;Firefox Facebook Chat Add-on Screenshot&quot; width=&quot;350px&quot; /&gt;&lt;br /&gt;&lt;br /&gt;It uses the built-in Firefox alert system, so you&#39;ll need Firefox 3 on OS X (for Growl) and 2 for other OSs. At the moment you also have to leave the Facebook page open, if I have time later, I&#39;ll deal with this so that you can close the Facebook page.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;PLUG&lt;/strong&gt;: &lt;a title=&quot;Donate 2 Date&quot; href=&quot;http://www.donate2date.net/&quot;&gt;Donate 2 Date&lt;/a&gt; is my Facebook app. It helps raise money for charity by setting people up on dates. Check it &lt;a href=&quot;http://www.donate2date.net/&quot;&gt;out&lt;/a&gt;!</content><link rel='replies' type='application/atom+xml' href='http://tech.geoff.me/feeds/4947472679528557546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1397432135341343773&amp;postID=4947472679528557546' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4947472679528557546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1397432135341343773/posts/default/4947472679528557546'/><link rel='alternate' type='text/html' href='http://tech.geoff.me/2008/04/facebook-chat-firefox-plugin.html' title='Facebook Chat Firefox Plugin'/><author><name>Geoff</name><uri>http://www.blogger.com/profile/17803545397617392400</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://1.bp.blogspot.com/_VcWdtebG1dM/SSk7qr7jKCI/AAAAAAAAAAg/A8jHD_Uys6I/S220/me.jpg'/></author><thr:total>0</thr:total></entry></feed>