<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" gd:etag="W/&quot;DUQNQXw6eSp7ImA9WhRQGUU.&quot;"><id>tag:blogger.com,1999:blog-33260599</id><updated>2011-12-15T14:16:30.211-08:00</updated><category term="sculpture" /><category term="China" /><category term="books" /><category term="free" /><category term="domain name" /><category term="scifi" /><category term="shopping" /><category term="relient k" /><category term="projects" /><category term="open source" /><category term="valentines" /><category term="microblogging" /><category term="Summer Palace" /><category term="firefox" /><category term="test" /><category term="The Great Wall" /><category term="travel" /><category term="yum" /><category term="Hangzhou" /><category term="Temple of Heaven" /><category term="css" /><category term=".so" /><category term="journal" /><category term="Tea Garden" /><category term="vim" /><category term="guitar" /><category term="mashup" /><category term="daughter" /><category term="opera" /><category term="laptop" /><category term="xml" /><category term="steganography" /><category term="septoplasty" /><category term="live coding" /><category term="pi day" /><category term="portable operating system" /><category term="vmware" /><category term="camping" /><category term="xo laptop" /><category term="claire" /><category term="rickshaw" /><category term="style" /><category term="c" /><category term="liturature" /><category term="nadsat" /><category term="The Lingering Garden" /><category term="haiku" /><category term="headspace" /><category term="Forbidden City" /><category term="android" /><category term="baby" /><category term="object oriented" /><category term="tiddlywiki" /><category term="mp3" /><category term="guess" /><category term="version control" /><category term="ubuntu" /><category term="JavaScript" /><category term="blogging" /><category term="puns" /><category term="limerick" /><category term="google" /><category term="GRE" /><category term="virtualization" /><category term="wiki" /><category term="vicodin" /><category term="Ming Tombs" /><category term="one laptop per child" /><category term="cryptography" /><category term="road trip" /><category term="q12" /><category term="early binding" /><category term="mashupcamp" /><category term="remodel" /><category term="http" /><category term="late binding" /><category term="cellular automata" /><category term="Ajax" /><category term="Greek" /><category term="fluxbuntu" /><category term="python" /><category term="cms" /><category term="oauth2" /><category term="browser" /><category term="internet" /><category term="codec" /><category term="hardy heron" /><category term="scorpion server" /><category term="oauth" /><category term="web server" /><category term="code" /><category term="learning" /><category term="app engine" /><category term="Shanghai" /><category term="operating system" /><category term="ogg vorbis" /><category term="linux" /><category term="screen" /><category term="currying" /><category term="jeffscudder.com" /><category term="techno" /><category term="linguistics" /><category term="vi" /><category term="programming" /><category term="wii" /><category term="music" /><category term="wife" /><category term="go" /><category term="code conventions" /><category term="dynamic linking" /><category term="blogger" /><category term="matrix" /><category term="food" /><category term="twitter" /><category term="poetry" /><category term="house" /><category term="https" /><category term="server" /><category term="gcc" /><category term="Huqiu Leaning Tower" /><category term="writing" /><category term="mercurial" /><category term="password" /><title>Imposterrific - a blog by Jeff Scudder</title><subtitle type="html">Coding can be fun!</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>94</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/Imposterrific" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="imposterrific" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DkINRH44eSp7ImA9WhZUFk0.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-1027142468258233525</id><published>2011-05-28T01:35:00.001-07:00</published><updated>2011-06-09T00:29:55.031-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-09T00:29:55.031-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><category scheme="http://www.blogger.com/atom/ns#" term="app engine" /><category scheme="http://www.blogger.com/atom/ns#" term="go" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Go on App Engine Example - Part 1</title><content type="html">The &lt;a href="http://code.google.com/appengine/"&gt;App Engine&lt;/a&gt; team recently &lt;a href="http://blog.golang.org/2011/05/go-and-google-app-engine.html"&gt;announced&lt;/a&gt; support for &lt;a href="http://golang.org/"&gt;Go&lt;/a&gt; as a runtime for use in apps. Summary up front, the &lt;a href="http://code.google.com/p/googleappengine/downloads/list"&gt;App Engine SDK&lt;/a&gt; for the Go runtime is the easiest way I've found yet to get started with Go. As I change my code, it is recompiled in the background when I make a request to my app, so it feels very much like developing in a scripting language.&lt;br /&gt;&lt;br /&gt;I've been excited about the Go language for some time now (specifics on why will have to wait for another post) so I was eager to try it out in one of my favorite platforms: App Engine. I wanted to start with something small, so I wrote a simplified version of a web app that I've been itching to write lately, a site for hosting plain text content. Specifically, I want something that preserves whitespace, allows me to line up columns of text, and supports non-English characters (Unicode). Those are the kinds of things I need to share and talk about code. Also there is a great deal more you can do with plain old monospaced text, maybe you'll find this useful as well.&lt;br /&gt;&lt;br /&gt;With that objective in mind I give you &lt;a href="http://plaintextmachine.appspot.com/"&gt;the Plain Text Machine&lt;/a&gt;. This little app lets you enter a small amount of text, somewhere around 2,000 characters, and gives you a link that others can visit to see an HTML reproduction of your writing. I mentioned I wanted to keep this simple, so here's the odd little bit, this app doesn't store your text anywhere. The URL that is generated contains the text, hence the somewhat low limit on message length. It certainly keeps the app simple, the most complex logic is that which converts the text from the URL into HTML.&lt;br /&gt;&lt;br /&gt;A request starts by hitting the &lt;code&gt;Init&lt;/code&gt; function:&lt;pre&gt;func init() {&lt;br /&gt; http.HandleFunc("/", handle)&lt;br /&gt; http.HandleFunc("/show", show)&lt;br /&gt;}&lt;/pre&gt;The main page, at &lt;code&gt;/&lt;/code&gt;, is just static content, we're just interested in the &lt;code&gt;/show&lt;/code&gt; handler. It looks like this:&lt;pre&gt;func show(w http.ResponseWriter, r *http.Request) {&lt;br /&gt; w.Header().Set("Content-Type", "text/html; charset=utf-8")&lt;br /&gt; // Get the message from the URL.&lt;br /&gt; PrintHtml(utf8.NewString(r.FormValue("msg")), w)&lt;br /&gt;}&lt;/pre&gt;The above does two things, sets the content type of our response so that the browser will know it is HTML, and reads the message URL parameter from the request to convert it to HTML.&lt;br /&gt;&lt;br /&gt;The PrintHtml method prints out some boilerplate HTML then reads the message one character at a time and converts each character to its HTML-safe equivalent. There's a tiny bit of complexity to make sure that the whitespace is preserved instead of being collapsed as would normally be done with repeated spaces in HTML. Here's the code:&lt;pre&gt;func PrintHtml(text *utf8.String, out http.ResponseWriter) {&lt;br /&gt; spaces := false&lt;br /&gt; &lt;br /&gt; fmt.Fprint(out, textHeader, middle)&lt;br /&gt; for i := 0; i &amp;lt; text.RuneCount(); i++ {&lt;br /&gt;  currentChar := text.At(i)&lt;br /&gt; &lt;br /&gt;  if currentChar == 32 &amp;amp;&amp;amp; !spaces {&lt;br /&gt;   // A first space.&lt;br /&gt;   fmt.Fprint(out, " ")&lt;br /&gt;   spaces = true&lt;br /&gt;  } else {&lt;br /&gt;   if currentChar == 32 {&lt;br /&gt;    // Space following another space&lt;br /&gt;    fmt.Fprint(out, "&amp;amp;nbsp;")&lt;br /&gt;   } else if currentChar == 10 {&lt;br /&gt;    // Newline&lt;br /&gt;    fmt.Fprint(out, "&amp;lt;br&amp;gt;")&lt;br /&gt;   } else if currentChar == 9 {&lt;br /&gt;    // Tab&lt;br /&gt;    fmt.Fprint(out, "&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp; ")&lt;br /&gt;   } else if currentChar == 38 {&lt;br /&gt;    // &amp;amp;&lt;br /&gt;    fmt.Fprint(out, "&amp;amp;amp;")&lt;br /&gt;   } else if currentChar == 60 {&lt;br /&gt;    // &amp;lt;&lt;br /&gt;    fmt.Fprint(out, "&amp;amp;lt;")&lt;br /&gt;   } else if currentChar == 62 {&lt;br /&gt;    // &amp;gt;&lt;br /&gt;    fmt.Fprint(out, "&amp;amp;gt;")&lt;br /&gt;   } else if currentChar &amp;lt; 31 || currentChar == 128 {&lt;br /&gt;    // Skip control characters.&lt;br /&gt;   } else if currentChar &amp;lt; 127 {&lt;br /&gt;    fmt.Fprintf(out, "%c", currentChar)&lt;br /&gt;   } else {&lt;br /&gt;    fmt.Fprintf(out, "&amp;amp;#%d;", text.At(i))&lt;br /&gt;   }&lt;br /&gt;   spaces = false&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; fmt.Fprint(out, footer)&lt;br /&gt;}&lt;/pre&gt;The &lt;code&gt;textHeader&lt;/code&gt;, &lt;code&gt;middle&lt;/code&gt;, and &lt;code&gt;footer&lt;/code&gt; variables are string constants containing the wrapper HTML which gives style information.&lt;br /&gt;&lt;br /&gt;If you're interested in the full source code for this tiny little app, you can find it in the &lt;a href="http://code.google.com/p/plain-text-machine/"&gt;Plain Text Machine open source project&lt;/a&gt;. Hopefully this example provides an easy to understand picture of what Go code for App Engine looks like.&lt;br /&gt;&lt;br /&gt;I had quite a bit of fun putting together this app. By keeping it simple I was able to go from idea to done in less time than it took me to write this blog post. As an added bonus, having an app with no persistent storage brings up some interesting philosophical questions. For example, if a message is created but no one stores the link to it, does it still exist?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-1027142468258233525?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/1027142468258233525/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=1027142468258233525" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/1027142468258233525?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/1027142468258233525?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2011/05/go-on-app-engine-example-part-1.html" title="Go on App Engine Example - Part 1" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;DEEGSHw8cSp7ImA9WhZUEUo.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-5716864271332478317</id><published>2011-05-09T13:21:00.000-07:00</published><updated>2011-06-04T01:37:09.279-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-04T01:37:09.279-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="oauth2" /><category scheme="http://www.blogger.com/atom/ns#" term="google" /><category scheme="http://www.blogger.com/atom/ns#" term="oauth" /><title>Setup OAuth2 for Google APIs</title><content type="html">Today I'm at &lt;a href="http://www.io-bootcamp.com/"&gt;I/O Bootcamp&lt;/a&gt; and helping out with a walkthrough on how to get starting using Google APIs in a variety of languages.&lt;br /&gt;&lt;br /&gt;One of the things I appreciate about the Google APIs is the authorization mechanism which lets me see which applications I've granted access for my data and allows me to revoke access. As an application developer, there are some things that I need to do to identify my application so that Google knows which app is requesting access so that it can show the user more information about my app. The first step, then, in writing an application that uses OAuth2 is registering your app.&lt;br /&gt;&lt;br /&gt;You can begin the registration process on the &lt;a href="https://code.google.com/apis/console/"&gt;Google API console&lt;/a&gt; by creating a project:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/-NvMboJYRPSU/TchTHHpJ0eI/AAAAAAAAAnA/pO9C6hkv0mU/s1600/Screen%2Bshot%2B2011-05-09%2Bat%2B1.47.39%2BPM.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 255px;" src="http://4.bp.blogspot.com/-NvMboJYRPSU/TchTHHpJ0eI/AAAAAAAAAnA/pO9C6hkv0mU/s400/Screen%2Bshot%2B2011-05-09%2Bat%2B1.47.39%2BPM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5604821118038430178" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-CQVe0NCqKPs/TchTgynXkFI/AAAAAAAAAnI/WEX7fRP9zsk/s1600/Screen%2Bshot%2B2011-05-09%2Bat%2B1.49.51%2BPM.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 172px;" src="http://1.bp.blogspot.com/-CQVe0NCqKPs/TchTgynXkFI/AAAAAAAAAnI/WEX7fRP9zsk/s400/Screen%2Bshot%2B2011-05-09%2Bat%2B1.49.51%2BPM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5604821559070396498" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now that you have an application, you'll need to configure it for use with OAuth2 and get the secret tokens that your application will use in its requests. For that create an OAuth2 client ID.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/-fXaXeBIif0g/Tchcsxr8WCI/AAAAAAAAAnQ/tWWprMXb7BQ/s1600/Screen%2Bshot%2B2011-05-05%2Bat%2B10.54.24%2BAM.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 176px;" src="http://2.bp.blogspot.com/-fXaXeBIif0g/Tchcsxr8WCI/AAAAAAAAAnQ/tWWprMXb7BQ/s400/Screen%2Bshot%2B2011-05-05%2Bat%2B10.54.24%2BAM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5604831660584228898" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The most vital decision to make during the sign up flow is if your application is a "web application" or an "installed application". If you're a site accessed in a browser and you're able to send the users to a Google web page for authorization and then have the broswer redirect back to your app, then you want web application. For an installed application, the user will still need to authorize your app by visiting a web page, but once authorization is complete, the secret token will be sent to the app either using a redirect to a local running web server or by having the user copy and paste the secret into your application.&lt;br /&gt;&lt;br /&gt;For the command line samples I've been playing with I choose installed application.&lt;br /&gt;&lt;br /&gt;After creating the client ID you should see information something like this&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Client ID:      #######.apps.googleusercontent.com&lt;br /&gt;Client secret:  Amzz5Yip2SJPqqq5Jx&lt;br /&gt;Redirect URIs:  urn:ietf:wg:oauth:2.0:oob&lt;br /&gt;                http://localhost&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You'll need to put this information into your application so that it can use the client ID and secret when making requests to get an authorization token from the user. This can be as simple as copying and pasting these strings into your code.&lt;br /&gt;&lt;br /&gt;The one other thing that needs to be done before you begin using OAuth2 with one of the Google APIs is to turn on the API for your application. This can be done on the developer console "Services" section.&lt;br /&gt;&lt;br /&gt;Let's say that I wanted to access the URL Shortener API. First I would need to enable it for my application.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-_aQhfvEiVg0/TchsR7eCJRI/AAAAAAAAAnY/nw5C9BJMpK0/s1600/Screen%2Bshot%2B2011-05-05%2Bat%2B10.53.12%2BAM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 74px;" src="http://4.bp.blogspot.com/-_aQhfvEiVg0/TchsR7eCJRI/AAAAAAAAAnY/nw5C9BJMpK0/s400/Screen%2Bshot%2B2011-05-05%2Bat%2B10.53.12%2BAM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5604848791539819794" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Then I would need to specify the URL Shortener's API scope when I request authorization from a specific user. The scopes that are requested by my app are turned into a list of APIs that the user must grant access to when they authorize my application.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-lKH4Ym7oqSM/Tcht_Lc23iI/AAAAAAAAAng/F5KqGpqsg00/s1600/Screen%2Bshot%2B2011-05-09%2Bat%2B3.42.32%2BPM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 248px;" src="http://1.bp.blogspot.com/-lKH4Ym7oqSM/Tcht_Lc23iI/AAAAAAAAAng/F5KqGpqsg00/s400/Screen%2Bshot%2B2011-05-09%2Bat%2B3.42.32%2BPM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5604850668435594786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The scope for an API can be found in the API documentation under authorization.&lt;br /&gt;&lt;br /&gt;For an example that brings all of these settings together, see the &lt;a href="http://code.google.com/p/google-api-python-client/source/browse/samples/urlshortener/urlshortener.py"&gt;urlshortener.py&lt;/a&gt; example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  FLOW = OAuth2WebServerFlow(&lt;br /&gt;      client_id='433807057907.apps.googleusercontent.com',&lt;br /&gt;      client_secret='jigtZpMApkRxncxikFpR+SFg',&lt;br /&gt;      scope='https://www.googleapis.com/auth/urlshortener',&lt;br /&gt;      user_agent='urlshortener-cmdline-sample/1.0')&lt;br /&gt;  ...&lt;br /&gt;  credentials = run(FLOW, storage)&lt;br /&gt;  ...&lt;br /&gt;  http = httplib2.Http()&lt;br /&gt;  http = credentials.authorize(http)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Python may not be your bag, but no worries, there are client libraries for the Google APIs in a variety of languages, and even better, there is an &lt;a href="http://code.google.com/apis/explorer/"&gt;API Explorer&lt;/a&gt; that lets you try out the underlying protocol without any language specific stuff getting in the way.&lt;br /&gt;&lt;br /&gt;For example, here is getting details and stats about a short URL:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-RQUc2DHdNLc/Tch5CqKoJTI/AAAAAAAAAno/rGX0pA8ajkg/s1600/Screen%2Bshot%2B2011-05-05%2Bat%2B10.40.40%2BAM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 200px;" src="http://1.bp.blogspot.com/-RQUc2DHdNLc/Tch5CqKoJTI/AAAAAAAAAno/rGX0pA8ajkg/s400/Screen%2Bshot%2B2011-05-05%2Bat%2B10.40.40%2BAM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5604862822848144690" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And here is creating a new short link:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-dgYXdVcyI7U/Tch7EzPWHlI/AAAAAAAAAoA/g7aK4Wk442A/s1600/Screen%2Bshot%2B2011-05-09%2Bat%2B4.38.21%2BPM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 112px;" src="http://4.bp.blogspot.com/-dgYXdVcyI7U/Tch7EzPWHlI/AAAAAAAAAoA/g7aK4Wk442A/s400/Screen%2Bshot%2B2011-05-09%2Bat%2B4.38.21%2BPM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5604865058666847826" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-03ubdyt9MUc/Tch5C_7mmiI/AAAAAAAAAnw/aX8AkPGbSKA/s1600/Screen%2Bshot%2B2011-05-05%2Bat%2B10.45.17%2BAM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 239px;" src="http://4.bp.blogspot.com/-03ubdyt9MUc/Tch5C_7mmiI/AAAAAAAAAnw/aX8AkPGbSKA/s400/Screen%2Bshot%2B2011-05-05%2Bat%2B10.45.17%2BAM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5604862828690709026" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;For all the details on using these APIs, take a look at the documentation. For example here are the &lt;a href="http://code.google.com/apis/urlshortener/v1/getting_started.html"&gt;URL shortener docs&lt;/a&gt;. The common first step for almost all of the Google APIs that access user information is the registration step we started with. For more details on &lt;a href="http://code.google.com/apis/accounts/docs/OAuth2.html"&gt;OAuth2 with Google APIs&lt;/a&gt;, there is some excellent documentation &lt;a href="http://code.google.com/apis/accounts/docs/OAuth2.html"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-5716864271332478317?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/5716864271332478317/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=5716864271332478317" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/5716864271332478317?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/5716864271332478317?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2011/05/setup-oauth2-for-google-apis.html" title="Setup OAuth2 for Google APIs" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-NvMboJYRPSU/TchTHHpJ0eI/AAAAAAAAAnA/pO9C6hkv0mU/s72-c/Screen%2Bshot%2B2011-05-09%2Bat%2B1.47.39%2BPM.png" height="72" width="72" /><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;A0cARXo8eyp7ImA9Wx9aF00.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-3342246172076718609</id><published>2011-03-08T07:19:00.000-08:00</published><updated>2011-03-09T14:17:24.473-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-09T14:17:24.473-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="poetry" /><title>Free Verse #397</title><content type="html">Sipping a late,&lt;br /&gt;writing a free verse poem.&lt;br /&gt;Hey look, a haiku!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-3342246172076718609?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/3342246172076718609/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=3342246172076718609" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/3342246172076718609?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/3342246172076718609?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2011/03/free-verse-397.html" title="Free Verse #397" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;A08DSXc-cSp7ImA9Wx9WGU4.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-6819048179265099702</id><published>2011-01-23T22:12:00.000-08:00</published><updated>2011-01-24T23:17:58.959-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-24T23:17:58.959-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mercurial" /><category scheme="http://www.blogger.com/atom/ns#" term="version control" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Mercurial in Five Commands</title><content type="html">Completing any sort of significant programming project can be nearly impossible without version control. For a class project, I recently collaborated with a small group on a large programming assignment. To keep all of our changes in sync while working miles away from each other, we used a centralized version control system.&lt;br /&gt;&lt;br /&gt;Since I've been working quite a bit with &lt;a href="http://mercurial.selenic.com/"&gt;Mercurial&lt;/a&gt; lately, we went with a free private project on &lt;a href="https://bitbucket.org/"&gt;bitbucket&lt;/a&gt;. It was the first time my teammates had used Mercurial so I gave a crash course that I thought would be helpful for others as well. A distributed version control system has a large number of commands and features, but 90% of the time, you're just dealing with the basics. When working with a team you can get by with just these five Mercurial commands: clone, commit, push, pull, and update.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Setting up the Project&lt;/h4&gt;&lt;br /&gt;As I mentioned, we were using a hosted repository on bitbucket, so getting a working copy of the repository on our machines started with each of us executing &lt;code&gt;hg clone&lt;/code&gt;. It looked a little something like this:&lt;pre&gt;hg clone https://your-username@bitbucket.org/your-username/project&lt;/pre&gt;This will create a local repository with a copy of each file and you're now free to make changes. You can edit files locally and Mercurial will track the changes for you. If you want to add new files or remove existing files, make sure to use &lt;code&gt;hg add&lt;/code&gt; and &lt;code&gt;hg remove&lt;/code&gt;. (Bonus commands!). Once you're happy with your changes and you want to gather them up in a logical unit it's time for:&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Local Snapshots&lt;/h4&gt;&lt;br /&gt;To save a set of changes locally, you use &lt;code&gt;hg commit&lt;/code&gt;. Be sure to write an informative description of this change set since you and others will want to remember what this set of changes was all about. There are a few arguments to the commit command that come in handy, often I do:&lt;pre&gt;hg ci -u your-username -m 'Reduces codebase entropy. All tests pass!'&lt;/pre&gt;For more options on the commit command and any other Mercurial command you can use &lt;code&gt;hg help &amp;lt;command&amp;gt;&lt;/code&gt;. You can also see a diff of your current files compared to the most recent snapshot using the &lt;code&gt;hg diff&lt;/code&gt; command. (Bonus times two!)&lt;br /&gt;&lt;br /&gt;Pinning your changes locally is a fantastic feature of distributed version control systems. As I work I tend to take a local snapshot several times an hour. These revisions just exist in your local copy, so you don't need to worry about clashing with other people's code at this point or making sure that all changes are usable. Sometimes when I decide I've gone down the wrong path I'll take a snapshot of the ill conceived changes before I roll them back, just in case I decide later on that some of the ideas weren't so bad after all.&lt;br /&gt;&lt;br /&gt;Once you have something that's ready for others to use, it's time to:&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Share&lt;/h4&gt;&lt;br /&gt;To get your changes into the hands of others, you can push them back up to the central repository. You do this with the &lt;code&gt;hg push&lt;/code&gt; command. Since we created our local repository using clone, your local copy knows where to send the changes. Also, if you'd like to do something different, like push your changes to a different location than where we cloned from, you can take a look at more options of the push command using &lt;code&gt;hg help push&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The push command will upload all of your local commits, along with those handy descriptions, for the rest of your team to see. &lt;br /&gt;&lt;br /&gt;Now others on your team are pushing up their changes too and it would be great to get their changes so you are all working on the same code. What's that saying, "It's better to give than to...?"&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Receive&lt;/h4&gt;&lt;br /&gt;To get the changes others have posted to the repository into your local copy, you use &lt;code&gt;hg pull&lt;/code&gt;. Running this command copies the change sets that you haven't received yet to your local repository, but it doesn't edit your files or apply the changes just yet. Since you might want to be selective about what changes you apply, Mercurial splits applying the changes into two steps. First you pull, then you use &lt;code&gt;hg update&lt;/code&gt;. When called with no additional arguments like that, &lt;code&gt;hg update&lt;/code&gt; applies all of the changes.&lt;br /&gt;&lt;br /&gt;I recommend pulling and updating often. In our group, since there were just a few of us, we'd post in a chat room when we pushed changes so others would know to pull. If you push before you pull in other people's changes, you might need to use &lt;code&gt;hg merge&lt;/code&gt;. Also, you can see all of the changes which are in your repository with the &lt;code&gt;hg log&lt;/code&gt; command. (Triple bonus, hey a hat-trick!)&lt;br /&gt;&lt;br /&gt;There you have it!&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Want more?&lt;/h4&gt;&lt;br /&gt;I tried to keep this simple and focused, the bare minimum to work on a project with a small team. Mercurial has more to offer. I haven't touched branches yet, or looking at diffs, or creating your own local repository from scratch, or sharing your changes by running your own server locally. All of these are just single commands! For further reading take a look at &lt;a href="http://hgbook.red-bean.com/read/"&gt;Mercurial: The Definitive Guide&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There are a few services which offer hosting of Mercurial repositories. For open source, &lt;a href="http://code.google.com/p"&gt;Project Hosting on Google Code&lt;/a&gt; is an option as well as bitbucket which I used for the first time this past week. Any other Mercurial hosting providers that you recommend?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-6819048179265099702?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/6819048179265099702/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=6819048179265099702" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6819048179265099702?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6819048179265099702?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2011/01/mercurial-in-five-commands.html" title="Mercurial in Five Commands" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkIDSH47eSp7ImA9Wx5XEUQ.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-9222063360923542109</id><published>2010-09-10T22:56:00.000-07:00</published><updated>2010-09-11T01:42:59.001-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-11T01:42:59.001-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="Greek" /><title>Greekish</title><content type="html">I've long been fascinated with other alphabets. All of these strange and unusual symbols, it's almost like a code. This love of secrets was one of the reasons that I studied Ancient Greek. After reading and writing quite a bit of Greek, reading the alphabet became second nature. I even began taking notes using the Greek alphabet but using English words (since my Greek vocabulary is sadly inadequate). Performing simple character substitution sounded like a perfect one-hour project so I whipped up a &lt;a href="http://www.jeffscudder.com/greekish"&gt;simple web page&lt;/a&gt; to convert English text into a Greek alphabet equivalent. I call it &lt;a href="http://www.jeffscudder.com/greekish"&gt;Greekish&lt;/a&gt;. For example, the phrase&lt;pre&gt;So long and thanks for all the fish.&lt;/pre&gt;becomes&lt;pre&gt;Σο λονγ ανδ θανκσ φορ αλλ θε φισh.&lt;/pre&gt;which would be quite confusing to a Greek speaker but perfectly natural to an English speaker who knows the Greek alphabet.&lt;br /&gt;&lt;br /&gt;Note that some English characters do not have direct equivalents in Greek. A c would be a κ, a σ, or a χ for ch. The h is one of the more interesting stories. For a leading h before a vowel Greek uses a &lt;a href="http://en.wikipedia.org/wiki/Greek_diacritics#Breathings"&gt;breathing mark&lt;/a&gt;. When combined with a consonant, special characters are used, like θ for th, χ for ch, and φ for ph. I chose to use φ only for the English f, not ph, since ph does not make the f sound in some English contexts. The word u&lt;em&gt;ph&lt;/em&gt;ill is one example. Also, I didn't bother to handle the special case of s at the end of word, for which ς is used instead of σ.&lt;br /&gt;&lt;br /&gt;Now that you know more than you ever wanted to about the Greek alphabet, what is a simple project that you can tackle in an hour? Don't just think of one, go do it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-9222063360923542109?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/9222063360923542109/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=9222063360923542109" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/9222063360923542109?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/9222063360923542109?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2010/09/greekish.html" title="Greekish" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;CUEEQ3o4cSp7ImA9WxFbEUk.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-1653518540945732437</id><published>2010-06-22T01:24:00.000-07:00</published><updated>2010-07-03T00:53:22.439-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-03T00:53:22.439-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="c" /><title>A Simple Testing Library for C</title><content type="html">To prepare for a recent post graduate computer science class, I wrote a small library in C which aids in the creation of lightweight, unit-test-like programs. The code can be found &lt;a href="http://code.google.com/p/c7e3/source/browse/#hg/testing"&gt;here&lt;/a&gt;, and using it looks a bit like this:&lt;pre&gt;#include"asserts.h"&lt;br /&gt;&lt;br /&gt;int main(void)&lt;br /&gt;{&lt;br /&gt;  c7e3_assert(1 == 1, "1 should equal 1");&lt;br /&gt;  c7e3_assert(2 == 2, "2 should equal 2");&lt;br /&gt;  c7e3_report();&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;/pre&gt;The design follows the &lt;a href="http://en.wikipedia.org/wiki/KISS_principle"&gt;KISS principle&lt;/a&gt; and I think it is a nice fit to the simplicity of C. While there is not much to it, I wrote numerous tests using it over the past couple of months and all of that testing certainly &lt;a href="http://twitter.com/jscud/status/17233896676"&gt;paid off&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-1653518540945732437?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/1653518540945732437/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=1653518540945732437" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/1653518540945732437?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/1653518540945732437?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2010/06/simple-testing-library-for-c.html" title="A Simple Testing Library for C" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkUESX8zeyp7ImA9WxFUEUQ.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-6382976484221023970</id><published>2010-06-18T00:30:00.000-07:00</published><updated>2010-06-22T01:10:08.183-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-22T01:10:08.183-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="app engine" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>JavaScript Tricks to Speed up Your Site</title><content type="html">One of the techniques which makes the web so powerful is the ability to load code, images, and other resources from all over the Internet. So often though, the process of loading these resources and ensuring that all of the required pieces are in place leads to a slow experience for visitors. With the ability to include so much code from across the web, visiting a site could potentially be like installing a new program when it comes to the amount of stuff that needs to be downloaded.&lt;br /&gt;&lt;br /&gt;With this in mind, there are a couple of nifty tricks that can help make your app more responsive and I've written up an example site and testing server that shows some ideas for speeding up the user experience when you need to wait for the DOM to load or for additional JavaScript to be fetched and run. We'll begin with document operations.&lt;br /&gt;&lt;br /&gt;Often the JavaScript running on a page manipulates the DOM, using &lt;code&gt;document.getElementById&lt;/code&gt; here and &lt;code&gt;document.createElement&lt;/code&gt; there. In order to ensure that all the pieces of the page are in place, web programmers often take advantage of the onload callback. It might be used like this&lt;pre&gt;&amp;lt;body onload="runMyCodeNow()"&amp;gt;&lt;/pre&gt;Using this technique ensures that all of the things your code might want to read and write from the page are in place. All images have been downloaded, CSS rules have been applied, the layout is all there. However, all of this comes with a cost, your code doesn't run until every last resource has been fetched and rendered. Even the little footer at the bottom of the page, for example, that your code doesn't care about.&lt;br /&gt;&lt;br /&gt;There is a another way, we could request that resources be loaded in parallel and start executing our code before the page is fully loaded. Chances are, your code doesn't need the complete page to be loaded before it starts running, and running before &lt;code&gt;onload&lt;/code&gt; will reduce the delay for your users. Before I dive into how this can be accomplished, lets look at an example which uses the old fashioned way.&lt;br /&gt;&lt;br /&gt;Lets say you have a web page, a little HTML which includes five JavaScript files. One may be a library used to do animation, another one for loading the users data. In any case, all of these files need to be loaded and some of them depend on others.&lt;br /&gt;&lt;br /&gt;The biggest bottleneck for your users is almost certainly having all of these resources load. Network latency is a killer, and something that is often overlooked during development. To create a simulated network environment which can give a more realistic (or even pessimistic) view of the cost of loading these resources, I wrote a "slow server" which can introduce a delay to the file requested. Here is the code for my testing server (designed to run on &lt;a href="http://code.google.com/appengine/"&gt;App Engine&lt;/a&gt;):&lt;pre&gt;def FilePath(path):&lt;br /&gt;  """The requested path into a local file path."""&lt;br /&gt;&lt;br /&gt;  return os.path.join(os.path.dirname(__file__), 'files', path[1:])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class SleepyRenderer(webapp.RequestHandler):&lt;br /&gt;  """Serves the requested page with a client configured delay.&lt;br /&gt;&lt;br /&gt;  Delay is given as a URL parameter in hundredths of a second to delay.&lt;br /&gt;  For example, 200 means wait 2 seconds before responding.&lt;br /&gt;&lt;br /&gt;  Example request:&lt;br /&gt;  http://localhost:8080/hi.html?delay=300&amp;contenttype=text/html&lt;br /&gt;  """&lt;br /&gt;&lt;br /&gt;  def get(self):&lt;br /&gt;    path = self.request.path&lt;br /&gt;    delay = self.request.get('delay')&lt;br /&gt;    content_type = self.request.get('contenttype') or 'text/html'&lt;br /&gt;    if delay:&lt;br /&gt;      time.sleep(int(delay)/100)&lt;br /&gt;    http_status = 200&lt;br /&gt;    requested_file = None&lt;br /&gt;&lt;br /&gt;    try:&lt;br /&gt;      requested_file = open(FilePath(path))&lt;br /&gt;      self.response.out.write(requested_file.read())&lt;br /&gt;      requested_file.close()&lt;br /&gt;    except IOError:&lt;br /&gt;      http_status = 404&lt;br /&gt;&lt;br /&gt;    self.response.set_status(http_status)&lt;br /&gt;    self.response.headers['Content-Type'] = content_type&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    application = webapp.WSGIApplication([('/.*', SleepyRenderer)],&lt;br /&gt;                                         debug=True)&lt;br /&gt;    util.run_wsgi_app(application)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    main()&lt;/pre&gt;With the above code we can introduce a delay on each individual file. To see this in action with our example, here is some HTML which shows a traditional approach, include script includes and an onload callback when everything has loaded.&lt;pre&gt;&amp;lt;html&amp;gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;    &amp;lt;script src="/testa.js?delay=500&amp;contenttype=text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;script src="/testb.js?delay=400&amp;contenttype=text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;script&amp;gt;&lt;br /&gt;      function init() {&lt;br /&gt;        document.getElementById('output');&lt;br /&gt;        output.innerHTML = [&lt;br /&gt;          'a is ' + a,&lt;br /&gt;          'b is ' + b,&lt;br /&gt;          'c is ' + c,&lt;br /&gt;          'd is ' + d,&lt;br /&gt;          'e is ' + e&lt;br /&gt;        ].join('&amp;lt;br&amp;gt;');&lt;br /&gt;      }&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;script src="/testc.js?delay=300&amp;contenttype=text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;  &amp;lt;body onload="init()"&amp;gt;&lt;br /&gt;    &amp;lt;script src="/testd.js?delay=200&amp;contenttype=text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;div id="output"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;script src="/teste.js?delay=100&amp;contenttype=text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;script src="/testa.js?delay=500&amp;contenttype=text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;With the above, the page takes several seconds to load and when the very last script has loaded, the 'output' div gets its contents. In many cases, the code really doesn't need to wait for all resources to load, only the ones that are necessary for the code to run. In this case, since the information is added to the output div, we need the output div to exist in the DOM, but we may not need the entire page to load.&lt;br /&gt;&lt;br /&gt;If you look at this loading process in a profiler you might see something like this:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_54HtFT6yBFI/TB_fhm_iT8I/AAAAAAAAAgk/tMxvwK8_qmM/s1600/Picture+37.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 228px;" src="http://2.bp.blogspot.com/_54HtFT6yBFI/TB_fhm_iT8I/AAAAAAAAAgk/tMxvwK8_qmM/s400/Picture+37.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5485348639656267714" /&gt;&lt;/a&gt;Now for our first nifty trick. One way to check to see if the necessary prerequisites are present, is by polling the DOM or the JavaScript environment, to see if conditions are right for the code to run. Here is an example of how this code might be rewritten when using some polling helper functions:&lt;pre&gt;    &amp;lt;script&amp;gt;&lt;br /&gt;      loader.whenNodePresent('output',&lt;br /&gt;        function() {&lt;br /&gt;          var output = document.getElementById('output');&lt;br /&gt;          loader.whenReady(function() {return window['a'];},&lt;br /&gt;              function() {&lt;br /&gt;                output.innerHTML += 'a is ' + a + '&amp;lt;br&amp;gt;';&lt;br /&gt;              }); &lt;br /&gt;          loader.whenReady(function() {return window['b'];},&lt;br /&gt;            function() {&lt;br /&gt;              output.innerHTML += 'b is ' + b + '&amp;lt;br&amp;gt;';&lt;br /&gt;            }); &lt;br /&gt;          loader.whenReady(function() {return window['c'];},&lt;br /&gt;            function() {&lt;br /&gt;              output.innerHTML += 'c is ' + c + '&amp;lt;br&amp;gt;';&lt;br /&gt;            }); &lt;br /&gt;          loader.whenReady(function() {return window['d'];},&lt;br /&gt;            function() {&lt;br /&gt;              output.innerHTML += 'd is ' + d + '&amp;lt;br&amp;gt;';&lt;br /&gt;            }); &lt;br /&gt;          loader.whenReady(function() {return window['e'];},&lt;br /&gt;            function() {&lt;br /&gt;              output.innerHTML += 'e is ' + e + '&amp;lt;br&amp;gt;';&lt;br /&gt;            }); &lt;br /&gt;        })&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;/pre&gt;The code to track the prerequisites and poll is quite simple:&lt;pre&gt;loader.waiting = [];&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;loader.whenReady = function(testFunction, callback) {&lt;br /&gt;  if (testFunction()) {&lt;br /&gt;    callback();&lt;br /&gt;  } else {&lt;br /&gt;    loader.waiting.push([testFunction, callback]);&lt;br /&gt;    window.setTimeout(loader.checkWaiting, 200);&lt;br /&gt;  }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;loader.checkWaiting = function() {&lt;br /&gt;  var oldWaiting = loader.waiting;&lt;br /&gt;  var numWaiting = oldWaiting.length;&lt;br /&gt;  loader.waiting = [];&lt;br /&gt;  for (var i = 0; i &lt; numWaiting; i++) {&lt;br /&gt;    if (oldWaiting[i][0]()) {&lt;br /&gt;      oldWaiting[i][1]();&lt;br /&gt;    } else {&lt;br /&gt;      loader.waiting.push(oldWaiting[i]);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (loader.waiting.length &gt; 0) {&lt;br /&gt;    window.setTimeout(loader.checkWaiting, 200);&lt;br /&gt;  }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;loader.whenNodePresent = function(nodeId, callback) {&lt;br /&gt;  loader.whenReady(function () {&lt;br /&gt;    return document.getElementById(nodeId);&lt;br /&gt;  }, callback);&lt;br /&gt;};&lt;/pre&gt;In the above we use the &lt;code&gt;whenReady&lt;/code&gt; function which takes a couple of functions, one to return a truthy or a falsey value, and one to call back when the first function evaluates to true. If the condition function isn't true when this first call is made, we check back every so often to see if it is ready.&lt;br /&gt;&lt;br /&gt;With these changes, we shave several seconds off of the user perceived loading time. Specifically we no longer need to wait for the duplicate load (of the &lt;code&gt;testa&lt;/code&gt; script) at the end of the body. The page also appears to be more responsive because the later script's messages appear just after they load but before the page is complete.&lt;br /&gt;&lt;br /&gt;Now that we've seen a way to work around the need for an onload callback, lets look at another place we can tweak the browser's behavior to make a web page more responsive: dynamic script loading.&lt;br /&gt;&lt;br /&gt;The most straightforward way to include new code in your page is to use a script tag, something like:&lt;pre&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;lt;script src="some_great_sites_javascript"&amp;gt;&lt;br /&gt;...&lt;/pre&gt;When the browser's JavaScript interpreter encounters this script src, it stops whatever it's doing and fetches that resource. It doesn't do any more rendering or executing of code until it's finished. This behavior varies a bit in different browsers and is likely an artifact of an old design in which this kind of single threaded behavior was the only option. Since some sites might depend on this linear behavior to get a script's dependencies all in order, this quirk might be with us for a long time. Most of this time, waiting like this is a really silly idea. How often do the scripts that you include depend on one another?&lt;br /&gt;&lt;br /&gt;There are a few parts to this trick. The first is to not put all of script includes in the HTML, you could have JavaScript add new script elements to the page which will cause new code to be loaded as needed. In this way, you could load only the resources that are needed at the moment, perhaps some resources would not end up being requested at all. Including a new script could be done in two ways:&lt;pre&gt;document.write('&amp;lt;script src="somefile.js"&amp;gt;&amp;lt;/script&amp;gt;');&lt;/pre&gt;or&lt;pre&gt;var newScript = document.createElement('script');&lt;br /&gt;newScript.src = 'somefile.js';&lt;br /&gt;document.body.appendChild(newScript);&lt;/pre&gt;Each of the above is appropriate in different situations. Document write adds HTML directly into the page at the point where the page is being loaded, it should only be used for script tag inclusion if the page is not yet loaded. If that page is loaded, using &lt;code&gt;document.write&lt;/code&gt; to add the script tag will wipe out the existing body entirely. I've seen this issue in the wild, if you assume &lt;code&gt;document.write&lt;/code&gt; is always safe, you'll be bitten when using it after the page has loaded.&lt;br /&gt;&lt;br /&gt;Instead you can perform a check to see if &lt;code&gt;document.body&lt;/code&gt; exists, if it does then use &lt;code&gt;document.body.appendChild&lt;/code&gt;. If it does not yet exist, use &lt;code&gt;document.write&lt;/code&gt;. The code for this loader logic might look something like this:&lt;pre&gt;loader.loadScript = function(url) {&lt;br /&gt;  if (document.body) {&lt;br /&gt;    var newScript = document.createElement('script');&lt;br /&gt;    newScript.type = 'text/javascript';&lt;br /&gt;    newScript.src = url;&lt;br /&gt;    document.body.appendChild(newScript);&lt;br /&gt;  } else {&lt;br /&gt;    document.write('&amp;lt;scr' + 'ipt type="text\/javascript" src="' +&lt;br /&gt;                   url + '"&amp;gt;&amp;lt;\/scr' + 'ipt&amp;gt;');&lt;br /&gt;  }&lt;br /&gt;};&lt;/pre&gt;Now we can request that new JavaScript code be loaded on the fly and it works when the page has not yet finished loaded as well as after it has. &lt;br /&gt;&lt;br /&gt;There is one more trick we can add to this loader. Some browsers will interpret the JavaScript in the order in which the scripts were requested, not the order in which they finished loading. That means that a fast loading script further down the list won't be run until a slower script, which appears above it, is loaded. One way we could defeat this delay, is to break the script includes out of linear execution in the JavaScript. If you use setTimeout to introduce a delay in adding the script include to the page, then the code which sets up the script requests can finish quickly and the browser will get back to the script requests later without the same linear constraints. In our code, we wrap the section of &lt;code&gt;loader.loadScript&lt;/code&gt; in a short timeout as follows:&lt;pre&gt;loader.loadScript = function(url) {&lt;br /&gt;  &lt;strong&gt;window.setTimeout(function() {&lt;/strong&gt;&lt;br /&gt;    if (document.body) {&lt;br /&gt;      var newScript = document.createElement('script');&lt;br /&gt;      newScript.type = 'text/javascript';&lt;br /&gt;      newScript.src = url;&lt;br /&gt;      document.body.appendChild(newScript);&lt;br /&gt;    } else {&lt;br /&gt;      document.write('&amp;lt;scr' + 'ipt type="text/javascript" src="' +&lt;br /&gt;                     url + '"&amp;gt;&amp;lt;\/scr' + 'ipt&amp;gt;');&lt;br /&gt;    }&lt;br /&gt;  &lt;strong&gt;}, 1);&lt;/strong&gt;&lt;br /&gt;};&lt;/pre&gt;With the above changes in place, our example page from before now loads like this when profiled (note that the messages appear in the order that the scripts were loaded, we don't have to wait for everything before we edit the page):&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_54HtFT6yBFI/TB_g-rV9bxI/AAAAAAAAAgs/-DJ53U2zw4s/s1600/Picture+38.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 228px;" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TB_g-rV9bxI/AAAAAAAAAgs/-DJ53U2zw4s/s400/Picture+38.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5485350238551895826" /&gt;&lt;/a&gt;Through the course of this post, I've written a small library for using these tricks when loading JavaScript dynamically in the page as well as a server for trying it out. These are available &lt;a href="https://code.google.com/p/c7e3/source/browse/#hg/slowserver"&gt;here&lt;/a&gt; as open source code. There are some improvements that could be made here. Off the top of my head, the checkWaiting function could eventually time out if a condition continues to not be met. Also the loader could do more to check to see if a requested script has already been loaded. Any more ideas?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-6382976484221023970?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/6382976484221023970/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=6382976484221023970" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6382976484221023970?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6382976484221023970?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2010/06/javascript-tricks-to-speed-up-your-site.html" title="JavaScript Tricks to Speed up Your Site" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_54HtFT6yBFI/TB_fhm_iT8I/AAAAAAAAAgk/tMxvwK8_qmM/s72-c/Picture+37.png" height="72" width="72" /><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;AkIBSHk6fSp7ImA9WxBaE0w.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-7977049507605419497</id><published>2010-03-12T20:26:00.001-08:00</published><updated>2010-03-22T21:29:19.715-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-22T21:29:19.715-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="travel" /><title>Quite a View</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_54HtFT6yBFI/S5u6Qa19M3I/AAAAAAAAAfE/ucQ0PV3NJJU/s1600-h/2010-03-09+11.08.21+(1).jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="http://4.bp.blogspot.com/_54HtFT6yBFI/S5u6Qa19M3I/AAAAAAAAAfE/ucQ0PV3NJJU/s320/2010-03-09+11.08.21+(1).jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5448152965481050994" /&gt;&lt;/a&gt;I recently had the privilege of working out of an office near the beach. The sun, the ocean breeze, the pounding of the surf. Laptop makers should really come up with a way to sand-proof these machines.&lt;br /&gt;&lt;br /&gt;I fully expect that this will draw some inside jokes from a few of you in the &lt;a href="http://webchat.freenode.net/?channels=googleajaxapis"&gt;#googleajaxapis IRC channel&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-7977049507605419497?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/7977049507605419497/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=7977049507605419497" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/7977049507605419497?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/7977049507605419497?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2010/03/quite-view.html" title="Quite a View" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_54HtFT6yBFI/S5u6Qa19M3I/AAAAAAAAAfE/ucQ0PV3NJJU/s72-c/2010-03-09+11.08.21+(1).jpg" height="72" width="72" /><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkYBRn8_eSp7ImA9WxBUGUU.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-3898783742476646871</id><published>2010-02-08T22:51:00.001-08:00</published><updated>2010-03-07T09:49:17.141-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-03-07T09:49:17.141-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="blogger" /><category scheme="http://www.blogger.com/atom/ns#" term="css" /><title>New Blog Template</title><content type="html">With the growing popularity of mobile phones and lower resolution netbooks, a fixed width blog format is looking less and less appealing. I decided to work on a new look for this blog. The main feature I was looking for was a set of CSS rules that would allow the main content to scale with the viewer's browser window width. If the window is too narrow, the less important content on the sidebar should flow down below the main content.&lt;br /&gt;&lt;br /&gt;I began with an existing theme for Blogger, Tekka, that pushes the right sidebar down beneath the main content when the window is narrow. I then made some CSS changes to tweak the look to fit my tastes.&lt;br /&gt;&lt;br /&gt;First things first, the default fonts must go! I drew much of my inspiration from &lt;a href="http://blog.notdot.net/"&gt;Nick Johnson's blog&lt;/a&gt;, specifically the way that serif fonts are used in some places for accent while sans-serif is used for most of the content. This is a technique that I experimented with a bit in a graphic design class in college.&lt;br /&gt;&lt;br /&gt;For titles I added:&lt;pre&gt;h1, h2 {&lt;br /&gt;  ...&lt;br /&gt;  font-family: Georgia, 'Times New Roman', Times, serif;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;h3 {&lt;br /&gt;  ...&lt;br /&gt;  font-family: Trebuchet MS, Verdana, Sans-serif;&lt;br /&gt;}&lt;/pre&gt;To change the font used for the main text in the page, I set a template variable as follows:&lt;pre&gt;&amp;lt;Variable name="bodyFont" description="Text Font"&lt;br /&gt;  type="font" &lt;br /&gt;  default="normal normal 100% 'Lucida Grande', &lt;br /&gt;    Verdana, Helvetica, sans-serif" &lt;br /&gt;  value="normal normal 100% 'Lucida Grande', &lt;br /&gt;    Verdana, Helvetica, sans-serif"&amp;gt;&lt;/pre&gt;Next I set about changing the style of the title link. You see, when someone is viewing a single post, the blog title becomes a link to the main blog page. So, the blog's title is styled like a link and I found the default link styles a bit jarring. This might seem like a minor change but so often it's the little things that matter. I set this style using&lt;pre&gt;h1 a:visited {&lt;br /&gt;  ...&lt;br /&gt;  color: #505050;&lt;br /&gt;}&lt;/pre&gt;Now that the text styling is done, the layout could use a bit of adjustment. I changed the margins on the main content to the following:&lt;pre&gt;#content-wrapper { &lt;br /&gt;  margin-top: 20px;&lt;br /&gt;  margin-$endSide: 30px;&lt;br /&gt;  margin-bottom: 30;&lt;br /&gt;  margin-$startSide: 35px;&lt;br /&gt; }&lt;/pre&gt;I removed the padding from the &lt;code&gt;h1&lt;/code&gt; title style and the date header. I also removed the style rule entirely for the description.&lt;br /&gt;&lt;br /&gt;We're nearing the end! The sidebar wasn't quite to my liking so I changed the border style. Lastly, in order to get the sidebar to flow down beneath the main content I set a minimum width. Here is the final style for the sidebar:&lt;pre&gt;#sidebar {&lt;br /&gt;  text-transform:none;&lt;br /&gt;  background-color: $sidebarBgColor;&lt;br /&gt;  color: $sidebarTextColor;&lt;br /&gt;  padding-$startSide: 20px;&lt;br /&gt;  width: 30%;&lt;br /&gt;  min-width: 215px;&lt;br /&gt;  float: $endSide;&lt;br /&gt;  font: $bodyFont;&lt;br /&gt;  border-$startSide:1px solid $mainTextColor;&lt;br /&gt;  word-wrap: break-word;&lt;br /&gt;  overflow: hidden;&lt;br /&gt;}&lt;/pre&gt;I debated about posting the whole template here, but I'm not sure that it would be very readable. If you would like the whole thing, leave a comment and I can email it to you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-3898783742476646871?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/3898783742476646871/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=3898783742476646871" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/3898783742476646871?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/3898783742476646871?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2010/02/new-blog-template.html" title="New Blog Template" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;A0YMRng6eCp7ImA9WxBWFkQ.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-354090936792118313</id><published>2010-01-13T23:45:00.000-08:00</published><updated>2010-02-08T23:06:27.610-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-08T23:06:27.610-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="q12" /><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="css" /><title>DOM Manipulation in JavaScript, a Utility</title><content type="html">In my latest addition to the &lt;a href="http://code.google.com/p/q12/"&gt;q12 JavaScript library&lt;/a&gt;, I have added a simple function to construct a nested document structure. I grew tired of writing repetitive and difficult to follow code like this &lt;pre&gt;var div = document.createElement('div');&lt;br /&gt;div['id'] = 'outer-div';&lt;br /&gt;var link = document.createElement('a');&lt;br /&gt;link.href = 'http://blog.jeffscudder.com';&lt;br /&gt;link.appendChild(document.createTextNode('my blog'));&lt;br /&gt;div.appendChild(link);&lt;/pre&gt;Using the new tree function this would become:&lt;pre&gt;var div = tree([&lt;br /&gt;    'div', {&lt;br /&gt;        id: 'outer-div'}, [&lt;br /&gt;      'a', {&lt;br /&gt;          href: 'http://blog.jeffscudder.com'}, [&lt;br /&gt;        'my blog']]]);&lt;/pre&gt;In the process of writing this utility function, I came across another small oddity in editing HTML using JavaScript related to the style attribute. Most HTML attributes are simple text properties, however when editing the CSS style of a node, you cannot set the full content of the style attribute. &lt;pre&gt;// You cannot do this:&lt;br /&gt;div.style = "color:red; font-weight:bold";&lt;/pre&gt;Instead, you set each sub item in the style attribute:&lt;pre&gt;// This works:&lt;br /&gt;div.style.color = 'red';&lt;br /&gt;div.style['font-weight'] = 'bold';&lt;/pre&gt;The tree function that I've written handles this nested style structure as well. Here's an example:&lt;pre&gt;['div', {&lt;br /&gt;    'id': 'foo', &lt;br /&gt;    'style': {'color': 'red', 'font-weight': 'bold'}}, &lt;br /&gt;  'This works too!']&lt;/pre&gt;Now that you have an idea of how to use this utility, here's the code. Minus the comments it's only about twenty-one lines.&lt;pre&gt;/**&lt;br /&gt; * Creates a DOM tree from a simple list.&lt;br /&gt; * The structure of the tree passed in is as follows:&lt;br /&gt; * ['elementTag', &lt;br /&gt; *      {attribute1: value,&lt;br /&gt; *       attribute2: value,&lt;br /&gt; *       style: {property1: value,&lt;br /&gt; *               property2: value}},&lt;br /&gt; *    'child text node',&lt;br /&gt; *    ['elementTag',&lt;br /&gt; *         {property: value},&lt;br /&gt; *       'grandchild text node'],&lt;br /&gt; *    'third node']&lt;br /&gt; * The above will result in a DOM node which has three child nodes, the&lt;br /&gt; * first and third will be text nodes because the values were strings.&lt;br /&gt; * The second child node will be a DOM node as well.&lt;br /&gt; *&lt;br /&gt; * @param {Array} t The tree's structure as a collection of strings, lists,&lt;br /&gt; *     and simple objects. The structure is as follows&lt;br /&gt; *     ['elementTag', {attributes}, child, child, child, ...]&lt;br /&gt; * @return {Element} Returns a new DOM element.&lt;br /&gt; */&lt;br /&gt;function tree(t) {&lt;br /&gt;  // Create the node using the tag which is first in the list.&lt;br /&gt;  var domNode = document.createElement(t[0]);&lt;br /&gt;  // Add all HTML attributes to the node.&lt;br /&gt;  for (var key in t[1]) {&lt;br /&gt;    // The style attributes get special treatment.&lt;br /&gt;    if (key == 'style') {&lt;br /&gt;      for (var styleAttribute in t[1].style) {&lt;br /&gt;        domNode.style[styleAttribute] = t[1].style[styleAttribute];&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      domNode[key] = t[1][key];&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  // Iterate over all child nodes, converting them to either text or HTML&lt;br /&gt;  // nodes.&lt;br /&gt;  for (var index = 2, child; child = t[index]; index++) {&lt;br /&gt;    if (typeof(child) == 'string') {&lt;br /&gt;      domNode.appendChild(document.createTextNode(child));&lt;br /&gt;    } else {&lt;br /&gt;      // Buid recursively.&lt;br /&gt;      domNode.appendChild(tree(child));&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return domNode; &lt;br /&gt;}&lt;/pre&gt;What do you think, is there anything you would add?&lt;br /&gt;&lt;br /&gt;On a realted note, if you're interested in a more powerful templating system, you might want to try &lt;a href="&lt;br /&gt;http://code.google.com/closure/templates/"&gt;Closure Templates&lt;/a&gt; which were &lt;a href="http://googlecode.blogspot.com/2009/11/introducing-closure-tools.html"&gt;recently open sourced&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-354090936792118313?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/354090936792118313/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=354090936792118313" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/354090936792118313?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/354090936792118313?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2010/01/dom-manipulation-in-javascript-utility.html" title="DOM Manipulation in JavaScript, a Utility" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;DkUNQno8cCp7ImA9WxNUGE4.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-6782376423778260654</id><published>2009-10-22T16:00:00.000-07:00</published><updated>2009-11-09T22:24:53.478-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-09T22:24:53.478-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><title>The joys of onKeyDown</title><content type="html">I recently found myself in need of a way to detect when a user presses enter when typing in a text input box and happened upon one of the &lt;a href="http://www.quirksmode.org/js/keys.html"&gt;quirkiest&lt;/a&gt; of browser features, key press detection in JavaScript. Searching the Internet provided a few ideas but nothing that quite fit what I was looking for, so I thought maybe I should get the word out about what I've learned.&lt;br /&gt;&lt;br /&gt;Lets assume that we have a form with an input box and we want to perform some special action when the user presses the enter key while focus is on the text input box. We begin by adding an onKeyDown event to the input box (you might also consider onKeyUp, but onKeyPress seems like it might be somewhat quirkier).&lt;pre&gt;&amp;lt;form&amp;gt;&lt;br /&gt;  &amp;lt;input type="text" id="my-input"&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;div id="output"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;&lt;br /&gt;  document.getElementById('my-input').onkeydown = function(e) {};&lt;br /&gt;&amp;lt;script&amp;gt;&lt;/pre&gt;Now the first hurdle is that in IE, Safari, and Chrome you can access the ASCII character code for the key which was pressed in &lt;code&gt;window.event.keyCode&lt;/code&gt; but in FireFox you'll need to get the event object and look at the &lt;code&gt;which&lt;/code&gt; member.&lt;pre&gt;&amp;lt;script&amp;gt;&lt;br /&gt; document.getElementById('my-input').onkeydown = function(e) {&lt;br /&gt;   var keyCode = 0;&lt;br /&gt;   if (window &amp;amp;&amp;amp; window.event &amp;amp;&amp;amp; window.event.keyCode) {&lt;br /&gt;     keyCode = window.event.keyCode;&lt;br /&gt;   } else if (e &amp;amp;&amp;amp; e.which) {&lt;br /&gt;     keyCode = e.which;&lt;br /&gt;   } else {&lt;br /&gt;     alert('fail');&lt;br /&gt;   }&lt;br /&gt;   document.getElementById('output').innerHTML = 'You pressed ' + keyCode;&lt;br /&gt; };&lt;br /&gt;&amp;lt;script&amp;gt;&lt;/pre&gt;Now there is one gotcha with what we've written so far. Each key that we press when focus is on the desired input causes our code to be run, but the browser also performs the default behavior for that key. If our input is a simple text box, the characters pressed will show up in the text box. If the input is part of a form with a submit button, pressing enter will cause the form to be submitted. If the form is submitted then the browser is sent to a different page which in this case is not what we want.&lt;br /&gt;&lt;br /&gt;There are a few ways to prevent the key presses from also triggering the default behavior and do only our behavior. One of the simplest ways is to return false from the &lt;code&gt;onKeyDown&lt;/code&gt; function. If we change our handler to the below, characters will not show up in a text input box because our handler consumes them.&lt;pre&gt; document.getElementById('my-input').onkeydown = function(e) {&lt;br /&gt;   var keyCode = 0;&lt;br /&gt;   if (window &amp;amp;&amp;amp; window.event &amp;amp;&amp;amp; window.event.keyCode) {&lt;br /&gt;     keyCode = window.event.keyCode;&lt;br /&gt;   } else if (e &amp;amp;&amp;amp; e.which) {&lt;br /&gt;     keyCode = e.which;&lt;br /&gt;   } else {&lt;br /&gt;     alert('fail');&lt;br /&gt;   }&lt;br /&gt;   document.getElementById('output').innerHTML = 'You pressed ' + keyCode;&lt;br /&gt;   return false;&lt;br /&gt; };&lt;/pre&gt;With the above, the last key press is displayed in the output, but in most cases we probably do want the user to see the chracters they've typed showing up in the input box. We just want to prevent the form submit when the user presses the enter key. To accomplish this behavior, we can return false when we don't want the key press to propoage and return true when the default behavior should also be performed.&lt;pre&gt; document.getElementById('my-input').onkeydown = function(e) {&lt;br /&gt;   var keyCode = 0;&lt;br /&gt;   if (window &amp;amp;&amp;amp; window.event &amp;amp;&amp;amp; window.event.keyCode) {&lt;br /&gt;     keyCode = window.event.keyCode;&lt;br /&gt;   } else if (e &amp;amp;&amp;amp; e.which) {&lt;br /&gt;     keyCode = e.which;&lt;br /&gt;   } else {&lt;br /&gt;     alert('fail');&lt;br /&gt;   }&lt;br /&gt;   document.getElementById('output').innerHTML = 'You pressed ' + keyCode;&lt;br /&gt;   if (keyCode == 13) { // 13 is the key code for the enter key.&lt;br /&gt;     return false;&lt;br /&gt;   } else {&lt;br /&gt;     return true;&lt;br /&gt;   }&lt;br /&gt; };&lt;/pre&gt;There you have it, a way to detect when the enter key is pressed in our input box while also preventing the form from being submitted.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-6782376423778260654?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/6782376423778260654/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=6782376423778260654" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6782376423778260654?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6782376423778260654?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/10/joys-of-onkeydown.html" title="The joys of onKeyDown" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;CEEARnY_eSp7ImA9WxNXEk0.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-8890003236705886325</id><published>2009-09-28T22:16:00.000-07:00</published><updated>2009-09-28T22:37:27.841-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-28T22:37:27.841-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="journal" /><category scheme="http://www.blogger.com/atom/ns#" term="writing" /><title>Journaling</title><content type="html">This evening I found my long lost journal. In dusting it off and cracking it open I learned that my last entry had been over a year ago. With the exception of this last year long hiatus, I've regularly kept a journal since sometime in 1999 or 2000. I've found writing my thoughts helps me crystallize them and I tend to deal with thoughts and emotions internally, mulling them for hours or days. This is one of the reasons I think I've stuck with blogging.&lt;br /&gt;&lt;br /&gt;Keeping a private journal is a completely different feeling than keeping a blog. When I write here I'm intensely (sometimes even paralyzingly) conscious of my audience. Not that it's a large one. Here I tend to focus on software related discoveries and creations. It is one creative outlet, but one decidedly different from when I journal. &lt;br /&gt;&lt;br /&gt;It was refreshing to scratch blank pages with pen, writing for an audience which understands me better than most.&lt;br /&gt;&lt;br /&gt;Okay now how many of you, when you saw the title, thought that I was going to talk about a &lt;a href="http://en.wikipedia.org/wiki/Journaling_file_system"&gt;file system&lt;/a&gt; or &lt;a href="http://labs.google.com/papers/bigtable-osdi06.pdf"&gt;database&lt;/a&gt;? :-) How about you, do you keep a private journal?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-8890003236705886325?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/8890003236705886325/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=8890003236705886325" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/8890003236705886325?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/8890003236705886325?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/09/journaling.html" title="Journaling" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;CEACRHY8eSp7ImA9WxNSE0U.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-5422201680782397777</id><published>2009-08-26T23:49:00.000-07:00</published><updated>2009-08-27T07:19:25.871-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-27T07:19:25.871-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="matrix" /><title>A Matrix Sim in JavaScript</title><content type="html">Do you ever find yourself working on a large project with a long lead-in time before you have visible results to show? I had been frustrated by this feeling recently. I had been focusing on a personal project which will likely be a long time in the making, so I decided to pick up a short, simple, and quick project to keep things lively and fresh. So our story begins.&lt;br /&gt;&lt;br /&gt;During high school, I was wowed by the movie The Matrix. The special effects were mind blowing (for their day) and the story line was something I could really get behind (not so with the sequels, but I digress). I imagine this is how my elders felt when Star Wars was first released.&lt;br /&gt;&lt;br /&gt;One of the most memorable visuals in the movie is the code of the matrix zipping by vertically on the screen in a retro green on black. Cypher, makes a statement, which seems to be a favorite among programmers, that he no longer sees the code itself, but what it represents. He has become fluent to the point of transcendence - a geek nirvana experience.&lt;br /&gt;&lt;br /&gt;At this point in my life I had written a few moderately complex programs, mostly using Turbo C, and I thought: "Why, I could make my computer look like the matrix." It was one of the more involved programs I had written at the time and I was quite pleased with the result. I brought it in to the computer lab at school, loaded it onto all of the machines, and had the whole lab running it at once. I'm pretty sure that &lt;a href="http://twitter.com/leebenjp"&gt;Ben&lt;/a&gt;, Yed, and Roman G. remember it. Sadly I doubt the code would run on the DOS emulator that ships with Windows XP and higher. I recall vaguely that Roman ported it to Win32 or something in our high school computer science class. Now that I think about it, I guess you could say that this was my first open source experience. &lt;br /&gt;&lt;br /&gt;In thinking of something quick and easy to create with some immediate visual payoff, I decided to recreate my little DOS matrix sim, but this time in the browser. Behold: &lt;a href="http://www.jeffscudder.com/matrix"&gt;The matrix in JavaScript&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;!--&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.jeffscudder.com/matrix"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 257px;" src="http://4.bp.blogspot.com/_54HtFT6yBFI/SpaT_6DoTHI/AAAAAAAAAcI/gylHk_PxWWI/s320/matrix_sim.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5374645931438787698" /&gt;&lt;/a&gt;--&gt;It turns out that the CSS rules needed to keep columns at a fixed width and height was actually quite complex and took longer than expected to get right. The vertical line height was still elusive in Chrome and Safari, but worked in Firefox and Opera. I don't think that this works yet in Internet Explorer. Though if past traffic is any indication, none of you would have noticed had I not said something :-)&lt;br /&gt;&lt;br /&gt;Many thanks to &lt;a href="http://scudmissile.net/"&gt;Scudmissile&lt;/a&gt; for his mighty CSS kung fu. Oh, and that whirring sound that you hear may be your CPU fan trying to take flight. Apparently rendering a vertical text flow like this using DOM manipulation, CSS, and Math.random may be a bit more processor intensive than my obsolete C code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-5422201680782397777?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/5422201680782397777/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=5422201680782397777" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/5422201680782397777?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/5422201680782397777?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/08/matrix-sim-in-javascript.html" title="A Matrix Sim in JavaScript" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;CkIBSXY8fyp7ImA9WxJaE0w.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-4504844384194444821</id><published>2009-08-01T00:38:00.000-07:00</published><updated>2009-08-03T07:42:38.877-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-03T07:42:38.877-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="http" /><category scheme="http://www.blogger.com/atom/ns#" term="app engine" /><category scheme="http://www.blogger.com/atom/ns#" term="https" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><title>A Test Client for App Engine</title><content type="html">I created a &lt;a href="http://code.google.com/p/scud-cms/source/browse/#svn/trunk/app_engine_http"&gt;simple utility library&lt;/a&gt; in Python to help anyone debug their App Engine application. Many of the App Engine apps that I've seen use HTTP, HTML form posts, and the &lt;a href="http://code.google.com/appengine/docs/python/users/overview.html"&gt;Users API&lt;/a&gt; and the easiest way to test these features is to fire up the web browser and click through the web pages generated by the app.  However this can be a bit slow and repetative and it is difficult to determine exactly what is being sent over the wire (though this is greatly helped by using &lt;a href="http://www.wireshark.org/"&gt;wireshark&lt;/a&gt;, &lt;a href="http://www.fiddler2.com/fiddler2/"&gt;fiddler&lt;/a&gt;, &lt;a href="http://www.tcpdump.org/"&gt;tcpdump&lt;/a&gt;, or antoher network packet sniffing tool).&lt;br /&gt;&lt;br /&gt;Enter &lt;a href="http://code.google.com/p/scud-cms/source/browse/trunk/app_engine_http/http.py"&gt;my little App Engine HTTP module&lt;/a&gt;. It provides a simple interface for making arbitrary HTTP requests and will print the full request and response to the terminal (though you can turn the noisy printing off if you want). &lt;a href="http://code.google.com/p/scud-cms/downloads/list"&gt;Download&lt;/a&gt;, copy the &lt;code&gt;http.py&lt;/code&gt; file to you working directory and try it out in your Python interpreter.&lt;br /&gt;&lt;br /&gt;For our first demonstration, let's try to visit the Google search page. &lt;pre&gt;import http&lt;br /&gt;client = http.Client()&lt;br /&gt;resp = client.request('GET', 'http://www.google.com')&lt;/pre&gt;You should see your request and the server's response (with the HTML for the Google Search page) in your terminal window. This should work with just about any website out there.&lt;br /&gt;&lt;br /&gt;Other HTTP debugging tools can show you the request and response like this, but I find that this kind of simple Python client can be useful in writing end-to-end or integration tests which contact your App Engine app remotely. &lt;br /&gt;&lt;br /&gt;Along those lines, one of the things which standard HTTP debugging tools do not provide, is a way to sign in to an App Engine app with a Google Account so that the App Engine Users API can identify the current user.  I wrote an extremely simple app which illustrates the Users API, try it out here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://jscudtest.appspot.com/user"&gt;http://jscudtest.appspot.com/user&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After signing in, the page should simply say, "Hello &lt;em&gt;yourusername&lt;/em&gt; (&lt;em&gt;yourusername&lt;/em&gt;@&lt;em&gt;yourdomain.com&lt;/em&gt;)" You'll notice that during the sign in process, you signed in on &lt;code&gt;www.google.com/accounts&lt;/code&gt; and were asked to approve access to the app. This kind of interaction works great in a browser, but can be tricky when you are using a command line, browserless, client.&lt;br /&gt;&lt;br /&gt;It is possible however, to sign in to an App Engine app without using a browser. You can use the same technique used in  &lt;code&gt;appcfg&lt;/code&gt;, use &lt;a href="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html"&gt;ClientLogin&lt;/a&gt; and use the authorization token to obtain an app specific cookie which indicates the current user. This simple HTTP library can do this for you and all subsequent requests will use this cookie to tell the App Engine app who the current user is. Try it out by making the request to the simple user app that you visited earlier:&lt;pre&gt;import http&lt;br /&gt;client = http.Client()&lt;br /&gt;client.appengine_login('jscudtest')&lt;br /&gt;resp = client.request('GET',&lt;br /&gt;                      'http://jscudtest.appspot.com/user')&lt;br /&gt;print resp.body&lt;/pre&gt;You should see the following text displayed in the terminal:&lt;pre&gt;Hello, &lt;em&gt;yourusername&lt;/em&gt; (&lt;em&gt;yourusername&lt;/em&gt;@&lt;em&gt;yourdomain.com&lt;/em&gt;)&lt;/pre&gt;You can use the &lt;code&gt;appengine_login&lt;/code&gt; method with your own app, just change the argument to the App ID of the app you want to access. &lt;br /&gt;&lt;br /&gt;Along with simplifying access to apps which use Google Accounts, I wanted this library to simplify the process of using another feature used by many web apps: HTML form posts. Now I'm certain you've used HTML forms before, here's a simple example: &lt;br /&gt;&lt;br /&gt;&lt;a href="http://shoutout.appspot.com/"&gt;http://shoutout.appspot.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The above app uses both the Users API and a simple form. As an alternative to visiting this page in the web browser, you can post your shout-out using the following:&lt;pre&gt;import http&lt;br /&gt;client = http.Client()&lt;br /&gt;client.appengine_login('shoutout')&lt;br /&gt;client.request('POST', 'http://shoutout.appspot.com/',&lt;br /&gt;    form_data={'who': raw_input('From: '),&lt;br /&gt;               'message': raw_input('Message: ')})&lt;/pre&gt;If you've even wondered what gets sent across the wire to post on a form like this, look back in your terminal to see the request from your computer and the response from the server (this is of course just the HTTP layer, wireshark will show you traffic on the IP and Ethernet layer as well). &lt;br /&gt;&lt;br /&gt;That's really all there is to it. I designed this as just a simple script to use on the command line and I wrote it in less time than it's taken me to write this blog post about it (I borrowed &lt;a href="http://code.google.com/p/gdata-python-client/source/browse/trunk/src/atom/http_core.py"&gt;atom.http_core&lt;/a&gt; from the &lt;a href="http://code.google.com/p/gdata-python-client/"&gt;gdata-python-client&lt;/a&gt; as a foundation). With some tweaks to remove the interactive (getpass and raw_input) calls and replace them with parameters, I could see this module as a utility layer in a larger, more complex, App Engine client application. If you're creating on I'd love to hear about it ;-)&lt;br /&gt;&lt;br /&gt;For more information on how the &lt;code&gt;appengine_login&lt;/code&gt; method works behind the scenes, see this presentation I gave a few months ago:&lt;br /&gt;&lt;br /&gt;&lt;iframe src="http://docs.google.com/present/embed?id=dcb4p69x_7fgnfgx28" frameborder="0" width="410" height="342"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Many thanks to &lt;a href="http://twitter.com/tjohns"&gt;Trevor Johns&lt;/a&gt; and &lt;a href="http://twitter.com/nicksdjohnson"&gt;Nick Johnson&lt;/a&gt; for helping me to understand how this ClientLogin-to-cookie exchange works.&lt;br /&gt;&lt;br /&gt;I'm sure that App Engine's Java runtime users would appreciate a port of this simple library to Java, if you feel so inclined.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-4504844384194444821?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/4504844384194444821/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=4504844384194444821" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/4504844384194444821?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/4504844384194444821?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/08/test-client-for-app-engine.html" title="A Test Client for App Engine" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;C04ER3s7fCp7ImA9WxJUF08.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-6992235213398099142</id><published>2009-07-14T23:01:00.000-07:00</published><updated>2009-07-15T22:25:06.504-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-15T22:25:06.504-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="app engine" /><category scheme="http://www.blogger.com/atom/ns#" term="cms" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><title>New version of my Dirt Simple CMS</title><content type="html">I have just uploaded "version 2" of the &lt;a href="http://code.google.com/p/scud-cms"&gt;dirt simple content management system&lt;/a&gt; which I developed &lt;a href="http://blog.jeffscudder.com/2008/08/dirt-simple-cms.html"&gt;almost a year ago&lt;/a&gt;. For those who don't recall, scud-cms is an extremely thin layer on top of App Engine with a plain text box based editor for creating web pages. The only new feature in this release is the ability to page through all of the content in your app by visiting the /content_lister page. I implemented the pagination system using "key only" queries and order-by-key which are fairly recent features in App Engine. For more potential designs to page through datastore entities, see this article on &lt;a href="http://code.google.com/appengine/articles/paging.html"&gt;pagination&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;I was inpired to work on this in part by a comment from &lt;a href="http://jawtek.com"&gt;Jason Worley&lt;/a&gt; (&lt;a href="http://twitter.com/swirleydude"&gt;swirleydude&lt;/a&gt; on twitter) who has been using it and appears to like it quite a bit. At some point I plan to do a version 3 release which will include file uploads (great for images) just as Jason has done in &lt;a href="http://projects.jawtek.com/litecms/"&gt;his own modifications&lt;/a&gt;. Having someone use your software, enjoy it, tweak it, and build on it is rewarding in a way which is quite unique.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-6992235213398099142?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/6992235213398099142/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=6992235213398099142" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6992235213398099142?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6992235213398099142?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/07/new-version-of-my-dirt-simple-cms.html" title="New version of my Dirt Simple CMS" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;D08MQHsyfCp7ImA9WxJUEUw.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-6586089585110584129</id><published>2009-07-07T23:01:00.000-07:00</published><updated>2009-07-08T22:04:41.594-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-08T22:04:41.594-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="xo laptop" /><category scheme="http://www.blogger.com/atom/ns#" term="one laptop per child" /><title>Updating my OLPC XO laptop</title><content type="html">For a few months &lt;a href="http://blog.jeffscudder.com/2008/03/xo-laptop.html"&gt;my XO laptop&lt;/a&gt; was my primary personal machine. I've &lt;a href="http://blog.jeffscudder.com/search/label/xo%20laptop"&gt;written before&lt;/a&gt; about some of my favorite features, and after a year and a half with it, my favorites are still:&lt;ul&gt;&lt;li&gt;A screen that is readable in full sunlight or glare&lt;/li&gt;&lt;li&gt;Linux&lt;/li&gt;&lt;li&gt;Long battery life&lt;/li&gt;&lt;li&gt;Runs absolutely silently, no fans or spinning hard drive&lt;/li&gt;&lt;/ul&gt;However, there were a few drawbacks. It is a bit short on ram and processing power but most of the time I'm using simple programming tools, so I don't need much. Web browsing on some sites was a bit slow though. The biggest pain point in my book was the lack of copy and paste capability. I often found myself needing to retype a lengthy command in the terminal. On other systems I would have just copied and pasted from the web browser. For a while, &lt;a href="http://blog.jeffscudder.com/2008/09/in-praise-of-screen.html"&gt;I worked around this&lt;/a&gt; by using &lt;a href="http://en.wikipedia.org/wiki/Gnu_screen"&gt;screen&lt;/a&gt;, which has a built in copy-paste buffer. &lt;br /&gt;&lt;br /&gt;It turns out, in a recent release, the OLPC team has assuaged my number one annoyance. It is now possible to copy text from any of the activities (programs) and paste into another (like the &lt;a href="http://wiki.laptop.org/go/Terminal_Activity"&gt;terminal&lt;/a&gt;). In order to use this new goodness, I needed to &lt;a href="http://wiki.laptop.org/go/Olpc-update"&gt;update my XO operating system&lt;/a&gt; from the current version (&lt;a href="http://wiki.laptop.org/go/Release_notes/656"&gt;656&lt;/a&gt;) to a newer build (&lt;a href="http://wiki.laptop.org/go/Release_notes/8.2.1"&gt;802&lt;/a&gt;). &lt;br /&gt;&lt;br /&gt;The process of updating turned out to be quite simple. If you are undertaking these same steps, please note, this does wipe out some parts of the operating system. New programs which you've installed using yum or rpm's might be erased depending on where they are installed. It looks like everything in the &lt;code&gt;/home/olpc&lt;/code&gt; directory is untouched. For complete instructions, see the &lt;a href="http://laptop.org/"&gt;laptop.org&lt;/a&gt; wiki page for &lt;a href="http://"&gt;olpc-update&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To update, open the terminal and become root by executing &lt;pre&gt;su&lt;/pre&gt; After that, make sure that your laptop is plugged in to a source of electricity and connected to the internet as this next step involved downloading the new operating system and overwriting your existing OS. Probably not something you want to lose power during.&lt;br /&gt;&lt;br /&gt;To download and install the new OS run &lt;pre&gt;/usr/sbin/olpc-update 802&lt;/pre&gt; Once you run this your laptop will spend the next several minutes to hours downloading and installing. A few exciting status messages will appear on your screen. Things like  "Deleting old pristine version" - yikes!&lt;br /&gt;&lt;br /&gt;I've found 802 to be a significant improvement over 656. The copy paste functionality is great and the UI looks and feels cleaner. Selecting applications and switching between them is easier than it used to be. With copy and paste now firmly in place I plan to use my little green machine quite a bit more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-6586089585110584129?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/6586089585110584129/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=6586089585110584129" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6586089585110584129?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6586089585110584129?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/07/updating-my-olpc-xo-laptop.html" title="Updating my OLPC XO laptop" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkQMRH0zfyp7ImA9WxJVEUk.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-8901353907487636115</id><published>2009-06-25T14:15:00.001-07:00</published><updated>2009-06-27T16:13:05.387-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-27T16:13:05.387-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="currying" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="code" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Partial Function Invocation</title><content type="html">My wife tells me that I often jump into an explanation by starting at the beginning of my train of thought without giving any indication of where I'm going. It would be better if I began with the point I'm trying to make, then explain how I reched my conclusion, How am I doing so far? Oh wait, right... Here is my conclusion:&lt;br /&gt;&lt;br /&gt;Allowing a function to be partially invoked, to allow some of the arguments to be specified at different times, can allow for code which is more flexible than by just using objects or pure functions.&lt;br /&gt;&lt;br /&gt;I've been thinking about this lately as I refactored the &lt;a href="http://code.google.com/p/gdata-python-client/"&gt;gdata-python-client&lt;/a&gt; which is a library which can be used with AtomPub services. I'll spare you all the gory details, and offer a simple example of how partial function invocation might come in handy.&lt;br /&gt;&lt;br /&gt;When making a request to a remote server, you might need the following information, just for example: username, password. URL, message body, and content type. So we start out by writing a stateless function to take this information and open a connnecion to the server, format our inputs, transmit our request, and parse the response. We'll call it &lt;code&gt;post&lt;/code&gt;, and using it looks like this:&lt;pre&gt;serverResponse = post(url, data, contentType, username, &lt;br /&gt;                      password)&lt;/pre&gt;This is all well and good, but suppose the final request, as shown above, is preceeded by a whole series of function calls. Each function would need to dutifully pass along parts of the request. Say for example that the user types in their username and password long before the request is made, so these get passed as parameters to lots of functions which only receive them so they can pass them on. In addition, the username and password are almost always the same from request to request, so the same values are being passed to the post function over and over. In cases like this, we will often use an object to hold common values.&lt;pre&gt;class Requestor {&lt;br /&gt;  username&lt;br /&gt;  password&lt;br /&gt;&lt;br /&gt;  method post(url, data, conteentType) {...}&lt;br /&gt;}&lt;/pre&gt;Now our request will look like:&lt;pre&gt;client = new Requestor(username, password)&lt;br /&gt;serverResponse = client.post(url, data, contentType)&lt;/pre&gt;What we've effectively done here is specified some of the information in advance and left other pieces of information to be specified at the last minute. I would argue that this make the code better (cleaner, less chance of human error in listing lots of parameters, perhaps less data on the call stack, etc.).&lt;br /&gt;&lt;br /&gt;Now the question becomes: Did we extract the right pieces of information from the function call into the object? Suppose the code you are writing needs to use a different password for each service you are making a request to, but the content type of the data is always the same. Then it would have made more sense to design our class like this:&lt;pre&gt;class Requestor {&lt;br /&gt;  username&lt;br /&gt;  contentType&lt;br /&gt;&lt;br /&gt;  method post(url, data, password) {...}&lt;br /&gt;}&lt;/pre&gt;Since we've established that not everyone who is using our code has the same usage patterns, lets design for utimate flexibility. Every parameter can be specified either in the object, or in the function call. Also, if the object has a parameter already, we could override it by passing in that parameter when we call the method. This is not too difficult in in Python, so here is a non-pseudocode example:&lt;pre&gt;class Requestor(object):&lt;br /&gt;  &lt;br /&gt;  def __init__(self, url=None, data=None, &lt;br /&gt;               content_type=None, username=None, &lt;br /&gt;               password=None):&lt;br /&gt;    self.url = url&lt;br /&gt;    self.data = data&lt;br /&gt;    self.content_type =  content_type&lt;br /&gt;    self.username = username&lt;br /&gt;    self.password = password&lt;br /&gt;  &lt;br /&gt;  def post(self, url=None, data=None, &lt;br /&gt;           content_type=None, username=None, &lt;br /&gt;           password=None):&lt;br /&gt;    url = url or self.url&lt;br /&gt;    data = data or self.data&lt;br /&gt;    content_type = content_type or self.content_type&lt;br /&gt;    username = username or self.username&lt;br /&gt;    password = password or self.password&lt;br /&gt;    # Now we have our inputs, code to make &lt;br /&gt;    # the request starts here&lt;br /&gt;    ...&lt;/pre&gt;If you think this seems a bit excessive, I would agree. I didn't go nearly this far when designing the library that started me thinking about this. There was one request parameter in particular though &lt;a href="http://code.google.com/p/gdata-python-client/source/browse/trunk/src/atom/client.py"&gt;that does use this pattern&lt;/a&gt;. (Five points to the first person to post it in the comments. ;-) &lt;br /&gt;&lt;br /&gt;To use the above class, you would do:&lt;pre&gt;requestor = Requestor(username='...')&lt;br /&gt;requestor.password = '...'&lt;br /&gt;...&lt;br /&gt;server_response = requestor.post(url, data, content_type)&lt;/pre&gt;It will also handle our alternate usage where we want to give the password to the post method and set the content type at the object level:&lt;pre&gt;requestor = Requestor(username='...', content_type='...')&lt;br /&gt;...&lt;br /&gt;server_response = requestor.post(url, data, password='...')&lt;/pre&gt;We can even override parameters which are set in the object:&lt;pre&gt;requestor = Requestor(password='...', content_type='...')&lt;br /&gt;requestor.username = '...'&lt;br /&gt;...&lt;br /&gt;# Override the content_type, just on this request.&lt;br /&gt;server_response = requestor.post(url, data, content_type='...')&lt;/pre&gt;With the above example we end up with a lot of code just to let us specify each parameter in either the object or as a function argument. In fact, this can introduce so cases where the user forgets to specify in either, which is possible because all function arguments are now optional. Wouldn't it be better if we could instead specify some of the function parameters, pass the half-specified function call around, and fill in the ramaining values when we finally invoke. For this illustration, I'm using the following syntax to show a partial invocation, &lt;code&gt;&amp;lt; &amp;gt;&lt;/code&gt; around arguments instead of &lt;code&gt;( )&lt;/code&gt;.&lt;pre&gt;function post(url, data, contentType, username, password) {...}&lt;br /&gt;&lt;br /&gt;started = post&amp;lt;username, password&amp;gt;&lt;br /&gt;...&lt;br /&gt;serverRespense = started(url, data, contentType)&lt;/pre&gt;Recall our case from earler, what if the contentType is constant but the password is instead more variable:&lt;pre&gt;started = post&amp;lt;username, contentType&amp;gt;&lt;br /&gt;...&lt;br /&gt;serverRespense = started(url, data, password)&lt;/pre&gt;It turns out I'm not the first person to think of this pattern, not by a long shot. Functional programming often makes use of this pattern, referred to as &lt;a href="http://en.wikipedia.org/wiki/Currying"&gt;function currying&lt;/a&gt;. I found the following &lt;a href="http://www.engr.uconn.edu/~jeffm/Papers/curry.html"&gt;example for Scheme&lt;/a&gt; which also shows how easy this is in Haskell. The prototype library for JavaScript includes a &lt;a href="http://www.prototypejs.org/api/function/bind"&gt;bind function&lt;/a&gt; which can accomplish the same thing. Here's a paper on the topic in C++: (&lt;a href="http://www.osl.iu.edu/publications/prints/2001/jarvi:01:mpool.pdf"&gt;pdf&lt;/a&gt;, &lt;a href="http://www.google.com/search?q=cache:www.osl.iu.edu/publications/prints/2001/jarvi:01:mpool.pdf+http://www.osl.iu.edu/publications/prints/2001/jarvi:01:mpool.pdf&amp;cd=1"&gt;Google cache HTML&lt;/a&gt;).  I also found &lt;a href="http://www.python.org/dev/peps/pep-0309/"&gt;PEP 309&lt;/a&gt; which was a proposal for this in Python. Perhaps I should have called my Python example above: Function Currying using Classes. If you can think of other examples, I'd love to see them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-8901353907487636115?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/8901353907487636115/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=8901353907487636115" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/8901353907487636115?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/8901353907487636115?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/06/partial-function-invocation.html" title="Partial Function Invocation" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;DkIFSX4zfyp7ImA9WxBWGEo.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-6765680047314759978</id><published>2009-05-17T13:11:00.000-07:00</published><updated>2010-02-10T23:48:38.087-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-10T23:48:38.087-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="road trip" /><title>Recently completed: our half-US tour</title><content type="html">With stops in &lt;br /&gt;&lt;br /&gt;Auburn CA&lt;br /&gt;Reno NV&lt;br /&gt;Winnemucca NV&lt;br /&gt;Wells NV&lt;br /&gt;Metropolis NV&lt;br /&gt;Bonneville Salt Flats UT&lt;br /&gt;Salt Lake City UT&lt;br /&gt;Green River WY&lt;br /&gt;Cheyenne WY&lt;br /&gt;Denver CO&lt;br /&gt;Littleton CO&lt;br /&gt;Centennial CO &lt;br /&gt;Lincoln NE&lt;br /&gt;Omaha NE&lt;br /&gt;West Des Moines IA&lt;br /&gt;Indianola IA&lt;br /&gt;Sigourney IA&lt;br /&gt;Fairfield IA&lt;br /&gt;Hannibal MO&lt;br /&gt;Fenton MO&lt;br /&gt;Crestwood MO&lt;br /&gt;Sikeston MO&lt;br /&gt;Memphis TN&lt;br /&gt;Little Rock AR&lt;br /&gt;Texarkana AR&lt;br /&gt;Texarkana TX&lt;br /&gt;Plano TX&lt;br /&gt;Dallas TX&lt;br /&gt;Garland TX&lt;br /&gt;Wichita Falls TX&lt;br /&gt;Amarillo TX&lt;br /&gt;Albuquerque NM&lt;br /&gt;Williams AZ&lt;br /&gt;Bakersfield CA&lt;br /&gt;Plesanton CA&lt;br /&gt;Milpitas CA&lt;br /&gt;&lt;br /&gt;(Edited) Here's a map of our approximate route:&lt;br /&gt;&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps?f=d&amp;amp;source=s_d&amp;amp;saddr=Auburn,+CA&amp;amp;daddr=Reno+NV+to:Winnemucca+NV+to:Wells+NV+to:Salt+Lake+City+UT+to:Green+River+WY+to:Denver+CO+to:Lincoln,+Nebraska+to:Omaha+NE+to:West+Des+Moines+IA+to:Indianola+IA+to:Sigourney+IA+to:Saint+Louis,+MO+63109+to:Memphis+TN+to:Little+Rock+AR+to:Texarkana+TX+to:Dallas,+Texas+to:Wichita+Falls+TX+to:Amarillo+TX+to:Albuquerque+NM+to:Williams+AZ+to:Bakersfield,+CA+to:Pleasanton,+Alameda,+California+to:Milpitas,+Santa+Clara,+California&amp;amp;hl=en&amp;amp;geocode=FbWDUQIdZoPI-CmfpXVVKAWbgDFweCwzC7ECOQ%3BFaEsWwIdVcnb-CmdoJKSrkCZgDGH9zh0zsXFQA%3BFZ4ycQId-377-CkpnimG612ggDEmngPIY_bIjw%3BFSVQcwId9scl-Sl3j_x8ANeogDHPdw1Z07DALg%3BFcv1bQIdma1U-SntMdGIlD1ShzHKMU1IoLdTWw%3BFQCteQIdeq15-SlHjCnh4X1ahzH4TPTd16zL2A%3BFRJfXgIdgQ---SnPFx8jqoBrhzHWNoon-PSOEQ%3BFc6pbgIdMcE8-illElbKWb6WhzG53tUfm4U6Yw%3BFXZ8dQId5ipH-int_Ay1yI2ThzEEtjeuT01CRg%3BFfxqegIdHBRq-ikfHoJbih_shzGXgwrTwJaJUw%3BFeASdwIdQm1s-ikB71z1OrDuhzHkYs_KX8CiMA%3BFWCydgIdrxGB-imJ3arOMOHlhzEcEtmTY2frwg%3BFWPATAIdoDWe-imxAg0d9LXYhzF2XAlLW0BsLg%3BFd5WGAIdLPah-ilFl0PqHn7VhzH-thpgFfOT0Q%3BFXEwEgIdxcV_-imbVh-hNKHShzEXW_MNEPUFNA%3BFeUG_gEdOPJk-ilXZKDCMkE0hjFwW9thkjxYnQ%3BFYuI9AEdfWg7-ilLl0V79xlMhjGPZ0f2pJvsuQ%3BFWx7BQIdNRwh-in12mJwkiBThjEAUurvy_oGHQ%3BFe1xGQIdfy3u-SkDz0Wy1EgBhzGv0jZoHNHz0A%3BFctYFwId_6Gk-Sl7gwnT3QoihzH99tm4zvjTwA%3BFTHdGQId5RlQ-SlVpQFT6Z4yhzGGU8DNx-elHQ%3BFezAGwIdKOvn-CkNy5S5yGvqgDHIdP6YCQw2WQ%3BFd-uPgIdCVe8-ClfdbphoumPgDEWferhR2irsw%3BFTAcOwIdQNq7-CnxXXjLADOOgDEnpTqtwOFjeA&amp;amp;mra=ls&amp;amp;sll=37.09024,-105.820312&amp;amp;sspn=23.553364,34.321289&amp;amp;ie=UTF8&amp;amp;ll=37.125286,-105.820312&amp;amp;spn=9.00626,32.41499&amp;amp;output=embed"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://maps.google.com/maps?f=d&amp;amp;source=embed&amp;amp;saddr=Auburn,+CA&amp;amp;daddr=Reno+NV+to:Winnemucca+NV+to:Wells+NV+to:Salt+Lake+City+UT+to:Green+River+WY+to:Denver+CO+to:Lincoln,+Nebraska+to:Omaha+NE+to:West+Des+Moines+IA+to:Indianola+IA+to:Sigourney+IA+to:Saint+Louis,+MO+63109+to:Memphis+TN+to:Little+Rock+AR+to:Texarkana+TX+to:Dallas,+Texas+to:Wichita+Falls+TX+to:Amarillo+TX+to:Albuquerque+NM+to:Williams+AZ+to:Bakersfield,+CA+to:Pleasanton,+Alameda,+California+to:Milpitas,+Santa+Clara,+California&amp;amp;hl=en&amp;amp;geocode=FbWDUQIdZoPI-CmfpXVVKAWbgDFweCwzC7ECOQ%3BFaEsWwIdVcnb-CmdoJKSrkCZgDGH9zh0zsXFQA%3BFZ4ycQId-377-CkpnimG612ggDEmngPIY_bIjw%3BFSVQcwId9scl-Sl3j_x8ANeogDHPdw1Z07DALg%3BFcv1bQIdma1U-SntMdGIlD1ShzHKMU1IoLdTWw%3BFQCteQIdeq15-SlHjCnh4X1ahzH4TPTd16zL2A%3BFRJfXgIdgQ---SnPFx8jqoBrhzHWNoon-PSOEQ%3BFc6pbgIdMcE8-illElbKWb6WhzG53tUfm4U6Yw%3BFXZ8dQId5ipH-int_Ay1yI2ThzEEtjeuT01CRg%3BFfxqegIdHBRq-ikfHoJbih_shzGXgwrTwJaJUw%3BFeASdwIdQm1s-ikB71z1OrDuhzHkYs_KX8CiMA%3BFWCydgIdrxGB-imJ3arOMOHlhzEcEtmTY2frwg%3BFWPATAIdoDWe-imxAg0d9LXYhzF2XAlLW0BsLg%3BFd5WGAIdLPah-ilFl0PqHn7VhzH-thpgFfOT0Q%3BFXEwEgIdxcV_-imbVh-hNKHShzEXW_MNEPUFNA%3BFeUG_gEdOPJk-ilXZKDCMkE0hjFwW9thkjxYnQ%3BFYuI9AEdfWg7-ilLl0V79xlMhjGPZ0f2pJvsuQ%3BFWx7BQIdNRwh-in12mJwkiBThjEAUurvy_oGHQ%3BFe1xGQIdfy3u-SkDz0Wy1EgBhzGv0jZoHNHz0A%3BFctYFwId_6Gk-Sl7gwnT3QoihzH99tm4zvjTwA%3BFTHdGQId5RlQ-SlVpQFT6Z4yhzGGU8DNx-elHQ%3BFezAGwIdKOvn-CkNy5S5yGvqgDHIdP6YCQw2WQ%3BFd-uPgIdCVe8-ClfdbphoumPgDEWferhR2irsw%3BFTAcOwIdQNq7-CnxXXjLADOOgDEnpTqtwOFjeA&amp;amp;mra=ls&amp;amp;sll=37.09024,-105.820312&amp;amp;sspn=23.553364,34.321289&amp;amp;ie=UTF8&amp;amp;ll=37.125286,-105.820312&amp;amp;spn=9.00626,32.41499" style="color:#0000FF;text-align:left"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-6765680047314759978?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/6765680047314759978/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=6765680047314759978" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6765680047314759978?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6765680047314759978?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/05/recently-completed-our-half-us-tour.html" title="Recently completed: our half-US tour" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>1</thr:total></entry><entry gd:etag="W/&quot;D08GQHo_cCp7ImA9WxJRF0w.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-5485765422037211950</id><published>2009-05-17T13:06:00.000-07:00</published><updated>2009-05-18T23:50:21.448-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-18T23:50:21.448-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="music" /><title>Anticipating "it's all crazy..."</title><content type="html">One of my favorite bands is releasing a new album tomorrow and I though I'd take a short moment to gush about it. I've been a long time admirer of &lt;a href="http://www.mewithoutyou.com/"&gt;mewithoutYou&lt;/a&gt;'s work. Their abstract poetry filled with vivid imagery combines with highly energetic rich sounds and hits a sweet-spot. Certain lines tend to resonate with me. I find "&lt;a href="http://www.last.fm/music/mewithoutYou/_/O,+Porcupine"&gt;O Porcupine&lt;/a&gt;" in particular to be moving.&lt;br /&gt;&lt;br /&gt;I like to think of myself as having broad musical tastes, but if I do tend to be critical of a song it is very often overly simplistic lyrics which I fault. I don't know that I've ever sat down and thought through why that is, perhaps another time. &lt;br /&gt;&lt;br /&gt;From listening to a few preview release tracks from this album, I don't think mewithoutYou runs any risk of that sort of problem. In fact, while listening to "the Fox, the Crow and the Cookie" I found myself pulling out the dictionary to look up the words &lt;a href="http://en.wikipedia.org/wiki/Canzonette"&gt;canzonette&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Corvidae"&gt;corvidae&lt;/a&gt;, and &lt;a href="http://wordnetweb.princeton.edu/perl/webwn?s=escadrille"&gt;escadrille&lt;/a&gt;! Some of those rhymes are absolutely majestic. I think this album may satsfy even Xirzec's bombastically erudite lexical tastes. Thanks again to Andy for introducing me to their music.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-5485765422037211950?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/5485765422037211950/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=5485765422037211950" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/5485765422037211950?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/5485765422037211950?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/05/anticipating-its-all-crazy.html" title="Anticipating &quot;it's all crazy...&quot;" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>2</thr:total></entry><entry gd:etag="W/&quot;C0QARHc6fCp7ImA9WxJSFUQ.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-2570254577969295783</id><published>2009-05-05T23:16:00.000-07:00</published><updated>2009-05-05T23:29:05.914-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-05T23:29:05.914-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="puns" /><title>A Two Pun Day</title><content type="html">If I were a rock, I wouldn't want to be marble because I wouldn't want people to take me for granite.&lt;br /&gt;&lt;br /&gt;I told one of my good friends that I was allergic to milk and he said, "No whey!"&lt;br /&gt;&lt;br /&gt;I would estimate that I make at least two puns per week. Something someone says will just strike me at an odd angle and the pun percolates out effortlessly. The other day I thought of the above two. I'm not sure if the first is original, but we were in an elevator debating whether the stone walls were made of marble or granite and I couldn't resist. Later that day, someone mentioned cottage cheese and I thought of the second. It's a certified groaner, five out of six friends agree!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-2570254577969295783?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/2570254577969295783/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=2570254577969295783" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/2570254577969295783?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/2570254577969295783?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/05/two-pun-day.html" title="A Two Pun Day" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;A0AAQ3k4cCp7ImA9WxJTFko.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-6967060481872143754</id><published>2009-03-17T23:16:00.000-07:00</published><updated>2009-04-25T10:15:42.738-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-25T10:15:42.738-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="laptop" /><category scheme="http://www.blogger.com/atom/ns#" term="pi day" /><title>Our New Pi Day Laptop</title><content type="html">To commemorate this year's pi day, we've christened our newly purchased laptop PiPuter. It's an Acer Aspire. We were looking for a super cheap full sized laptop with decent spects and found a good deal at the MicroCenter. Vanessa says that our computer has graduated magniu cum laude from its factory and it is currently watching us - watching, watching, always watching. &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_54HtFT6yBFI/SfNDnJa2QII/AAAAAAAAAZ4/s82nZSw6cwc/s1600-h/2009-04-22+033.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="http://4.bp.blogspot.com/_54HtFT6yBFI/SfNDnJa2QII/AAAAAAAAAZ4/s82nZSw6cwc/s320/2009-04-22+033.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5328677123931914370" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_54HtFT6yBFI/SfNDeh_ggDI/AAAAAAAAAZw/1c_VsWEnTxw/s1600-h/2009-04-22+021.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_54HtFT6yBFI/SfNDeh_ggDI/AAAAAAAAAZw/1c_VsWEnTxw/s320/2009-04-22+021.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5328676975909306418" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-6967060481872143754?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/6967060481872143754/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=6967060481872143754" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6967060481872143754?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/6967060481872143754?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/03/our-new-pi-day-laptop.html" title="Our New Pi Day Laptop" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_54HtFT6yBFI/SfNDnJa2QII/AAAAAAAAAZ4/s82nZSw6cwc/s72-c/2009-04-22+033.jpg" height="72" width="72" /><thr:total>0</thr:total></entry><entry gd:etag="W/&quot;DkQNRXc7fSp7ImA9WxVVE0U.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-2245753391273195878</id><published>2009-03-05T07:21:00.001-08:00</published><updated>2009-03-06T16:06:34.905-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-06T16:06:34.905-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="vicodin" /><category scheme="http://www.blogger.com/atom/ns#" term="septoplasty" /><title>Septoplasty</title><content type="html">I never dreamed that I too would be undergoing &lt;a href="http://en.wikipedia.org/wiki/Septoplasty"&gt;septoplasty&lt;/a&gt; just a few short months after &lt;a href="http://bitworking.org/news/375/Septoplasty"&gt;Joe Greogorio had his operation&lt;/a&gt;. It turns out I had a pretty severely deviated septum which likely contributed to the annual sinus infections which I have been experiencing. My allergist recommended I have this operation to straighten the inside of my nose, as my left nasal passage was almost completely blocked near the bridge of my nose. In looking for a possible sinus infection I had a CT scan of my sinuses which revealed just how severe the deviation was. By the way, looking at a slice by slice cross section of your own head is fascinating.&lt;br /&gt;&lt;br /&gt;This past Wednesday I had the surgery and I will have the stitches and splints removed next Tuesday. It's been a bit different than I expected. I anticipated some pain but, surprisingly, the part that hurts most is my upper front teeth. I also didn't expect quite so much bleeding. In any case, I'm not feeling too bad and I am looking forward to the long term benefits. Also, this has been my first experience BWOV (blogging while on &lt;a href="http://en.wikipedia.org/wiki/Vicodin"&gt;vicodin&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-2245753391273195878?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/2245753391273195878/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=2245753391273195878" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/2245753391273195878?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/2245753391273195878?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/03/septoplasty.html" title="Septoplasty" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;CEcFQXc5fCp7ImA9WxVVE00.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-7674732341545067883</id><published>2009-03-01T21:00:00.001-08:00</published><updated>2009-03-05T17:13:30.924-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-05T17:13:30.924-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="headspace" /><title>Two Guys Arguing - Five Questions</title><content type="html">I noticed that my friend &lt;a href="http://twitter.com/leebenjp"&gt;Ben&lt;/a&gt; is participating in a new blog named &lt;a href="http://twoguysarguing.wordpress.com/"&gt;Two Guys Arguing&lt;/a&gt; (in addition to blogs &lt;a href="http://yardspoon.blogspot.com/"&gt;one&lt;/a&gt; &lt;a href="http://themeatsweats.blogspot.com/"&gt;two&lt;/a&gt; and &lt;a href="http://regenmytoolkit.blogspot.com/"&gt;three&lt;/a&gt; :-) and I thought I'd piggy back on a recent &lt;a href="http://twoguysarguing.wordpress.com/2009/03/01/five-questions-a-response/"&gt;ice-breaker post&lt;/a&gt;.&lt;br /&gt;&lt;blockquote&gt;&lt;ol&gt;&lt;li&gt;What are you currently hacking on?&lt;/li&gt;&lt;li&gt;What are you currently getting better at?&lt;/li&gt;&lt;li&gt;What do you do when your computer is asleep?&lt;/li&gt;&lt;li&gt;Describe that ‘big fish’ project that’s been stewing in your brain.&lt;/li&gt;&lt;li&gt;What are you gonna post about this coming week?&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;br /&gt;On to the answers:&lt;ol&gt;&lt;li&gt;As a small part of my day job I'm currently working on a rewrite of significant portions of the &lt;a href="http://code.google.com/p/gdata-python-client/"&gt;gdata-python-client&lt;/a&gt; to support version two of the Google Data API protocol (check out the &lt;a href="http://code.google.com/p/gdata-python-client/source/browse/#svn/branches/v2"&gt;v2 branch&lt;/a&gt;). &lt;/li&gt;&lt;li&gt;I've been trying to focus on writing speedier unit tests. Unit testing is great, but sometimes it is necessary to write end to end tests which can take a rather long time to run. Slow tests are run less often, so I've been thinking of some ways to cache some of the more expensive pieces of these end to end tests while still preserving their utility.&lt;/li&gt;&lt;li&gt;Playing with Claire is easily one of my favorite non-computer related activities. I have a wonderful wife who keeps me company and there's nothing like relaxing with the family. Aside from the yard work, remodeling, and other house related items, I do very much enjoy playing guitar now and again. I even have a somewhat regular weekly gig.&lt;/li&gt;&lt;li&gt;I have a few side projects I like to hack on here and there in my spare time. One of them I've been thinking more about recently is a computer language I've decided to call "&lt;a href="http://code.google.com/p/headspace"&gt;Headspace&lt;/a&gt;". I wanted to try an experiment and see what the effects would be of applying &lt;a href="http://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two"&gt;the rule of seven&lt;/a&gt; to programming.&lt;/li&gt;&lt;li&gt;I've been planning to write a bit about the surgery I've recently undergone so stay tuned ;-)&lt;/li&gt;&lt;/ol&gt;Two Guys Arguing seems like it's off to a great start and I look forward to reading along.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-7674732341545067883?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/7674732341545067883/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=7674732341545067883" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/7674732341545067883?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/7674732341545067883?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/03/two-guys-arguing-five-questions.html" title="Two Guys Arguing - Five Questions" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;DEUMSXc8fCp7ImA9WxVWFk8.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-8993787774065128940</id><published>2009-02-25T21:20:00.001-08:00</published><updated>2009-02-25T21:31:28.974-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-25T21:31:28.974-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="twitter" /><category scheme="http://www.blogger.com/atom/ns#" term="google" /><title>Google Tweets</title><content type="html">Earlier today, &lt;a href="http://twitter.com/google/status/1251523388"&gt;Google&lt;/a&gt; posted the following on Twitter&lt;blockquote&gt;I'm 01100110 01100101 01100101 01101100 01101001 01101110 01100111 00100000 01101100 01110101 01100011 01101011 01111001 00001010&lt;/blockquote&gt;This looks suspiciously like ASCII, so I set out to render it in a more human readable form:&lt;pre&gt;Bin       Hex ASCII&lt;br /&gt;0110 0110 66  f&lt;br /&gt;0110 0101 65  e&lt;br /&gt;0110 0101 65  e&lt;br /&gt;0110 1100 6C  l&lt;br /&gt;0110 1001 69  i&lt;br /&gt;0110 1110 6E  n&lt;br /&gt;0110 0111 67  g&lt;br /&gt;0010 0000 20    (space)&lt;br /&gt;0110 1100 6C  l&lt;br /&gt;0111 0101 75  u&lt;br /&gt;0110 0011 63  c&lt;br /&gt;0110 1011 6B  k&lt;br /&gt;0111 1001 79  y&lt;br /&gt;0000 1010 0A  \n&lt;br /&gt;&lt;/pre&gt;I doubt I'm the first to post this, but there you have it :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-8993787774065128940?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/8993787774065128940/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=8993787774065128940" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/8993787774065128940?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/8993787774065128940?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/02/google-tweets.html" title="Google Tweets" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>3</thr:total></entry><entry gd:etag="W/&quot;Dk8MQnczcSp7ImA9WxVXGE0.&quot;"><id>tag:blogger.com,1999:blog-33260599.post-4156704969603181070</id><published>2009-02-01T21:29:00.000-08:00</published><updated>2009-02-16T09:21:23.989-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-16T09:21:23.989-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="test" /><category scheme="http://www.blogger.com/atom/ns#" term="GRE" /><title>Brief Haitus</title><content type="html">I'm taking a short break from posting here to study for the &lt;a href="www.ets.org/gre/"&gt;GRE&lt;/a&gt;. I'm planning to apply to grad school in the not too distant future, and figured I should study. Apologies for the pusillanimously prosaic post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33260599-4156704969603181070?l=blog.jeffscudder.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jeffscudder.com/feeds/4156704969603181070/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=33260599&amp;postID=4156704969603181070" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/4156704969603181070?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/33260599/posts/default/4156704969603181070?v=2" /><link rel="alternate" type="text/html" href="http://blog.jeffscudder.com/2009/02/brief-haitus.html" title="Brief Haitus" /><author><name>Jeffrey Scudder</name><uri>http://www.blogger.com/profile/13002167096451875802</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="24" height="32" src="http://1.bp.blogspot.com/_54HtFT6yBFI/TIHZVaOA8II/AAAAAAAAAh8/SnKC58oFOEA/S220/edited_1017.jpg" /></author><thr:total>1</thr:total></entry></feed>

