<?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: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-4289590770839747775</id><updated>2012-02-16T21:46:12.431Z</updated><category term='linux'/><category term='blaze-ipp'/><category term='macos'/><category term='ironpython'/><category term='sysadmin'/><category term='personal'/><category term='buxfer'/><category term='movies'/><category term='buxfersubmit'/><category term='development'/><category term='methodology'/><category term='slightlyfictional'/><category term='zerofriction'/><category term='envy'/><category term='visual studio'/><category term='c#'/><category term='cullwindows'/><category term='scrum'/><category term='couchd'/><category term='netponto'/><category term='powershell'/><category term='python'/><category term='macbook'/><category term='script'/><category term='mingle'/><category term='windows'/><category term='.net'/><category term='tdd'/><category term='irrelevantinfo'/><category term='tryingtowinascholarship'/><category term='dotnet'/><category term='google'/><title type='text'>Bruno Lopes' Lack of imagination</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>43</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-2985370381549745581</id><published>2011-04-15T16:24:00.000+01:00</published><updated>2011-04-15T16:24:39.608+01:00</updated><title type='text'>Error 2 when trying to start windows event log service</title><content type='html'>Today my Windows 7 machine couldn't start the windows event log service, and so I couldn't open the event log viewed.&lt;br /&gt;
&lt;br /&gt;
After a bit of troubleshooting I found out that the issue seemed with a particular registry key.&lt;br /&gt;
&lt;br /&gt;
Removing the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\parameters registry key allowed event log to start without problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-2985370381549745581?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/2985370381549745581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=2985370381549745581' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2985370381549745581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2985370381549745581'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2011/04/error-2-when-trying-to-start-windows.html' title='Error 2 when trying to start windows event log service'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-8978219358104923604</id><published>2010-10-04T21:35:00.000+01:00</published><updated>2010-10-04T21:35:00.154+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='zerofriction'/><category scheme='http://www.blogger.com/atom/ns#' term='powershell'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='blaze-ipp'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='dotnet'/><title type='text'>Zero-friction deployment: from local to github in one powershell command</title><content type='html'>&lt;p&gt;Short version: &lt;/p&gt;  &lt;p&gt;Code is &lt;a href="http://github.com/brunomlopes/Blaze-IronPythonPlugins/blob/master/upload_github.psm1" target="_blank"&gt;here&lt;/a&gt;, depends on a dll &lt;a href="http://github.com/brunomlopes/Blaze-IronPythonPlugins/blob/master/lib/Krystalware.UploadHelper.dll" target="_blank"&gt;here&lt;/a&gt; and &lt;a href="http://github.com/brunomlopes/Blaze-IronPythonPlugins/blob/master/lib/html.agility.pack/HtmlAgilityPack.dll" target="_blank"&gt;here&lt;/a&gt;. Allows for a powershell script to upload a file to github.&lt;/p&gt;  &lt;p&gt;Long version:&lt;/p&gt;  &lt;p&gt;There are one core belief powering this post: &lt;/p&gt;  &lt;p&gt;- There should be zero friction in getting a software release out in the open. In the age of build scripts and APIs and everything over http there is no reason a human should be involved in cutting a version except in the decision to make it so.&lt;/p&gt;  &lt;p&gt;The main side effect of having very little to no friction in cutting a release is that it happens way more often, since there is no reason not to do it if the code is good to go.&lt;/p&gt;  &lt;p align="left"&gt;In this particular case, all I lacked was a way to upload a file to github. The packaging was already done, courtesy of psake+write-zip. Having found nothing in the powershell world to do it, I ended up “porting” part of &lt;a href="http://github.com/Constellation/ruby-net-github-upload/blob/master/lib/net/github-upload.rb" target="_blank"&gt;a ruby script&lt;/a&gt;.&lt;/p&gt;  &lt;p align="left"&gt;Github stores files in Amazon’s S3, so there were two steps to uploading a file:   &lt;br /&gt;-Telling github that the file exists and getting a token to pass to S3.&lt;/p&gt;  &lt;p align="left"&gt;-Uploading the file itself to S3 using the token gotten from step 1.&lt;/p&gt;  &lt;p align="left"&gt;The biggest issue ended up being that WebClient doesn’t handle POST’ing to an url with MIME values, which is what S3 expects in this scenario. Using the underlying *thingie* directly from powershell would bit a bit harder and more prone to errors, so I just used an existing &lt;a href="http://aspnetupload.com/" target="_blank"&gt;helper&lt;/a&gt; to upload the file correctly.&lt;/p&gt;  &lt;p align="left"&gt;The powershell code that uses it is available &lt;a href="http://github.com/brunomlopes/Blaze-IronPythonPlugins/blob/master/upload_github.psm1" target="_blank"&gt;here&lt;/a&gt;, with another extra dependency on the &lt;a href="http://htmlagilitypack.codeplex.com/" target="_blank"&gt;Html Agility Pack&lt;/a&gt; to scrape the downloads page for info on existing downloads.&lt;/p&gt;  &lt;p align="left"&gt;The function itself (not iet a full commandlet due to lack of time) is &lt;em&gt;upload_file_to_github:&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;function upload_file_to_github($login, $repo, $api_key, $file, $filename, $description){   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; [void][System.Reflection.Assembly]::LoadFrom((get-item &amp;quot;lib\Krystalware.UploadHelper.dll&amp;quot;))    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $full_repo = $login+&amp;quot;/&amp;quot;+$repo    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $downloads_path = &amp;quot;&lt;a href="http://github.com/&amp;quot;+"&gt;http://github.com/&amp;quot;+&lt;/a&gt;$full_repo+&amp;quot;/downloads&amp;quot;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post = new-object System.Collections.Specialized.NameValueCollection    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('login',$login)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('token',$api_key)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('file_size',$file.Length)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('content_type',&amp;quot;application/octet-stream&amp;quot;)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('file_name',$filename)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('description',$description)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $wc = new-object net.webclient    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $upload_info = [xml][System.Text.Encoding]::ASCII.GetString($wc.UploadValues($downloads_path, $post))    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post = new-object System.Collections.Specialized.NameValueCollection    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('FileName',$filename)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('policy',$upload_info.hash.policy)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('success_action_status',&amp;quot;201&amp;quot;)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('key',$upload_info.hash.prefix+$file.Name)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('AWSAccessKeyId',$upload_info.hash.accesskeyid)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('signature',$upload_info.hash.signature)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $post.Add('acl',$upload_info.hash.acl)    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $upload_file = new-object Krystalware.UploadHelper.UploadFile $file.FullName, &amp;quot;file&amp;quot;, &amp;quot;application/octet-stream&amp;quot;     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; [void][Krystalware.UploadHelper.HttpUploadHelper]::Upload(&amp;quot;&lt;a href="http://github.s3.amazonaws.com/&amp;quot;"&gt;http://github.s3.amazonaws.com/&amp;quot;&lt;/a&gt;, $upload_file, $post)    &lt;br /&gt;}&lt;/p&gt;  &lt;p align="left"&gt;As you can see, it’s just a normal POST to the web to notify github of a new file, interpreting the response as xml to extract needed information for the S3 POST. That one uses the helper to pass the parameters as MIME-encoded values.&lt;/p&gt;  &lt;p align="left"&gt;I have to say, using xml in powershell was a lot easier than I thought it would be. I wonder if JSON is equally well supported…&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-8978219358104923604?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/8978219358104923604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=8978219358104923604' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8978219358104923604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8978219358104923604'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/10/zero-friction-deployment-from-local-to.html' title='Zero-friction deployment: from local to github in one powershell command'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-7701536004790330788</id><published>2010-09-27T10:25:00.000+01:00</published><updated>2010-09-27T10:25:00.196+01:00</updated><title type='text'>Blaze-IPP – Design choices and implementation</title><content type='html'>&lt;p&gt;When I set out to create the support to code commands in IronPython, there were two main guidelines in my mind: simple commands and fast feedback.&lt;/p&gt;  &lt;h4&gt;Simple commands&lt;/h4&gt;  &lt;p&gt;Commands should only need to be a name and the definition of execute for the simplest of cases. No imports, just the code:&lt;/p&gt; &lt;script src="http://gist.github.com/587376.js"&gt; &lt;/script&gt;  &lt;p&gt;This is the simplest way to define a command as of version 1.2. As you can see, there’s nothing that isn’t required, and simple shortcuts or expanders could be defined by a simple method&lt;/p&gt;  &lt;p&gt;The side effect is that even short or small tasks can be easily automated since there’s no ceremony.&lt;/p&gt;  &lt;p&gt;There are two main features to accomplish this guideline:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Incrementing the scope with the extra classes and namespaces.&lt;/li&gt;    &lt;li&gt;Some “smarts” to allow for both methods and classes to be commands. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Adding definitions to a scope is simple, since all we need to do is associate a value to a symbol:&lt;/p&gt;  &lt;h4 id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;ScriptScope scope = _engine.CreateScope();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;scope.SetVariable(&lt;span style="color: #006080"&gt;&amp;quot;BaseIronPythonCommand&amp;quot;&lt;/span&gt;, ClrModule.GetPythonType(&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(BaseIronPythonCommand)));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;scope.SetVariable(&lt;span style="color: #006080"&gt;&amp;quot;UserContext&amp;quot;&lt;/span&gt;, UserContext.Instance);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/h4&gt;

&lt;p&gt;Allowing for both classes and methods is also trivial, since we can filter the values on the scope by type after executing code. &lt;/p&gt;

&lt;p&gt;Anything that’s a PythonType for which the CLR type can be assigned to an IIronPythonCommand is a command class.&lt;/p&gt;

&lt;p&gt;Anything that is callable and a PythonFunction that doesn’t start with “_” is a command method.&lt;/p&gt;

&lt;h4&gt;Fast feedback&lt;/h4&gt;

&lt;p&gt;I shouldn’t need to reload the application just to pick up a new script or a change to an existing one. &lt;a href="http://blaze-wins.sourceforge.net/" target="_blank"&gt;Blaze&lt;/a&gt; should pick up changes from the file system and reload the files as needed.&lt;/p&gt;

&lt;p&gt;Here the big issue was locked files when the “file changed” was triggered.&lt;/p&gt;

&lt;p&gt;This meant that changed files were placed in a queue, and a timer running in the background pulled files from the queue and reloaded them. If a file is locked, then put it again in the queue. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-7701536004790330788?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/7701536004790330788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=7701536004790330788' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7701536004790330788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7701536004790330788'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/09/blaze-ipp-design-choices-and.html' title='Blaze-IPP – Design choices and implementation'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-5958049895803906309</id><published>2010-09-20T10:30:00.000+01:00</published><updated>2010-09-20T10:30:00.969+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='powershell'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='c#'/><title type='text'>Forked dbdeploy.net</title><content type='html'>&lt;p&gt;Dbdeploy.net is a database change management library, used by us at weListen to track the changes made to a database in the scope of a project and apply them as needed. It takes a folder full of ordered files containing the change scripts in sql and applies them as needed to a given database. It tracks which changes have been applied in a special table.&lt;/p&gt;  &lt;p&gt;We’ve used for quite some time, and were mostly happy with it, except when using it from the command line on the servers. The msbuild task worked well to obtain and apply the changes, but the command line application added extra information to the output that needed to be trimmed before applying it to the server.&lt;/p&gt;  &lt;p align="left"&gt;Well, it annoyed me enough to fork it from its &lt;a href="http://sourceforge.net/projects/dbdeploy-net/" target="_blank"&gt;home&lt;/a&gt; at sourceforge to a &lt;a href="http://github.com/brunomlopes/dbdeploy.net" target="_blank"&gt;new place&lt;/a&gt; at github. &lt;/p&gt;  &lt;p align="left"&gt;I refactored to projects a bit, to exclude a direct dependency on NAnt, and added a Dbdeploy.Powershell assembly with 3 commands:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="left"&gt;&lt;strong&gt;Export-DbUpdate&lt;/strong&gt;, which outputs the scripts that need to be applied. Can write the scripts directly to a file;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="left"&gt;&lt;strong&gt;Push-DbUpdate&lt;/strong&gt;, which applies the changes to the database (still has a bug when using with SQL Server, as I need to split the resulting script into batches);&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="left"&gt;&lt;strong&gt;Select-DbUpdate&lt;/strong&gt; outputs information about which sets need to be applied;&lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p align="left"&gt;All of these commands take a path to a config file describing the database type, connection string and table name to store the changelog, and the directory where the actual changesets exist.&lt;/p&gt;  &lt;p align="left"&gt;Also, instead of mucking about with NAnt as the build script host, I’m using psake. I really can’t stand using xml to describe build steps anymore.&lt;/p&gt;  &lt;p align="left"&gt;To use it just download the release, and in powershell “import-module Dbdeploy.Powershell.dll”.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-5958049895803906309?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/5958049895803906309/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=5958049895803906309' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5958049895803906309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5958049895803906309'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/09/forked-dbdeploynet.html' title='Forked dbdeploy.net'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-7329311289350428651</id><published>2010-09-06T19:37:00.000+01:00</published><updated>2010-09-06T19:37:59.531+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='blaze-ipp'/><category scheme='http://www.blogger.com/atom/ns#' term='ironpython'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Anatomy of a command</title><content type='html'>Creating an IronPython command to launch the command prompt isn’t much, since you can have a shortcut to do just that.&lt;br /&gt;
You might have noticed that the command subclasses “BaseIronPythonCommand”. This is an abstract class that implements the basics and just leaves the Name and the Execute method for you to implement:&lt;br /&gt;
&lt;a href="http://lh6.ggpht.com/_iGn-y-G_07U/THrnLfCWTmI/AAAAAAAAAas/sPREzed5yW8/s1600-h/image3.png"&gt;&lt;img alt="image" border="0" height="276" src="http://lh4.ggpht.com/_iGn-y-G_07U/THrnMTlcP9I/AAAAAAAAAaw/rFkmAArbthk/image_thumb1.png?imgmax=800" style="background-image: none; border-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="383" /&gt;&lt;/a&gt;&lt;br /&gt;
There’s also an interface you can implement directly (IIronPythonCommand) if you want to assume control of everything.&lt;br /&gt;
BaseIronPythonCommand’s implementation is quite simple. Most of it is boilerplate for a &lt;a href="http://blaze-wins.sourceforge.net/" target="_blank"&gt;Blaze&lt;/a&gt; plugin, leaving only the name and execution to be done in the script:&lt;br /&gt;
&lt;script src="http://gist.github.com/552424.js"&gt;
 
&lt;/script&gt;  &lt;br /&gt;
&lt;b&gt;GetName&lt;/b&gt; is the name used to invoke the command. It should be something unique and descriptive. A long name is good, and you can teach Blaze a shortcut to it by typing the shortcut and selecting the command on the dropdown box. This teaches blaze about the shortcut. &lt;br /&gt;
&lt;b&gt;GetDescription &lt;/b&gt;returns a description for the command, to be shown on the line below it on the interface.&lt;br /&gt;
&lt;b&gt;AutoComplete&lt;/b&gt; returns a completion for the given string. This means you can transform “off” into “office” inside your command.&lt;br /&gt;
&lt;b&gt;Execute&lt;/b&gt; tells the command the user has selected it. The command returns either a program to execute in the form of a path concatenated with the arguments, null if the command has executed itself.&lt;br /&gt;
Any one of these can be implemented in ironpython, although for basic commands you only need to implement GetName (or a property called Name) and &lt;b&gt;Execute&lt;/b&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-7329311289350428651?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/7329311289350428651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=7329311289350428651' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7329311289350428651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7329311289350428651'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/09/anatomy-of-command.html' title='Anatomy of a command'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_iGn-y-G_07U/THrnMTlcP9I/AAAAAAAAAaw/rFkmAArbthk/s72-c/image_thumb1.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-1498074751245397255</id><published>2010-09-01T10:27:00.000+01:00</published><updated>2010-09-01T10:27:00.230+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='blaze-ipp'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Tutorial: How to create your own plugins for Blaze-IronPythonPlugins (BPP)</title><content type='html'>&lt;p&gt;On the first post in this series I presented &lt;a href="http://blaze-wins.sourceforge.net/" target="_blank"&gt;Blaze&lt;/a&gt; IPP, which allows for scripting of a launcher using IronPython. Here I tell you how you can write your own plugins from scratch. It will be a short post.&lt;/p&gt;  &lt;p&gt;First of all, I assume you’re already running Blaze with the plugin installed. &lt;/p&gt;  &lt;p&gt;Let’s create a new directory under “Plugins” to store our new plugins. This way if any future upgrade on IPP brings an ironpython file with the same name, your plugin will not be overwritten. We’ll call it LocalIronPythonPlugins:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_iGn-y-G_07U/THbqXUNzXfI/AAAAAAAAAac/JH8jw7VAOyY/s1600-h/Untitled%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Untitled" border="0" alt="Untitled" src="http://lh5.ggpht.com/_iGn-y-G_07U/THbqYSWx6SI/AAAAAAAAAag/wUlXvWscm-0/Untitled_thumb.png?imgmax=800" width="244" height="119" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now, configure IPP to also monitor that directory. Call Blaze, right click on it and in the “settings” open the “Plugins” tab. Select “IronPythonHostPlugin”, “Configure” and add the newly created directory (you can either browse to it with “b” or you can paste the path into the text box):&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_iGn-y-G_07U/THbqY_Jdl0I/AAAAAAAAAak/4sDzBJ0cg4U/s1600-h/Untitled2%5B4%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Untitled2" border="0" alt="Untitled2" src="http://lh4.ggpht.com/_iGn-y-G_07U/THbqZgu7v0I/AAAAAAAAAao/yp9zfj0v1fU/Untitled2_thumb%5B2%5D.png?imgmax=800" width="459" height="348" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now that we have a clean directory to work with, let’s create a file called “OpenCommandLine.ipy”. Open it and add the following code:&lt;/p&gt; &lt;script src="http://gist.github.com/552373.js"&gt; &lt;/script&gt;  &lt;p&gt;As you can probably guess, this will create a new command named OpenCommandLine, and when you select it it will open a new command prompt. IPP is clever enough to know that if a command returns a string, it is a program to be executed. &lt;/p&gt;  &lt;p&gt;This is just a simple example to get you started. Since this is IronPython, you can use the entire .Net framework and fetch things from the web, open the registry, connect to a remote host or start a service, to give a couple of examples.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-1498074751245397255?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/1498074751245397255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=1498074751245397255' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1498074751245397255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1498074751245397255'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/09/tutorial-how-to-create-your-own-plugins.html' title='Tutorial: How to create your own plugins for Blaze-IronPythonPlugins (BPP)'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_iGn-y-G_07U/THbqYSWx6SI/AAAAAAAAAag/wUlXvWscm-0/s72-c/Untitled_thumb.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-6593266185994522670</id><published>2010-08-30T10:25:00.001+01:00</published><updated>2010-08-30T10:25:00.106+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='dotnet'/><title type='text'>Creating plugins in IronPython for Blaze, an application launcher.</title><content type='html'>&lt;p&gt;Everyone loves the start menu from Windows Vista/7, but compared to Launchy, Quicksilver or any other application launchers it’s a bit anemic. No wonder, since it’s just a search over your start menu and indexed locations.&lt;/p&gt;  &lt;p&gt;But even any one of those is a bit lacking in support for runtime extensibility. You can create plugins, but it involves C (or Objective-C in the case of Quicksilver) and restarting the app. Not very friendly for those cases where you just want to pick up the currently selected object and throw it over an scp connection autocompleted from your favorites. &lt;/p&gt;  &lt;p&gt;Enter &lt;a href="http://blaze-wins.sourceforge.net/" target="_blank"&gt;Blaze&lt;/a&gt; + &lt;a href="http://github.com/brunomlopes/Blaze-IronPythonPlugins" target="_blank"&gt;IronPythonPlugins&lt;/a&gt;. Blaze is a .Net application launcher for Windows that includes a learning facility that tries to complete repetitive tasks for you. IronPythonPlugins is a plugin itself for Blaze that hosts simple plugins written in IronPython.&lt;/p&gt;  &lt;p&gt;If you want to try it just download the latest version from &lt;a href="http://github.com/brunomlopes/Blaze-IronPythonPlugins/downloads" target="_blank"&gt;here&lt;/a&gt;. It includes a version all ready to use. Start the app, call it with Ctrl+Alt+Space and try a couple of plugins:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div&gt;Open windows explorer, select a text file, call blaze and type “edit with notepad2”. If you have notepad2 in your path, it will be launched with the selected file opened.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div&gt;If you use putty (the ssh terminal), “putty &amp;lt;name of session&amp;gt;” will launch putty with that session. Here session is autocompleted, so if you have a session called “my.home.box”, typing “putty home&amp;lt;tab&amp;gt;” will autocomplete to “putty my.home.box”.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div&gt;Type “random-text &amp;lt;number&amp;gt;” and your clipboard will have up to &amp;lt;number&amp;gt; random letters. Useful to generate test data.&lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;There are a couple more plugins in the “Plugins\IronPythonPlugins” directory, so if you’re interested in how it’s done, open them in your editor of choice. In the next few days I’ll do a couple of posts on how you can create your own plugins without having to guess from the already existing ones.&lt;/p&gt;  &lt;p&gt;If you want to change anything edit the file, save it and Blaze will automatically pick it up, together with any other .ipy file in that directory that has changed. If you open Blaze’s configuration page for the plugin, you can add other directories to search for .ipy files. Useful to keep your personal plugins separated from the “official” ones.&lt;/p&gt;  &lt;p&gt;Sadly errors are still only sent to a System.Diagnostics.Debug, so if you edit a file and can no longer use it, open DbgView, edit the file again to force blaze to reload it and be on the lookout for output starting with “Error with file”. There will be a stacktrace telling you what you did wrong.&lt;/p&gt;  &lt;p&gt;So, try it, have fun and comment on it. I’d love to hear what other people think of it.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-6593266185994522670?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/6593266185994522670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=6593266185994522670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/6593266185994522670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/6593266185994522670'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/08/creating-plugins-in-ironpython-for.html' title='Creating plugins in IronPython for Blaze, an application launcher.'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-1871266319713358046</id><published>2010-04-09T17:25:00.001+01:00</published><updated>2010-04-09T17:25:33.256+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='c#'/><title type='text'>Toggling with an IEnumerator and yield.</title><content type='html'>&lt;p&gt;
Instead of using a state variable to jog around and check what is the state of a window, why not just use an infinite collection?
&lt;/p&gt;
&lt;pre&gt;
public partial class Admin : Window
    {
        private IEnumerator _toggler;

        public Admin()
        {
            InitializeComponent();
            _toggler = Toggler().GetEnumerator();
        }

        public void Toggle()
        {
            _toggler.MoveNext();
        }

        private IEnumerable Toggler()
        {
            while (true)
            {
                Show();
                yield return new object();
                Hide();
                yield return new object();
            }
        }
    }
&lt;/pre&gt;
&lt;p&gt;
I wonder if this is a good pattern, or just me being "clever".
&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-1871266319713358046?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/1871266319713358046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=1871266319713358046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1871266319713358046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1871266319713358046'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/04/toggling-with-ienumerator-and-yield.html' title='Toggling with an IEnumerator and yield.'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-4371466636333819807</id><published>2010-03-17T10:00:00.000Z</published><updated>2010-03-17T10:00:03.356Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='ironpython'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Using ironpython to generate nhibernate mapping files from a fluent
configuration</title><content type='html'>&lt;p&gt;I admit, this is a bit of a goldberg machine, but here's the context:&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;In my current project we need to store a bunch of events related to some 20 entities. Those events are the same for all entities, and they all need to be stored in the database. We use NHibernate to handle the "talk to the database" part, and fluent-nhibernate to configure the mappings via conventions for the most part. There are some entities which are either manually mapped or have specific overrides for particular cases (manually mapping some entities and automapping others is a mistake I think I'll avoid in the future).&lt;/p&gt;
&lt;p&gt;For the most part this setup does wonders. We can add and change our entities and the mappings are generated without we thinking much of it. Since development speed has a larger priority than runtime speed in this project, we don't have much problems with the default mappings that are generated.&lt;/p&gt;
&lt;p&gt;Except in the case of those events. Since the events are the same for all the entities, the event classes are open generics. Now, nhibernate can't map open generics, but can map closed generics, which is how we do it. We just iterate through all the types in the assembly that correspond to our entities and map those with fluent. The problem then becomes one of performance. As of now there are about 500 generated classes, and those take a while to discover and map. Since we're using fluent nhibernate to configure it in runtime, this means that each time the app is restarted there's a small downtime (on the scale of about 1 minute). While that's not much for an app in production, while developing one tends to restart the app a bit often. That's a problem in the day to day work.&lt;/p&gt;
&lt;p&gt;I used our integration test suite to time the mapping, and on my pc it took about a minute to run. On the other hand, if we already have an .hbm file to feed to nhibernate, we can shave about 30 to 40 seconds. That's less than half the time for a simple trade-off of having to know when to generate the mappings.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;To generate the mapping we could have created a new console project to just call the correct methods on our session provider factory, which would be a bit overkill. All we needed was to setup the environment and ask fluent for the mapping file.&lt;/p&gt;
&lt;p&gt;And here is something that IronPython shines in.&lt;/p&gt;
&lt;p&gt;Just create an .ipy file, add references to the correct assemblies, and since there's a clear separation of concerns (or so we hope), it's easy to configure fluent and ask it to generate the mappings for us. Most of the time you'll use .net code like you do in c# or vb.&lt;/p&gt;
&lt;p&gt;There are only a couple of issues here:&lt;/p&gt;
&lt;p&gt;- We need to remember to regenerate the hbm files when classes/mapping changes&lt;/p&gt;
&lt;p&gt;- We need to remember to recompile both before and after regenerating hbms. Since we're loading the assemblies with ironpython outside of visual studio, the script doesn't know if the assemblies are stale or not.&lt;/p&gt;
&lt;p&gt;Most of this can be avoided by having a build server rebuild the hbm files on its own and fail the build if they don't match with the latest in the source control. Of course, we could just have it commit the files, but that's one step I'm not fully comfortable with.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-4371466636333819807?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/4371466636333819807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=4371466636333819807' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/4371466636333819807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/4371466636333819807'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/03/using-ironpython-to-generate-nhibernate.html' title='Using ironpython to generate nhibernate mapping files from a fluent&#xA;configuration'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-8514201265102845253</id><published>2010-03-10T10:00:00.000Z</published><updated>2010-03-10T10:00:04.095Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='methodology'/><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>Notes and reflections about the second annual scrum meeting in Portugal</title><content type='html'>&lt;p&gt;A month ago I attended the second annual Scrum Meeting in Portugal, and ended up taking some notes about two of the presentations.&lt;/p&gt;
&lt;p&gt;Here they are, both for the team at &lt;a href="http://welisten.eu." title="weListen, Business Solutions"&gt;weListen&lt;/a&gt;, and for the world at large.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Notes on "A Practical Roadmap to Great Scrum:A Systematic Guide to Hyperproductivity" presented by Jeff Sutherland&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;This was the presentation that changed the way I perceive scrum. Where before I saw scrum as an agile software development methodology like xp, now I see it as a management and process template. This means a team should use scrum and complement it with technical practices. Apart from that, the largest focus to me was on 3 velocity multipliers for teams and 2 required practices for hyper-productive teams.&lt;/p&gt;
&lt;p&gt;The 3 velocity multipliers are &lt;b&gt;ready&lt;/b&gt;, &lt;b&gt;done&lt;/b&gt; and &lt;b&gt;self-organization&lt;/b&gt;. Ready and done shield an iteration from churn. A story only enters development when it is ready, and by the end of the sprint it should be done. Ready means that the story is understood and sized by the team with input from the client, and done means the same story is tested and approved to be deployed. Self-organization ties nicely into the two required practices mentioned: &lt;b&gt;talk about problems&lt;/b&gt; and &lt;b&gt;fix the same problems&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;Two classes of problems mentioned: bugs that take more than 8 hours to fix, and stories that take more than twice the calendar time than estimated ideal time. For each bug or story in this situation, do a root cause analysis to figure out the underlying problem. This can be multitasking (a dev assigned to several projects), final inspection or testing done too late or interruptions and form the basis of an impediment list for the team together with the product owner and scrum-master to solve.&lt;/p&gt;
&lt;p&gt;There were several interesting data points mentioned. In a given company, the peak of productivity was measured on 60 hour work weeks in waterfall projects, and on 30 hour work weeks with double story points delivered. This means an productivity increase of about 3 to 4 times. Part of the presentation used Systematic, a dutch software company as the source for several data points for scrum teams. They noticed a linear scale in developer productivity for project sizes, going against Brook's Law, although I think the Law mentioned adding people to an already late project, and didn't mention scale. They apparently implemented scrum using Mary Poppendieck's lean tools as described in her &lt;a href="http://www.amazon.com/exec/obidos/ASIN/0321150783/poppendieckco-20"&gt;book&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some "required truths" about hyper-productive teams were discussed, and I found them interesting insights. The ones that stuck to my mind the most were:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Everyone must be trained in scrum. This is so that all the players follow the same playbook and the concepts and practices are well understood.&lt;/li&gt;

  &lt;li&gt;Backlog must be &lt;b&gt;ready&lt;/b&gt; to implement before the sprint, and &lt;b&gt;done&lt;/b&gt; by the end (tying into the concepts of ready and done mentioned earlier) .&lt;/li&gt;

  &lt;li&gt;Pair immediately on a task if there's only one person capable of handling it, to avoid bottlenecks in the throughput.&lt;/li&gt;

  &lt;li&gt;Short sprints (often just one week)&lt;/li&gt;

  &lt;li&gt;Servant leadership, where the product owner and scrum-master are a resource of the team, instead of using the team as a resource.&lt;/li&gt;

  &lt;li&gt;No multitasking. This one I'm a bit curious as to how realistic it is, considering maintenance work, and teams with many past projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall it was a very interesting presentation. There are several videos on youtube with Jeff Sutherland about scrum. &lt;a href="http://www.youtube.com/watch_popup?v=Ht2xcIJrAXo#t=351"&gt;This one&lt;/a&gt; is very similar to the one I saw.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Notes on "Scrum and TFS 2010" by Mitch Lacey&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;This was mostly a storytelling and demo about new features in TFS. I did end up asking Mitch about the importance of accounting for time originally estimated, time in task and time remaining for small and recent to scrum teams, as to me it seemed a bit too much bureaucracy for a small team. The answer was enlightening, since those numbers are particularly important for those teams as a way to surface problems (either with estimation, delays, interruptions or other causes). This means I might start looking into accounting for such numbers. "You can't manage what you don't measure". When a team gels and is proficient, the numbers may not matter as the team starts getting into the groove and noticing impediments becomes intuitive.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-8514201265102845253?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/8514201265102845253/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=8514201265102845253' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8514201265102845253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8514201265102845253'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/03/notes-and-reflections-about-second.html' title='Notes and reflections about the second annual scrum meeting in Portugal'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-7940123002529755313</id><published>2010-01-24T01:19:00.000Z</published><updated>2010-01-24T01:19:51.019Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='script'/><title type='text'>OCD and filenames, as pertaining to TV series</title><content type='html'>I like all my file names to be similar, and this applies to both mp3 and series. For mp3 I use the wonderful &lt;a href="http://www.foobar2000.org/"&gt;foobar2k &lt;/a&gt;to tag and rename files, but for series I ended up coding my own formatter. All this assumes a linux system (as is my home server, flatline)&lt;br /&gt;
First of all, to extract the video files from the rars:&lt;br /&gt;
&lt;blockquote&gt;
find -name "*rar" -type f -exec rar e {} \;&lt;br /&gt;
&lt;/blockquote&gt;
This extracts all rars from under the current directory (and subdirectory) into the working directory.&lt;br /&gt;
If the files already come uncompressed, but are in subdirectories:&lt;br /&gt;
&lt;blockquote&gt;
find -name "*mkv" -type f -exec mv {} . \;&lt;br /&gt;
&lt;/blockquote&gt;
Replace mkv with avi if that's what you've got.&lt;br /&gt;
Now, sometimes the files come in different formats. Some have [season][episode], others [season]x[episode], and I'd like for them all to be in the same [title]S[season]E[episode] format.&lt;br /&gt;
For that all you need is the following python script:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="http://gist.github.com/284901.js"&gt;&lt;/script&gt;
&lt;br /&gt;
It can take 2 arguments: the directory where the file names are, and a title that overrides the one found in the file names.
Hope this is helpful for someone else :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-7940123002529755313?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/7940123002529755313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=7940123002529755313' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7940123002529755313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7940123002529755313'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/01/ocd-and-filenames-as-pertaining-to-tv.html' title='OCD and filenames, as pertaining to TV series'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-933598060287489631</id><published>2010-01-03T04:01:00.000Z</published><updated>2010-01-03T04:02:20.856Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='macbook'/><title type='text'>Just replaced the fan on my laptop</title><content type='html'>&lt;p&gt;For a couple of months my macbook's fan has been a bit on the fritz. Sometimes when the CPU got a bit hot it started making some noises and once in a while stopped working completely.&lt;/p&gt;
&lt;p&gt;The repair would most likely cost me about 100€ (50 for the fan, 50 for replacing it), so I decided to look up if anyone sold those. Thankfully a store in Hong-Kong does, and they ship international (they're called &lt;a href="http://www.eeshops.com/" target="_blank"&gt;eeshop&lt;/a&gt;). It took a while to get here (a month and a half, perhaps) but for 10€ that's not a big issue. I did ask after a while if it was supposed to take so long, and answer was prompt (apparently the postal office was swamped).&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/brunomlopes/4238171357/sizes/l/" target="_blank"&gt;&lt;img src="http://farm5.static.flickr.com/4025/4238171357_877940870b_b.jpg" width="480" height="304" style="float:left;" /&gt;&lt;/a&gt; Opening the laptop was another issue. First attempt failed when some of the screws were a bit too small for the screwdrivers I was using, so today I went and bought a new set with a better grip. After removing the screws, I just had to take care to remove the connection for the trackpad and slide the keyboard left to remove it. Opening it gave me access to the internals as you can see on the left. Replacing the fan was a matter of just removing the connector, two screws and taking it off. The right screw was hidden under some cables, but gently pulling them off gave me access to remove it.&lt;/p&gt;
&lt;p&gt;I do have to say, putting it all back again was a bit difficult, since on the right hand there are some "hooks" that you need to slide the keyboard plate into, and that wasn't trivial. Other than that it was easy.&lt;/p&gt;
&lt;p&gt;And now my laptop doesn't scream anymore when hot! And it cost me just about 15€ plus an hour and a half. Yay.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-933598060287489631?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/933598060287489631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=933598060287489631' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/933598060287489631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/933598060287489631'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2010/01/just-replaced-fan-on-my-laptop.html' title='Just replaced the fan on my laptop'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm5.static.flickr.com/4025/4238171357_877940870b_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-607767070918659950</id><published>2009-12-26T18:08:00.001Z</published><updated>2009-12-26T18:10:07.813Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='netponto'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>My presentation for netponto about Lucene.net</title><content type='html'>&lt;p&gt;A couple of weeks ago I did a presentation for a .net user group (&lt;a href="http://www.netponto.org/" target="_blank"&gt;netponto&lt;/a&gt;) here in Portugal about Lucene.net, an open source textual search engine. You can see the slide deck &lt;a href="http://www.slideshare.net/NetPonto/introducao-ao-lucenenet" target="_blank"&gt;here&lt;/a&gt;, and I've uploaded the source code used in the demo to &lt;a href="http://github.com/brunomlopes/netponto-lucene" target="_blank"&gt;my github page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;What went well&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Using git (with the git-extensions gui) to bookmark the various steps I wanted for the code demo. This is something that surprised me on how well it worked and actually ended up being remarked upon. It also generated a few questions about git that were answered mid-demo in a bit of a detour.&lt;/p&gt;
&lt;p&gt;The pattern here is to build the demo as a series of linear git branches, each a small step in the demo, and instead of writing the code in-loco at the demo or storing snippets to be used, I just move up along the branches. Each step is small and self contained, and I spend more time just highlighting the important parts and the decisions I made and less time typing.&lt;/p&gt;
&lt;p&gt;Also, using git gives me the advantage to rewrite history, so that if I figure out that the demo app needs to have a foo somewhere from the start, I can switch to the root branch, make the edit, and rebase the branches to update them with the change. With some care, this propagates the change upwards with little to no conflicts.&lt;/p&gt;
&lt;p&gt;Short deck of slides, demo with ongoing q&amp;amp;a. While the initial presentation didn't take long, the demo ended up going into overtime. If there's no hard and fast timebox, this is good, otherwise answering questions in the middle of the demo can push a well timed demo out of its allotted time. But the audience seemed to be enjoying it, and I think this helps spread knowledge a bit better than just giving a presentation and showing code.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;What could have been better&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;The "oh dear, a crash" gimmick had an interesting effect, but I think it was wasted on a slightly minor point. A better and more important part to emphasize would have been the impact of the analyzer on both indexed fields and search query. I should have had an unexpected result for a search be the "crash" and not the lack of values for fields.&lt;/p&gt;
&lt;p&gt;I used a transition I saw on slide:ology in 3 slides, which while cool, might have been more distracting than useful. On the other hand, I think it emphasized the fact that the index was shared between the indexer and the searcher, which part of the point I was trying to make.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;What I'll improve&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Relax. Although the feedback I gathered from some of the audience was that it didn't show, I felt like I had zipped through the initial presentation. Ended up taking a bit of a breather while answering questions.&lt;/p&gt;
&lt;p&gt;Might be interesting to consider dropping even more slides in detriment of more code. Although I think there's a minimum needed to intro the subject matter. Otherwise people will look at the code but the concepts will not gel.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Feedback&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;If you were there, I'd love to hear from you. I've received the report from Caio with the flattering comments, but I'd also love to hear your opinion on what could have been improved.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-607767070918659950?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/607767070918659950/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=607767070918659950' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/607767070918659950'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/607767070918659950'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/12/my-presentation-for-netponto-about.html' title='My presentation for netponto about Lucene.net'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-3276225014227314770</id><published>2009-08-30T17:24:00.001+01:00</published><updated>2009-08-30T17:24:52.793+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='macbook'/><category scheme='http://www.blogger.com/atom/ns#' term='envy'/><title type='text'>I wish I could put 4gb of ram on my macbook...</title><content type='html'>&lt;p&gt;According to an article on &lt;a href="http://news.cnet.com/8301-13579_3-10320314-37.html?part=rss&amp;amp;tag=feed&amp;amp;subj=News-Apple"&gt;cnet&lt;/a&gt; Snow Leopard boots by default into a 32-bit kernel and mentions that macbooks with an older, 32-bit EFI chipset (which handles the early boot process) cannot boot the 64-bit version.&lt;/p&gt;
&lt;p&gt;Although having a 64-bit kernel would be good, I'm more interested in the side effect of the 32-bit chipset memory limitation. The same article mentions that a 32-bit EFI chipset cannot address more than 3Gb of ram. Since most likely this is the chipset installed on my mac, I think that's the reason why 4 Gb are not supported. I wonder if the chipset could be replaced. Doubt it, since it must be soldered into the board.&lt;/p&gt;
&lt;p&gt;Sadly, that's pretty much the largest reason I'm envious of the new macs. 2 gb isn't much, specially if you're like me and have tons of apps loaded all the time.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-3276225014227314770?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/3276225014227314770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=3276225014227314770' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3276225014227314770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3276225014227314770'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/08/i-wish-i-could-put-4gb-of-ram-on-my.html' title='I wish I could put 4gb of ram on my macbook...'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-8541839363081937821</id><published>2009-08-19T20:07:00.002+01:00</published><updated>2009-08-20T14:55:19.723+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tryingtowinascholarship'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><title type='text'>Man, it would be awesome to go...</title><content type='html'>Roy Osherove is giving an hands-on TDD Masterclass in the UK, September 21-25. Roy is author of "The Art of Unit Testing" (&lt;a href="http://www.artofunittesting.com/"&gt;http://www.artofunittesting.com/&lt;/a&gt;), a leading tdd &amp;amp; unit testing book; he maintains a blog at &lt;a href="http://iserializable.com/"&gt;http://iserializable.com&lt;/a&gt; (which amoung other things has critiqued tests written by Microsoft for &lt;a href="http://asp.net/"&gt;asp.net&lt;/a&gt; MVC - check out the testreviews category) and has recently been on the Scott Hanselman podcast (&lt;a href="http://bit.ly/psgYO"&gt;http://bit.ly/psgYO&lt;/a&gt;) where he educated Scott on best practices in Unit Testing techniques. For a further insight into Roy's style, be sure to also check out Roy's talk at the recent Norwegian Developer's Conference (&lt;a href="http://bit.ly/NuJVa"&gt;http://bit.ly/NuJVa&lt;/a&gt;).  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Full Details here: &lt;a href="http://bbits.co.uk/tddmasterclass"&gt;http://bbits.co.uk/tddmasterclass&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Update:&lt;br /&gt;&lt;br /&gt;bbits are holding a raffle for a free ticket for the event. To be eligible to win the ticket (worth £2395!) you MUST paste this text, including all links, into your blog and email &lt;a href="mailto:Ian@bbits.co.uk"&gt;Ian@bbits.co.uk&lt;/a&gt; with the url to the blog entry. The draw will be made on September 1st and the winner informed by email and on bbits.co.uk/blog&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-8541839363081937821?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/8541839363081937821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=8541839363081937821' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8541839363081937821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8541839363081937821'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/08/man-it-would-be-awesome-to-go.html' title='Man, it would be awesome to go...'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-2031344859685515484</id><published>2009-08-08T21:10:00.001+01:00</published><updated>2009-08-08T21:10:33.221+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Windows 7 blues</title><content type='html'>&lt;p style="text-align: right;"&gt;&lt;i&gt;why haven't I learned yet not to go for first releases?&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;With Windows 7 reaching RTM status and the general low working demands of August, I decided it was time to kick Vista to the curb on my dev machine and get myself a brand new shiny OS.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Triple-headed Blue-screens&lt;/b&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Because it can never be two easy, the upgrade experience ended up being rather painful, with both attemps resulting in blue screens before the setup was finished. Deciding that perhaps it was some conflict with device drivers, one of the many programs I had installed or just bad luck, I decided to cut my losses and just repave the system partition to do a clean install.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Still no joy. Same bluescreen at the end of the setup.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;The dev machine's most unusual part is the two NVIDIA display adapters installed on the PCI-E slots, which are used to drive 3 monitors. Remembering that vista also had some troubles with the secondary adapter, I removed it and gave it another shot.&lt;/p&gt;
&lt;p&gt;Yap, that worked. Setup finished and gave me a brand new Windows 7 installation to play with.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;You no like restart?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;With a folder full of drivers for the display adapters, chipset, SATA controllers and audio, I spent some time updating them before heading home. Somewhere around the end ,after a restart, another bluescreen.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;That's no good.&lt;/p&gt;
&lt;p&gt;After some fiddling I found out that it only happened on a restart, and never on a cold boot.&lt;/p&gt;
&lt;p&gt;Oh well, that's a bother but at least it's work-around-able. Off I go to install sql server, visual studio and a miriad of essential software for a dev machine. Thankfully with portable apps living in a dropbox folder most of it takes little to no time at all.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;System Interrupts should not interrupt all the time.&lt;/b&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Some time afterwards, while installing some software I decided to look at resource monitor as a source of "oh, look at all that data" entertainment.&lt;/p&gt;
&lt;p&gt;And it showed "System Interrupts" using about 40% of the cpu, all the time. What. The. Hell. At first I thought it would have something to do with the troublesome display adapters, and reverting to the standard VGA adapter driver reduced the usage, but it still was too high (about 20, 25%)&lt;/p&gt;
&lt;p&gt;Again, after some fiddling around, uninstalling drivers, disabling devices I found out that the problem was with the "Marver 88SE611 SATA V12069" drivers. Reverting the drivers to the default drivers makes the cpu usage go back to normal.&lt;/p&gt;
&lt;p&gt;Sadly, the reboot bluescreen still appears from time to time. I think it always happened when the second display adapter was installed, but I'm not sure.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;In a nutshell.&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;When upgrading/installing, remove the secondary display adapter.&lt;/li&gt;

  &lt;li&gt;With two display adapters, don't reboot (away from the machine)&lt;/li&gt;

  &lt;li&gt;Don't install the Marvell SATA driver&lt;/li&gt;

  &lt;li&gt;Get a rabbit's foot.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I do hope the reboot bluescreen gets fixed soon, but for now I'm happy with having a usable machine.&lt;/p&gt;
&lt;p&gt;In the case someone has the same problem, this machine has the following specs:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Core2Duo E8400&lt;/li&gt;

  &lt;li&gt;4 x 2Gb Kingston ValueRam&lt;/li&gt;

  &lt;li&gt;Asus P5Q Pro&lt;/li&gt;

  &lt;li&gt;NVIDIA 7300 LE&lt;/li&gt;

  &lt;li&gt;NVIDIA 7200 GS&lt;/li&gt;

  &lt;li&gt;Western Digital 150Gb Velociraptor&lt;/li&gt;

  &lt;li&gt;Seagate 200Gb&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-2031344859685515484?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/2031344859685515484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=2031344859685515484' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2031344859685515484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2031344859685515484'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/08/windows-7-blues.html' title='Windows 7 blues'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-7741830734898789794</id><published>2009-06-11T00:15:00.001+01:00</published><updated>2009-06-11T00:25:40.347+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Dr. Googlelove or: How I Learned to Stop Worrying and Love the Cloud</title><content type='html'>&lt;p style="text-align: right;"&gt;&lt;i&gt;written in full blown hipster &amp;lt;censured&amp;gt; mode on a macbook, listening to an ipod and sipping an iced mocca at a starbucks.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Having three different machines that I work with daily tends to cause me some pain whenever I try to send an email from the two that are work/fun related. I've got 3 pcs that I use on a daily basis:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;My personal laptop is a black Macbook called mac-sendai. It handles my email, IM, photos, videos and contacts. My social life is stored in it, being it work or personal and I carry it around for most of the time.&lt;/li&gt;

  &lt;li&gt;My home machine is armitage. It's a Windows XP machine that I use mostly as a gaming/movie machine.&lt;/li&gt;

  &lt;li&gt;My work machine is 2finn. It runs Windows Vista 64 and is my main development machine at the office.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From each of the 3 pcs I usually need to send off emails. I use Gmail on any of the desktops and Mail.app on the laptop. Since mac-sendai handles the social part, my address book there is where I aggregate and collate most of the information about contacts and sync both the cell phone and an ipod touch to it.&lt;/p&gt;
&lt;p&gt;Up until now the only connection to the cloud was from the laptop to Plaxo, to get auto-updates for contact information of friends and "business associates". It works rather well and the best way to prove it is that most of the time I don't even notice it.&lt;/p&gt;
&lt;p&gt;But lately I've noticed that OS X is able to sync remotely to google contacts, and since I use gmail on the desktops, having it synchronized with the address books is the last step in having a always available and up to date contact store on all machines, with the added benefits of remote backups (besides the time machine I store at home).&lt;/p&gt;
&lt;p&gt;So nowadays here's the replication flow for my "personal network":&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="http://farm3.static.flickr.com/2473/3614574277_5abdea8026_o.png" width="385" height="378" alt="Sync network" /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Dropbox handles the "utils", which are about 50 or 100 megs of portable apps including notepad2, gnu utils and Launchy's preferences, current working set documents, emacs site-lisp and .emacs. Some time ago I used foldershare for the utils, but it was too finicky, and with hard links from the dropbox to the correct places, it all works much better.&lt;/li&gt;

  &lt;li&gt;Gmail handles the email. I've used IMAP even before gmail on all desktop programs and now on the ipod touch, so I haven't even thought about that particular issue for a while.&lt;/li&gt;

  &lt;li&gt;Google Calendar keeps some of my appointments, with a shared calendar for weListen.&lt;/li&gt;

  &lt;li&gt;And now, Google Contacts stores the emails and phone numbers for my little social network.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="text-align: left;"&gt;The migration of contacts ended up costing about half an hour of deduplication and merging, but thankfully the address book includes a shortcut to merge contacts, so most of the time it was spent cleaning up emails that I don't need and just checking the contacts to see that nothing was mangled.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;My biggest worry now is that the address book will become bloated. GMail adds any address that you send email to to the contacts list, and most of the time if it's a one time only reply I won't want it to be replicated, so the jury's still out on how I'll handle that.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;But for now, I'm a happy camper. No more copy pasting of emails, and my inner software engineer feels more confortable at not having duplicated information spread around.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;Edit: thanks to &lt;a href="http://twitter.com/edgargoncalves/status/2109859997"&gt;Edgar Gonçalves&lt;/a&gt; for the title fix :) In my defence Starbucks had no wifi, and I was unsure of the correct title :)&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-7741830734898789794?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/7741830734898789794/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=7741830734898789794' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7741830734898789794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7741830734898789794'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/06/long-live-google-or-how-i-gave-up-and.html' title='Dr. Googlelove or: How I Learned to Stop Worrying and Love the Cloud'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-806449982519871587</id><published>2009-04-22T12:33:00.001+01:00</published><updated>2009-04-22T12:33:33.461+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='irrelevantinfo'/><title type='text'>My delicious tag wordle</title><content type='html'>&lt;p&gt;Does it show that I am a .net developer with a python deviancy?&lt;/p&gt;
&lt;p&gt;&lt;img src="http://farm4.static.flickr.com/3531/3464732889_9e989f6750.jpg" width="480" height="210" alt="Delicious Tag Wordle.png" /&gt;&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-806449982519871587?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/806449982519871587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=806449982519871587' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/806449982519871587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/806449982519871587'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/04/my-delicious-tag-wordle.html' title='My delicious tag wordle'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3531/3464732889_9e989f6750_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-3238875088449708554</id><published>2009-01-13T19:57:00.000Z</published><updated>2009-01-13T19:58:23.602Z</updated><title type='text'>Typemock Isolator Plug (to get a free license and try out something other than Moq)</title><content type='html'>&lt;p&gt;&lt;a href="http://www.typemock.com/vbpage.php?utm_source=vbp&amp;amp;utm_medium=typeblog&amp;amp;utm_campaign=isolatorvb"&gt;Programming Visual Basic&lt;/a&gt; applications?&lt;/p&gt;  &lt;p&gt;Typemock have released a new version of their &lt;a href="http://www.typemock.com/?utm_source=hp&amp;amp;utm_medium=typeblog&amp;amp;utm_campaign=isolatorvb"&gt;unit testing&lt;/a&gt; tool, Typemock Isolator 5.2.   
This version includes a new friendly &lt;a href="http://www.typemock.com/vbpage.php?utm_source=vbp&amp;amp;utm_medium=typeblog&amp;amp;utm_campaign=isolatorvb"&gt;VB.NET &lt;/a&gt;API which makes Isolator the best Isolation tool for &lt;a href="http://www.typemock.com/vbpage.php?utm_source=vbp&amp;amp;utm_medium=typeblog&amp;amp;utm_campaign=isolatorvb"&gt;unit testing A Visual Basic (VB) .NET application&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Isolator now allows unit testing in VB or C# for many ‘hard to test’ technologies such as &lt;a href="http://typemock.com/sharepointpage.php?utm_source=spp&amp;amp;utm_medium=typeblog&amp;amp;utm_campaign=isolatorvb"&gt;SharePoint&lt;/a&gt;, ASP.NET MVC, partial support for Silverlight, WPF, LINQ, WF, Entity Framework, &lt;a href="http://www.typemock.com/wcfpage.php?utm_source=wcfp&amp;amp;utm_medium=typeblog&amp;amp;utm_campaign=isolatorvb"&gt;WCF unit testing&lt;/a&gt; and more.&lt;/p&gt;  &lt;p&gt;Note that the first 25 bloggers who blog this text in their blog and tell us about it, will get a &lt;strong&gt;Free Full Isolator license&lt;/strong&gt; (worth $139). If you post this in a &lt;strong&gt;VB.NET dedicated blog&lt;/strong&gt;, you'll get a license automatically (even if more than 25 submit) during the first week of this announcement.&lt;/p&gt;  &lt;p&gt;Go ahead, click the following link for &lt;a href="http://blog.typemock.com/2009/01/get-free-isolator-licnese-for-helping.html?utm_source=vb_blog&amp;amp;utm_medium=typeblog&amp;amp;utm_campaign=isolatorvbblog"&gt;more information &lt;/a&gt;on how to get your free license.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-3238875088449708554?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/3238875088449708554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=3238875088449708554' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3238875088449708554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3238875088449708554'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/01/typemock-isolator-plug-to-get-free.html' title='Typemock Isolator Plug (to get a free license and try out something other than Moq)'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-3888138806007489408</id><published>2009-01-07T16:25:00.001Z</published><updated>2009-01-07T16:25:58.770Z</updated><title type='text'>Evil Extension Method, or just handy?</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;I just realized I can have the following extension method:&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4; max-height: 200px"&gt;   &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;     &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; StringExtensions&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; HasContent(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; self)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; !&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.IsNullOrEmpty(self);&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;And then use it like this:&lt;/p&gt;

&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4; max-height: 200px"&gt;
  &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;
    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; [TestFixture]&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; StringExtensionMethodsTest&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;     [Test]&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;     [Category(&lt;span style="color: #006080"&gt;&amp;quot;Unit&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; TestStringHasContentWorksForNullStrings()&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; s = &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;         Assert.IsFalse(s.HasContent());&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;     [Test]&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  13:&lt;/span&gt;     [Category(&lt;span style="color: #006080"&gt;&amp;quot;Unit&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  14:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; TestStringHasContentWorksForEmptyStrings()&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  15:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  16:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; s = &lt;span style="color: #006080"&gt;&amp;quot;&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  17:&lt;/span&gt;         Assert.IsFalse(s.HasContent());&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  18:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;     [Test]&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  21:&lt;/span&gt;     [Category(&lt;span style="color: #006080"&gt;&amp;quot;Unit&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; TestStringHasContentWorksForStringsWithContent()&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  24:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; s = &lt;span style="color: #006080"&gt;&amp;quot;content&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  25:&lt;/span&gt;         Assert.IsTrue(s.HasContent());&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #606060"&gt;  27:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The interesting part is that a method call on a null object for an extension method works, which allows for the s.HasContent() idiom, instead of string.HasContent(s).&lt;/p&gt;

&lt;p&gt;My question is the following: is relying on such “detail” good, in the way that it allows for less line noise and cleaner code, or bad for violating the idea that you can call a method on a null object and not get an exception?&lt;/p&gt;

&lt;p&gt;I’d love to hear opinions on this.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-3888138806007489408?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/3888138806007489408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=3888138806007489408' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3888138806007489408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3888138806007489408'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2009/01/evil-extension-method-or-just-handy.html' title='Evil Extension Method, or just handy?'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-3599641621836349742</id><published>2008-12-07T00:47:00.001Z</published><updated>2008-12-07T00:47:27.278Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>2 more apps I end up installing on all computers I use.</title><content type='html'>&lt;p&gt;&lt;a href="http://www.foldershare.com/"&gt;Foldershare&lt;/a&gt;, set to sync my utils folder, which includes putty, teracopy, some gnu command line utils, a couple of scripts to start and stop services, .net reflector, keepass binary, notepad2, and some other portable apps that I end up collecting over time.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.getdropbox.com/"&gt;Dropbox&lt;/a&gt;, mostly for the public folder. The explorer/finder integration is a wonderful piece of magic. Dropping a file on the public folder and using the "Copy the public link" item on the context menu ended up being my favorite way to share files with the world.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-3599641621836349742?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/3599641621836349742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=3599641621836349742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3599641621836349742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/3599641621836349742'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/12/2-more-apps-i-end-up-installing-on-all.html' title='2 more apps I end up installing on all computers I use.'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-9194702412593656040</id><published>2008-11-26T01:39:00.001Z</published><updated>2008-11-26T01:39:36.832Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='couchd'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>First shot at a couchdb installer on windows</title><content type='html'>&lt;p&gt;Disclaimer: this is for the brave folks who wish to try things out. So far I've only tested the installer on my own machine and on a clean Windows XP vmware image.&lt;/p&gt;
&lt;p&gt;Now for the running bits: &lt;a href="http://www.brunomlopes.com/software/couch-db-binaries"&gt;here's&lt;/a&gt; a first try at getting a workable couchdb installer on Windows.&lt;/p&gt;
&lt;p&gt;There were some instructions down at the &lt;a href="http://wiki.apache.org/couchdb/Installing_on_Windows"&gt;wiki&lt;/a&gt; on how to get all the bits and bobs running together, but parts of it required a C compiler and some rather tedious setup on part of the user. It was also a bit frail, as noted by me trying for a couple of afternoons to get the unit tests to pass.&lt;/p&gt;
&lt;p&gt;The biggest problem was that Spidermonkey must be compiled in a specific way (one that wasn't apparent to me at first), which I dismissed at first because the blog containing &lt;a href="http://blog.endflow.net/?p=55&amp;amp;lang=en"&gt;the steps&lt;/a&gt; was down. Also, one must use lowercase paths on the config file, otherwise erlang will choke when trying to open the database files.&lt;/p&gt;
&lt;p&gt;The next step would be to create a shortcut on the start menu, get some sensical text on the installer, do some error checking to find out if the prerequisites are installed, and create an uninstaller.&lt;/p&gt;
&lt;p&gt;For now, here's the installer for the world to try. Leave a comment if you find a problem or have a suggestion.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-9194702412593656040?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/9194702412593656040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=9194702412593656040' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/9194702412593656040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/9194702412593656040'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/11/first-shot-at-couchdb-installer-on.html' title='First shot at a couchdb installer on windows'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-8384033927241998331</id><published>2008-10-28T23:12:00.001Z</published><updated>2008-10-28T23:12:12.685Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='movies'/><title type='text'>Thank you guys!</title><content type='html'>&lt;p&gt;My great friends and colleagues at weListen just offered my these two wonderful sets of DVDs for my birthday:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://farm4.static.flickr.com/3042/2981928025_c82df9f180.jpg" width="480" height="360" alt="birthday presents" /&gt;&lt;/p&gt;
&lt;p&gt;These are Q&amp;amp;A sessions Kevin Smith does at packed theaters with thousands of people, and where he talks about everything and anything. It includes some inside jokes for his Askewniverse fans, and each disk is about an hour and a half of him just having fun with the audience. And that's the brilliance of it. It really is just a dude with some great stories having fun with people who dig his work, and he just goes with the flow, having people from the audience up on the stage for whatever reason it is at the time, or going on a binge discussing Prince's flamboyance.&lt;/p&gt;
&lt;p&gt;For anyone who's a fan of Kevin Smith's movies, these dvds are must see. And I really thank the guys at work for the gift. :D&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-8384033927241998331?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/8384033927241998331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=8384033927241998331' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8384033927241998331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8384033927241998331'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/10/thank-you-guys.html' title='Thank you guys!'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3042/2981928025_c82df9f180_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-764182369055458656</id><published>2008-10-17T00:42:00.001+01:00</published><updated>2008-10-17T00:42:36.599+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Unexpected change</title><content type='html'>&lt;p&gt;Got home at 11pm. General slouch mode before bedtime engaged. Open firefox for one last news feed round and look at a large change on iGoogle.&lt;/p&gt;
&lt;p&gt;My iGoogle page is "designed" to fit on one 20" screen at 1650x1050 or thereabouts. I want to open it, scan it quickly for any big news and move on. Now it takes two screens with a short description for each feed item (as most of the widgets are based on newspapers and tv channels' feeds), which kind of defeats the purpose. Also, the tabs moved to the left, which may be a good option for today's wide screens, but looks a bit odd having lots of negative space on a vertical bar.&lt;/p&gt;
&lt;p&gt;Thankfully a quick trip to the tab's settings shows me a way to remove the new feed behavior and return the page to a more sane, no scroll, quick scan mode. It also shows a manual backup for the iGoogle page, and export settings. May pay someday to add these settings to the backup jobs at home.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-764182369055458656?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/764182369055458656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=764182369055458656' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/764182369055458656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/764182369055458656'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/10/unexpected-change.html' title='Unexpected change'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-2155609534195601748</id><published>2008-10-12T18:33:00.000+01:00</published><updated>2008-10-12T18:34:24.373+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='visual studio'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='cullwindows'/><title type='text'>My first Visual Studio Plugin - CullWindows</title><content type='html'>&lt;p style="text-align: right;"&gt;&lt;em&gt;okay, that was hell...&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;In the .net world I usually work with the wonder tool that is Resharper. For this particular story, the main points are the hugely useful navigation shortcuts: Go to symbol, Go to file, Go to type, Navigate to implementation, Navigate to base, you get the picture.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;So, the Solution Explorer window usually doesn't get much work, but the tab strip on top is cluttered beyond recognition even after just half an hour or two of work. And while most of the source navigation is done via shortcuts, sometimes it's handy to just click on the respective tab. Just try and find it amidst 20 other files.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;Hence, my idea to expand Visual Studio with the simple ability to keep just the top X files I use.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;Meet &lt;a href="http://www.brunomlopes.com/software/cullwindows" title="Cull Windows Visual Studio Plugin"&gt;CullWindows&lt;/a&gt;. Direct download link &lt;a href="http://brunomlopes-visualstudioplugins.googlecode.com/files/DeployCullWindows.msi" title="Cull Windows Installed"&gt;here&lt;/a&gt;, google code home page &lt;a href="http://code.google.com/p/brunomlopes-visualstudioplugins/" title="Cull Windows Google Code Home Page"&gt;here&lt;/a&gt;. A simple solution to a simple problem. It was the implementation that was hell.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;&lt;strong&gt;The Plugin&lt;/strong&gt;&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The plugin's logic is quite simple. Keep track of which files are opened for editing. When the user views a file, see if we've hit the limit. If we have, close documents, from last to first, until we're back on the limit. Ignore unsaved files, and things that are not files.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The limit is configured on the Visual Studio Options panel, under Cull Windows.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The installed is "dumb", meaning it won't give you any feedback, and just install and go away. I'm sorry for that.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;For now the plugin is Visual Studio 2008 only, no version for 2005, mostly because I don't use it. If there's any demand, I may give it a try.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;&lt;strong&gt;The Implementation&lt;/strong&gt;&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;Firstly, I created an add-in. Seeing an example that came with the SDK, I managed to get a reference to the Running Documents Table, which contains all currently opened documents, and registered an event sink to listen for changes in the table.&lt;br /&gt;&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;This was all good until I get home and try the same code on another computer. There, the table was nowhere to be found, and found no error. I could've tried to debug it for some time, but it seemed better to just restart it.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;&lt;/p&gt;
&lt;p style="background-color: rgba(0, 0, 0, 0); color: #333333; font-family: Verdana; font-size: 13px; line-height: 16px; margin-bottom: 13px; margin-left: 0px; margin-right: 0px; margin-top: 13px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-indent: 0px; clip-rule: nonzero; flood-color: #000000; flood-opacity: 1; lighting-color: #FFFFFF; stop-color: #000000; stop-opacity: 1; pointer-events: visiblepainted; color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; fill: #000000; fill-opacity: 1; fill-rule: nonzero; image-rendering: auto; shape-rendering: auto; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; text-rendering: auto; alignment-baseline: auto; baseline-shift: baseline; dominant-baseline: auto; text-anchor: start; writing-mode: lr-tb; glyph-orientation-horizontal: 0deg; glyph-orientation-vertical: auto;"&gt;Next up, a VSPackage. From what I had gathered, it is a "new" and more powerful way to extend VS, and the API and samples seemed to be a bit more OO. I managed to get it to work, again, but now the debugging is a bit stranger than when it was an add-in, since it now needs to be installed on the registry hive for the IDE.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;Most of the interaction is a bit nasty from someone used to clean, object oriented APIs. It seems most of the extensibility points in Visual studio are done via COM, which is a beast I've encountered few times before, and I dont' keep fond memories from those occasions. It may be that I'm too new working with it, but the code that resulted wasn't exactly... pleasant. Feel free to browse the &lt;a href="http://code.google.com/p/brunomlopes-visualstudioplugins/source/browse/#svn/trunk" title="svn repository for cull windows"&gt;code&lt;/a&gt;. It isn't pretty, specially the first iterations, but I'm open to suggestions and criticisms.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;Now, the API is HUGE. There are hundreds of interfaces on each of the Visual Studio namespaces, and trying to get from one point to another is somewhat tiring. I have a document cookie from the event handler, now how to get the name of the file. Okay, have the name, now how can I find if it is modified. Where to keep the preferences, and how to access them. There were some concepts which I lacked, but mostly it was just be being a newbie at it and not knowing where to look.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The solution ended up being simply 3 files: the package, the document monitor and the options page.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The package was built mostly by the VSPackage template, and the only things I changed was to remove the menu item, add the option pane and build the document monitor. Some of the attribute values were a matter of faith on the documentation, and I didn't dare touch most of the generated code.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The document monitor does the grunt work of maintaining a document list, ordered by access date, listening to the running document table events, and closing documents when the limit is reached. The first implementation was fetching the window frames from the UI shell and iterating through it to find the one with the file to close. Not optimal by a long shot, but worked. A second (and final) iteration used the IsDocumentOpen method from VsShellUtilities to get the window frame. This was not intuitive. I expected a method starting with Is to just return true or false, and not return more information.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The option page was actually the easier part, with all of the work taken care of by the DialogPage class, including persistence. It's a bit too much magic for my tastes, but it works. I was supposed to access the options via DTE, but it kept throwing an invalid cast exception, and I gave up and just passed the option page object to the monitor.&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;&lt;strong&gt;The Next Steps&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;The first implementation is done, but I think I can make it a bit smarter if I have the time:&lt;/p&gt;
&lt;p style="text-align: justify;"&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Ponder the amount of time spent on a given file when picking which one to remove. This would prevent newer files which were opened by mistake to stay opened when earlier files which had more use go away.&lt;/li&gt;

  &lt;li&gt;Keep a background timer to cull windows after some time with no visits to the document.&lt;/li&gt;

  &lt;li&gt;Have the installed say something instead of just installing and going away.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I guess that's all for now, thanks for tuning in.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-2155609534195601748?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/2155609534195601748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=2155609534195601748' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2155609534195601748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2155609534195601748'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/10/my-first-visual-studio-plugin.html' title='My first Visual Studio Plugin - CullWindows'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-5899644189939156520</id><published>2008-09-29T01:14:00.001+01:00</published><updated>2008-09-29T01:14:50.486+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='buxfer'/><title type='text'>More Buxfer goodies (this time, a backup script)</title><content type='html'>&lt;p&gt;I just realized that I'm putting a fair amount of effort (and information) on Buxfer. Been tracking rather faithfully my expenses for a month, and I'm rather happy with the service.&lt;/p&gt;
&lt;p&gt;But what if it someday fails, or disappears? What about the data? &lt;span style="text-decoration: line-through;"&gt;It seems to have a data export facility implemented, but it's for Pro accounts only.&lt;/span&gt; Okay, it seems like either I misread the membership plans, or they changed them. Either way, the data export is good for when you want to take a look at the statements in excel, but as far as a backup goes, it's no good for me.&lt;/p&gt;
&lt;p&gt;A backup must be something I don't have to think about for it to work. It should do its job in the background as much as possible, and only warn me if something goes wrong. Thankfully Buxfer's API has enough functionality to cover this.&lt;/p&gt;
&lt;p&gt;This is a small script that fetches all transactions from your Buxfer accounts (or one particular account) to disk. It has some smarts implemented to "continue" a previous backup, so you can setup a scheduled task periodically to fetch the transactions, and it should just fetch the new ones after the first run.&lt;/p&gt;
&lt;p&gt;So far it stores the info as pure json, which was the easiest way to implement it. Should I require in the future to actually act on this, I can parse the json again and convert it to csv, or any other format I need.&lt;/p&gt;
&lt;p&gt;The source is &lt;a href="http://dl.getdropbox.com/u/118385/backup_buxfer.py" title="backup_buxfer.py"&gt;here&lt;/a&gt;, and a skeleton configuration file is &lt;a href="http://dl.getdropbox.com/u/118385/backup_buxfer.ini.skel" title="backup_buxfer.ini"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To use it you'll need Python (I tested it with 2.5, but I think it should work with 2.4) and &lt;a href="http://pypi.python.org/pypi/simplejson" title="simplejson package"&gt;simplejson&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Configuration should be self-explanatory, it needs you to fill out your username, password, and filename for backup.&lt;/p&gt;
&lt;p&gt;To run it, you can pass the path to the configuration file (this way you can have several different configurations, one for each account, or for different users).&lt;/p&gt;
&lt;p&gt;I guess this is it. Any questions, feedback or general chat, just leave a comment.&lt;/p&gt;
&lt;p&gt;That's all, folks!&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-5899644189939156520?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/5899644189939156520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=5899644189939156520' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5899644189939156520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5899644189939156520'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/09/more-buxfer-goodies-this-time-backup.html' title='More Buxfer goodies (this time, a backup script)'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-7087016544063147608</id><published>2008-09-26T16:02:00.001+01:00</published><updated>2008-09-26T16:02:55.442+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='buxfersubmit'/><title type='text'>v1.2 of BuxferSubmit</title><content type='html'>&lt;p&gt;Version 1.2 is up at the project's &lt;a href="http://code.google.com/p/dashboardbuxfersubmit/" title="BuxferSubmit Google Code Page"&gt;page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Direct &lt;a href="http://dashboardbuxfersubmit.googlecode.com/files/BuxferSubmit-v1.2.zip" title="v1.2"&gt;link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Changes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Uses jQuery for the async request.&lt;/li&gt;

  &lt;li&gt;Stores the user's password in the user's keychain (thanks to Stan Lemon for the code and plugin)&lt;/li&gt;

  &lt;li&gt;Minor interface tweak (pressing enter when on the tags text field submits the transaction)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's all for now. I'd love to get some tag auto-complete, but getting a good interface for it has proven difficult.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-7087016544063147608?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/7087016544063147608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=7087016544063147608' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7087016544063147608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7087016544063147608'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/09/v12-of-buxfersubmit.html' title='v1.2 of BuxferSubmit'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-7071910950831460091</id><published>2008-09-10T22:25:00.001+01:00</published><updated>2008-09-10T22:25:48.909+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='slightlyfictional'/><title type='text'>Waiting.</title><content type='html'>&lt;p style="text-align: right;"&gt;&lt;em&gt;on the topic of forced rest time&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I actually like waiting for someone at the airport. Of course, this may have something to do with the fact that this only happens once in a while, when I do so I've got my trusty macbook with me, and that I'm not all that eternally busy that I can't spare some time to sit at a coffee shop with wireless.&lt;/p&gt;
&lt;p&gt;Not that I do much work, or read all that much, or have a positive contribution to give the world while I'm sitting here. I actually spend most of the time just enjoying people going to and fro or milling about while waiting, like me, for someone else. The coffee shop I'm sitting in has about 32 tables, and it's directly in front of the arrival gate. There are 12 people here, all of them sitting the same way, having a dring, chatting on the phone or just waiting with a bit of a blank stare.&lt;/p&gt;
&lt;p&gt;And I'm talking about a mildly peripheral airport like the Lisbon one, which I bet doesn't have one tenth of the cultural cross section of a Heathrow, or an LAX. On the other hand, because it's a smallish one, I can sit at the coffee shop and view most of the arrivals, and it doesn't sound hectic or too crowded. It's just a bunch of people. Waiting.&lt;/p&gt;
&lt;p&gt;And you can play a small game, while waiting. Try to think of all these people, every single one of them, as an distinct individual, not just "people". With needs, thoughts, desires, passions, pains and hungers. All of them have a single shared purpose here, but that can be the only thing that they have in common. Imagine the diversity. The blonde lady, dressed in a white short sleeve which passes you? Is she coming or going? Where to? The couple (or so you assume, it's a man and a woman, they could be brothers, or just friends) with the large backpacks. Travelling the world, vacations or volunteer work? The black family, waiting for a relative? Perhaps a son, gone abroad to meet some friends. Of course, the suit, lugging his laptop luggage, blackberry on hand talking and looking just a bit lost, almost (just almost) looking like it's the first time he's here.&lt;/p&gt;
&lt;p&gt;And then it starts thinning out. More people are leaving than arriving. There are less flights. And now it's my turn to go. My charge has arrived.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-7071910950831460091?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/7071910950831460091/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=7071910950831460091' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7071910950831460091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7071910950831460091'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/09/waiting.html' title='Waiting.'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-8035330733722242125</id><published>2008-09-02T01:09:00.000+01:00</published><updated>2008-09-02T01:10:08.546+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='buxfersubmit'/><title type='text'>v1.1 of BuxferSubmit</title><content type='html'>&lt;p&gt;&lt;strong&gt;For the impatient:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;New version. Should not freeze if Buxfer is slow to respond or if you forget to plug in the cable. Also some better handling of special characters while talking to the server.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="http://dashboardbuxfersubmit.googlecode.com/files/BuxferSubmit-v1.1.zip" title="BuxferSubmit v1.1"&gt;here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For the curious:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Buxfer seemed to have had a bit of trouble with site slowness, which exposed my cluelessness in developing widgets that connect to web services (as in, forgot to consider timeouts of XMLHttpRequests).&lt;/p&gt;
&lt;p&gt;I was a bit amazed to find neither much info on Apple's site nor on the web on how to best handle these problems. Ended up just setting a timeout before the request, clearing it on success and canceling the request should the timeout happen. Perhaps I should look into a javascript library such as mochikit or jquery to help with the heavy lifting.&lt;/p&gt;
&lt;p&gt;Also, most of the code feels to me a bit fragile. The error handling is primitive, and the fact that the request is async makes me a bit queasy. I'll chalk that up to immaturity on my part in developing in javascript, but I hope to have the time to refactor it a bit.&lt;/p&gt;
&lt;p&gt;As I said before, the code is up on &lt;a href="http://code.google.com/p/dashboardbuxfersubmit/" title="BuxferSubmit Google Code Page"&gt;google code&lt;/a&gt;, so feel free to browse around, all three of you who are reading this :)&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-8035330733722242125?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/8035330733722242125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=8035330733722242125' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8035330733722242125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8035330733722242125'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/09/v11-of-buxfersubmit.html' title='v1.1 of BuxferSubmit'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-8655015997745763030</id><published>2008-08-30T20:01:00.001+01:00</published><updated>2008-08-30T20:01:43.953+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='buxfersubmit'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Google Code page for the widget</title><content type='html'>&lt;p&gt;Well, instead of just having a zip file containing the source code, I decided to put it up on google code. The page is &lt;a href="http://code.google.com/p/dashboardbuxfersubmit/" title="BuxferSubmit Google Code Page"&gt;here&lt;/a&gt; and I've updated the &lt;a href="http://www.brunomlopes.com/software/buxfersubmit" title="BuxferSubmit Home Page"&gt;homepage&lt;/a&gt; to include a link to it. The releases will live there, also, and I think I'll look into getting the widget to autoupdate or something, with the files on google code I think I can get an rss feed to the releases.&lt;/p&gt;
&lt;p&gt;I'll keep y'all posted.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-8655015997745763030?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/8655015997745763030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=8655015997745763030' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8655015997745763030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8655015997745763030'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/08/google-code-page-for-widget.html' title='Google Code page for the widget'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-5948477090250631267</id><published>2008-08-30T12:37:00.001+01:00</published><updated>2008-08-30T12:37:25.681+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='macos'/><category scheme='http://www.blogger.com/atom/ns#' term='buxfersubmit'/><title type='text'>Buxfer widget</title><content type='html'>&lt;p&gt;If you follow my twitter &lt;a href="http://twitter.com/brunomlopes" title="twitter feed"&gt;feed&lt;/a&gt;, you may have noticed that I’ve created and published a small widget to use with buxfer &lt;a href="http://www.brunomlopes.com/software/buxfersubmit" title="BuxferSubmit Home Page"&gt;here&lt;/a&gt;. Right now it’s just two forms, one to submit and one to configure, and it isn’t all that smart at what it does.&lt;/p&gt;
&lt;p&gt;My intent is to keep the interface simple, but to improve on three points:&lt;br /&gt;
- Usability-wise, I want to be faster at adding the transaction, and perhaps have some suggestion or completion for the tags. Right now it is a bit dumb, and doesn’t provide much help besides being a form.&lt;br /&gt;
- As far as the interface goes, I want it to occupy a bit less space, and to be prettier. As you can see on the screenshot, the widget uses the standard parts and the labels have the default size. I think I can do better than that and still keep it usable.&lt;br /&gt;
- The configuration is stored on the preferences cache for widgets Apple provides, which isn’t quite the best choice as far as storing sensitive data. I should put it in the keychain, and perhaps even use the same as Safari.&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;I was quite impressed with the ease with which you can produce widgets. Having Dashcode helps a lot, both with the layout of the forms and the debugging of the code. Widgets being based on javascript and html gives them a low entry cost if one is already used to client-side web programming, and opens up the opportunity for some rather interesting moves, such as using JQueryUI or Moo on the widget.&lt;/p&gt;
&lt;p&gt;If you’ve tried the widget and want to leave feedback, please, feel free to contact me or to drop a comment here :)&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-5948477090250631267?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/5948477090250631267/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=5948477090250631267' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5948477090250631267'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5948477090250631267'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/08/buxfer-widget.html' title='Buxfer widget'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-6603234280562189275</id><published>2008-07-21T19:00:00.001+01:00</published><updated>2008-07-21T19:00:37.651+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Perhaps extra keys on a keyboard aren't that bad an idea...</title><content type='html'>&lt;p style="text-align: right;"&gt;&lt;em&gt;on the newfound use of useless keys&lt;/em&gt;&lt;/p&gt;
&lt;p style="text-align: left;"&gt;Whilst my work is nowadays mostly done on my macbook (running windows, much to my lament), at home I usually drop the laptop besides my desktop, boot it into mac os x, and use it as a communications central with email and IM open while working on some other thing on the desktop. Call it a strange dual-head, quad-core computing station.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;Instead of switching around keyboards when I need to pass from one computer to the other, I use (and profoundly adore) &lt;a href="http://synergy2.sourceforge.net/" title="synergy homepage"&gt;synergy&lt;/a&gt;, which allows one keyboard+mouse combo to control more than one computer, via the network. But until now, to change from one computer to another (let's say to talk to someone on IM) my hand had to leave the home keys of the keyboard to use the mouse to move from one computer to the other (since synergy uses the edges of the monitor to cross control from one computer to another).&lt;/p&gt;
&lt;p style="text-align: left;"&gt;But no more! Remembering some button with "hotkeys" on the label on the configuration manager, I managed to bind two keys, one to move left, other to move right. But then the dilema... which keys?&lt;/p&gt;
&lt;p style="text-align: left;"&gt;I didn't want a hard, multiple key, emacs-like, binding. I was looking for a short stroke to allow for quick back-and-forth, since that was the most common scenario (coding on the desktop, and going to the laptop to spout nonsense on im and back again to serious business).&lt;/p&gt;
&lt;p style="text-align: left;"&gt;Enter the most useless keys on my current keyboard, a logitech internet navigator. A fine keyboard all in all, with a wheel on the left side (which I tend to use quite often, as a matter of fact) and two keys below it, go and back.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;&lt;a href="http://www.flickr.com/photos/26078380@N08/2690014552/"&gt;&lt;img src="http://farm4.static.flickr.com/3061/2690014552_770340a77b_m.jpg" height="190" width="240" alt="Previously useless keys" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="text-align: left;"&gt;Bind go for left, back to right and we've got a pan-computer Alt-Tab.&lt;/p&gt;
&lt;p style="text-align: left;"&gt;Joy.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-6603234280562189275?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/6603234280562189275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=6603234280562189275' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/6603234280562189275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/6603234280562189275'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/07/perhaps-extra-keys-on-keyboard-aren.html' title='Perhaps extra keys on a keyboard aren&amp;#39;t that bad an idea...'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3061/2690014552_770340a77b_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-5525087656687494665</id><published>2008-07-05T15:46:00.001+01:00</published><updated>2008-07-06T11:46:11.728+01:00</updated><title type='text'>Using capistrano to deploy a python application</title><content type='html'>&lt;p&gt;At &lt;a href="http://www.welisten.eu" target="_blank"&gt;weListen&lt;/a&gt; we have a small python web application that we use as a time tracker and status keeper. Something akin to twitter, but with less social and more tracking. It lives in a Linux server together with a couple of our other services, and tends to be something I work on from time to time to improve small details or to fix small bugs.&lt;/p&gt;  &lt;p&gt;Now, most of the time the development of this app is rather iterative. I think up of something new to try or a small improvement, code it on my local workstation, test it with live data taken from the production environment and if I'm happy with the results, I commit the change and deploy it on the production server. It's a small app, with a couple of services running, so the upgrade protocol is direct. It is still a couple of steps, and sometimes I tend to forget one of them (usually the one where I refresh the source code from subversion).&lt;/p&gt;  &lt;p&gt;So, that looks like a good excuse to try out &lt;a href="http://www.capify.org/"&gt;capistrano&lt;/a&gt;, of which I've heard many things, mostly coming from the ruby on rails community.&lt;/p&gt;  &lt;p&gt;In a nutshell, what I wanted was for a library to take care of the connection and execution of commands on remote servers with minimal fuss. And that's what I got.&lt;/p&gt;  &lt;h4&gt;The Good&lt;/h4&gt;  &lt;p&gt;After installing the one click version, ruby was setup and ready to go. The gems library was already included, and installing capistrano was a matter of invoking the stanza featured on the project's home page.&lt;/p&gt;  &lt;p&gt;Creating a script to perform the same steps I previously did manually was straightforward enough, and rather &amp;quot;obvious&amp;quot;, apart from the trick to get sudo not to complain:&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;task :update, :hosts =&amp;gt; &lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;server&amp;gt;&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;do&lt;/span&gt;
  default_run_options[:pty] = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt; # required so that sudo doesn't complain
  run &lt;span style="color: #006080"&gt;&amp;quot;svn update ~/&amp;lt;directory&amp;gt;&amp;quot;&lt;/span&gt;
  sudo &lt;span style="color: #006080"&gt;&amp;quot;/etc/init.d/&amp;lt;service 1&amp;gt; restart&amp;quot;&lt;/span&gt;
  sudo &lt;span style="color: #006080"&gt;&amp;quot;/etc/init.d/&amp;lt;service 2&amp;gt; restart&amp;quot;&lt;/span&gt;
end&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The server can use the standard &lt;a href="mailto:username@host:post"&gt;username@host:post&lt;/a&gt; format, and the ssh framework plays nice with pageant, making passwordless logins updates real easy.&lt;/p&gt;

&lt;h4&gt;The Bad&lt;/h4&gt;

&lt;p&gt;No documentation, apart from a getting started tutorial, which is completely geared to deploying ruby on rails applications according to their methodology. Both seem to be a known problem, as a quick Google search for &amp;quot;capistrano documentation&amp;quot; gives us some pages with a call for help, and a mailing list post for it, and the getting started page warns us about it being devoted to ruby on rails. &lt;/p&gt;

&lt;p&gt;Still, It leaves the rest of the world in the dark about how to use cap to deploy other kind of applications. Also, there isn't much in the way of explaining what is their methodology, and how can a user skip or customize some of the steps.&amp;#160; I ended up just running the commands with no automatic error checking. Which works for now, but leaves me a bit unsure as to how solid it is.&lt;/p&gt;

&lt;h4&gt;The Ugly&lt;/h4&gt;

&lt;p&gt;Since I use a private/public key combo to login to the server with a non-root user (standard Linux sysadmin practice), and sudo requires a password by default, the script didn't work out as well as I wanted it to be on the first try. I could either type the password each time I wanted to deploy the app, get ssh to pass through my private key from pageant to sudo, or tell sudo that the services don't require a password.&lt;/p&gt;

&lt;p&gt;I ended up compromising and going with the last choice. It means that an attacker which gains access to the server via the normal user can restart the services and do a bit of damage, but if he's already inside, then a couple of services going down is the least of my worries.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;If I already had ruby installed, adding capistrano to the list of dependencies is not a big deal. The library is small and easy to install using gems. The problem was simple, and with the right tools the solution was equally direct. The lack of documentation wasn't that big a deal, but mostly because the problem was small. &lt;/p&gt;

&lt;p&gt;Sadly, capistrano is the only reason for me to have ruby installed, so far, making the dependency a large and difficult one to explain. I might have a look in the future for python based alternatives.&lt;/p&gt;

&lt;p&gt;Also, I'm starting to wonder if I could use the idea behind capistrano to deploy windows based applications. The biggest problem would be the remote connection, but I believe that Windows Server 2008 already has some support for console based remote connections. If this were possible, deployments could be more easily automated, which would most likely reduce the overhead of getting a new version of a web based application live.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-5525087656687494665?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/5525087656687494665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=5525087656687494665' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5525087656687494665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5525087656687494665'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/07/using-capistrano-to-deploy-python.html' title='Using capistrano to deploy a python application'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-1109691656977114443</id><published>2008-05-07T06:38:00.001+01:00</published><updated>2008-05-07T06:38:32.349+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><title type='text'>The biggest disadvantage to a coffee machine at home...</title><content type='html'>&lt;p&gt;... is the period while you adapt to having a coffee machine at home.&lt;/p&gt;
&lt;p&gt;The one where you learn when &lt;strong&gt;not&lt;/strong&gt; to drink coffee.&lt;/p&gt;
&lt;p&gt;The same one where you end up awake at 6am because you &lt;strong&gt;just&lt;/strong&gt; had to get a cup of delicious, tasteful, awesomely scented java after dinner.&lt;/p&gt;
&lt;p style="text-align: right;"&gt;&lt;span style="font-style: italic;"&gt;Live and learn...&lt;/span&gt;&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-1109691656977114443?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/1109691656977114443/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=1109691656977114443' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1109691656977114443'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1109691656977114443'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/05/biggest-disadvantage-to-coffee-machine.html' title='The biggest disadvantage to a coffee machine at home...'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-59184390246715698</id><published>2008-05-02T21:16:00.001+01:00</published><updated>2008-05-02T21:16:20.895+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Python, windows, sockets and Ctrl-c</title><content type='html'>&lt;p&gt;One of the minor annoyances of python on windows has to do with the sockets' blocking behavior.&lt;/p&gt;
&lt;p&gt;Python's libs have several implementations of simple server loops, where the server listens for connections or specific requests and passes it on to a handler function on your code. To do so, the server blocks on an accept or read. Which, on Windows, means you can't ctrl-c out of a server process to test a code change.&lt;/p&gt;
&lt;p&gt;To get around this little quirk I found a small workaround. We need two tools:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx"&gt;Powershell&lt;/a&gt; to launch the python script&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896683.aspx"&gt;PsKill&lt;/a&gt; to kill the python process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stick an infinite loop outside the python script (like this "while (1) { server.py }"). Now, open another command prompt (I use &lt;a href="http://sourceforge.net/projects/console/"&gt;Console&lt;/a&gt; for managing those) and, when you want to reload the server, just run "pskill python". Sadly this kills all python sessions currently running, which may not be what you want. But for now it works well enough.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-59184390246715698?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/59184390246715698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=59184390246715698' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/59184390246715698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/59184390246715698'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/05/python-windows-sockets-and-ctrl-c.html' title='Python, windows, sockets and Ctrl-c'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-5559712423609864272</id><published>2008-04-28T15:31:00.001+01:00</published><updated>2008-04-28T15:31:29.208+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>My personal list of must-install apps</title><content type='html'>&lt;p&gt;For the umpteenth time I re-installed windows today on armitage (my desktop). For the first time I did it from a flash drive, since my dvd reader was on the fritz. I considered buying a new one, but after spending quite a lot upgrading the computer, I felt some effort could be spent in trying out the usb method. Much to my amazement it turned out to be quite easy. &lt;a href="http://www.vandomburg.net/installing-windows-xp-from-usb/"&gt;This&lt;/a&gt; link explained everything, and is actually quite straightforward. But that's not the point here, this is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://synergy2.sourceforge.net/"&gt;Synergy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Small application which allows one computer to control many via the network. A godsend for us with both laptop and desktop, an a desire to have more screens than a stock market analyst.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.bluemars.org/clipx/"&gt;ClipX&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kill ring for windows. Before, I had to remember to get a notepad window, paste the clipboard I wanted to use afterwards, copy something, go back to notepad, rinse, wash, repeat. How in the 9 circles of hell did I live like that I don't know. But now that way is no more. ClipX remembers the copy operations you make, and has a pretty intuitive way to go back and forth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.launchy.net/"&gt;Launchy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I understand the need for a start menu. I just don't understand why must I use it every single time I want to open a browser, visual studio or anything else. This one takes care of that. Not as good as Quicksilver, but it's good enough for me not to rip out my eyeballs out while trying to get stuff done.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.google.pt/url?sa=t&amp;amp;ct=res&amp;amp;cd=1&amp;amp;url=http%3A%2F%2Fwww.dexpot.de%2F&amp;amp;ei=Fd8VSPOUKJKAwgG5zZBv&amp;amp;usg=AFQjCNF3rdqrfGckdL9ccByEdMAS_fqhjA&amp;amp;sig2=_BQQ_fSjPRZUMzf9QwKKiA"&gt;&lt;strong&gt;Dexpot&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The sanest virtual desktop manager for windows. Works extremely well almost all the time, and stays out of your way.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-5559712423609864272?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/5559712423609864272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=5559712423609864272' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5559712423609864272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5559712423609864272'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/04/my-personal-list-of-must-install-apps.html' title='My personal list of must-install apps'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-5022704838816065465</id><published>2008-04-25T20:14:00.001+01:00</published><updated>2008-04-25T20:14:13.656+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><title type='text'>Dual core ftw!</title><content type='html'>&lt;p&gt;One of my small gripes with games are that you can't usually alt-tab out of them. This results in lossage of time when you happen to press a key which shows the start menu, or a modal dialog pops up (yes, I'm looking at you, windows firewall), or when you just need to pause out of a game to see some other thing.&lt;/p&gt;
&lt;p&gt;Well, I think that's a problem no more!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/26078380@N08/2440812845/"&gt;&lt;img src="http://farm4.static.flickr.com/3289/2440812845_d4a1a8f078_m.jpg" height="240" width="213" alt="task manager.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Given a dual-core processor and the propensity of games to be monothreaded, I can now alt-tab out of a game, do what I must and return to it without having to spend half an hour waiting for it. On the other hand, 2 Gb of ram almost seems too little nowadays...&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-5022704838816065465?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/5022704838816065465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=5022704838816065465' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5022704838816065465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/5022704838816065465'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/04/dual-core-ftw.html' title='Dual core ftw!'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3289/2440812845_d4a1a8f078_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-2836088742035591003</id><published>2008-04-23T17:59:00.001+01:00</published><updated>2008-04-23T18:27:23.804+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='mingle'/><title type='text'>And for my next trick, lets Mingle</title><content type='html'>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Thoughtworks has recently released version 2.0 of their Agile Project Management tool, called &lt;a href="http://studios.thoughtworks.com/mingle-project-intelligence"&gt;Mingle&lt;/a&gt;. Apart from some new features and ways to manage the large amount of information a project usually produces and consumes, version 2.0 brings about one very interesting capability: REST APIs, accessed via http.&lt;/p&gt;  &lt;p&gt;After spending about 1 hour before figuring out that &amp;quot;basic_auth_enabled: true&amp;quot; is different from &amp;quot;basic_auth_enabled:true&amp;quot;, I was ready to at last work on a small app to use as a dashboard for a small &amp;quot;game&amp;quot; we play at weListen. &lt;/p&gt;  &lt;p&gt;And what better way to do it than to try out the new features of .net 3.5 (and beyond), including LINQ and the ASP.Net MVC framework. Since this will be an internal project, it is an excellent chance to try them out.&lt;/p&gt;  &lt;p&gt;For reference, here some example code to fetch the list of users from mingle:&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;     &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; List&amp;lt;User&amp;gt; getUsers() {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    &lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    String url = &lt;span style="color: #006080"&gt;&amp;quot;http://mingle.example.com/users.xml&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    HttpWebRequest req = (HttpWebRequest) WebRequest.CreateDefault(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Uri(url));&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    String username = &lt;span style="color: #006080"&gt;&amp;quot;john.doe&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    String password = &lt;span style="color: #006080"&gt;&amp;quot;secret&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] credentials = Encoding.ASCII.GetBytes(username + &lt;span style="color: #006080"&gt;&amp;quot;:&amp;quot;&lt;/span&gt; + password);&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    String base64Credentials = Convert.ToBase64String(credentials);&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    req.Headers.Add(&lt;span style="color: #006080"&gt;&amp;quot;Authorization&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;                    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #006080"&gt;&amp;quot;Basic {0}&amp;quot;&lt;/span&gt;, base64Credentials));&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;&amp;#160;&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;    StreamReader reader = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; StreamReader(req.GetResponse().GetResponseStream());&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    var users = (from node &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; XDocument.Load(reader).Descendants(&lt;span style="color: #006080"&gt;&amp;quot;user&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;                 select &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; User&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;                            {&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;                                Username = node.Element(&lt;span style="color: #006080"&gt;&amp;quot;login&amp;quot;&lt;/span&gt;).Value,&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;                                Id = &lt;span style="color: #0000ff"&gt;int&lt;/span&gt;.Parse(node.Element(&lt;span style="color: #006080"&gt;&amp;quot;is&amp;quot;&lt;/span&gt;).Value),&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;                                Name = node.Element(&lt;span style="color: #006080"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;).Value,&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;                                Email = node.Element(&lt;span style="color: #006080"&gt;&amp;quot;email&amp;quot;&lt;/span&gt;).Value&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;                            });&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"&gt;    &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;User&amp;gt;(users);&lt;/pre&gt;

    &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Most if it is rather straightforward, taking advantage of linq to iterate through the xml to fetch user's data and transform it to a business object. The User class here is just a container for the information, for now, and I use c#'s new property assignments in the constructor mainly to get a feel for it. Linq to me seems like a variant on python's &lt;a href="http://docs.python.org/tut/node7.html#SECTION007140000000000000000"&gt;list comprehensions&lt;/a&gt;, which is a good thing. It creates terse code, and abstracts away the cycle to focus on what you do with the information. To me this is a huge gain in c#'s expressiveness.&lt;/p&gt;

&lt;p&gt;For me, the trickiest part was in getting the authorization correct. I now wonder if the problem was really with the code or mingle's not being correctly configured. I'll try a couple of other approaches to see if we can move away from having to encode manually the header and use .net's credentials' api.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-2836088742035591003?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/2836088742035591003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=2836088742035591003' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2836088742035591003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/2836088742035591003'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/04/and-for-my-next-trick-let-mingle.html' title='And for my next trick, lets Mingle'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-910838524695262745</id><published>2008-04-22T16:32:00.001+01:00</published><updated>2008-04-22T16:34:27.843+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><title type='text'>Page fault accessing Mocks</title><content type='html'>&lt;p&gt;I realised how little I know about mocking, stubbing et. al. as soon as I read the &lt;a href="http://code.google.com/p/moq/wiki/QuickStart"&gt;quickstart&lt;/a&gt; for &lt;a href="http://code.google.com/p/moq/"&gt;moq&lt;/a&gt;. Main problem was the domain language. &lt;/p&gt;  &lt;blockquote&gt;   &lt;br/&gt;    &lt;p&gt;&lt;strong&gt;Expect&lt;/strong&gt;? What the hell.. Does mock expect anything? Or do we expect something from mock? Perhaps I can gain some more insight by reading about other mocking frameworks...&lt;/p&gt;    &lt;br/&gt; &lt;/blockquote&gt;  &lt;p&gt;Clicking a &lt;a href="http://www.ayende.com/Wiki/(S(gtsaaq45luwfjj55wlskhgjg))/Default.aspx?Page=Rhino+Mocks+Introduction&amp;amp;AspxAutoDetectCookieSupport=1"&gt;link&lt;/a&gt; about Rhino showed me some more code, and a clearer mental image started to form. Still, gained a faint image of the meaning of Expect, but had two more keywords I know nothing about: &lt;strong&gt;ReplayAll&lt;/strong&gt; and &lt;strong&gt;VerifyAll&lt;/strong&gt;. &lt;/p&gt;  &lt;p&gt;Previously I had encountered and used the concept of a mock when dealing with tests and database connections. Not wanting to setup a whole database with test data just to make sure a method's implemented correctly, I ended up writing custom mocks which implement an in-memory version of the database, returning test values when certain methods are called. Useful? yes. Practical? no, not really. Different tests had different needs, and the choice became to either implement different mocks, or to have one mock support a bunch of tests. Not the most efficient way to do it, as it is now a bit clearer.&lt;/p&gt;  &lt;p&gt;So, here's to a cup of juice, feet up and some &lt;a href="http://www.clariusconsulting.net/blogs/kzu/archive/2007/12/21/47152.aspx"&gt;light&lt;/a&gt; &lt;a href="http://martinfowler.com/articles/mocksArentStubs.html"&gt;reading&lt;/a&gt;! I feel it is just the beginning...&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-910838524695262745?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/910838524695262745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=910838524695262745' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/910838524695262745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/910838524695262745'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/04/page-fault-accessing-mocks.html' title='Page fault accessing Mocks'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-8907107709764663705</id><published>2008-04-07T23:59:00.001+01:00</published><updated>2008-04-07T23:59:07.590+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>Linux sysadmin'ing tip of the day (again)</title><content type='html'>&lt;p style="text-align: right;"&gt;&lt;span style="font-style: italic;"&gt;On the theme of clocks, virtual machines and just not getting it.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So you've got a spankin' new server, one with humongous disk space, lots of ram and cpus on the double digits. What's the first thing you think? "We can put all sorts of machines here and there'll still be space for a fool around linux to try new things".&lt;/p&gt;
&lt;p&gt;Yes, the joys of virtualization. In this case, a centos machine running xen. Installation was simple enough, and after the first hour I already had a virtualized linux and was starting the process of installing a windows machine to serve as a development playground for everyone. By the second day I had already forgotten about the xen installation, since its purpose was being fully fulfilled as the host for the many (okay, just 3...) virtual machines installed on top.&lt;/p&gt;
&lt;p&gt;Fast forward some weeks, and here I am trying to figure out why the bloody server is one hour ahead of time. NTPd is running, I can see the messages telling me that "yes, the time was a bit ahead, and we've got it right, now", but still the time was ahead one hour and it was not right. Damned time zones, I think! and so (naively, as it is clear to me now) I set the time zone from Europe/Lisbon to GMT in an attempt to make the system think he's one hour behind. And it works.&lt;/p&gt;
&lt;p&gt;Until today.&lt;/p&gt;
&lt;p&gt;After committing a couple of files to the server, and before packing on home, I check the integration server to see if everything is okay and the build is on its way to a green icon. Nop, still green. Last build time... yesterday. Strange. Picking through the logs I find out the server's not picking the latest change in the source. And that's when I notice that the commit emails fired from subversion come out one hour ahead. &lt;strong&gt;Again&lt;/strong&gt;. Back I go, logging in to the server and trying to think up why the hell the hour kept going back.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Perhaps ntpd isn't working properly? Was it syncing to a bad server? Was the daemon not running correctly?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A peek in the logs shows ntpd trying to sync the time, time and time again without success, with no reason as to why it wasn't working. Okay, let's try and set the date by hand. Good, it works. And now it's back to normal. Hun?! Why... did the date... change &lt;strong&gt;by itself&lt;/strong&gt;? Hardware clock?&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Oh, wait... hardware clock... in a xen environment?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, ladies and gentlemen, the answer right before our silly little noses. The hardware clock, in a xen virtualized environment, is managed by the host (dom0 in xen parlance), and unless specified otherwise in an obscure flag it stays that way, not allowing changes in the client environments.&lt;/p&gt;
&lt;p&gt;And so, all it was needed was for the host machine to have a correct time and all was well in the land. Setting the correct date and time on the host machine sets also the correct date and time on the clients.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-8907107709764663705?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/8907107709764663705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=8907107709764663705' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8907107709764663705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/8907107709764663705'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/04/linux-sysadmin-tip-of-day-again.html' title='Linux sysadmin&amp;#39;ing tip of the day (again)'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-1553861778839413305</id><published>2008-04-06T05:17:00.000+01:00</published><updated>2008-04-06T05:21:33.168+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>Linux sysadmin'ing tip of the day</title><content type='html'>&lt;p&gt;When you install a brand new linux machine, one of the things that is usually setup for you is a daily report of the system, sent by email to the root user.&lt;/p&gt;
&lt;p&gt;If you're like me, you only notice it a month later when you've got a spool file with 30 emails or so.&lt;/p&gt;
&lt;p&gt;And now, for the tip.&lt;/p&gt;
&lt;p&gt;Two steps to solve the spool problem. Firstly, setup a .forward file on root's homedir with your email.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;echo (your email) &amp;gt; ~/.forward&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;will take take of that quite nicely.&lt;/p&gt;
&lt;p&gt;Test the config by sending a mail to root.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;mail -s test root&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;will send the message as soon as you press Ctrl+d.&lt;/p&gt;
&lt;p&gt;With the mail going to the correct address, the next step is to flush all those mail messages back through the mail pipe so you receive them on your email account.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;formail -s /usr/lib/sendmail (your email) &amp;lt; /var/spool/mail/root&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;will do that for you. All that's missing if to clean up the queue, since you've got those messages out of the system.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;cp /dev/null /var/spool/mail/root&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;is what you're looking for.&lt;/p&gt;
&lt;p&gt;And that's all for today!&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-1553861778839413305?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/1553861778839413305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=1553861778839413305' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1553861778839413305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/1553861778839413305'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/04/linux-sysadmin-tip-of-day.html' title='Linux sysadmin&amp;#39;ing tip of the day'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-7515076574718274005</id><published>2008-04-05T20:29:00.000+01:00</published><updated>2008-04-05T19:30:01.472+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>Useful surprise of the day</title><content type='html'>&lt;p&gt;With putty you can actually create new ssh tunnels on an existing connection by changing the settings of the connection and pressing apply. And here I was dropping and recreating the connection like an idiot...&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-7515076574718274005?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/7515076574718274005/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=7515076574718274005' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7515076574718274005'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/7515076574718274005'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/04/useful-surprise-of-day.html' title='Useful surprise of the day'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4289590770839747775.post-4359785636871075329</id><published>2008-01-10T02:21:00.001Z</published><updated>2008-01-10T02:21:30.718Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><title type='text'>Good customer service pays off</title><content type='html'>Monday I went to a sports superstore to get some clothes as additional motivators for getting in shape. Expecting a supermarket of sports goods, I did most of the shopping by myself, getting a set of pants, some sweatshirts and eyeballing a GPS thingamajig which I set as a reward in a couple of months should I be good and reach my goals.

But when I came to the shoes I actually was a bit undecided. Most of the other articles were rather straightforward. I had a clear picture of what I wanted, and the prices matched what I was willing to give. Not so much for a set of running shoes, which went from 20/30€ all the way to 120€.

The adverts or informational flyers talked about how the lower priced items were for small walks or runs, once a week, in good conditions. The more expensive ones talked about competitive running, more frequently and in all conditions.

So, instead of simmering over which ones I should pick, I called up on someone from the shop to ask what really was the difference and why were some marked as for once in a while jogging, and others more frequent running.

To my (somewhat dumb) amazement, the clerk dropped what she was doing, and started explaining. So the whole frequency thing has to do with durability. If I'm going to run once in a while, less expensive shoes will do for a couple of years. But ramp up and those shoes last only months. Also, I should be on the look out for softer sole, which usually is better for jogging since it molds better to the motion of the foot. After fetching 3 or 4 pairs, the advantages of each were discussed, and the main points of how a set of shoes should be picked. I ended up buying the first and foremost recommendation of a brand I didn't knew about, in spite of trying both an adidas and a reebok pair.  

All in all, 10 minutes later and 5 pairs tried out, a client wishing to spend 30€ was convinced to spend 60€. And was happy about it. I got to try the different options, and was given good reasons for a more expensive buy from someone who was willing to spend the time to inform.

The main point? Good, qualified, knowledgeable and, most of all, &lt;strong&gt;willing&lt;/strong&gt; customer facing employees are worth their price, for the shop and for the client.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4289590770839747775-4359785636871075329?l=blog.brunomlopes.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.brunomlopes.com/feeds/4359785636871075329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4289590770839747775&amp;postID=4359785636871075329' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/4359785636871075329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4289590770839747775/posts/default/4359785636871075329'/><link rel='alternate' type='text/html' href='http://blog.brunomlopes.com/2008/01/good-customer-service-pays-off.html' title='Good customer service pays off'/><author><name>Bruno</name><uri>http://www.blogger.com/profile/07207770895217924448</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
