
<?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>mcdonaldland &#187; Software</title>
	<atom:link href="http://www.mcdonaldland.info/category/software/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.mcdonaldland.info</link>
	<description>A magical discussion of software, economics, and other assorted theories.</description>
	<lastBuildDate>Thu, 15 Jul 2010 00:19:19 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Ruby based image crawler</title>
		<link>http://www.mcdonaldland.info/2010/05/18/ruby-based-image-crawler/</link>
		<comments>http://www.mcdonaldland.info/2010/05/18/ruby-based-image-crawler/#comments</comments>
		<pubDate>Wed, 19 May 2010 02:47:40 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/?p=242</guid>
		<description><![CDATA[I don&#8217;t write much code these days and felt it was time to sharpen the saw. I have a need to download a ton of images from a site (I got permission first&#8230;) but it is going to take forever to do by hand. Even though there are tons of tools out there for image [...]]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t write much code these days and felt it was time to <a href="https://www.stephencovey.com/7habits/7habits-habit7.php" target="_blank">sharpen the saw</a>.</p>
<p>I have a need to download a ton of images from a site (I got permission first&#8230;) but it is going to take forever to do by hand. Even though there are tons of tools out there for image crawling I figured this would be a great exercise to brush up on some skills and delve further into a language I am still fairly new to, Ruby. This allows me to use basic language constructs, network IO, and file IO, all while getting all the images I need in a fast manner.</p>
<p>As I have mentioned a few times on this blog, I am still new to Ruby so any advice for how to make this code cleaner is appreciated.</p>
<p>You can download the file here: <a href="http://www.mcdonaldland.info/files/crawler/crawl.rb" target="_blank">http://www.mcdonaldland.info/files/crawler/crawl.rb</a></p>
<p>Here is the source:</p>
<pre>
require 'net/http'
require 'uri'

class Crawler

&nbsp;&nbsp;# This is the domain or domain and path we are going
&nbsp;&nbsp;# to crawl. This will be the starting point for our
&nbsp;&nbsp;# efforts but will also be used in conjunction with
&nbsp;&nbsp;# the allow_leave_site flag to determine whether the
&nbsp;&nbsp;# page can be crawled or not.
&nbsp;&nbsp;attr_accessor :domain
&nbsp;&nbsp;
&nbsp;&nbsp;# This flag determines whether the crawler will be
&nbsp;&nbsp;# allowed to leave the root domain or not.
&nbsp;&nbsp;attr_accessor :allow_leave_site

&nbsp;&nbsp;# This is the path where all images will be saved.
&nbsp;&nbsp;attr_accessor :save_path

&nbsp;&nbsp;# This is a list of extensions to skip over while
&nbsp;&nbsp;# crawling through links on the site.
&nbsp;&nbsp;attr_accessor : omit_extensions # Remove space - added to keep smiley from showing up
&nbsp;&nbsp;
&nbsp;&nbsp;# This keeps track of all the pages we have visited
&nbsp;&nbsp;# so we don't visit them more than once.
&nbsp;&nbsp;attr_accessor :visited_pages
&nbsp;&nbsp;
&nbsp;&nbsp;# This keeps track of all the images we have downloaded
&nbsp;&nbsp;# so we don't download them more than once.
&nbsp;&nbsp;attr_accessor :downloaded_images

&nbsp;&nbsp;def begin_crawl
&nbsp;&nbsp;&nbsp;&nbsp;if domain.nil? || domain.length < 4 || domain[0, 4] != "http"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@domain = "http://#{domain}"
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;crawl(domain)
&nbsp;&nbsp;end

&nbsp;&nbsp;private
&nbsp;&nbsp;
    def initialize
    &nbsp;&nbsp;@domain = ""
    &nbsp;&nbsp;@allow_leave_site = false
        @save_path = ""
        @omit_extensions = []
        @visited_pages = []
        @downloaded_images = []
    end

&nbsp;&nbsp;def crawl(url = nil)

&nbsp;&nbsp;&nbsp;&nbsp;# If the URL is empty or nil we can move on.
&nbsp;&nbsp;&nbsp;&nbsp;return if url.nil? || url.empty?

&nbsp;&nbsp;&nbsp;&nbsp;# If the allow_leave_site flag is set to false we
&nbsp;&nbsp;&nbsp;&nbsp;# want to make sure that the URL we are about to
&nbsp;&nbsp;&nbsp;&nbsp;# crawl is within the domain.
&nbsp;&nbsp;&nbsp;&nbsp;return if !allow_leave_site &#038;&#038; (url.length < domain.length || url[0, domain.length] != domain)
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Check to see if we have crawled this page already.
&nbsp;&nbsp;&nbsp;&nbsp;# If so, move on.
&nbsp;&nbsp;&nbsp;&nbsp;return if visited_pages.include? url
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;puts "Fetching page: #{url}"
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Go get the page and note it so we don't visit it again.
&nbsp;&nbsp;&nbsp;&nbsp;res = fetch_page(url)
&nbsp;&nbsp;&nbsp;&nbsp;visited_pages << url

&nbsp;&nbsp;&nbsp;&nbsp;# If the response is nil then we cannot continue. Move on.
&nbsp;&nbsp;&nbsp;&nbsp;return if res.nil?
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Some links will be relative so we need to grab the
&nbsp;&nbsp;&nbsp;&nbsp;# document root.
&nbsp;&nbsp;&nbsp;&nbsp;root = parse_page_root(url)

&nbsp;&nbsp;&nbsp;&nbsp;# Parge the image and anchor tags out of the result.
&nbsp;&nbsp;&nbsp;&nbsp;images, links = parse_page(res.body)

&nbsp;&nbsp;&nbsp;&nbsp;# Process the images and links accordingly.
&nbsp;&nbsp;&nbsp;&nbsp;handle_images(root, images)
&nbsp;&nbsp;&nbsp;&nbsp;handle_links(root, links)
&nbsp;&nbsp;end

&nbsp;&nbsp;def parse_page_root(url)
&nbsp;&nbsp;&nbsp;&nbsp;end_slash = url.rindex("/")
&nbsp;&nbsp;&nbsp;&nbsp;if end_slash > 8
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url[0, url.rindex("/")] + "/"
&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url + "/"
&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;end

&nbsp;&nbsp;def discern_absolute_url(root, url)
&nbsp;&nbsp;&nbsp;&nbsp;# If we don't have an absolute path already, let's make one.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;if !root.nil? &#038;&#038; url[0,4] != "http"
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# If the URL begins with a slash then it is domain
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# relative so we want to append it to the domain.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Otherwise it is document relative so we want to
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# append it to the current directory.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if url[0, 1] == "/"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url = domain + url
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url = root + url
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;while !url.index("//").nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url.gsub!("//", "/")
&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Our little exercise will have replaced the two slashes
&nbsp;&nbsp;&nbsp;&nbsp;# after http: so we want to add them back.
&nbsp;&nbsp;&nbsp;&nbsp;url.gsub!("http:/", "http://")
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;url
&nbsp;&nbsp;end
&nbsp;&nbsp;

&nbsp;&nbsp;def handle_images(root, images)
&nbsp;&nbsp;&nbsp;&nbsp;if !images.nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;images.each {|i|

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Make sure all single quotes are replaced with double quotes.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Since we aren't rendering javascript we don't really care
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# if this breaks something.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i.gsub!("'", "\"")&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Grab everything between src=" and ".
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;src = i.scan(/src=[\"\']([^\"\']+)/i)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if !src.nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;src = src[0]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if !src.nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;src = src[0]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# If the src is empty move on.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next if src.nil? || src.empty?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# We want all URLs we follow to be absolute.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;src = discern_absolute_url(root, src)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;save_image(src)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;end

&nbsp;&nbsp;def save_image(url)
&nbsp;&nbsp;&nbsp;&nbsp;# Check to see if we have saved this image already.
&nbsp;&nbsp;&nbsp;&nbsp;# If so, move on.
&nbsp;&nbsp;&nbsp;&nbsp;return if downloaded_images.include? url&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Save this file name down so that we don't download
&nbsp;&nbsp;&nbsp;&nbsp;# it again in the future.
&nbsp;&nbsp;&nbsp;&nbsp;downloaded_images << url

&nbsp;&nbsp;&nbsp;&nbsp;# Parse the image name out of the url. We'll use that
&nbsp;&nbsp;&nbsp;&nbsp;# name to save it down.
&nbsp;&nbsp;&nbsp;&nbsp;file_name = parse_file_name(url)

&nbsp;&nbsp;&nbsp;&nbsp;while File.exist?(save_path + "\\" + file_name)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file_name = "_" + file_name
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;# Get the response and data from the web for this image.
&nbsp;&nbsp;&nbsp;&nbsp;response = fetch_page(url)

&nbsp;&nbsp;&nbsp;&nbsp;# If the response is not nil, save the contents down to
&nbsp;&nbsp;&nbsp;&nbsp;# an image.
&nbsp;&nbsp;&nbsp;&nbsp;if !response.nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "Saving image: #{url}"&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File.open(save_path + "\\" + file_name, "wb+") do |f|
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f << response.body
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;end
&nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp;def parse_file_name(url)
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Find the position of the last slash. Everything after
&nbsp;&nbsp;&nbsp;&nbsp;# it is our file name.
&nbsp;&nbsp;&nbsp;&nbsp;spos = url.rindex("/")
&nbsp;&nbsp;&nbsp;&nbsp;url[spos + 1, url.length - 1]
&nbsp;&nbsp;end
&nbsp;&nbsp;

&nbsp;&nbsp;def handle_links(root, links)
&nbsp;&nbsp;&nbsp;&nbsp;if !links.nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;links.each {|l|&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Make sure all single quotes are replaced with double quotes.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Since we aren't rendering javascript we don't really care
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# if this breaks something.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l.gsub!("'", "\"")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Grab everything between href=" and ".
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;href = l.scan(/(\href+)="([^"\\]*(\\.[^"\\]*)*)"/i)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if !href.nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;href = href[0]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if !href.nil?
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;href = href[1]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# We don't want to follow mailto or empty links
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next if href.nil? || href.empty? || (href.length > 6 &#038;&#038; href[0,6] == "mailto")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# We want all URLs we follow to be absolute.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;href = discern_absolute_url(root, href)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Down the rabbit hole we go...
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;crawl(href)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;end
&nbsp;&nbsp;

&nbsp;&nbsp;def parse_page(html)&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;images = html.scan(/<img [^>]*>/i)
&nbsp;&nbsp;&nbsp;&nbsp;links = html.scan(/<a [^>]*>/i)

&nbsp;&nbsp;&nbsp;&nbsp;return [ images, links ]
&nbsp;&nbsp;end

&nbsp;&nbsp;def fetch_page(url, limit = 10)
&nbsp;&nbsp;&nbsp;&nbsp;# Make sure we are supposed to fetch this type of resource.
&nbsp;&nbsp;&nbsp;&nbsp;return if should_omit_extension(url)
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# You should choose better exception.
&nbsp;&nbsp;&nbsp;&nbsp;raise ArgumentError, 'HTTP redirect too deep' if limit == 0

&nbsp;&nbsp;&nbsp;&nbsp;response = Net::HTTP.get_response(URI.parse(url))

&nbsp;&nbsp;&nbsp;&nbsp;case response
&nbsp;&nbsp;&nbsp;&nbsp;when Net::HTTPSuccess     then response
&nbsp;&nbsp;&nbsp;&nbsp;when Net::HTTPRedirection then fetch_page(response['location'], limit - 1)
&nbsp;&nbsp;&nbsp;&nbsp;else
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# We don't want to throw errors if we get a response
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# we are not expecting so we will just keep going.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nil
&nbsp;&nbsp;&nbsp;&nbsp;end
&nbsp;&nbsp;end

&nbsp;&nbsp;
&nbsp;&nbsp;def should_omit_extension(url)
&nbsp;&nbsp;&nbsp;&nbsp;# Get the index of the last slash.
&nbsp;&nbsp;&nbsp;&nbsp;spos = url.rindex("/")
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Get the index of the last dot.
&nbsp;&nbsp;&nbsp;&nbsp;dpos = url.rindex(".")
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# If the last dot is before the last slash, we don't have
&nbsp;&nbsp;&nbsp;&nbsp;# an extension and can return.
&nbsp;&nbsp;&nbsp;&nbsp;return false if spos > dpos
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# Grab the extension.
&nbsp;&nbsp;&nbsp;&nbsp;ext = url[dpos + 1, url.length - 1]
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;# The return value is whether or not the extension we
&nbsp;&nbsp;&nbsp;&nbsp;# have for this URL is in the omit list or not.
&nbsp;&nbsp;&nbsp;&nbsp;omit_extensions.include? ext
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;end

end

crawler = Crawler.new
crawler.save_path = "C:\\Users\\jmcdonald\\Desktop\\CrawlerOutput"
crawler.omit_extensions = [ "doc", "pdf", "xls", "rtf", "docx", "xlsx", "ppt",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"pptx", "avi", "wmv", "wma", "mp3", "mp4", "pps", "swf" ]
crawler.domain = "http://www.yoursite.com/"
crawler.allow_leave_site = false
crawler.begin_crawl
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2010/05/18/ruby-based-image-crawler/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mistaken identity</title>
		<link>http://www.mcdonaldland.info/2010/05/16/mistaken-identity/</link>
		<comments>http://www.mcdonaldland.info/2010/05/16/mistaken-identity/#comments</comments>
		<pubDate>Mon, 17 May 2010 03:40:25 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2010/05/16/mistaken-identity/</guid>
		<description><![CDATA[I&#8217;ve gotten a few questions lately about a car wreck in my area involving two motorcycles and some deaths. Turns out one of the people involved was named Jason McDonald as well. Just to clear things up, this wasn&#8217;t me. I wasn&#8217;t in Mt. Pleasant that day and have not been in a wreck.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve gotten a few questions lately about a car wreck in my area involving two motorcycles and some deaths. Turns out one of the people involved was named Jason McDonald as well.</p>
<p>Just to clear things up, this wasn&#8217;t me. I wasn&#8217;t in Mt. Pleasant that day and have not been in a wreck. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2010/05/16/mistaken-identity/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pop-warner players signed to NFL &#8211; Disaster Ensues</title>
		<link>http://www.mcdonaldland.info/2009/10/11/pop-warner-players-signed-to-nfl-disaster-ensues/</link>
		<comments>http://www.mcdonaldland.info/2009/10/11/pop-warner-players-signed-to-nfl-disaster-ensues/#comments</comments>
		<pubDate>Sun, 11 Oct 2009 23:25:48 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2009/10/11/pop-warner-players-signed-to-nfl-disaster-ensues/</guid>
		<description><![CDATA[For the first time ever, pee-wee football players, an entire team to be exact, has been signed to the NFL. In a questionable move late Saturday evening, Jacksonville Jaguars head coach Jack Del Rio opted to cut his entire team and instead sign, &#8220;The Marauders&#8221;, a pop-warner team from the Jacksonville metro area. Due to [...]]]></description>
			<content:encoded><![CDATA[<p>For the first time ever, pee-wee football players, an entire team to be exact, has been signed to the NFL. In a questionable move late Saturday evening, Jacksonville Jaguars head coach Jack Del Rio opted to cut his entire team and instead sign, &#8220;The Marauders&#8221;, a pop-warner team from the Jacksonville metro area. Due to the late timing of the decision the team did not have sufficient time to print new jerseys with the correct names on them. As a result, the tiny tikes donned the jerseys of their professional counterparts, filling them out surprisingly well.</p>
<p style="text-align: center"><img src="http://www.mcdonaldland.info/wp-content/uploads/2009/10/team.jpg" alt="team.jpg" width="170" height="128" /></p>
<p style="text-align: center"><em>&#8220;The Marauders&#8221; after their Saturday Pop-Warner game. </em></p>
<p>&#8220;I just felt like it was time for a change. I have been thinking about doing this for a number of weeks now and late last night my gut told me that the time was right.&#8221;, says Del Rio.</p>
<p>When asked of the wisdom behind picking little league players as opposed to free agents, players from the practice squad, college players, or even high school players Del Rio stared off into the distance, slowly shook his head and said, &#8220;I don&#8217;t know. It just felt right.&#8221;</p>
<p>But the decision wasn&#8217;t popular across the board. Star running back, Maurice Jones-Drew, told press in an interview after the game Sunday that, &#8220;I feel that it was a horrible decision. If we were playing poorly I could understand it. But we are coming off two big wins and have good momentum. Now you have kids out there, wearing our names, and performing poorly. Not only does that reflect poorly on the team, but since they are using our names it reflects poorly on us.&#8221;</p>
<p>&#8220;Jones-Drew&#8221;, being filled by 5&#8242; 6&#8243; Jamal Jackson, age 13, posted a paltry 34 yards rushing over 12 carries. The real Jones-Drew declined to give in depth commentary on the performance of the youngster, only saying that he was proud that the &#8220;little guy&#8221; was able to get that much against a professional team.</p>
<p>The decision ended up costing the Jaguars the game and has led many to wonder, even further, if this will be the last year we will see Del Rio in the greater Jacksonville area.</p>
<p>Long time fan, Jason McDonald, sent an email to local press saying, &#8220;I love this team and I think they were headed in the right direction. I&#8217;ve always thought that Del Rio did a good job and that the rumors lately of him being released have been unfounded.&#8221;, going on to say, &#8220;but I&#8217;m not so sure about the latest decision. While a certain part of the crushing loss can be attributed to the team and their lack of performance, you can&#8217;t entirely discount the coaching staff and their responsibility.&#8221;</p>
<p>McDonald goes on to say that, &#8220;I will continue to be a fan, as will many others. It is just hurts to proudly wear my team&#8217;s colors only to see them have their ass handed to them by a team with no wins. Had they at least scored a point, it wouldn&#8217;t have been as bad. I will stick with them but really hope that they can turn things around for the rest of the season.&#8221;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2009/10/11/pop-warner-players-signed-to-nfl-disaster-ensues/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ruby on Rails with Paypal</title>
		<link>http://www.mcdonaldland.info/2009/05/22/ruby-on-rails-with-paypal/</link>
		<comments>http://www.mcdonaldland.info/2009/05/22/ruby-on-rails-with-paypal/#comments</comments>
		<pubDate>Fri, 22 May 2009 21:27:22 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2009/05/22/ruby-on-rails-with-paypal/</guid>
		<description><![CDATA[I couldn&#8217;t find a good example of a Ruby on Rails Paypal Website Payments Standard implementation that I could open the hood and dig around in. The majority of the ones I found were partially implemented, geared at Website Payments Pro ($30/month), or were commercial products. So I decided to write a test to see [...]]]></description>
			<content:encoded><![CDATA[<p>I couldn&#8217;t find a good example of a Ruby on Rails Paypal Website Payments Standard implementation that I could open the hood and dig around in. The majority of the ones I found were partially implemented, geared at Website Payments Pro ($30/month), or were commercial products. So I decided to write a test to see how it all worked.</p>
<p><strong>DISCLAIMER[0]:</strong> I am NOT a good Ruby on Rails coder. I am from a Java world and still find the world of closures and dynamically typed variables a little disorienting. That said, things could definitely be cleaned up and made to work better. I am open to house cleaning suggestions.</p>
<p><strong>DISCLAMER[1]</strong>: This was done over the past couple months in my spare time only so there are likely bugs. I have only tested this in the Paypal Sandbox and have NOT used it in any production capacity. If you plan on using this in a production environment DO NOT assume that it all works correctly. TEST! Let me know if you find any bugs.</p>
<p>Let&#8217;s jump in.</p>
<p>The first thing you should know is that accessing the cart automatically and randomly generates and stores down inventory so you don&#8217;t have to worry about it.</p>
<p>This test covers the following scenarios:</p>
<ul>
<li><strong>Basic connection using form variables and posting in the same window.<br />
</strong>This is the most straight forward scenario. The page has hidden fields that contain all the information needed to start the payment process. When the user click the checkout button they are directed over to Paypal for payment. They then have the option of returning to the site after the payment process completes or if they decide to cancel.</li>
<li><strong>Return URL order detail validation.</strong><br />
When the user clicks &#8220;Return to Paypal Test Site&#8221; on the payment confirmation page within Paypal this site then validates the data submitted from that click in order to ensure payment and order details are correct. <strong>NOTE</strong>: This is not secure and was just the first step of the test. Don&#8217;t do this in real life!</li>
<li><strong>Payment data transfer (PDT) order detail validation.</strong><br />
PDT basically sends an encrypted token back which can then be posted to Paypal to get payment and order details. This allows the server to verify details about the transaction, removing the ability for users to change the validation data. Note that PDT only happens if the user returns to the site from the payment confirmation page within Paypal. All but the first scenario use PDT and IPN together.</li>
<li><strong>Instant payment notification (IPN) order detail validation.</strong><br />
IPN is the same as PDT, only it happens regardless of whether the user returns to the site. All but the first scenario use PDT and IPN together.</li>
<li><strong>Page level redirection to Paypal.</strong><br />
This hides the paypal form tags on a redirect page that is only displayed briefly. This moves the Paypal form variables off the cart page, where they tempt people to try to change them, off into a briefly displayed redirect page. This by no means offers any real security, however it does obscure the process a little bit, making it less tempting to play with. IPN and PDT are in place for this option as well.</li>
<li><strong>Controller level redirection &#8211; not fully working.</strong><br />
The idea behind this one is that it passes all the Paypal form fields across at the server level, removing the ability for users to interact with or change them. This uses the Net::HTTP code to do some funky POSTs and redirects but is failing at the moment. I have the code so that it submits via Net:HTTP in the controller and follows the redirects, however it is not transferring cookie or form data correctly (not sure which/either), which causes Paypal to redirect to an error page. I would be very interested to see if anyone can get this one working.</li>
<li><strong>DHTML popup window payments.</strong><br />
This is the same basic concept as the standard flow with page level redirection only the Paypal site is displayed in a centered popup window. Cancelling the paypal transaction simply closes the popup, leaving you still at the shopping cart. Completing the transaction redirects the entire window to the payment confirmation page.</li>
</ul>
<p>Changes that you will need to make to get this working:</p>
<ol>
<li>Update models/util.rb to point to your email addresses and Paypal sandbox info.</li>
<li>Update config/environments/development.rb to point to your SMTP server.</li>
<li>Update the controllers/website_payment_standard_controller.rb PDT_IDENTITY_TOKEN variable to point to match your PDT identity token.</li>
</ol>
<p><strong>NOTE:</strong> I removed the Test folders to lighten the load and quickly remove a bunch of SVN folders but didn&#8217;t try it out after this. If you are having any errors revolving around tests, create a new project then copy the test folder and its contents over to this one.</p>
<p>The files:  <a href="http://www.mcdonaldland.info/wp-content/uploads/2009/05/paypal.zip" target="_blank" title="Direct link to file">paypal.zip</a></p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2009/05/22/ruby-on-rails-with-paypal/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Impending Auto Bubble</title>
		<link>http://www.mcdonaldland.info/2008/11/19/the-impending-auto-bubble/</link>
		<comments>http://www.mcdonaldland.info/2008/11/19/the-impending-auto-bubble/#comments</comments>
		<pubDate>Wed, 19 Nov 2008 17:22:51 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2008/11/19/the-impending-auto-bubble/</guid>
		<description><![CDATA[Right now the big 3 US car makers, GM, Ford, and Chrysler, are hurting. Bad. In fact, they are hurting so bad that they are telling congress that they need a portion of the bailout money in order to stay afloat. So what happens if they go under? My prediction is that we will see [...]]]></description>
			<content:encoded><![CDATA[<p>Right now the big 3 US car makers, GM, Ford, and Chrysler, are hurting. Bad. In fact, they are hurting so bad that they are telling congress that they need a portion of the bailout money in order to stay afloat.</p>
<p>So what happens if they go under? My prediction is that we will see a very short bubble in the auto industry. The reason for this is basic supply and demand. Let&#8217;s explore this with a simple example.</p>
<p>Right now let&#8217;s say that we have Ford, GM, Chrysler, Toyota, Honda, and Mazda operating in the United States and no others. Each one can produce 100 cars per day at maximum capacity but demand only has them producing at 75% capacity. So right now things are in harmony where demand equals supply with 450 cars per day being produced.</p>
<p>But now we are shutting three of them down because they have been mismanaged for years (or any other of the 1,000 reasons why they are hurting now) and we don&#8217;t want to bail them out. So now we must remove their daily production and capacity from the mix &#8211; to keep it simple we will assume that none of them are bought by the other auto makers. This takes the supply down to 225 cars per day yet the demand still sits at 400 cars per day. To accommodate this reduced supply the remaining car makers max their output, raising it up to 300 cars per day.</p>
<p>This is still a supply deficit though. The demand is still 100 cars per day higher than the supply is able to accommodate. This will cause two changes.</p>
<p>First, the remaining auto makers will increase production capacity to account for the variance. This will take time and will be a long term solution.</p>
<p>Second, the auto makers will raise prices. In order to try to reach an equillibrium where they are producing at optimal efficiency they will raise the prices to increase profits and lower demand. Likewise, the lessened availability of cars in the market will reduce the purchasing power of consumers, allowing dealerships to raise the prices without an immediate penalty for doing so.</p>
<p>From an economic standpoint this will have a number of effects. First, the dealerships and auto makers will increase their short term profits. Second, the auto makers will grow larger. Third, autos may actually appreciate slightly, at least for the short term. Finally, autos will be overvalued, creating a severe negative equity situation.</p>
<p>The first and second points are pretty straight forward. As the prices rise but costs remain relatively stable the auto makers and dealerships will recognize an increased profit. This increased profit will help to fuel growth, which must occur to stabilize the supply and demand disparity, which will cause the companies to grow.</p>
<p>The third point, that autos may appreciate slightly, comes from the same supply and demand concept that caused the prices to rise. This doesn&#8217;t mean that the new car you bought will be worth more than you paid for it. It simply means that the car you have had for a while that has severely depreciated may come back up a bit. For example, if you bought a car at $30,000 and it was valued at $17,000 after two years you may find that this bubble causes the depreciation to lessen slightly, making the value of the car $18,000.</p>
<p>The last point is the most scary from an economic viewpoint. As prices rise they will reach artificial levels that would not stand in the typical economy where supply and demand were fairly close to one another. These artificially high prices mean that, at least in the short term, people will have to pay more for vehicles. This additional purchase price will cause people to have higher payments for less real value. Once the market corrects itself the real value of the vehicles will shift downward, leaving a large chunk of negative equity. This will eventually cause hardships like we hae seen with the housing bubble, albeit not of the same magnitude.</p>
<p>Let&#8217;s use an example here. Keep in mind that the numbers are illustrative only and will not add up correctly.</p>
<p>My car breaks down and dies during the height of the auto bubble. I go and buy a brand new BMW for $45,000. This car would ordinarily be priced at $35,000 but the bubble has artificially inflated the price. I am forced to take a monthly payment of $650 per month (I had no money to put down) in order to own the car. As the bubble breaks the price of the car drops back down to its standard price of $35,000. So the car that I bought at $45,000 is now instantly worth $10,000 les than it was. In addition, the payments of $650 per month are not reflective of the value of the car so no banks will finance a refinance <a href="http://www.creditloan.com/">loan</a> (at least that will drop the payments) because of the extreme amount of negative equity. Likewise, I can&#8217;t sell the car on the used market because I need more to pay it off than what you can buy a new car for. The payments eventually become too much &#8211; perhaps I lost my job, made some bad decisions, got sick, etc. &#8211; and I default on my loan. To recoup some losses the bank turns around and sells my car at a high discount from the current market price, in order to liquify it quickly, and takes a huge loss. Now magnify this situation across the United States.</p>
<p>Sound familiar? It should &#8211; this is exactly what happened with the housing bubble. If the big 3 go under and are not purchased by competitors then we could see such a situation. It is our responsibility to be vigalent and prevent another bubble from hurting ours or the world economy. The solution is pretty simple: don&#8217;t by things for more than they are worth. If a car is worth $20,000 today and $25,000 tomorrow, that should raise a red flag. Just be a smart consumer and the bubble will remain small, isolated, and insignificant.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2008/11/19/the-impending-auto-bubble/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Tipping the Scales</title>
		<link>http://www.mcdonaldland.info/2008/11/10/tipping-the-scales/</link>
		<comments>http://www.mcdonaldland.info/2008/11/10/tipping-the-scales/#comments</comments>
		<pubDate>Mon, 10 Nov 2008 18:38:56 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2008/11/10/tipping-the-scales/</guid>
		<description><![CDATA[Every organization needs procedures. What needs to happen when I file a bug? How do I notify clients that their feature is in production? Who do I bill this work to? All of these are simplified by procedure. Instead of having to figure out the issue and devise a solution every time the problem arises, [...]]]></description>
			<content:encoded><![CDATA[<p>Every organization needs procedures. What needs to happen when I file a bug? How do I notify clients that their feature is in production? Who do I bill this work to? All of these are simplified by procedure. Instead of having to figure out the issue and devise a solution every time the problem arises, procedures allow us to base our actions on past thought and designs.</p>
<p>Procedures can be good but they can also create more problems than they are worth. When procedures reach a point to where you are spending more time trying to find the right procedure than you would if you just figured out the problem from scratch, they are a burden.</p>
<p>A happy medium should be targeted. This middle ground should ultimately save time &#8211; otherwise it is a wasteful venture. If you have to spend long amounts of time searching for or through documents just to figure out what is supposed to happen then the system is a failure.</p>
<p>Next time you are going to add a procedure you should stop to ask yourself the following:</p>
<p><strong>1. Does this solve a tough problem?</strong><br />
Does this procedure save people an inordinate amount of time or money? Does this procedure address an issue that is difficult to figure out on a case by case basis? If either of these are true then the procedure is likely to be a good addition.</p>
<p><strong>2. Is this going to simplify things or add undue process?</strong><br />
Will people have to take a lot of extra steps just for the sake of procedure? If any part of the procedure is there just for the sake of it and doesn&#8217;t produce a tangible output then the overall process is diminished.</p>
<p><strong>3. How easy will this be to remember?</strong><br />
If a procedure is hard to remember then people will need to look it up each time. This, by itself, doesn&#8217;t necessarily negate usefulness. However, if people have to look it up and it is tough to find then the process becomes much less useful and less likely to succeed.</p>
<p><strong>4. How often is this likely to happen and what is the importance when it happens?</strong><br />
If the item in question rarely happens and/or doesn&#8217;t have a lot of value associated with it then there is likely not a need for detailed procedures. For example, declaring war doesn&#8217;t happen that often but has a high cost so a procedure is needed. Conversely, working showstoppers happens far more frequently but has a relatively low cost.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2008/11/10/tipping-the-scales/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Book Review: Code Complete</title>
		<link>http://www.mcdonaldland.info/2008/10/18/book-review-code-complete/</link>
		<comments>http://www.mcdonaldland.info/2008/10/18/book-review-code-complete/#comments</comments>
		<pubDate>Sat, 18 Oct 2008 14:43:05 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Book Reviews]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2008/10/18/book-review-code-complete/</guid>
		<description><![CDATA[Code Complete: A Practical Handbook of Software Construction, by Steve McConnell This was a great book. If you have been in software for a significant time you are likely to have picked up many of these habits already, assuming you have been around experienced engineers who have helped you cultivate your software capabilities. It is [...]]]></description>
			<content:encoded><![CDATA[<p align="center"><a href="http://www.amazon.com/gp/product/0735619670?ie=UTF8&amp;tag=mcdo-20&amp;link_code=wql&amp;camp=212361&amp;creative=380601" target="_blank">Code Complete: A Practical Handbook of Software Construction</a>, by Steve McConnell<a href="http://www.amazon.com/gp/product/0671792806?ie=UTF8&amp;tag=mcdo-20&amp;link_code=wql&amp;camp=212361&amp;creative=380601" target="_blank"> </a></p>
<p style="text-align: center"><a href="http://www.amazon.com/gp/product/0735619670?ie=UTF8&amp;tag=mcdo-20&amp;link_code=wql&amp;camp=212361&amp;creative=380601" target="_blank" title="Direct link to file"><img src="http://www.mcdonaldland.info/wp-content/uploads/2008/10/51jokj0md0l_sl500_bo2204203200_aa219_pisitb-sticker-dp-arrowtopright-24-23_sh20_ou01_.jpg" alt="51jokj0md0l_sl500_bo2204203200_aa219_pisitb-sticker-dp-arrowtopright-24-23_sh20_ou01_.jpg" /></a></p>
<p>This was a great book. If you have been in software for a significant time you are likely to have picked up many of these habits already, assuming you have been around experienced engineers who have helped you cultivate your software capabilities. It is chock full of useful information that every engineer should know. While this is a sure career improver for new engineers it has information that could easily be digested and used by even the most experienced programmer.</p>
<p>Seminal book. Buy it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2008/10/18/book-review-code-complete/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Over the code</title>
		<link>http://www.mcdonaldland.info/2008/10/17/over-the-code/</link>
		<comments>http://www.mcdonaldland.info/2008/10/17/over-the-code/#comments</comments>
		<pubDate>Fri, 17 Oct 2008 14:42:25 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Misc]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2008/10/17/over-the-code/</guid>
		<description><![CDATA[I realized a while back that I have grown tired of writing code. I still enjoy the design aspects of software and enjoy being involved in the software creation process. I just no longer care for the implementation aspect of the business. I haven&#8217;t written a line of code in probably three months. I haven&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>I realized a while back that I have grown tired of writing code. I still enjoy the design aspects of software and enjoy being involved in the software creation process. I just no longer care for the implementation aspect of the business. I haven&#8217;t written a line of code in probably three months.  I haven&#8217;t written a significant portion of code in at least six months. And I&#8217;m happy.</p>
<p>My parents were both programmers so I got a very early introduction to software. I began by writing little programs, address books and the like, throughout childhood and high school. I then jumped into a data entry job, which I quickly figured out how to write a script to help automate. From there I never had a job where I didn&#8217;t write code. Until now.</p>
<p>My parents both ended up in management &#8211; my Dad has relatively recently returned to writing code. They both told me the same thing at one point or another: they just got tired of writing code and wanted something new. I believed them and figured that I would follow suit sooner or later.</p>
<p>About five years ago I was still pretty into writing code still but looking to the future I saw myself growing wary in the next five to seven years. My timing ended up being just right. About five years ago I started getting into more leadership roles &#8211; leading software teams while still having my hands in the code. Over that span I have slowly become more and more removed from the code until just a few months ago when I decided I was completely done with it. I am now in a role where I don&#8217;t write any code but still have a high level of involvement in the design and development processes.</p>
<p>I may still write about code and software practices here and there however, as is obvious by my post trends, they will become less frequent. I imagine that the main focus of my writings will continue to be business of software and economics, with a little bit of software sprinkled in here and there.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2008/10/17/over-the-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Riddle</title>
		<link>http://www.mcdonaldland.info/2008/09/20/a-riddle/</link>
		<comments>http://www.mcdonaldland.info/2008/09/20/a-riddle/#comments</comments>
		<pubDate>Sun, 21 Sep 2008 00:40:42 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2008/09/20/a-riddle/</guid>
		<description><![CDATA[Last night we were preparing for my daughter&#8217;s third birthday party and decided to go grab fast food. After leaving the drive through I put a french fry in my mouth only to find that the outside was cool but the inside was very hot. My daughter asked for one and I said they are [...]]]></description>
			<content:encoded><![CDATA[<p>Last night we were preparing for my daughter&#8217;s third birthday party and decided to go grab fast food. After leaving the drive through I put a french fry in my mouth only to find that the outside was cool but the inside was very hot. My daughter asked for one and I said they are too hot, so hot they are burning Daddy&#8217;s mouth. Here response was simple enough: &#8220;Blow on it.&#8221; So I posed the following question:</p>
<p align="center"> &#8220;How do you blow on something that is in your mouth?&#8221;</p>
<p>Before you continue reading take a moment to think about this.</p>
<p>If you are like me the first thing that came to mind is the reverse blow. You know this. It&#8217;s where you all of a sudden find yourself with something too hot in your mouth and frantically suck in air to try to cool the food down. But this isn&#8217;t right.</p>
<p>The answer is simple: take it out of your mouth.</p>
<p>I posed this question to her then immediately recognized the riddle like nature of it and started thinking about the answer. My three year old told me the correct answer before I arrived at it on my own.  Perhaps it is a testament to the complexity of our adult minds but the natural tendency for most of us is to start at the complex and work back to the simple. Conversely, the child brain seems to naturally start with the simple and work towards the complex. We shouldn&#8217;t do this &#8211; we should start with the simple and work towards the complex.</p>
<p>This concept can be applied time and time again in the software industry. I can&#8217;t count the number of systems that I have seen that have been over designed and/or over engineered. Many times these systems would have worked just was well, if not better, with a simple solution. However, the simple option was either never noticed or was ignored.</p>
<p>The Keep It Simple (KIS &#8211; alternatively KISS for Keep It Simple, Stupid) principle should always apply &#8211; the design and engineering should be as simple as possible while still meeting the needs of the system.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2008/09/20/a-riddle/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Strike me down</title>
		<link>http://www.mcdonaldland.info/2008/09/19/strike-me-down/</link>
		<comments>http://www.mcdonaldland.info/2008/09/19/strike-me-down/#comments</comments>
		<pubDate>Fri, 19 Sep 2008 14:14:19 +0000</pubDate>
		<dc:creator>Jason McDonald</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.mcdonaldland.info/2008/09/19/strike-me-down/</guid>
		<description><![CDATA[No sooner had I finished writing yesterday&#8217;s post than did my Windows OS decide to punish me for the slander against its progenitor. I closed the window for my browser and my taskbar immediately exploded. It appears to have created a window and task bar item for every process and driver running. Nice.   Click [...]]]></description>
			<content:encoded><![CDATA[<p>No sooner had I finished writing <a href="http://www.mcdonaldland.info/2008/09/18/being-sub-par/">yesterday&#8217;s post</a> than did my Windows OS decide to punish me for the slander against its progenitor. I closed the window for my browser and my taskbar immediately exploded. It appears to have created a window and task bar item for every process and driver running. Nice.</p>
<p align="center"> <a href="http://www.mcdonaldland.info/wp-content/uploads/2008/09/wtf.JPG" onclick="return false;" title="Direct link to file"><img src="http://www.mcdonaldland.info/wp-content/uploads/2008/09/wtf.JPG" alt="wtf.JPG" height="78" width="525" /><br />
<em>Click for larger view</em><br />
</a></p>
<p>I am sooooo wanting to switch to Mac/Linux.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mcdonaldland.info/2008/09/19/strike-me-down/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
