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

<channel>
	<title>vaderpi&#039;s random rants</title>
	<atom:link href="http://vaderpi.com/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://vaderpi.com/blog</link>
	<description>babblings!</description>
	<lastBuildDate>Mon, 24 May 2010 19:04:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Dynamic DNS with Rackspace Apps Control Panel</title>
		<link>http://vaderpi.com/blog/?p=666</link>
		<comments>http://vaderpi.com/blog/?p=666#comments</comments>
		<pubDate>Mon, 24 May 2010 19:04:34 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=666</guid>
		<description><![CDATA[I use Rackspace Apps for email across all of my domains, and I am using them as a domain registrar, too. A few days ago, I wanted to create a subdomain, like example.vaderpi.com, that pointed to my computer at home. I didn&#8217;t want to use one of the free dynamic dns services, and I wanted [...]]]></description>
			<content:encoded><![CDATA[<p>I use <a href="http://www.rackspace.com/apps">Rackspace Apps</a> for email across all of my domains, and I am using them as a domain registrar, too. A few days ago, I wanted to create a subdomain, like example.vaderpi.com, that pointed to my computer at home. I didn&#8217;t want to use one of the free dynamic dns services, and I wanted to be able to create the subdomain for a domain that I already own.</p>
<p>Through the Rackspace Apps <a href="http://cp.rackspace.com">control panel</a>, I can change all of the DNS entries for any of the domains that they are hosting. To create a subdomain, all I have to do is create an A record entry for &#8220;example&#8221; that points to my home ip address. I used <a href="http://whatismyip.org">whatismyip.org</a> to look that up. Clicked &#8220;save&#8221;, and then the address started resolving right away. Perfect. Well, at least until my ISP hands out a different ip address. </p>
<p>What I needed was a programmatic way to detect that my ip address has changed and then update the A record entry for example.vaderpi.com with the new ip address.</p>
<p>I dug through the Rackspace Apps <a href="http://api-wiki.apps.rackspace.com/api-wiki/index.php/Main_Page">API documentation</a> looking for a published way to do this, but I was unable to find one. Then I realized that I could just treat the control panel website as an API by driving it with a headless browser, like HtmlUnit.</p>
<p>There are several ruby gems that provide the ability to drive headless browsers. I took a quick look at <a href="http://celerity.rubyforge.org/">celerity</a>, <a href="http://mechanize.rubyforge.org/mechanize/">mechanize</a>, <a href="http://github.com/svenfuchs/steam">steam</a>, and <a href="http://code.google.com/p/selenium/wiki/GettingStarted">webdriver</a>. I settled in on using steam, because it seemed like it had the fewest number of layers between it and the headless browser. I had also never used it before, and wanted to get a feel for how well it worked.</p>
<p>I have posted the <a href="http://gist.github.com/412271">resulting script</a> as a github gist. Take a peek. Comments and forks are welcome. Note that all domain names, user names, and passwords have been replaced with made up examples.</p>
<p>As for running the script, I set up an @hourly entry in cron. The script only contacts the Rackspace Apps control panel if it detects an ip address change, so the risk of accidentally hammering their web server with this, should be low. (I point that out in case any of my old coworkers stumbles across this. :))</p>
<p>The current implementation can only update an existing DNS entry, but it should not be too hard to extend it to support creating additional DNS entries. Anyone that goes to implement this should make sure to correctly handle clicking the add link if there are not any empty rows in the entry table.</p>
<p>In addition to supporting creating new entries, there are a few improvements that I would like to make to this script. (1) I&#8217;d like to have an external service resolve the domain. This is going to become critical, because I want my the domain to resolve to the private ip address for devices that are on the private network. (2) I&#8217;d like to not rely on whatismyip.org. </p>
<p>I&#8217;m thinking of writing a small web service that I can install on my server that will address both of these. The service will be able to do DNS resolution, and it will be able to detect the public ip address of the caller.</p>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=666</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Marvel Civil War: Reading Order</title>
		<link>http://vaderpi.com/blog/?p=662</link>
		<comments>http://vaderpi.com/blog/?p=662#comments</comments>
		<pubDate>Thu, 08 Apr 2010 14:45:27 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=662</guid>
		<description><![CDATA[The recent release of the comic book applications for the iPhone and iPad have sparked my interest in the comic book world. I am fascinated by the concept of the Civil War story arc, but I want to make sure that I read the content in the best order. After doing some research, this is [...]]]></description>
			<content:encoded><![CDATA[<p>The recent release of the comic book applications for the iPhone and iPad have sparked my interest in the comic book world. I am fascinated by the concept of the Civil War story arc, but I want to make sure that I read the content in the best order. After doing some research, this is what I came up with. I have based this list on the <a href="http://marvel.com/news/comicstories.766.civil_war_cover_gallery">Marvel Civil War Cover gallery</a>, which seemed to be the most complete listing.</p>
<p>This is a ton of content. Wish me luck!</p>
<ol>
<li>Amazing Spider-Man #529</li>
<li>Amazing Spider-Man: Decisions</li>
<li>Road to Civil War TPB</li>
<li>Amazing Spider-Man #530</li>
<li>Amazing Spider-Man #531</li>
<li>Fantastic Four #536</li>
<li>New Avengers: Illuminati Special</li>
<li>Civil War: Opening Shot Sketchbook</li>
<li>Fantastic Four #537</li>
<li>Civil War #1</li>
<li>She-Hulk #8</li>
<li>Wolverine #42</li>
<li>Amazing Spider-Man #532</li>
<li>Civil War: Front Line #1</li>
<li>Thunderbolts #103</li>
<li>Thunderbolts: Swimming With Sharks</li>
<li>Civil War #2</li>
<li>Civil War: Front Line #2</li>
<li>Amazing Spider-Man #533</li>
<li>New Avengers #21</li>
<li>Fantastic Four #538</li>
<li>Wolverine #43</li>
<li>X-Factor #8</li>
<li>Civil War: Front Line #3</li>
<li>Thunderbolts #104</li>
<li>Civil War #3</li>
<li>Civil War: X-Men #1</li>
<li>Cable &#038; Deadpool #30</li>
<li>X-Factor #9</li>
<li>New Avengers #22</li>
<li>Black Panther #18</li>
<li>Wolverine #44</li>
<li>Civil War: Young Avengers &#038; Runaways #1</li>
<li>Daily Bugle Special Edition: Civil War</li>
<li>Civil War: Front Line #4</li>
<li>Amazing Spider-Man #534</li>
<li>Fantastic Four #539</li>
<li>Ms. Marvel #6</li>
<li>Civil War: Front Line #5</li>
<li>Thunderbolts #105</li>
<li>Civil War: X-Men #2</li>
<li>Heroes For Hire #1</li>
<li>Wolverine #45</li>
<li>New Avengers #23</li>
<li>Cable &#038; Deadpool #31</li>
<li>Civil War: Young Avengers &#038; Runaways #2</li>
<li>Civil War Files</li>
<li>Ms. Marvel #7</li>
<li>Civil War #4</li>
<li>Wolverine #46</li>
<li>Civil War: X-Men #3</li>
<li>Amazing Spider-Man #535</li>
<li>Civil War: Front Line #6</li>
<li>Heroes For Hire #2</li>
<li>Cable &#038; Deadpool #32</li>
<li>Captain America #22</li>
<li>Fantastic Four #540</li>
<li>Civil War: Front Line #7</li>
<li>Ms. Marvel #8</li>
<li>Civil War: X-Men #4</li>
<li>Wolverine #47</li>
<li>New Avengers #24</li>
<li>Civil War: Choosing Sides</li>
<li>Captain America #23</li>
<li>Heroes For Hire #3</li>
<li>Black Panther #21</li>
<li>Black Panther: War Crimes TPB</li>
<li>Civil War: Young Avengers &#038; Runaways #4</li>
<li>Civil War #5</li>
<li>New Avengers #25</li>
<li>Iron Man #13</li>
<li>Amazing Spider-Man #536</li>
<li>Civil War: Front Line #8</li>
<li>Wolverine #48</li>
<li>Punisher War Journal #1</li>
<li>Black Panther #22</li>
<li>Captain America #24</li>
<li>Civil War: War Crimes #1</li>
<li>Civil War: Casualties of War TPB</li>
<li>Iron Man #14</li>
<li>Iron Man/Captain America: Casualties of War</li>
<li>Fantastic Four #541</li>
<li>Civil War: Front Line #9</li>
<li>Winter Soldier: Winter Kills</li>
<li>Black Panther #23</li>
<li>Civil War #6</li>
<li>Civil War: Front Line #10</li>
<li>Amazing Spider-Man #537</li>
<li>Punisher War Journal #2</li>
<li>Thunderbolts #110</li>
<li>Blade #5</li>
<li>Fantastic Four #542</li>
<li>Punisher War Journal #3</li>
<li>Civil War: The Return #1</li>
<li>Moon Knight #7</li>
<li>Moon Knight #8</li>
<li>Ghost Rider #8</li>
<li>Ghost Rider #9</li>
<li>Ghost Rider #10</li>
<li>Ghost Rider #11</li>
<li>Black Panther #24</li>
<li>Civil War #7</li>
<li>Amazing Spider-Man #538</li>
<li>Civil War Poster Book</li>
<li>Black Panther #25</li>
<li>Civil War: Front Line #11</li>
<li>Captain America #25</li>
<li>Civil War: The Confession</li>
<li>Civil War: The Initiative</li>
<li>Fallen Son: Death of Captain America TPB</li>
<li>Fantastic Four #543</li>
<li>Civil War: Battle Damage Report</li>
<li>Marvel Spotlight: Civil War Aftermath</li>
<li>Marvel Spotlight: Captain America Remembered</li>
<li>Civil War Chronicles #1</li>
<li>Civil War Chronicles #2</li>
<li>Civil War Chronicles #3</li>
<li>Civil War Chronicles #4</li>
<li>Civil War Chronicles #5</li>
<li>Civil War Chronicles #6</li>
<li>Civil War Chronicles #7</li>
<li>Civil War Chronicles #8</li>
<li>Civil War Chronicles #9</li>
<li>Civil War Chronicles #10</li>
<li>Civil War Chronicles #11</li>
<li>Civil War Chronicles #12</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=662</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Open Source Fear, Uncertainty and Doubt (FUD)</title>
		<link>http://vaderpi.com/blog/?p=657</link>
		<comments>http://vaderpi.com/blog/?p=657#comments</comments>
		<pubDate>Thu, 08 Apr 2010 03:27:28 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=657</guid>
		<description><![CDATA[I just read an article that Andrea sent me a while back titled, Lobby Group Says Open-Source Threatens Capitalism. I must say that I am a little shocked. But before I rant about why, take a few minutes and read the article. I&#8217;ll be here when you get back.
Read it? Good. Time for my rant.
In [...]]]></description>
			<content:encoded><![CDATA[<p>I just read an article that Andrea sent me a while back titled, <a href="http://mashable.com/2010/02/24/open-source-threatens-capitalism/">Lobby Group Says Open-Source Threatens Capitalism</a>. I must say that I am a little shocked. But before I rant about why, take a few minutes and read the article. I&#8217;ll be here when you get back.</p>
<p>Read it? Good. Time for my rant.</p>
<p>In case you have a horrible memory (or more likely you did not actually read it, yet. Tisk. Tisk), the short version of the article is that an intellectual property group has requested that certain countries be added to the &#8220;Special 301 watchlist&#8221; because they advocate using open source technologies for government work. </p>
<p>The first issue that came to mind when I read that is that the organization forgot to request the same status for <a href="http://www.oregon.gov/ODOT/CS/ISB/docs/OregonStateGovernmentOpenSourceInventoryInitiativeandCharter.doc">Oregon</a> and <a href="http://www.boston.com/business/technology/articles/2005/02/14/government_agencies_adopt_open_source/">Massachusetts</a>. You know, states that are part of the United States. I am sure that I could have dug up more states if I had spent more time with my pal Google. But 2 minutes seemed like enough to prove my point.</p>
<p>To address the second issue, I need to draw your attention to the organization&#8217;s own words. Let me paraphrase.</p>
<blockquote><p>
&#8220;The Indonesian government’s policy … weakens the software industry &#8230; [because] it fails to build respect for intellectual property rights.&#8221;
</p></blockquote>
<p>Ugh! Without strong intellectual property rights open source would not be possible. <em>Every</em> <a href="http://www.opensource.org/licenses">open source license</a> builds on the foundation of the author&#8217;s copyright as established by law. These licenses provide authors with a legal framework for granting permissions to others.</p>
<p>&lt;sigh/&gt;</p>
<p>I really hope this request was not taken seriously. I had thought that fear, uncertainty and doubt (FUD) attacks against open source were behind us. This was a tactic that Microsoft employed very heavily during its spats with Netscape and the U.S. Department of Justice. </p>
<p>But in recent years, Microsoft has started developing new products out in the open that are under open source licenses. This is in addition to the large amount of code that was initially developed internally and then later released to the public as open source. New projects include, <a href="http://www.ironruby.net">IronRuby</a> and the <a href="http://www.asp.net/mVC/">ASP.NET MVC library</a>. While the other class includes <a href="http://wix.sourceforge.net/">WiX</a>[1] and a host more.</p>
<p>[1]: Shameless plug: I have contributed code to the WiX project, and <a href="http://sourceforge.net/tracker/index.php?func=detail&#038;aid=1675194&#038;group_id=105970&#038;atid=642714">it was accepted</a>!</p>
<p>The final issue that I will point out is the incredibly large number of companies that this group represents. It does not represent any company directly, but instead does so through other industry groups. But the list is quite large. And amusingly enough, if you dig deep enough into the member lists, you will find some big corporate open source supporters such as Adobe, Apple, IBM and Microsoft. </p>
<ul>
<li>Association of American Publishers (AAP) &#8211; [274 <a href="http://www.publishers.org/main/Membership/member_02.htm">members</a>]</li>
<li>Business Software Alliance (BSA) &#8211; [35 <a href="http://www.bsa.org/country/BSA%20and%20Members/Our%20Members.aspx">members</a>]</li>
<li>Entertainment Software Association (ESA) &#8211; [29 <a href="http://www.theesa.com/about/members.asp">members</a>]</li>
<li>Independent Film &#038; Television Alliance (IFTA) &#8211; [143 <a href="http://www.afma.com/AboutIFTA/member_directory.aspx?index=%">members</a>]</li>
<li>Motion Picture Association of America (MPAA) &#8211; [6 <a href="http://www.mpaa.org/AboutUsMembers.asp">members</a>]</li>
<li>National Music Publishers’ Association (NMPA) &#8211; [more than 800 <a href="http://www.nmpa.org/aboutnmpa/index.asp">members</a>]</li>
<li>Recording Industry Association of America (RIAA) &#8211; [3905 <a href="http://www.riaa.com/aboutus.php?content_selector=aboutus_members">members</a>]</li>
</ul>
<p>(Note: the member counts may be a little misleading, because many companies are represented by several of these groups. Don&#8217;t just add them all up if you are trying to get a total count.)</p>
<p>This is normally where I would stick a nice little conclusion paragraph to tie my whole post together, but I am too tired. So, I&#8217;ll leave that part as an exercise for the reader. What conclusions do you draw from this information? Leave them in the comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=657</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>What iPad keyboard layout does Wayne Westerman use?</title>
		<link>http://vaderpi.com/blog/?p=652</link>
		<comments>http://vaderpi.com/blog/?p=652#comments</comments>
		<pubDate>Sun, 28 Mar 2010 07:31:31 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=652</guid>
		<description><![CDATA[I have been rather disappointed by the iPad&#8217;s apparent exclusion of the Dvorak keyboard layout. But the other day, I had a funny idea. That the creator of the iPod Touch/iPhone/iPad touch technology, Wayne Westerman, originally developed the technology for use in keyboards. These keyboards were sold by his company Fingerworks. The acknowledgements section in [...]]]></description>
			<content:encoded><![CDATA[<p>I have been rather disappointed by the <a href="http://www.fingerworks.com/layouts.html">iPad&#8217;s apparent exclusion of the Dvorak keyboard layout</a>. But the other day, I had a funny idea. That the creator of the iPod Touch/iPhone/iPad touch technology, <a href="http://www.ece.udel.edu/~westerma/About_Wayne.html">Wayne Westerman</a>, originally developed the technology for use in keyboards. These keyboards were sold by his company Fingerworks. The acknowledgements section in <a href="http://www.ece.udel.edu/~westerma/main.pdf">Westerman&#8217;s dissertation</a> provides evidence that his own repetitive stress injury (RSI) was the driving force behind developing a keyboard that did not require a finger to exert any pressure to press a key. He specifically thanks several individuals that he had type for him, until he was able to &#8220;[perfect] less fatiguing forms of data entry&#8221;.</p>
<p>Furthermore, the shinning star of the Fingerworks product line up, the TouchStream, supported a multitude of <a href="http://www.fingerworks.com/layouts.html">firmware enabled keyboard layouts</a>. One of these was an experimental layout, named Qwerak. It was meant to be a modified version of the Dvorak keyboard layout with the hope of addressing two problems. (1) The difficulty involved when typing on a surface with no tactile feedback, and (2) the steep learning curve that is involved in switching from Qwerty to Dvorak. (&lt;bragging&gt;Although, it can&#8217;t be too steep, because Andrea was able to pick it up in about three weeks. But then again, she is super smart.&lt;/bragging&gt;)</p>
<p>Did Wayne Westerman use the Qwerak keyboard layout himself? I don&#8217;t know; I can only speculate. But as an RSI sufferer myself that types in a non-Qwerty layout, I can confidently state two facts. (1) Without a doubt, typing with Dvorak induces less pain than typing in Qwerty, and (2) when forced to use the Qwerty keyboard layout to interact with a computer, my productivity becomes dramatically reduced. I estimate that my productivity when using Qwerty is about 1/5th of my productivity when I am using Dvorak. From these two pieces of data, I speculate that if Mr. Westerman found Qwerak to be even easier to type with than either Qwerty or Dvorak, then it would be very painful, frustrating, and counter-productive for him to type with any other keyboard layout.</p>
<p>So this brings me back to my original question: When Wayne Westerman turns on an iPad, launches the email app, touches the compose email icon and then touches inside the address bar, what keyboard layout is displayed on the screen? I&#8217;d be willing to bet a whole bunch of donuts that it is not Qwerty. </p>
<p>Hopefully, one day, when I repeat those same steps, I&#8217;ll be presented with a Dvorak keyboard layout. Until I can assure that I will, I won&#8217;t be spending a dime on an iPad. Who knows, maybe Google will come out with a Android based tablet device. If so, I&#8217;ll flock to that. On that platform, developers have the ability to write custom input methods, <em>including</em> alternative keyboard layouts. </p>
<p>What do you think Google will call such a device, if they develop one? I like Nexus Prime. But, since I name my computers after Transformers, I might not be the best person to ask about potential names.</p>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=652</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FF7: Voices of the Lifestream</title>
		<link>http://vaderpi.com/blog/?p=649</link>
		<comments>http://vaderpi.com/blog/?p=649#comments</comments>
		<pubDate>Tue, 09 Feb 2010 14:22:49 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=649</guid>
		<description><![CDATA[&#60;sigh/&#62; For once, I have more to say than will fit in a tweet.
Over the weekend I pulled down OC ReMix&#8217;s Final Fantasy VII: Voices of the Lifestream project. I listened to the entire four-disc set while coding at work yesterday, and about half of it while coding in bed last night. 
I really enjoyed [...]]]></description>
			<content:encoded><![CDATA[<p>&lt;sigh/&gt; For once, I have more to say than will fit in a tweet.</p>
<p>Over the weekend I pulled down OC ReMix&#8217;s <a href="http://ff7.ocremix.org/">Final Fantasy VII: Voices of the Lifestream</a> project. I listened to the entire four-disc set while coding at work yesterday, and about half of it while coding in bed last night. </p>
<p>I really enjoyed listening to the album while coding, but I did not enjoy listening to the songs as my primary focus. I have two theories about this. One, these songs were composed to be background music, so if you pay too much attention to them, you break the spell. Or two, listening to these songs directly conjures up images of tireless sitting in front of a small television with a Playstation controller in my sweaty hands, desperately trying to figure out where to go next, and hoping that I don&#8217;t run into a battle along the way, because the battle music always makes me jump out of my skin when it starts at 3 AM, and I am pretty low on life anyway, though I really could use the XP, all the while wondering why Aeris had to die.</p>
<p>Yeah, my money is on the second theory.</p>
<p>[Disclaimer: I worked on this game on and off from mid 1998 through about 2005. I never finished, stuck in the ice mountains on the third disc. Oh, and the movie was beautiful, but it sucked, so I protested by buying it anyway.]</p>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=649</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rake recipes for working with Visual Studio projects</title>
		<link>http://vaderpi.com/blog/?p=633</link>
		<comments>http://vaderpi.com/blog/?p=633#comments</comments>
		<pubDate>Wed, 03 Feb 2010 17:09:26 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=633</guid>
		<description><![CDATA[Despite spending my day job coding in Microsoft-land, I find myself using ruby tools more and more during my daily development. I recently wrote some rake tasks that I think are worth sharing and explaining. Specifically, I wrote a tasks to control building with msbuild (seems redundant, I know) and some tasks for starting and [...]]]></description>
			<content:encoded><![CDATA[<p>Despite spending my day job coding in Microsoft-land, I find myself using ruby tools more and more during my daily development. I recently wrote some rake tasks that I think are worth sharing and explaining. Specifically, I wrote a tasks to control building with msbuild (seems redundant, I know) and some tasks for starting and stopping Cassini (or webdev.webserver as it is now named). </p>
<p><b>Monkey patch <code>Pathname</code> for Windows paths</b></p>
<p>Since I am using the <code>Pathname</code> class to build paths in my examples, I need to give you the monkey patch that I use to make <code>Pathname</code> correctly display win32 paths.</p>
<pre><code>
require 'pathname'
class Pathname
  alias_method :original_to_s, :to_s
  def to_s
    original_to_s.gsub('/', '\\')
  end
end
</code></pre>
<p><b>Visual Studio command line environment</b></p>
<p>Running command line Visual Studio tools requires having certain environment variables loaded. This can be done by running <code>vsvars32.bat</code> directly or by launching a Visual Studio command prompt from the start menu. This is something that I always forget to do; my terminal windows spring into life by typing <code>cmd</code> in the run box. So, I wanted to write a task to ensure that the environment was properly set up.</p>
<p>I am working with Visual Studio 2005. If you want to use the Visual Studio 2008 tools then you will need to adjust the <code>vsvars32_bat</code> variable accordingly.</p>
<pre><code>
vsvars32_bat = Pathname.new(
  "c:\\program files\\") +
  "microsoft visual studio 8" +
  "common7\\tools\\vsvars32.bat"
task :vsvars do
  if ENV["VSINSTALLDIR"].nil?
    `\"#{vsvars32_bat}\" &#038;&#038; set`.each do |line|
      if line =~ /(\w+)=(.+)/
        ENV[$1] = $2
      end
    end
  end
  raise "Eek!" if ENV["VSINSTALLDIR"].nil?
end
</code></pre>
<p>This code is the product of about 30 minutes of googling. I eventually found this trick in the <a href="http://github.com/whymirror/shoes/blob/master/Rakefile">shoes rakefile</a>[<a href="#footnote-1">1</a>].</p>
<p>Now any task that needs to call a Visual Studio command line tool just needs to declare <code>vsvars</code> as a prerequisite, like so.</p>
<pre><code>
task :csc => [:vsvars] do
  sh "csc test.cs"
end
</code></pre>
<p><b>Building with msbuild</b></p>
<p>This is actually pretty easy once we have the environment set up correctly. Just create a task that calls msbuild from a <code>sh</code> call.</p>
<pre><code>
namespace :build do
  desc "Build the core project"
  task :core => [:vsvars] do
    sh "msbuild #{core_solution_path}"
  end
end
</code></pre>
<p><b>Controlling Cassini (or webdev.webserver)</b></p>
<p>There is a lot going on here, so let me first overwhelm you with the code, and then explain what it is doing.</p>
<pre><code>
def wait_until_site_loaded
  puts "Please be patient. Waiting for site to respond...."
  site_loaded = false
  until site_loaded
    site_loaded = system
      "curl -L -I -f http://localhost:2088/Default.aspx > NUL 2>&#038;1"
  end
  puts "done."
end

namespace :web do
  desc "Start the local web server"
  task :start => [:vsvars] do
    Thread.new do
      sh "webdev.webserver /path:#{web_root_path} /port:2088 /vpath:/"
    end    

    wait_until_site_loaded
  end

  desc "Stop the local web server"
  task :stop => [:vsvars] do
    `taskkill /im webdev.webserver.exe > NUL 2>&#038;1`
  end

  desc "Restart the local web server"
  task :restart => [:vsvars, :stop, :start]
end
</code></pre>
<p>Before the code block above will work, you will need to create a <code>web_root_path</code> variable that points to the <i>absolute path</i> of your website. Relative paths will not work.</p>
<p>The <code>web:start</code> task will start the web server. If an instance is already running at the specified port, then you will get an error message in the form of a dialog box. I wish I knew how to make it fail silently. (I also wish I knew how to prevent it from displaying an annoying balloon notification.)</p>
<p>After starting the web server, the <code>web:start</code> calls out to <code>curl</code> to make sure that the site is responding to get requests. I have <code>curl</code> installed as a result of installing <a href="http://www.cygwin.com/">cygwin</a>. There are <a href="http://curl.haxx.se/latest.cgi?curl=win32-nossl">other ways</a> to get <code>curl</code>, but you will need to make sure your path points at it&#8217;s location to use the code above without modifications. <code>curl</code> is pretty chatty, so I have silenced it by routing its standard out and standard error streams to <code>NUL</code>.</p>
<p>The <code>web:stop</code> task will kill all instances of Cassini. This might be annoying if you have more than one instance running. If that is the case, then you will need to write in some form of accounting for the process id of the web server process, or develop a way to figure out which process id owns the port you want. Once you know the specific pid, you can call <code>taskkill /pid</code> and pass it the pid of the process you want to kill.</p>
<p>The <code>web:restart</code> task will call <code>web:stop</code> task followed by <code>web:start</code> task.</p>
<p><b>One more thing: display available tasks from default task</b></p>
<p>Note: This recipe does not apply to just Windows development. It will work on any platform.</p>
<p>You can get a list of documented tasks by calling <code>rake -T</code>, but I always forget to do that. I usually just call <code>rake</code> when I want to know what it does. So I created a <code>:default</code> task that displays the same task list that you get when you call <code>rake -T</code>.</p>
<pre><code>
task :default do |task|
  puts "You must specifiy a task. Available tasks are listed below:"
  task.application.options.show_task_pattern = /.*/
  task.application.display_tasks_and_comments
end
</code></pre>
<p>That&#8217;s it. I hope you found these recipes helpful. Happy coding!</p>
<p>[<a name="footnote-1">1</a>]: This is way off topic, so I stuck it in a footnote. I know it&#8217;s old news, but the way _why, the creator of shoes, <a href="http://www.rubyinside.com/why-the-lucky-stiff-is-missing-2278.html">committed Internet suicide</a> really irritates me. There were a lot of people benefiting from his contributions to the ruby world, and then one day he just decides to take his toys and go home. He could have bowed out graciously, explaining that he had moved on, but he instead chose identity death. His works remain in <a href="http://viewsourcecode.org/why/">archived form</a>, but I am not sure if there is still any energy behind them.</p>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=633</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Watir Wait</title>
		<link>http://vaderpi.com/blog/?p=630</link>
		<comments>http://vaderpi.com/blog/?p=630#comments</comments>
		<pubDate>Thu, 07 Jan 2010 14:09:27 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=630</guid>
		<description><![CDATA[I have been working with watir over the last couple of days. I quickly became frustrated with numerous errors claiming that the element I wanted to perform an operation on did not exist. I found the Watir::Waiter class and started using it extensively. So extensively, that I decided to write a little monkey patch to [...]]]></description>
			<content:encoded><![CDATA[<p>I have been working with <a href="http://watir.com/">watir</a> over the last couple of days. I quickly became frustrated with numerous errors claiming that the element I wanted to perform an operation on did not exist. I found the Watir::Waiter class and started using it extensively. So extensively, that I decided to write a little monkey patch to make my life easier.</p>
<p>The application that I am working with performs a lot of client-side DOM manipulation. This can create instances where my script was asking Watir to perform operations on DOM objects that didn&#8217;t exist. To defend against that, every time that I called click or set or select on various DOM objects, I wrote two additional statements. One to make sure the browser had finished whatever it was working on, and one to make sure that the element I was about to interact with actually existed. </p>
<p>The code looked something like this.</p>
<pre><code>
  @browser = Watir::Browser.new
  @browser.goto("http://localhost")

  @browser.wait
  Watir::Waiter.wait_until { @browser.text_field(:name, /UserName/).exists? }
  @browser.text_field(:name, /UserName/).set("Admin")

  @browser.wait
  Watir::Waiter.wait_until { @browser.text_field(:name, /Password/).exists? }
  @browser.text_field(:name, /Password/).set("Password")

  @browser.wait
  Watir::Waiter.wait_until { @browser.button(:name, /Submit/).exists? }
  @browser.button(:name, /Submit/).click
</code></pre>
<p>While that works, I got really sick of having to re-type the selector for the DOM element that I wanted to muck with. What I wanted to do was write code that looked something like this.</p>
<pre><code>
  @browser = Watir::Browser.new
  @browser.goto("http://localhost")

  @browser.text_field(:name, /UserName/).wait_to_set("Admin")
  @browser.text_field(:name, /Password/).wait_to_set("Password")
  @browser.button(:name, /Submit/).wait_to_click
</code></pre>
<p>Wow. That is much more concise and easier to understand. Even a non-programmer can understand what is happening now.</p>
<p>To make this code actually work, I decided to write a quick monkey patch that adds a &#8220;wait_to_&#8221; alternative for every method that can be called on input elements and links. These methods call <code>@browser.wait</code>, ask <code>Watir::Waiter</code> to wait for the element to exist, and then call the requested method. </p>
<p>I called my monkey patch <a href="http://gist.github.com/271231">Watir Wait</a>. (Get it? I crack myself up! :)) Take a peek and let me know what you think. If I get enough positive feedback, I&#8217;ll rework this into a proper patch and submit it to the Watir team for inclusion.</p>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=630</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Blood sucking time vampire</title>
		<link>http://vaderpi.com/blog/?p=628</link>
		<comments>http://vaderpi.com/blog/?p=628#comments</comments>
		<pubDate>Tue, 29 Dec 2009 22:35:55 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=628</guid>
		<description><![CDATA[A blood sucking time vampire. That is what XKCD is. I clicked on a link from twitter, and 25 minutes later, I had all of the following pages open in tabs, because I wanted to share them. Too long for a tweet.

Christmas Plans
Abstraction
Natural Parenting
The Sun
Experiment
Sagan-Man &#8211; Andrea has to take a look at this one!
Sympathy
Lego
Movie [...]]]></description>
			<content:encoded><![CDATA[<p>A blood sucking time vampire. That is what XKCD is. I clicked on a link from twitter, and 25 minutes later, I had all of the following pages open in tabs, because I wanted to share them. Too long for a tweet.</p>
<ul>
<li><a href="http://xkcd.com/679/">Christmas Plans</a></li>
<li><a href="http://xkcd.com/676/">Abstraction</a></li>
<li><a href="http://xkcd.com/674/">Natural Parenting</a></li>
<li><a href="http://xkcd.com/673/">The Sun</a></li>
<li><a href="http://xkcd.com/669/">Experiment</a></li>
<li><a href="http://xkcd.com/663/">Sagan-Man</a> &#8211; Andrea has to take a look at this one!</li>
<li><a href="http://xkcd.com/660/">Sympathy</a></li>
<li><a href="http://xkcd.com/659/">Lego</a></li>
<li><a href="http://xkcd.com/657/">Movie Narrative Charts</a> &#8211; my favorite is the one for Primer.</li>
<li><a href="http://xkcd.com/651/">Bag Check</a></li>
<li><a href="http://xkcd.com/627/">Tech Support Cheat Sheet</a></li>
<li><a href="http://xkcd.com/616/">Lease</a></li>
<li><a href="http://xkcd.com/612/">Estimation</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=628</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More letters: this time, Sprint about device pricing and cancellation fees</title>
		<link>http://vaderpi.com/blog/?p=624</link>
		<comments>http://vaderpi.com/blog/?p=624#comments</comments>
		<pubDate>Tue, 22 Dec 2009 17:03:43 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=624</guid>
		<description><![CDATA[It appears that I am on a letter writing binge. Here is the email that I just fired off to Sprint&#8217;s CEO, Dan Hesse, and President of Strategy and Corporate Initiatives, Keith Cowan.

Mr. Dan Hesse and Mr. Keith Cowan:
I am writing to talk about the differences I have noticed between the way Sprint treats new [...]]]></description>
			<content:encoded><![CDATA[<p>It appears that I am on a letter writing binge. Here is the email that I just fired off to Sprint&#8217;s CEO, Dan Hesse, and President of Strategy and Corporate Initiatives, Keith Cowan.</p>
<blockquote><p>
Mr. Dan Hesse and Mr. Keith Cowan:</p>
<p>I am writing to talk about the differences I have noticed between the way Sprint treats new customers versus the way it treats existing customers. My observations are specifically based on pricing, but I feel that the pricing sends a message. I am not sure it is the message that Sprint intends to send. Furthermore, I would like to propose a new method for calculating device discounts and cancellation fees.</p>
<p>Starting service in 03/2009, I am a data only user of the Sprint network. I use a PC card with my laptop when I am out of the house. I am very happy with the coverage area the speeds that I have been getting with the device on Sprint&#8217;s network. I have recommended Sprint&#8217;s data services to friends and family, and I plan on continuing to do so.</p>
<p>I recently learned about the Novatel MiFi 2200, and I wanted to know the cost for purchasing one. I am greatly discouraged by what I have learned.</p>
<p>Sprint is selling the Novatel MiFi 2200 to new customers for $50, after instant savings and a mail-in rebate. Since this offer is only available to new customers, I was expecting to have to pay a little more for the device. My price for the device is $299. No instant savings and no mail-in rebates. Full price. Repeat, no discounts.</p>
<p>After talking to a representative in a Sprint store, I learned that after one year of service, I am eligible for a $75 discount on a new device. After two years of service, I am eligible for a $150 discount on a new device. The Sprint store employee was only able to estimate my cancellation fee at about $150.</p>
<p>Reading through the website and a quick customer service chat, transcript attached, confirmed this information. The chat customer service representative was able to provide me the exact cancellation fee: $140.</p>
<p>Here are the options that I put together after collecting this information.<br />
* remain a current customer and purchase the new device: $299.<br />
* terminate service and sign a new contract: $190.<br />
* wait until I have been a customer for one year: $224.<br />
* wait until I have been a customer for two years: $150.</p>
<p>It appears that preferential pricing is being given to new customers, while existing customers are forced to pay higher prices. In my case, the cancellation fee when combined with the cheap introductory price is encouraging me to cancel my service. I find it startling that Sprint would ever introduce pricing schemes that make it appealing for me to discontinue service. Because if I am willing to sign a new contract with Sprint then why not a competitor?</p>
<p>I feel that the discount pricing and cancellation pricing has become too confusing. Steps need to be taken to simplify the pricing model. While doing so, care should be taken to ensure that discounts are applied fairly to both new and existing customers. Care should also be taken to ensure that cancel fees do not appear to be alarmingly high.</p>
<p>Arguments that have been given to the FCC and Congress by cell phone providers in response to inquiries about high cancellation fees have lead me to assume that the cancellation fee exists to defray the cost of discounting devices for customers. </p>
<p>If this is truly the case, then should be clear in Sprint&#8217;s initial pricing. And it should apply to upgrade pricing as well. To make this clear, I have a three part proposal. </p>
<p>First, make device prices the same for everyone but fluctuate the discount based on how long the person has been a customer. New customers pay full price minus $240, called the &#8220;new customer discount&#8221;. (Example: $299 &#8211; $240 = $59) Existing customers pay full price minus $10 multiplied by the number of months the customer has had service, called the &#8220;existing customer discount&#8221;. (Example: $299 &#8211; (10 * 9) = $209) In this scheme, an existing customer gets the same discount as a new customer every two years.</p>
<p>Second, the cancellation fee should be $240 minus $10 multiplied by the number of months since the customer has purchased a device with a discount. (Example: $240 &#8211; (10 * 9) = 209). </p>
<p>Third, make the device discount and the cancellation fee very visible. They should be visible when you log in to the website, and they should appear on the paper bill. Being more transparent about the fees and discounts is going to significantly cut down on the confusion and frustration surrounding them.</p>
<p>In this scheme, customers are incentivised to stay with Sprint in two ways, accumulating discounts towards new devices and avoiding a high cancellation fee.</p>
<p>I thank you for taking the time to read my suggestions. I wish you and your family a happy holiday season.</p>
<p>Sincerely,<br />
M. Scott Ford
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=624</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>I wrote my respresentatives today. About food.</title>
		<link>http://vaderpi.com/blog/?p=621</link>
		<comments>http://vaderpi.com/blog/?p=621#comments</comments>
		<pubDate>Mon, 21 Dec 2009 15:08:41 +0000</pubDate>
		<dc:creator>vaderpi</dc:creator>
				<category><![CDATA[]]></category>

		<guid isPermaLink="false">http://vaderpi.com/blog/?p=621</guid>
		<description><![CDATA[I sent the following letter to my congressman, Eric Cantor and to my senators, Mark R. Warner and Jim Webb.

&#60;Mr. Representative&#62;:
This letter was prompted by my viewing the recent documentary, Food, Inc. by filmmaker Robert Kenner.
I encourage you to reintroduce, or support the reintroduction, of &#8220;Kevin&#8217;s Law,&#8221; introduced in the 109th Congress as H.R.3160. I [...]]]></description>
			<content:encoded><![CDATA[<p>I sent the following letter to my congressman, Eric Cantor and to my senators, Mark R. Warner and Jim Webb.</p>
<blockquote><p>
&lt;Mr. Representative&gt;:</p>
<p>This letter was prompted by my viewing the recent documentary, Food, Inc. by filmmaker Robert Kenner.</p>
<p>I encourage you to reintroduce, or support the reintroduction, of &#8220;Kevin&#8217;s Law,&#8221; introduced in the 109th Congress as H.R.3160. I feel strongly that granting additional authority to the Secretary of Agriculture and subsidiary agencies will lead to a safer food system and a healthier public.</p>
<p>I also encourage you to support the &#8220;local food&#8221; movement. The public needs better access to local food sources. Having a &#8220;local food&#8221; section in every grocery store will go a long way to providing this access. A more practical solution is to encourage and support the development and growth of farmer&#8217;s markets, especially in urban areas.</p>
<p>I would like to close with an appeal to watch the documentary, Food, Inc.. This movie has greatly affected how I feel about the food that reaches my mouth, however, I feel powerless to effect change. My hope is that if more public officials with power to effect change become aware of the current situation then true change will begin to take shape.</p>
<p>Thank you for your time. I wish you and your family a wonderful holiday season.</p>
<p>Sincerely,<br />
-M. Scott Ford
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://vaderpi.com/blog/?feed=rss2&amp;p=621</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
