<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>teebes.com</title><link>http://teebes.com/blog/</link><description>teebes.com's blog</description><language>en-us</language><lastBuildDate>Sat, 04 Sep 2010 19:48:43 -0000</lastBuildDate><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/teebescom" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="teebescom" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><title>Microsoft has a confiance issue</title><link>http://teebes.com/blog/26/microsoft-has-a-confiance-issue</link><description>&lt;p&gt;So Microsoft &lt;a href='http://arstechnica.com/microsoft/news/2010/06/inform-the-next-of-kin-microsofts-tweenager-phone-is-no-more.ars'&gt;killed the Kin&lt;/a&gt;, 6 weeks after its release. There is now an unfortunate trend starting at Microsoft, from a consumer perspective, of quitting out of their projects very early on: just last April, it was both &lt;a href='http://dvice.com/archives/2010/04/microsoft-disco.php'&gt;the Courrier&lt;/a&gt; and the &lt;a href='http://techcrunch.com/2010/04/29/hewlett-packard-to-kill-windows-7-tablet-project/'&gt;Slate&lt;/a&gt;. The Slate was the highlight of Ballmer's &lt;a href='http://gizmodo.com/5442200/hps-windows-7-slate-device-revealed-by-steve-ballmer'&gt;keynote to CES&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What's so surprising about the Kin is that it was axed so soon after its release. I mean fail fast is one thing but if your consumers start worrying that every time you put out a new product you might discontinue it, you've got all sorts of problems. This is especially true in the hot-red, media-hyped market that mobile computing currently is.&lt;/p&gt;
</description><guid>http://teebes.com/blog/26/microsoft-has-a-confiance-issue</guid></item><item><title>Apple gets to start over</title><link>http://teebes.com/blog/25/apple-gets-to-start-over</link><description>&lt;p&gt;&lt;img style="margin-left: 5px" src="/static/mediacenter/ipad-vs-mbp.png" border="0" alt="The iPad with a bluetooth keyboard compared to a MacBook Pro" width="300" align="right" /&gt;
Recently, I visited a friend of mine that, Jeff, who was thinking about buying a computer. He's never owned one before but uses his partner's Laptop in the evenings to do e-mail, browse Facebook and watch a few YouTube videos. Of course more and more the two find themselves fighting over who gets to use the Laptop and an extra dedicated device would solve the problem. I was there with my iPad and after spending half an hour with it Jeff concluded that this did everything he needed and, since it was cheaper than a MacBook, bought one. He's been glued to it ever since.&lt;/p&gt;

&lt;p&gt;This is of course a common tale, and a very encouraging one because an iPad is a lot easier to use and maintain than a Mac. That, combined with the lower price points, mean we have a whole new class of users that are now part of the game. To those users, the Laptop is still the benchmark because it comes down to what this new device can &lt;strong&gt;do&lt;/strong&gt;, more than its specs and features. If Tablets are to truly succeed as a long-term consumer product, they'll have to empower users to similar degrees as traditional Operating Systems have. Simple questions like "can I save the video that was attached to this e'mail" will need to be answered, eventually. Likewise, users will need a way to input text to the device at an acceptable speed, and a way to watch content on it without holding the device up yourself.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;One of the major selling point of the iPad, iPhone and iPod, is that they use a completely new type of Operating System to begin with, one that &lt;em&gt;feels&lt;/em&gt; nice to use. Starting over is a very rare opportunity in software, even moreso with large companies that actually have a shot at making it work and successfully adopted by the masses. It's common for Applications to support a number of previous platform versions so as not to scare away the customers who don't upgrade every six months. Enhancements always need to be carefully introduced in ways that sacrifice neither present nor future backwards compatibility. If this is true at the platform level, then about about the basic human interaction level? Taking away the keyboard and mouse of a computer should  be usually unthinkable. But we were lucky to the timely catalyst of a new technological space.&lt;/p&gt;

&lt;p&gt;We now know that Apple first built an iPad &lt;a href='http://blogs.barrons.com/techtraderdaily/2010/06/02/d8-jobs-says-apple-actually-developed-ipad-before-the-iphone/
'&gt;before it built an iPhone&lt;/a&gt;. It could have built a tablet right away and have it face-off against laptops. But then you'd be looking at re-training your users completely, re-wiring them (or rather un-wiring them), breaking the rules. It's impossible. Paving a new road and writing the rules yourself, however, is somewhat doable. Mobile computing was it. Easier to educate than re-educate. Since users didn't really know yet how to do mobile computing on the go, and since the existing technology had to be adapted anyway, either way users had to learn something new. And once the iPhone was a massive success, everyone already knew how to use the next thing.&lt;/p&gt;

&lt;p&gt;The jury is still out as to whether iOS can end up being a better system in itself than OSX, and the broad nature of this comparison ensures that there will probably not be a definitive answer (depends on for whom, etc). But what's certain is that Apple believes in iOS. They believe that it is not just a &lt;em&gt;different&lt;/em&gt; experience but also a &lt;em&gt;superior&lt;/em&gt; one, and their focus seems single-mindedly on developing the multitouch platform paragidm further. On the hardware side, the updates to the MacBook Pro line -- which used to be the highlight of a Macworld keynote -- are now just a quiet update on the store pages. On the software side, last week the WWDC sessions were &lt;a href="http://daringfireball.net/2010/06/wwdc_wrapup"&gt;mostly focused on iOS&lt;/a&gt; while OSX seemed more on the backburner (although reportedly still on track). Recent &lt;a href='http://arstechnica.com/apple/news/2010/06/developers-expect-ios-and-mac-os-to-merge-over-time.ars
'&gt;interviews by Ars Technica&lt;/a&gt; of successful Mac developers seem to confirm 10.7 will probably be made closer to iOS in a lot of ways. Maybe it will allow apps to be built that work for both platforms, or make it easier in general to develop for iOS on OSX. But Apple is betting the house on this.&lt;/p&gt;


&lt;p&gt;What I'm having a hard time getting past when I try to envision a world where the iOS paragdism wins, is how are we going to address its basic limitations:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;It's a perfect device for viewing content but you always have to hold it up. Laptops have a sturdy platform for a good reason. While travelling last month my fiancee and I would try to use the iPad to watch movies in bed or on airplanes but soon switched back to using the Laptop because it gets tiring to hold the thing up for 2 hours. It's actually heavier than it seems at first. Yes there are iPad cases that work also as stands but the natural form of a Tablet makes this a seemingly impossible problem to solve.&lt;/li&gt;
    &lt;li&gt;How do you capture significant amounts of textual input on this thing? How can you write your long report, paper, novel. Sure a Bluetooth keyboard works OK but it's pretty awkward to be typing while sitting away from the screen but having to put your fingers on it to do anything other than what you're currently doing. Tablets will be judged in the long term by what they can do. To the most casual users it will either work as a device that fulfills all their basic requirements or they'll get a Laptop.&lt;/li&gt;
    &lt;li&gt;People's files are their things. The lack of access to the filesystem was the first thing I hated about the iPhone. Nowadays I'm giving it the benefit of the doubt because filesystems are messy, terrifying things. Apple doesn't think the benefit of exposing it to the users will be worth the harm. I'm sure they're working on a cloud-based filesystem that will be completely abstracted from the users but there will still be a fundamental problem: users feel that their files are their objects, their things. It's like trying to abstract shelf space or money. And until they're successfull, geeks should get used to answering the question of how to save arbitrary file types that are attached to e-mails and doing something with them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But even if eventually the multitouch tablet looses steam and becomes a niche product, which I hope it won't, I feel very fortunate that we get to witness such a rare opportunity: the chance to let go of know and start over on fresh, clean ideas that are amount to such a positive experience.&lt;/p&gt;
</description><guid>http://teebes.com/blog/25/apple-gets-to-start-over</guid></item><item><title>Facebook needs to slow down</title><link>http://teebes.com/blog/24/facebook-needs-to-slow-down</link><description>&lt;p&gt;Facebook is a poster-child for exponential-growth Silicon Valley startups, and it has been furiously innovating and morphing its product over the last 5 years. About a year ago, it reached that critical mass where your parents and grandparents start being aware of its existence and even mentioning it in every day conversations. Some might even be fortunate enough to now be Facebook friends with your Mother-in-law, as I am.&lt;/p&gt;
&lt;p&gt;When web applications start to reach the 'rest of the world' is usually when things start getting seriously profitable. And there we are, with TechCrunch first reporting just last September that &lt;a href='http://techcrunch.com/2009/09/15/facebook-crosses-300-million-users-oh-yeah-and-their-cash-flow-just-went-positive/'&gt;Facebook had become profitable&lt;/a&gt;, with a whopping 300 million users. I think it's safe to say that it has reached that critical mass.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;The thing is, there is a cost to having that many people as a source of your income. If you start rocking the boat in any direction you're going to piss off some portion of your users. Facebook is still operating under the 'must grow' mindset, but doing that starting with almost half a billion users isn't exactly easy. The going only gets tougher and yet the show must go on. So how do you keep growing?&lt;/p&gt;
&lt;p&gt;Well to grow profits you can either increase margins or increase volume. In this case it's becoming harder and harder to reach the remaining 6.5 billion of humans who are not yet plugged in (but they show 2 year olds using iPads now so fingers crossed for BabyFacebook), so there goes volume. And so they figure that the obvious answer is to milk your existing user-base for more cash than you previously were (margins). Queue Facebook's '&lt;a href="http://techcrunch.com/2010/04/21/zuckerbergs-buildin-web-default-social/"&gt;Web where the default is social&lt;/a&gt;' master plan.&lt;/p&gt;
&lt;p&gt;Now there are &lt;a href="http://gizmodo.com/5530178/top-ten-reasons-you-should-quit-facebook"&gt;plenty of gripes&lt;/a&gt; already with the substance of Facebook's recent changes, but perhaps the deeper problem is that there are so many changes to begin with. Small tweaks are great. Gradual usability improvements are essential to any product or service. But complete site overhauls where logging in forces you to choose privacy settings?&lt;/p&gt;
&lt;p&gt;Here is a different idea: Facebook needs to stop pushing so hard for constant change and innovation. Big brand names need to be predictable and trusted. I think Facebook still has a lot of room to grow, but what it needs is not so much new features but rather just for time to go by.  There are still plenty of people who would love the site if they tried it but haven't been sufficiently peer-pressed by their relatives. There are still entire segments of consumers who don't have reliable internet access. There are still people who have not yet warmed up to the idea of social networks themselves.&lt;/p&gt;
&lt;p&gt;What Facebook is currently doing instead is basically sacrificing trust for revenue, which wouldn't be so bad from a business perspective if the added revenue from the users who stay offsets the loss of the users who drop out. Only this is a social network we're talking about. Building the network is the game. If you start breaking it up, you don't just lose points, you lose the very ground you stand on.&lt;/p&gt;
&lt;p&gt;The true value of Facebook is the connections it allows you to have with people you wouldn't otherwise be connected to. It is the very fabric of what is making it so popular. It's what no other social site has.  You stay on Facebook because there are people you just wouldn't know how to each otherwise. The scarier the site is to whoever is furthest connected to you, the lesser the value of it to the people in the middle of the network.&lt;/p&gt;
</description><guid>http://teebes.com/blog/24/facebook-needs-to-slow-down</guid></item><item><title>Introducing Squeak, an open source SQLite schema editor</title><link>http://teebes.com/blog/23/introducing-squeak-an-open-source-sqlite-schema-editor</link><description>&lt;p&gt;&lt;strong&gt;To just view or download the code, &lt;a href="http://teebes.com/squeak.py/"&gt;click here&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I love SQLite, I really do. For any quick and dirty development, there is no other relational database like it: it's fast, lightweight, ultra portable, DB-API 2.0 compliant, and baked right into any distro of Python 2.5 or higher.&lt;/p&gt;
&lt;p&gt;Using it is generally a very positive experience right up until the point where you need to make any changes to a table's schema. I distinctly remember that when learning Django it was particularly annoying to have to do a little SQLite dance every time I would need to edit or remove a model field.&lt;/p&gt;
&lt;p&gt;It is to scratch that itch that I've created Squeak, which is a python module that can be used as a script from the command line or as a library that lets you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drop a table column&lt;/li&gt;
&lt;li&gt;Rename a table column&lt;/li&gt;
&lt;li&gt;Change the definition of a table column (for example foreign key relationships or constraints)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is a test scenario where a column gets dropped from a table schema:&lt;/p&gt;
&lt;pre&gt;$ sqlite3 db
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite&amp;gt; CREATE TABLE "my_table" (
   ...&amp;gt;     "id" integer NOT NULL PRIMARY KEY,
   ...&amp;gt;     "name" varchar (20) NOT NULL DEFAULT ""
   ...&amp;gt; )
   ...&amp;gt; ;
sqlite&amp;gt; .quit

$ ./squeak.py db my_table drop_column name
Column 'name' dropped

$ sqlite3 db .schema
CREATE TABLE "my_table" (
    "id" integer NOT NULL PRIMARY KEY
);
&lt;/pre&gt;
&lt;p&gt;For more examples and further details, please visit the project's GitHub page at &lt;a href="http://github.com/teebes/squeak"&gt;http://github.com/teebes/squeak&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;Squeak should be used for development purposes only. It is not meant to be used on production systems and is nowhere near fool proof.&lt;/p&gt;
</description><guid>http://teebes.com/blog/23/introducing-squeak-an-open-source-sqlite-schema-editor</guid></item><item><title>Introducing Git-Goggles</title><link>http://teebes.com/blog/22/introducing-git-goggles</link><description>&lt;p&gt;&lt;strong&gt;&lt;a href="http://github.com/nowells/git-goggles" target="_blank"&gt;Git-Goggles&lt;/a&gt;&lt;/strong&gt;: &lt;em&gt;A series of Git utilities to streamline working with remote branches and reviewing code.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The development team I am in at PBS has adopted a new methodology over the last few months that includes using &lt;a href="http://git-scm.com/" target="_blank"&gt;Git&lt;/a&gt;, frequent (2 weeks) release cycles and rigorous code reviews. We spend a reasonable amount of time reviewing every iteration since every line of code gets critiqued by at least 2 members of the team before it gets pushed to production.&lt;/p&gt;
&lt;p&gt;We also have a lot of branches. Since git makes branching so inexpensive and easy to manage, we use them to group tasks by functional area. We generally have in between 10 to 15 branches at once that regularly get merged into our master repo, which is the production code.&lt;/p&gt;
&lt;p&gt;What is tricky in all this is keeping track of what code still needs to be reviewed versus what is good to go. We wanted a way to get both a nice visual, tabulated view of the state of our branches along with their status from a code review standpoint. It was to scratch that itch that our team lead, &lt;a href="http://twitter.com/nowells" target="_blank"&gt;Nowell Strite&lt;/a&gt;, created git-goggles. The project was open sourced last week under the MIT license.&lt;/p&gt;
&lt;p&gt;Here's a demonstration of git-goggles using a basic scenario that utilizes this type of workflow. The goal here is to push a new piece of code to a production repository, while making sure that it has been reviewed and that the branches are merged in.&lt;/p&gt;
&lt;p&gt;First I create a new repo, a test file, and push it to github.&lt;/p&gt;
&lt;!--more--&gt;
&lt;pre class="highlight"&gt;$ mkdir demo
$ cd demo
$ git init
$ echo "file1" &amp;gt; file1
$ git add file1
$ git commit -m "initial"
$ git remote add origin git@github.com:teebes/demo.git
$ git push origin master
$ git push origin master:refs/heads/staging
$ git branch --track staging origin/staging
$ git co staging
$ git goggles
&lt;/pre&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-1.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I then introduce a code change and commit. The table shows that 1 commit has not been reviewed, and that I need to push the change to origin.&lt;/p&gt;
&lt;pre&gt;$ echo "file2" &amp;gt; file2
$ git add file2
$ git commit -m "added second file"
$ git goggles
&lt;/pre&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-2.png" alt="" /&gt;&lt;/p&gt;
&lt;pre&gt;$ git push
$ git goggles
&lt;/pre&gt;
&lt;p&gt;After pushing, the 'Push' column now has a green checkmark instead of the red X.&lt;/p&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-3.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I start the code review, which brings up a diff between the development branch (staging) and the production branch (master). Once the review is complete, running git-goggles again shows that there is now no unreviewed code. Also, the status of the staging branch has changed to 'MERGE', which indicates that those changes still need to be merged back into the production branch.&lt;/p&gt;
&lt;pre class="highlight"&gt;$ git goggles codereview
diff --git a/file2 b/file2
new file mode 100644
index 0000000..6c493ff
--- /dev/null
+++ b/file2
@@ -0,0 +1 @@
+file2

[ &amp;hellip; code changes happen &amp;hellip;]

$ git goggles codereview complete
Counting objects: 1, done.
Writing objects: 100% (1/1), 194 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:teebes/demo.git
 * [new tag]         codereview--staging -&amp;gt; codereview--staging
Created tag codereview--staging
Already on 'staging'
Switched back to staging branch.

$ git goggles
&lt;/pre&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-4.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Now staging is ready for production and therefore gets merged back into master.&lt;/p&gt;
&lt;pre class="highlight"&gt;$ git co master
$ git merge staging
$ git push
&lt;/pre&gt;
&lt;p&gt;The status column now reads 'DONE', showing that no further action needs to be taken.&lt;/p&gt;
&lt;p&gt;&lt;img class="shell_img" src="/static/mediacenter/git-workflow-5.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Any subsequent pushed changes to the staging branch will change its status to 'REVIEW', and the codereview command will bring up the diff since the last review.&lt;/p&gt;
</description><guid>http://teebes.com/blog/22/introducing-git-goggles</guid></item><item><title>New year&amp;#39;s Python meme</title><link>http://teebes.com/blog/21/new-years-python-meme</link><description>&lt;p&gt;&lt;img style="border: 1px solid black; float: right; margin: 0 0 5px 5px;" src="http://teebes.com/static/mediacenter/python.jpg" alt="python" width="150" align="right" /&gt;Following in &lt;a href="http://tarekziade.wordpress.com/2009/12/28/new-years-python-meme/" target="_blank"&gt;Tyrek Ziade's footsteps&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. What&amp;rsquo;s the coolest Python application, framework or library you have discovered in 2009 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I hate to be so unoriginal... Django. I had been mostly using Zope 2/3 and &lt;a href="http://grok.zope.org/" target="_blank"&gt;Grok&lt;/a&gt; for web development (the latter is actually pretty cool).&amp;nbsp; But starting to work for PBS I got my first taste of Django and I was able to really completely dive in. Incredibly happy with it, I really appreciate all of the core functionality as well as a lot of contrib (not so much comments though, seriously what happened there?) and I feel like I can just get up and running much more quickly than before with minimal aggravation.&lt;/p&gt;
&lt;p&gt;Just because Django is such a boring answer, I'll mention a few other really cool Python libs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://bitbucket.org/jespern/django-piston/" target="_blank"&gt;Piston&lt;/a&gt; (been using that one a whole lot, creates RESTful APIs)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pygments.org/" target="_blank"&gt;Pygments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sphinx.pocoo.org/" target="_blank"&gt;Sphinx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://feedparser.org/" target="_blank"&gt;feedparser.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/ask/celery" target="_blank"&gt;Celery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;strong&gt;2. What new programming technique did you learn in 2009 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;High concurrency programming. I just made that name up but basically what I mean are programs where a lot of concurent requests come in and it becomes important to utilize asynchronous, evented and non-blocking I/O wherever possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. What&amp;rsquo;s the name of the open source project you contributed the most in 2009 ? What did you do ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well... basically none. I'm such a user! I submitted a couple patches to Django but one got the shot down and the other is in limbo, and other than that the only open source projects I've contributed to are my own, on which I am the only comitter anyway. I'm hoping to do better here in 2010 but it's difficult sometimes to contribute back because a lot of your suggestions have ramifications down the road you couldn't quite grok when you suggested them and then you get (rightfull) shot down. As frameworks evolve the problems left to fix become less and less trivial and it's tough to know where to start sometimes.&lt;/p&gt;
&lt;p&gt;But so the actual correct answer to the question would be &lt;a href="http://github.com/teebes/stark" target="_blank"&gt;Stark&lt;/a&gt;, my web-based visual open &lt;a href="http://en.wikipedia.org/wiki/MUD" target="_blank"&gt;MUD&lt;/a&gt; project. It's basically a web-based mud that has (non-flash) visual components to it and is open to the world via a sorta-RESTful API. The front-end is in &lt;a href="http://processingjs.org/" target="_blank"&gt;processing.js&lt;/a&gt; + &lt;a href="http://jquery.com/" target="_blank"&gt;jQuery&lt;/a&gt; (yes, I owe a lot to John Resig...) and the back-end is Django + Piston. This started as a weekend project to learn processing.js and has turned into a pretty big animal at this point...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. What was the Python blog or website you read the most in 2009 ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have a Twitter list with some of the web development people I follow: &lt;a href="http://twitter.com/teebesz/dev" target="_blank"&gt;http://twitter.com/teebesz/dev&lt;/a&gt;. I definitely keep up with that...&lt;/p&gt;
&lt;p&gt;I also have a google reader feed where I follow a wide range of tech people from Simon Willison to Paul Graham.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. What are the three top things you want to learn in 2010 ?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to build things people actually want :)&lt;/li&gt;
&lt;li&gt;The world outside Django: I think it's easy once you enter the Django world to become pretty consumed by its greater universe, and I need to remember to keep looking at other cool pythonic things as well as other progamming languages (Erlang &amp;amp; Ruby might be cool to learn...)&lt;/li&gt;
&lt;li&gt;Become good at automated testing... tough but essential.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy new year's everyone!&lt;/p&gt;
</description><guid>http://teebes.com/blog/21/new-years-python-meme</guid></item><item><title>An easy way to forward your django feed to feedburner</title><link>http://teebes.com/blog/20/an-easy-way-to-forward-your-django-feed-to-feedburner</link><description>&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/feedburner.png" alt="feedburner traffic" width="125" align="right" /&gt;If you are using Django's &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/syndication/#ref-contrib-syndication"&gt; syndication framework&lt;/a&gt; and want an easy way to track subscribers, &lt;a href="http://feedburner.google.com/"&gt;Feedburner&lt;/a&gt; is probably your best bet. Of course if you already have a substantial amount of subscribers you'll want to forward the old feed to the new feed so as not to lose them.&lt;/p&gt;
&lt;p&gt;I did this last week for this site and it worked very well, so if anyone else is trying to accomplish the same thing here is how I went about it:&lt;/p&gt;
&lt;p&gt;First, I added the feedburner URL in settings.py (replace the URL with your own):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;FEEDBURNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;http://feeds.feedburner.com/teebescom/&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;!--more--&gt;
&lt;p&gt;Then, in urls.py I changed the view being called for the feed from the standard syndication view to a custom wrapper around that view.&lt;/p&gt;
&lt;p&gt;So before it was:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^(?P(rss|atom))/$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;django.contrib.syndication.views.feed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;feed_dict&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And I changed it to:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^(?P(rss|atom))/$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;blog.views.rss.feedburner&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;feed_dict&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Last, I added a rss.py file in the blog.views package with the following contents:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.syndication.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;feedburner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed_dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;FEEDBURNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;FEEDBURNER&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;FEEDBURNER&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;META&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;HTTP_USER_AGENT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;FeedBurner&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;feed_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FEEDBURNER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And that's it! that should be all you need.&lt;/p&gt;
&lt;p&gt;Post a comment if you have any questions.&lt;/p&gt;
</description><guid>http://teebes.com/blog/20/an-easy-way-to-forward-your-django-feed-to-feedburner</guid></item><item><title>Playing with Google&amp;#39;s Closure JS library</title><link>http://teebes.com/blog/19/playing-with-googles-closure-js-library</link><description>&lt;img align='right' src='http://teebes.com/static/mediacenter/google-closure.png' alt='google closure tools image'/&gt;

&lt;p&gt;On Thursday, &lt;a href="http://googlecode.blogspot.com/2009/11/introducing-closure-tools.html"&gt;Google open-sourced&lt;/a&gt; several of its JavaScript building blocks: a compiler, a library and template extension. They've been released together as the &lt;a href="http://code.google.com/closure/"&gt;Closure&lt;/a&gt; toolkit.&lt;/p&gt;

&lt;p&gt;I was particularly interested in its JavaScript library, specifically the graphics engine that implements the W3C's Scalable Vector Graphics as well as the event listeners. So I threw together a very small interactive app to play with the parts I'm curious about:&lt;/p&gt;
&lt;!--more--&gt;
    Use your up / down / right / left arrow keys to move the ball:
    
    &lt;div id="shapes"&gt;&lt;/div&gt;
  
    &lt;script type="text/javascript"&gt;
    var graphics = goog.graphics.createGraphics(200, 150);

    // define the colors for the squares and the dot
    var square_fill = new goog.graphics.SolidFill('yellow');
    var square_stroke = new goog.graphics.Stroke(2, 'green');
    var dot_fill = new goog.graphics.SolidFill('blue');
    var dot_stroke = new goog.graphics.Stroke(1, 'black');

    // the dot's initial position
    var dot = {x: 1, y: 1};

    // properties    
    var size = 40;
    var margin = 5;
    var width = size - margin;
    var num_rows = 3;
    var num_cols = 4;

    // draw the squares
    for (var x = 0; x &lt; num_cols; x++) {
        for (var y = 0; y &lt; num_rows; y++) {
            graphics.drawRect(margin + x * size, margin + y * size, width, width, square_stroke, square_fill);
        }
    }

    // draw the dot
    dot['graphic'] = graphics.drawEllipse(margin + dot['x'] * size + width / 2, margin + dot['y'] * size + width / 2, width / 4, width / 4, dot_stroke, dot_fill);
    
    // call if the dot's position changes
    redraw_dot = function() {
        dot['graphic'].setCenter(margin + dot['x'] * size + width / 2, margin + dot['y'] * size + width / 2);
    }
    
    // key event handler
    var key_handler = new goog.events.KeyHandler(document);
    var key_event = function (e) {
        if (e.keyCode == goog.events.KeyCodes.UP &amp;&amp; dot['y'] &gt; 0) {
            dot['y'] -= 1;
        } else if (e.keyCode == goog.events.KeyCodes.RIGHT &amp;&amp; dot['x'] &lt;= num_cols - 2) {
            dot['x'] += 1;
        } else if (e.keyCode == goog.events.KeyCodes.DOWN &amp;&amp; dot['y'] &lt;= num_rows - 2) {
            dot['y'] += 1;
        } else if (e.keyCode == goog.events.KeyCodes.LEFT &amp;&amp; dot['x'] &gt; 0) {
            dot['x'] -= 1;
        }
        redraw_dot();
    }
    
    // put everything together
    goog.events.listen(key_handler, 'key', key_event);
    graphics.render(document.getElementById('shapes'));
  &lt;/script&gt;

&lt;p&gt;If you're reading this via rss, or if you want to see it in its own window, &lt;a href="http://teebes.com/closure/"&gt;click here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This does something similiar to what I do on &lt;a href="http://stark.teebes.com/"&gt;Stark&lt;/a&gt;, my visual mud project [&lt;a href="http://github.com/teebes/stark/"&gt;github page&lt;/a&gt;] that uses the &lt;a href="http://processingjs.org/"&gt;Processing&lt;/a&gt; JS library and the HTML 5 &amp;lt;canvas&amp;gt; element, but instead of creating a draw function that is executed x amounts per seconds, you just call a draw function and assign it to a variable for future interactions. Here is the source code for this example (you can also just look at the current page's source):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE HTML&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Playing with Closure graphics &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; events&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;text/css&amp;#39;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
    
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/static/js/closure-library-read-only/closure/goog/base.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.graphics&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.events&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.events.KeyCodes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;goog.events.KeyHandler&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    Use your up / down / right / left arrows to move the ball
    
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;shapes&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;graphics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createGraphics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;// define the colors for the squares and the dot&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;square_fill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SolidFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;yellow&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;square_stroke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;green&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dot_fill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SolidFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;blue&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dot_stroke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stroke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c"&gt;// the dot&amp;#39;s initial position&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c"&gt;// properties    &lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;num_cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c"&gt;// draw the squares&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;num_cols&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;num_rows&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;square_stroke&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;square_fill&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// draw the dot&lt;/span&gt;
    &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;graphic&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawEllipse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;dot_stroke&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;dot_fill&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    
    &lt;span class="c"&gt;// call if the dot&amp;#39;s position changes&lt;/span&gt;
    &lt;span class="nx"&gt;redraw_dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;graphic&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;setCenter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                            &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    
    &lt;span class="c"&gt;// key event handler&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;key_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;key_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UP&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RIGHT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;num_cols&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOWN&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;num_rows&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LEFT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;redraw_dot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    
    &lt;span class="c"&gt;// put everything together&lt;/span&gt;
    &lt;span class="nx"&gt;goog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key_handler&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;key&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key_event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;shapes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;margin-left: 20px;&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://teebes.com/blog/19/playing-with-googles-closure-js-library&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;what&amp;#39;s this?&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</description><guid>http://teebes.com/blog/19/playing-with-googles-closure-js-library</guid></item><item><title>A power companion for your smartphone</title><link>http://teebes.com/blog/18/a-power-companion-for-your-smartphone</link><description>&lt;p&gt;&lt;!--img src="http://teebes.com/static/mediacenter/iphone-duracell.png" alt="iphone with duracell instant charger" width="300" align="right" /--&gt;Often find yourself on the run with your phone or your iPod running low and no time to stop and recharge?&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I just picked up (at Harris Teeter, of all places) this pretty awesome gizmo I had read about a few weeks ago on one of the gadget blogs and that pretty much solves exactly that problem: the &lt;a href="http://www.amazon.com/Duracell-Instant-Charger-Lithium-universal/dp/B002FU6KF2/ref=sr_1_1?ie=UTF8&amp;amp;s=hpc&amp;amp;qid=1255291060&amp;amp;sr=8-1" target="_blank"&gt;Duracell Instant Usb Charger&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Costs $35 (that's what I paid at the store and what Amazon sells it at, but I remember reading it can be gotten for as low as $20... Either way, it's cheap).&lt;/li&gt;
&lt;li&gt;Gave my iPhone 60% power back in just over an hour and a half.&lt;/li&gt;
&lt;li&gt;Uses USB both to charge other devices and itself be recharged.&lt;/li&gt;
&lt;li&gt;Starts giving power as soon as you plug it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall a very thoughtfully designed little device that is cheap, slick, and useful to boot.&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell.png" alt="" width="320px" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell-before.png" alt="" width="320px" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell-after.png" alt="" width="320px" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/iphone-duracell2.png" alt="" width="320px" /&gt;&lt;/p&gt;
</description><guid>http://teebes.com/blog/18/a-power-companion-for-your-smartphone</guid></item><item><title>Simple Python Twitter rss feed parser</title><link>http://teebes.com/blog/17/simple-python-twitter-rss-feed-parser</link><description>&lt;p&gt;If you want to display your tweets somewhere on your own web page, the easiest way is to use the RSS feed in your Twitter profile page (for example &lt;a href="http://twitter.com/teebesz"&gt;http://twitter.com/teebesz&lt;/a&gt;). Of course if you want to parse the @, # and links, you need just a little bit of code. &lt;/p&gt;

&lt;p&gt;Here is the Python script I use for this site's Twitter display. You'll need the &lt;a href="http://feedparser.org/"&gt;feedparser&lt;/a&gt; library installed (how have you been living without it anyway!)&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;feedparser&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
    
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_twitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Takes a twitter rss feed and returns a list of dictionaries, one per&lt;/span&gt;
&lt;span class="sd"&gt;    tweet. Each dictionary contains two attributes:&lt;/span&gt;
&lt;span class="sd"&gt;        - An html ready string with the @, # and links parsed to the correct&lt;/span&gt;
&lt;span class="sd"&gt;        html code&lt;/span&gt;
&lt;span class="sd"&gt;        - A datetime object of the posted date&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;twitter_entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;entries&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;

        &lt;span class="c"&gt;# convert the given time format to datetime&lt;/span&gt;
        &lt;span class="n"&gt;posted_datetime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;updated_parsed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mf"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# format the date a bit&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;posted_datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;posted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posted_datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%b &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;posted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posted_datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%b &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt; %y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# strip the &amp;quot;&amp;lt;username&amp;gt;: &amp;quot; that preceeds all twitter feed entries&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^\w+:\s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        
&lt;span class="c"&gt;# parse links&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;r&amp;quot;[^&lt;/span&gt;&lt;span class="se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="s"&gt;](http://(\w|\.|/|\?|=|%|&amp;amp;)+)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;a href=&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/a&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# parse @tweeter&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;r&amp;#39;@(\w+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;a href=&amp;#39;http://twitter.com/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/a&amp;gt;&amp;quot;&lt;/span&gt;\
                 &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="c"&gt;# parse #hashtag&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;r&amp;#39;#(\w+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;a href=&amp;#39;http://twitter.com/search?q=&lt;/span&gt;&lt;span class="si"&gt;%%&lt;/span&gt;&lt;span class="s"&gt;23&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/a&amp;gt;&amp;quot;&lt;/span&gt;\
                 &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="n"&gt;twitter_entries&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;posted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;posted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;twitter_entries&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

</description><guid>http://teebes.com/blog/17/simple-python-twitter-rss-feed-parser</guid></item><item><title>Remember to do nothing</title><link>http://teebes.com/blog/16/remember-to-do-nothing</link><description>&lt;p&gt;&lt;img src="http://teebes.com/static/mediacenter/workflow.jpg" alt="complicated workflow" width="200" align="right" /&gt;In the technology world we're always after the next big thing, the differentiating feature that our competitor doesn't yet have and that will give us the edge. The emphasis is always on adding stuff, and hardly ever on staying lean and focused. This is mostly good for innovation, but not necessarily good for the end users.&lt;/p&gt;
&lt;p&gt;It's always striking to me to see how many of the really successful companies out there can be differentiated by what they didn't do. The concept of narrowing down your vision, concentrating it on a smaller subset of what's possible has sometimes surprising repercussions. Here are some famous examples:&lt;/p&gt;
&lt;!--more--&gt; 
&lt;ul&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a href="http://twitter.com" target="_blank"&gt;Twitter&lt;/a&gt;: not only do they limit tweets to 140 characters, they've also kept their site surprisingly light and simple, letting 3rd parties do the work of extending their core functionality for them. Can you imagine how many people have likely suggested to Twitter adding multimedia to tweets or fancy ways to arrange threads?&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a href="http://www.apple.com/iphone/" target="_blank"&gt;The iPhone&lt;/a&gt;: it only has 1 central button. It also has 3 other buttons and a switch but at its core it really just has one button. I have no doubt the Apple designers filtered thousands of suggestions to add either a few more buttons to the front-end or even a full keyboard. Of course they had to do a great deal of innovation to overcome this limitation but that's exactly my point: sometimes you need to limit your options in order to truly get creative.&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a href="http://www.google.com" target="_blank"&gt;&lt;span style="font-weight: normal;"&gt;Google&lt;/span&gt;&lt;/a&gt; and &lt;a href="http://www.craigslist.org/" target="_blank"&gt;Craigslist&lt;/a&gt;: they mostly use plain markup, very close to basic html. Over the years each site has probably had hundreds of consulting firm pitch them projects to pimp-up their pages with all sorts of eye candy. And yet they've made the conscious decision to stay very simple.&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a title="woot.com" href="http://woot.com" target="_blank"&gt;Woot.com&lt;/a&gt;: they sell only one item per day. Of course they get around this limitation a bit with wootoffs, but the vast majority of the time there is still just one item per day.&lt;/li&gt;
&lt;li style="margin-bottom: 10px"&gt;&lt;a title="stack overflow" href="http://stackoverflow.com" target="_blank"&gt;Stack Overflow&lt;/a&gt;: the popular site only answers programming questions. It would be easy to say, once the initial concept has proven successful (which it absolutely has), to say "ok let's expand this to all technology questions". But instead Stack Overflow created a second site, also focused on a specific topic, Super User. Sure they use the same engine, look the same, and probably have a large overlap in their communities but nevertheless, each site is focused on one area and does not expand for the sake of expansion alone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's easy to see why this is a rare thing, why consciously limiting your focus is difficult. Managers, &amp;nbsp;Board Members, Shareholders, they want to see growth, deltas, news, they need to see visible changes. If you've got the project team that goes to a CEO and says that they have decided to add nothing new, they'll probably be a lot less likely to say that again next quarter because they'll no longer have their job.&lt;/p&gt;
&lt;p&gt;And yet, it's always easier to not do something in the first place than it is later to try and scale back in a re-focusing effort. So remember: sometimes, the correct decision is to do nothing, and sometimes you'll need to work your butt off in order to look like you did nothing.&lt;/p&gt;
</description><guid>http://teebes.com/blog/16/remember-to-do-nothing</guid></item><item><title>One year and 60 pounds later, five weightloss tips</title><link>http://teebes.com/blog/15/one-year-and-60-pounds-later-five-weightloss-tips</link><description>&lt;p&gt;A year ago today, I began following a nutrition and workout program with the goal in mind of losing weight. The very initial plan was to just follow Bill Phillip's 90 days &lt;a href="http://en.wikipedia.org/wiki/Body_for_life" target="_blank"&gt;Body for Life&lt;/a&gt; challenge, but I also had a more long term goal of dropping about 70 lbs although I didn't quite know how long that would take. It turns out I've lost 60 lbs so far. Not nearly as impressive as &lt;a href="http://consumerist.com/5314225/how-i-lost-1004-pounds-in-6-months" target="_blank"&gt;some others&lt;/a&gt;, but still a decent amount.&lt;/p&gt;
&lt;p&gt;What I'm going to offer here are tips and advice that I've drawn from my experience. I don't pretend to have all of the answers but I can promise an honest, open account of my experience. First though, the before-after shots:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://teebes.com/static/mediacenter/weightloss-before.png"&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/weightloss-before.png" alt="" width="250px" /&gt;&lt;/a&gt; &lt;a href="http://teebes.com/static/mediacenter/weightloss-after2.png"&gt;&lt;img style="border: 1px solid #000000; margin-left: 50px;" src="http://teebes.com/static/mediacenter/weightloss-after2.png" alt="" width="250px" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;span style="font-size: 12px; font-weight: bold; "&gt;Nutrition&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Sunday-Friday, I eat six meals a day, relatively evenly spaced throughout the day:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meal one: omelette made with 1 egg beater serving and 1 real egg, fat-free yougurt&lt;/li&gt;
&lt;li&gt;Meal two: protein shake/bar (I use regular Myoplex and Cliff Bars)&lt;/li&gt;
&lt;li&gt;Meal three: turkey-cheese sandwhich, raw carrots&lt;/li&gt;
&lt;li&gt;Meal four: protein shake/bar&lt;/li&gt;
&lt;li&gt;Meal five: this is the meal I eat when I get back from work, it varies every day but is always chicken, lean meat or fish, a serving of carbs (potato or whole weat pasta / rice) and a serving of veggies&lt;/li&gt;
&lt;li&gt;Meal six: It varies somewhat but usually either chicken and rice soup or a burrito with 1 full egg and 2 egg whites with a slice of cheese.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This adds up to just over 2,000 calories a day. If I get hungry throughout the day, I eat almonds or a slice of turkey. On Saturdays I eat &lt;em&gt;whatever I want&lt;/em&gt;. No restriction whatsoever. I typically don't feel that good on Sunday mornings...&lt;/p&gt;
&lt;h3&gt;Exercise&lt;/h3&gt;
&lt;p&gt;I exercise in the mornings Sunday-Friday (Saturdays are off). I do the cardio on an empty stomach and don't eat for an hour after I'm done (this allows the body to keep burning fat even after you're done). For lifting, though, I do eat beforehand to have more strength and push harder.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sun-Tue-Thu: 20 min of stationary bike at variable intensity.&lt;/li&gt;
&lt;li&gt;Fridays: 45 min of Biceps &amp;amp; Back&lt;/li&gt;
&lt;li&gt;Mondays: 45 min of Chest &amp;amp; Triceps&lt;/li&gt;
&lt;li&gt;Wednesdays: 45 min Quads, Hamstrings, Calves, Abs and Shoulders&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although you can lose weight with just cardio or even just by changing your nutrition alone, you'd be missing out on a huge aid. Lifting doesn't just develop your muscles: it raises your whole metabolism, training your body to burn more calories, not just when you're lifting but all the time.&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;I started at 289 pounds. I'm now at 229. That's 60 pounds even. I've kept a pretty good record of my progress, which can be found on &lt;a href="http://teebes.com/weightloss/teebes/" target="_blank"&gt;this chart&lt;/a&gt;. Here is the small version of it:&lt;/p&gt;
&lt;p&gt;&lt;img style="border: 1px solid #000000;" src="http://teebes.com/static/mediacenter/weightloss-chart.png" alt="" width="400px" /&gt;&lt;/p&gt;
&lt;p&gt;The spikes in the line are almost always caused by my day off. Since I can eat whatever I want on that day I'll sometimes gain 4 or 5 pounds from that day alone. Of course most of that is just water retention, and I lose it all and then some during the week.&lt;/p&gt;
&lt;p&gt;Obviously, this is getting harder as time goes on. I lost 21 pounds in the first quarter, 20 in the second, 11 in the third and 8 in the fourth. I seem to be approaching some sort of natural weight plateau, barely losing a quarter pound a week. Some of this may be due to the fact that my muscles have been developing quite a bit in the last couple of months and there is no doubt that it's adding weight to the equation.&lt;/p&gt;
&lt;p&gt;Although I had initially fantasized about losing 100 pounds (it was easy to get carried away when I was losing 2 pounds a week...), I think that even losing the last 10 pounds is going to be difficult so at this point my goal is to reach 220 and stay there relatively stable for a while.&lt;/p&gt;
&lt;h3&gt;Tip #1: Plan, plan, plan&lt;/h3&gt;
&lt;p&gt;I cannot stress enough how true the old adage is: &lt;em&gt;if you are failing to plan you are planning to fail&lt;/em&gt;. This is especially true if you're going to go on the 6 meals a day plan, because on most days you'll be away from home for more than 3-4 hours at a time which means that you need to bring a meal with you. If you don't bring a meal with you and go hungry, you'll be in a real danger zone to go binge somewhere. It can be as simple as grabbing a protein bar on your way out, but you have to get into the habbit to always know exactly when and what your next meal will be. This is also true if you only have 3 meals a day: it's still important for you to know when those meals will be, and what you will eat at least half a day in advance. I've definitely observed a rule in my own behavior that the hungrier I am, the more likely I am to eat crap. If I am hungry and haven't planned what to eat, healthy food just doesn't seem as appealing to me.&lt;/p&gt;
&lt;p&gt;This isn't always as easy as it sounds. This plan was definitely easier to follow when I was working on my startup full time from home. There I had control over everything. Now that I work at PBS, it's not as easy. There have been several get togethers every week of my employment so far where there are cup cakes, chips, ice cream, hot dogs, burgers &amp;amp; pizzas everywhere. They like to go out to lunch and grab beers after work. They bake sutff.&lt;/p&gt;
&lt;p&gt;This is where another kind of planning can be useful. You can plan out in your mind how the event will go, say something like this to yourself: "I'm going to go to this get together, I won't eat any chips or any cake, if someone offers me something I'll smile and decline, then I'll go home and I'll feel great about myself for having said no." What this does is give your mind a clear map of how you're going to get through that situation with a positive outcome and will be able to fight against the urge to eat. Visualize the way you'll decline the food too. Imagine yourself saying 'no thanks' and passing the bowl of chips to the guy on your left. It really does help...&lt;/p&gt;
&lt;p&gt;The silver lining here is that when you're faced with a dangerous situation and come out on top, you are re-wiring your brain, teaching it that you have control and that the stimulus of food doesn't necessarily need to generate the response of eating. In the long run, this is the real goal and can only be accomplished by planning how you're going to do it.&lt;/p&gt;
&lt;h3&gt;Tip #2: Get a very basic understanding of nutrition &amp;amp; learn to read labels:&lt;/h3&gt;
&lt;p&gt;You don't need a degree in nutrition, but a few simple rules of thumb go a long way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Protein = good&lt;/li&gt;
&lt;li&gt;Carbs = good in moderation, but:                            
&lt;ul&gt;
&lt;li&gt;The more dietary fiber the better&lt;/li&gt;
&lt;li&gt;Sugar is not great, it will be converted into fat later, so try to keep that low&lt;/li&gt;
&lt;li&gt;Whole wheat really is better. I use 7-grain bread. Stay away from processed carbs, they soak in fat like a sponge&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sodium = the lower the better, but for some people (like me), salty tastes better so don't use too much but don't cut every pleasure out of your life either: you can use a little salt.&lt;/li&gt;
&lt;li&gt;Fat = usually bad, but:                            
&lt;ul&gt;
&lt;li&gt;Saturated fat is the enemy. Any serving with more than 3g should raise your eyebrows.&lt;/li&gt;
&lt;li&gt;Unsaturated fat is ok, in moderation. For example &lt;a href="http://caloriecount.about.com/calories-almonds-i12061" target="_blank"&gt;almonds&lt;/a&gt; are fine to eat because their fat is mostly unsaturated.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Tip #3: learn to cook basic stuff&lt;/h3&gt;
&lt;p&gt;I recently read &lt;a href="http://www.amazon.com/End-Overeating-Insatiable-American-Appetite/dp/1605297852/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1253405298&amp;amp;sr=8-1" target="_blank"&gt;the end of overeating: taking control of the insatiable american appetite&lt;/a&gt; by David Kessler, a former FDA commissioner, after I heard him on Bill Maher.&amp;nbsp; If I came away with one thing, it's that restaurants really are the devil when it comes to dieting. Even when you order a seemingly healthy meal it's often loaded with hidden fat, sugar and salt to make your brain chemically crave more (caesar salads are a notorious example). The best way to not be tricked is to learn to cook food: instead of going to a restaurant for lunch, bring a sandwich with healthy ingredients that you made in the morning, and instead of ordering dinner, cook a decent meal. It doesn't have to be time consuming, and it will also have the side bonus of saving you money.&lt;/p&gt;
&lt;h3&gt;Tip #4: Work out in the mornings&lt;/h3&gt;
&lt;p&gt;A bit afer I graduated from college I'd work out three times a week after work, and even though that worked well enough I've done it in the mornings for the last year and I like it a lot better, and here's why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You burn extra calories throughout the day if you worked out in the morning&lt;/li&gt;
&lt;li&gt;The gyms are &lt;em&gt;far&lt;/em&gt; less crowded&lt;/li&gt;
&lt;li&gt;You'll skip less because you won't have to go work out feeling shitty from working all day&lt;/li&gt;
&lt;li&gt;It helps you focus throughout the day&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The #1 downside is that it makes you wake up earlier, which makes you go to bed earlier, and that to me is a real sacrifice to make because I'm naturally quite the night owl.&lt;/p&gt;
&lt;h3&gt;Tip #5: Don't skip the day off&lt;/h3&gt;
&lt;p&gt;Like I said above, I sometimes gain 3, 4 or even 5 pounds on saturdays when I can eat whatever I want. You may be tempted to think that if you just didn't take that Saturday off you'd lose more weight more quickly. You'd be right of course, but taking the day off is important. The trick of dieting is to find a way to do it that is sustainable for the rest of your life. Otherwise you're just doing a temporary dip and you'll always regain the weight, like I have once.&lt;/p&gt;
&lt;p&gt;The weekly day off lets you tell yourself when you see food that looks really good "I'll have that Saturday" as opposed to saying "I'll never be able to have that." For me, that's been a huge difference. It keeps me sane.&lt;/p&gt;
&lt;h3&gt;What's difficult&lt;/h3&gt;
&lt;p&gt;There are, unfortunately, a few challenges to doing this kind of diet. The biggest problem, I'd say, is that social events become problematic: many of them involve either eating restaurant food or drinking, neither of which are great. On the one hand, it sucks seeing a week's worth of work foiled because you had a plate of cheese nachos after downing shots and beers late night at a bar... on the other, you gotta keep seeing your friends. So it's a careful balance of willpower and compromise, but it can definitley be challenging.&lt;/p&gt;
&lt;p&gt;Sometimes, you're hungry. I wish I could say that it never happen, but it does. Drinking water, light soda or coffee with skim milk helps, and if that's not enough I snack on almonds or on a slice or two of turkey.&lt;/p&gt;
&lt;p&gt;Temptation is everywhere. It is to the food industry's best interest that you eat fattening food. They've gotten very good at marketing, food manipulation and altering the very chemical circuitry in your brain to make you crave more fattening food. This is where planning helps, for example so that you make sure you don't go hungry at times where you might be tempted were you on an empty stomach.&lt;/p&gt;
&lt;h3&gt;Do this sooner rather than later (I've done this before)&lt;/h3&gt;
&lt;p&gt;So this is actually the second (and hopefully last) time I do this kind of weight drop. Five years ago, I lost over 40 pounds over about 9 months but I then took a consulting gig and stopped working out completely because I was traveling every week... and regained it all over about 3 years, with an extra layer as a free bonus.&lt;/p&gt;
&lt;p&gt;Why is this relevant? because it was much easier five years ago. I only worked out 3 times a week, I didn't diet nearly as strictly as I do now and I was drinking much more... and I lost at a comparable rate. So if you want to lose weight, don't wait, because it only gets harder.&lt;/p&gt;
&lt;p&gt;Thankfully, this time around I have a really solid understanding of nutrition that has opened my eyes to a lot of things, and in a lot of ways I don't think I'd be able to go back now. My views on food have fundamentally changed, and hopefully will keep on changing for the better as I keep working on this.&lt;/p&gt;
&lt;h3&gt;Thank yous&lt;/h3&gt;
&lt;p&gt;Just wanted to give a shout out to a few people...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My fiancee Galen, not only for supporting my nazi meal schedule and working out with me in the mornings but for telling me I was at the perfect weight when I was almost 300 pounds.&lt;/li&gt;
&lt;li&gt;My brother Tristan who gave me many great tips and who ultimately showed me just how insignificant my workout routine was when he started training for triathlons.&lt;/li&gt;
&lt;li&gt;Scott Monsefan, who introduced me a lot of the fundamental principles of nutrition and working out. This basic understanding of nutrition went a long way when I'd try to cook healthy but tasty meals.&lt;/li&gt;
&lt;li&gt;Bill Philips, since after all he did come up with the basic rules of the diet I followed. Props to him for designing a system that you can keep up in the long run. To me that's the biggest win in his program.&lt;/li&gt;
&lt;li&gt;Anyone else who's ever encouraged me&lt;/li&gt;
&lt;/ul&gt;
</description><guid>http://teebes.com/blog/15/one-year-and-60-pounds-later-five-weightloss-tips</guid></item><item><title>Django - Facebook Connect integration with ajax (no middleware)</title><link>http://teebes.com/blog/13/django-facebook-connect-integration-with-ajax-no-middleware</link><description>&lt;p&gt;&lt;img src="http://teebes.com/static/images/base/connect/facebook.png" border="1" alt="Facebook Connect logo" align="right" /&gt;&lt;/p&gt;

&lt;div style='padding: 3px; border: 1px solid #e02b2b; margin-left: 50px; float: left; text-align: center;'&gt;
&lt;b&gt;There is now a github repo for this code at&lt;br /&gt;
&lt;a href="http://github.com/teebes/django-facebookconnect/"&gt;http://github.com/teebes/django-facebookconnect/&lt;/a&gt;&lt;/b&gt;
&lt;/div&gt;
&lt;div style='clear: both'&gt;&lt;/div&gt;

&lt;h3&gt;Why do this&lt;/h3&gt;
&lt;p&gt;I hardly need to prove the point that web authentication is fast becoming a nightmare for users to manage. Obviously Facebook Connect and Open ID are interesting solutions but implementing them is not always straight forward... and yet until every site starts implementing those solutions, the problem will remain. I looked around for a really simple Django solution and found a few but they involved either custom middleware or even a custom auth backend.&lt;/p&gt;
&lt;p&gt;So I took at stab at this using only Ajax, jQuery and standard django stuff, with the aim of making the integration as minimalist and non-invasive as possible.&lt;/p&gt;
&lt;p&gt;This site's facebook connect implementation started out with exactly the code below. It's a bit more sophisticated now because I had to implement various linking capabilities. If there is any interest in seeing the source code as it currently stands leave a comment and I'll look into it.&lt;/p&gt;

&lt;h3&gt;Requirements&lt;/h3&gt;
&lt;p&gt;None! all of this should come standard with Python and Django.&lt;/p&gt;
&lt;h3&gt;How it works&lt;/h3&gt;
&lt;p&gt;If a connect login occurs and that facebook ID has never logged in to this site, a standard django.contrib.auth user is created, as well as a custom FacebookUser object that keeps track of the Facebook - Django user ID mappings. If the user has previously logged on, the corresponding Django user is used. The user is then logged in just like any regular Django user.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Start by &lt;a href="http://wiki.developers.facebook.com/index.php/Trying_Out_Facebook_Connect"&gt;creating a facebook application&lt;/a&gt; that your site will use for connect.&lt;/p&gt;
&lt;p&gt;The important thing here is to get the 'Connect URL' right, which is under the 'Connect' tab. It will be http://&amp;lt;your-project's-address&amp;gt;/facebookconnect&lt;/p&gt;
&lt;p&gt;In your django project that will be using connect, setup the following values in your settings.py (or wherever you keep your settings) (replace the xxxxxxxx with the correct values for your setup):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# where the user will go after they log in via facebook&lt;/span&gt;
&lt;span class="n"&gt;LOGIN_REDIRECT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/blog/&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;# the application api key given by facebook&lt;/span&gt;
&lt;span class="n"&gt;FACEBOOK_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xxxxxxxx&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;# the applications ecret key given by facebook&lt;/span&gt;
&lt;span class="n"&gt;FACEBOOK_APPLICATION_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xxxxxxxx&amp;#39;&lt;/span&gt;

&lt;span class="c"&gt;# add the facebookconnect app to the list of apps.&lt;/span&gt;
&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;facebookconnect&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Include the facebook connect url file to your urls:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^facebookconnect/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebookconnect.urls&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;The facebookconnect Django app&lt;/h3&gt;
&lt;p&gt;Here is the anatomy of the app. Make sure the app is in your python path or site-packages directory.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;facebookconnect&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
	&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
	&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
	&lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
	&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
        &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
                &lt;span class="n"&gt;xd_receiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;htm&lt;/span&gt;
	&lt;span class="n"&gt;templatetags&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
		&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
		&lt;span class="n"&gt;facebookconnect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;models.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FacebookUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;facebook_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;contrib_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;contrib_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;views.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.template&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;facebookconnect.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login_facebook_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;unknown failure&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;expires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;expires&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ss&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;session_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;session_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;user&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sig&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;pre_hash_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;expires=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;session_key=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;ss=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;user=&lt;/span&gt;&lt;span class="si"&gt;%s%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;session_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FACEBOOK_APPLICATION_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;post_hash_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;md5&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;post_hash_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pre_hash_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;post_hash_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facebook_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;logged in existing user&amp;quot;&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DoesNotExist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;u&amp;quot;fbuser_&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

                &lt;span class="n"&gt;fb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FacebookUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;facebook_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contrib_user&lt;/span&gt;

                &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sha1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
                &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
                &lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;created new user&amp;quot;&lt;/span&gt;

            &lt;span class="n"&gt;authenticated_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                         &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                         &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contrib_password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;wrong hash sig&amp;#39;&lt;/span&gt;

            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;FBConnect: user &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; with exit status &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Exception thrown in the FBConnect ajax call: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;xd_receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebookconnect/xd_receiver.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;urls.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf.urls.defaults&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.views.generic.simple&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;direct_to_template&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebookconnect.views&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^xd_receiver\.htm$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;direct_to_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;template&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;xd_receiver.htm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;xd_receiver&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;^login_facebook_connect/$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;login_facebook_connect&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebook_connect_ajax&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;templates/xd_receiver.htm&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Strict//EN&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;   &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;templatetags/facebookconnect.py&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.urlresolvers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Library&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FacebookScriptNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;script src=&amp;quot;http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt; FB.init(&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;);&lt;/span&gt;
&lt;span class="s"&gt;                function facebook_onlogin() {&lt;/span&gt;
&lt;span class="s"&gt;                    var uid = FB.Facebook.apiClient.get_session().uid;&lt;/span&gt;
&lt;span class="s"&gt;                    var session_key = FB.Facebook.apiClient.get_session().session_key;&lt;/span&gt;
&lt;span class="s"&gt;                    var expires = FB.Facebook.apiClient.get_session().expires;&lt;/span&gt;
&lt;span class="s"&gt;                    var secret = FB.Facebook.apiClient.get_session().secret;&lt;/span&gt;
&lt;span class="s"&gt;                    var sig = FB.Facebook.apiClient.get_session().sig;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                    fb_connect_ajax(expires, session_key, secret, uid, sig);&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                }&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                function fb_connect_ajax(expires, session_key, ss, user, sig) {&lt;/span&gt;
&lt;span class="s"&gt;        &lt;/span&gt;
&lt;span class="s"&gt;                    var post_string = &amp;#39;expires=&amp;#39; + expires;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;session_key=&amp;#39; + session_key;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;ss=&amp;#39; + ss;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;user=&amp;#39; + user;&lt;/span&gt;
&lt;span class="s"&gt;                    post_string = post_string + &amp;#39;&amp;amp;sig=&amp;#39; + sig;&lt;/span&gt;
&lt;span class="s"&gt;    &lt;/span&gt;
&lt;span class="s"&gt;                    $.ajax({&lt;/span&gt;
&lt;span class="s"&gt;                        type: &amp;quot;POST&amp;quot;,&lt;/span&gt;
&lt;span class="s"&gt;                        url: &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;,&lt;/span&gt;
&lt;span class="s"&gt;                        data: post_string,&lt;/span&gt;
&lt;span class="s"&gt;                        success: function(msg) {&lt;/span&gt;
&lt;span class="s"&gt;                            window.location = &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;; //.reload()&lt;/span&gt;
&lt;span class="s"&gt;                        }&lt;/span&gt;
&lt;span class="s"&gt;                    });&lt;/span&gt;
&lt;span class="s"&gt;                } &lt;/span&gt;
&lt;span class="s"&gt;            &amp;lt;/script&amp;gt;       &lt;/span&gt;
&lt;span class="s"&gt;            &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FACEBOOK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;xd_receiver&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;facebook_connect_ajax&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOGIN_REDIRECT_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;facebook_connect_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FacebookScriptNode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facebook_connect_script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FacebookLoginNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;lt;fb:login-button onlogin=&amp;#39;facebook_onlogin();&amp;#39;&amp;gt;&amp;lt;/fb:login-button&amp;gt;&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;facebook_connect_login_button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FacebookLoginNode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;facebook_connect_login_button&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Putting it all together&lt;/h3&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt;
      &lt;span class="na"&gt;xmlns:fb=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.facebook.com/2008/fbml&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Login via facebook!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

{% load facebookconnect %}
{% facebook_connect_login_button %}

{% facebook_connect_script %}

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

</description><guid>http://teebes.com/blog/13/django-facebook-connect-integration-with-ajax-no-middleware</guid></item><item><title>Video of my assembled Lego set of Frank Lloyd Wright&amp;#39;s Falling Water</title><link>http://teebes.com/blog/14/video-of-my-assembled-lego-set-of-frank-lloyd-wrights-falling-water</link><description>&lt;p&gt;A &lt;a href="http://twitter.com/ravenell" target="_blank"&gt;friend of mine&lt;/a&gt; sent me &lt;a href="http://www.wired.com/gadgetlab/2009/05/frank-lloyd-wright-lego-sets/" target="_blank"&gt;a link&lt;/a&gt; a while back to a 800+ pieces Lego set of &lt;a href="http://en.wikipedia.org/wiki/Frank_lloyd_wright" target="_blank"&gt;Frank Lloyd Wright&lt;/a&gt;'s &lt;a href="http://en.wikipedia.org/wiki/Falling_Water" target="_blank"&gt;Falling Water&lt;/a&gt;, which I studied for an architecture class in College and have loved ever since. So I decided to get the set last week and put it together for a couple hours on Friday and another couple hours today. Here is the finished result (some bricks in this video are a little loose, I've tightened the whole thing since). This was really a lot of fun, I hadn't built lego since I was maybe 14 and it brought back all sorts of great memories. I'm definitely considering getting more sets.&lt;/p&gt;
&lt;p&gt;Video was taken with my iPhone 3GS. The grass and trees to the back corners of the set look black but that's just the iPhone sucking at recording green.&lt;/p&gt;
&lt;p&gt;
&lt;object width="425" height="344"&gt;
&lt;param name="movie" value="http://www.youtube.com/v/x0PK13IFFUo&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" /&gt;
&lt;param name="allowFullScreen" value="true" /&gt;
&lt;param name="allowscriptaccess" value="always" /&gt;&lt;embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/v/x0PK13IFFUo&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" allowscriptaccess="always" allowfullscreen="true"&gt;&lt;/embed&gt;
&lt;/object&gt;
&lt;/p&gt;
</description><guid>http://teebes.com/blog/14/video-of-my-assembled-lego-set-of-frank-lloyd-wrights-falling-water</guid></item><item><title>My upgrade to Snow Leopard: a non-event</title><link>http://teebes.com/blog/12/my-upgrade-to-snow-leopard-a-non-event</link><description>&lt;p&gt;&lt;img src="http://teebes.com/images/2009-08/snow-leopard.png" alt="Apple Snow Leopard OSX 10.6" width="200" align="right" /&gt;So I upgraded to Snow Leopard today. I pre-ordered it right as it became available on Monday and it was waiting for me when I got home from work today. I popped the CD in, clicked the upgrade button, typed my password, watched an episode of Damages and by the time it was done my computer was all set (so less than an hour). Talk about painless...&lt;/p&gt;
&lt;p&gt;As far as the OS itself goes, it's basically as if nothing had happened. Everything is where it was, all of my applications still work and Time Machine is backing up the 6 or so new gigs as I'm writing this (in reality Snow Leopard &lt;a href="http://gizmodo.com/5283157/everything-you-need-to-know-about-snow-leopard" target="_blank"&gt;gives you space back&lt;/a&gt;, but it's still new data as far as Time Machine is concerned).&lt;/p&gt;
&lt;p&gt;I haven't really noticed any speed difference yet but then again I wasn't expecting to: most applications haven't yet been built to take advantage of Snow Leopard's new capabilities and I haven't run many heavy programs simultaneously. I definitely like the tweaks to expose though, and the new QuickTime is pretty slick.&lt;/p&gt;
&lt;p&gt;Of course none of this was unexpected: Snow Leopard's improvements are supposed to be all under the hood and Apple has been pretty upfront about that from the get go. All in all the upgrade was a non-event (I say this in a good way), and well worth the $29.&lt;/p&gt;
&lt;p&gt;If you want more info on Snow Leopard, here are the links to the reviews from &lt;a href="http://www.engadget.com/2009/08/26/snow-leopard-review/" target="_blank"&gt;Engadget&lt;/a&gt;, &lt;a href="http://gizmodo.com/5346418/snow-leopard-review-lightened-and-enlightened" target="_blank"&gt;Gizmodo&lt;/a&gt; and &lt;a href="http://www.macworld.com/article/142423/2009/08/snow_leopard_review.html" target="_blank"&gt;MacWorld&lt;/a&gt;.&lt;/p&gt;
</description><guid>http://teebes.com/blog/12/my-upgrade-to-snow-leopard-a-non-event</guid></item><item><title>Never temporarily disable your backup</title><link>http://teebes.com/blog/10/never-temporarily-disable-your-backup</link><description>&lt;p&gt;&lt;img src="http://teebes.com/images/2009-08/time-machine.png" border="1" alt="Apple Time Machine" width="250" align="right" /&gt;I often find myself in the situation where there are too many simultaneous data transfers going on over my Wi-Fi at home: I'll be watching a TV show on my Apple TV (which streams from the Drobo, see &lt;a href="http://teebes.com/2009/07/28/me-and-my-apple-tv-life-after-cable/" target="_blank"&gt;this post&lt;/a&gt; for details), transferring media to the Drobo, doing a large svn or git checkout, all of this while Time Machine is wirelessly backing up the Mac.&lt;/p&gt;
&lt;p&gt;At this point I'll often decide to stop the ongoing backup, knowing it will start again an hour later when there is less going on. So far so good. What's less good is that when Time Machine is doing a very large backup, say 20 gigs, I know I will need to stop it every hour for a while and I'll sometimes disable it with the intent of re-enabling it once all of the data transfers are done.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;This has proven to be a horrible idea: I am missing all of the first half of May and the 2nd half of July. Reason? I completely forgot both times to turn Time Machine back on. Now thanks to Murphy's Law, you can be sure that the files you'll need to recover will always happen to fall within the time period where you forgot to re-enable your backup.&lt;/p&gt;
&lt;p&gt;I'm tempted to say that disabling your backup is OK as long as you also create a reminder to turn it back on, but chances are you'll either forget to create it or dismiss it as something you feel confident you'll remember on your own (you won't).&lt;/p&gt;
&lt;p&gt;Yes, backing up can be inconvenient sometimes, but nothing is more important than keeping your life's data safe. Don't disable your backup.&lt;/p&gt;
</description><guid>http://teebes.com/blog/10/never-temporarily-disable-your-backup</guid></item><item><title>Is Apple more or less vulnerable than ever?</title><link>http://teebes.com/blog/9/is-apple-more-or-less-vulnerable-than-ever</link><description>&lt;p align="left"&gt;&lt;img style="margin-right: 5px" src="http://teebes.com/images/2009-08/apple-logo.png" border="0" alt="The Apple logo with a black background" width="150" height="150" align="left" /&gt;I hate to be blogging about Apple again, but it has been quite ubiquitous of late and given that I own an iPhone, an Apple TV, a Mac Mini and a MacBook Pro, I cannot help caring.&lt;/p&gt;
&lt;p align="left"&gt;From a PR standpoint, the last few months have been mixed at best for Apple (more on that later), and yet from a financial standpoint it has been pretty much blowing right through this recession, &lt;a href="http://www.apple.com/pr/library/2009/07/21results.html" target="_blank"&gt;announcing just last month&lt;/a&gt; the best non-holiday quarter in the company's history. What's going on here?&lt;/p&gt;
&lt;p align="left"&gt;I grew up very much a PC guy. My first computer, in 1994, was a 75MHz  Pentium I with 8 megs of ram and a 500 MB hard drive running Dos and Windows 3.11. I was 14, it was by far the coolest thing I'd ever owned, and the internet was just getting started. Sometimes when I'd go visit a school buddy of mine, I'd take a peek at his dad's Apple PowerPC which, he liked to remind me, was much more expensive and cooler than my PC. It also wasn't compatible with mine. It never would be. Apple actually cultivated that difference, it thrived on it: remember 'think different?'&lt;/p&gt;
&lt;p align="left"&gt;From that point until about a year and a half ago, I discarded Macs as over-priced, over-engineered rounded cubes without a delete key or a right-click button, designed by and for intellectual snobs.&lt;/p&gt;
&lt;p align="left"&gt;&lt;!--more--&gt;Fast forward 15 years and I find two facts remarkable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I now use Macs almost exclusively, at home and at work&lt;/li&gt;
&lt;li&gt;Apple switched to Intel&lt;/li&gt;
&lt;/ul&gt;
&lt;p align="left"&gt;The two are of course very closely related. Apple switching to Intel enabled software developers to easily make software that worked in Windows, Linux, and OSX as desktop &lt;em&gt;and&lt;/em&gt; cloud applications. This made Apple more a part of the greater ecosystem. Now the lines in between Mac and PC users weren't so clearly defined.&lt;/p&gt;
&lt;p align="left"&gt;I think it's fair to say that the switch to Intel was mostly a financial decision, and one to everyone's best interest since it takes a lot of capital to put out high quality hardware &lt;em&gt;and&lt;/em&gt; software. But the bottom line is Apple is still exclusive at heart. It still wants everyone to do things their way. That's exactly why the iPhone still only has one App Store, one browser, and why it considers that people who want to play with the guts of the hardware they paid for &lt;a href="http://reviews.cnet.com/8301-19512_7-10298646-233.html" target="_blank"&gt;are threats to national security&lt;/a&gt;.&lt;/p&gt;
&lt;p align="left"&gt;So Apple is now more inclusive than it used to be thanks to the Intel switch. It even &lt;a href="http://www.macworld.com/article/138000/2009/01/drm_faq.html" target="_blank"&gt;gave up on the DRM battle&lt;/a&gt;, a move which I personally believe it disagreed with from a philosophic standpoint but just had to do for PR reasons. Moves like this sometimes give the impression that Apple wants to play nice with the other kids, but that is simply not the case. The last few months have happened to highlight of a lot of its core values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sling and Skype apps &lt;a href="http://news.cnet.com/8301-13579_3-10239277-37.html" target="_blank"&gt;not allowed to&lt;/a&gt; &lt;a href="http://gizmodo.com/5198594/fcc-pressed-to-investigate-apple-att-regarding-skype-over-3g" target="_blank"&gt;work over 3G&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apple specifically &lt;a href="http://www.precentral.net/apple-blocks-palm-pre-itunes-syncing" target="_blank"&gt;blocking iTunes from syncing with the Pre&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Numerous accounts of the &lt;a href="http://mobile.slashdot.org/story/09/07/20/2318201/How-Apples-App-Review-Is-Sabotaging-the-iPhone?from=rss" target="_blank"&gt;poor and arbitrary approval process&lt;/a&gt; of the app store&lt;/li&gt;
&lt;li&gt;Safari and Apple's own store still the only browser/app stores allowed on the device&lt;/li&gt;
&lt;li&gt;And the big one, last week &lt;a href="http://www.techcrunch.com/2009/07/27/apple-is-growing-rotten-to-the-core-and-its-likely-atts-fault/" target="_blank"&gt;Apple rejecting the Google Voice app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p align="left"&gt;To those saying that #1 and #5 are AT&amp;amp;T's doing, even if it is (I think it's more complicated than that) you can't have a partnership when it's profitable that you then denounce when it's not convenient from a PR standpoint. It's too easy.&lt;/p&gt;
&lt;p align="left"&gt;Now you have high-profile bloggers like &lt;a href="http://gigaom.com/2009/02/11/my-big-iphone-break-up/" target="_blank"&gt;Om Malik&lt;/a&gt; and &lt;a href="http://www.techcrunch.com/2009/07/31/i-quit-the-iphone/" target="_blank"&gt;Michael Arrington&lt;/a&gt; that are quitting their iPhones and the whole tech press is upside down over the Google Voice controversy. But like I said, Apple has always had this 'do it my way' attitude. Sure they like innovation, but only so far as it benefits their advancement.&lt;/p&gt;
&lt;p align="left"&gt;So then one may ask, is all of this bad press hurting the company's bottom-line? It may be a little early to tell, but I would be willing to bet they'll do fine next quarter, and will hit it out of the park again during the holiday quarter. I think there are a few reasons for this.&lt;/p&gt;
&lt;p align="left"&gt;First, Apple gets about half of its business from the international market. The issues they are having as a consequence of the partnership with AT&amp;amp;T (Google Voice, poor call quality, MMS) are simply not factors abroad.&lt;/p&gt;
&lt;p align="left"&gt;Second, what makes the phone most useful to a lot of us are all of the 3rd party apps: &lt;a href="http://www.rememberthemilk.com/" target="_blank"&gt;Remember the Milk&lt;/a&gt;, &lt;a href="http://evernote.com/" target="_blank"&gt;Evernote&lt;/a&gt;, &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=289943355&amp;amp;mt=8" target="_blank"&gt;Air Sharing&lt;/a&gt;, games... Once you're hooked to the system you start depending on it, and that brings an additional, hidden factor against switching that most users will be well aware of once their contracts expire and they need to make a decision on which new phone to get.&lt;/p&gt;
&lt;p align="left"&gt;Last, and most importantly, Apple's biggest asset is its ability to create &lt;em&gt;envy&lt;/em&gt; in consumers. It is a delicate balance of software design, hardware design and marketing that so far Apple has been the only one to strike. But they remain an advocate of a 'complete Apple solution'. Your phone, hardware, software, everything should have the Apple brand. If you're trying to mix and match, you will always run into the rougher edges of the company's philosophy.&lt;/p&gt;
&lt;p align="left"&gt;At the end of the day, it's a matter of how much of your freedom as a consumer are you willing to give up. This makes the technological world delicate to walk into as you are always locking yourself into contract, or buying devices that are really the foot into the door of a much larger structure that will keep you hooked for many years.&lt;/p&gt;
</description><guid>http://teebes.com/blog/9/is-apple-more-or-less-vulnerable-than-ever</guid></item><item><title>Click by click: how to browse files offline on the iPhone</title><link>http://teebes.com/blog/8/click-by-click-how-to-browse-files-offline-on-the-iphone</link><description>&lt;p&gt;I wanted to be able to browse code on the bus going to and from work. So I asked &lt;a href="http://stackoverflow.com" target="_blank"&gt;Stack Overflow&lt;/a&gt; for a suggestion and got &lt;a href="http://stackoverflow.com/questions/1197643/iphone-code-browser" target="_blank"&gt;two answers&lt;/a&gt;: Air Sharing and Mobile Me. I decided to go with Air Sharing because I like paying the fixed price of $4.99 instead of being sucked in by the free trial.&lt;/p&gt;
&lt;p&gt;The way it works is you connect to your iPhone from your Mac via Wi-Fi, copy your files and folders to your phone and browse them anywhere thereafter, even offline.&lt;/p&gt;
&lt;p&gt;I've had a very good experience with it so far: it can read most well-known file formats including pictures, PDFs, Word, Excel and even source code. I can load the django source tree and read the code whenever I'm thinking about implementing a solution. It even colors the code.&lt;/p&gt;
&lt;p&gt;In my opinion, well worth the money. Here is the click by click:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Buy and download &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=289943355&amp;amp;mt=8" target="_blank"&gt;Air Sharing&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=289943355&amp;amp;mt=8" target="_blank"&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-1.png" border="0" alt="" align="middle" /&gt;&lt;!--more--&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure the phone and your laptop are on the same wifi network. I recommend doing this at home on your secure network.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Start the app and click the &lt;em&gt;Done&lt;/em&gt; button&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-2.png" alt="" width="320" height="480" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click the wheel with spokes on it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-3.png" alt="" width="320" height="480" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write down the IP address (for example http://192.168.1.113:8080) of your phone as is displayed just below the Port field&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-4.png" alt="" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to your Mac and connect to your phone by clicking and holding the Finder icon and selecting &lt;em&gt;Connect to Server...&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-5.png" alt="" width="267" height="225" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt; In the &lt;strong&gt;Server Address&lt;/strong&gt; box, enter your phone's IP address (for example http://192.168.1.113:8080) and click &lt;em&gt;Connect&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-6.png" alt="" width="548" height="321" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drag and drop the folders you want to see, wait for the transfer to complete (it's quite slow)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-7.png" alt="" width="569" height="421" align="middle" /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;That's it! go to the Air Sharing app and browse your files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-8.png" alt="" width="320" height="480" align="middle" /&gt;&lt;img src="http://www.teebes.com/images/2009-08/airsharing-walkthrough-9.png" alt="" width="320" height="480" align="middle" /&gt;Some parting words: one can never be too careful with security, so if you're going to be putting sensitive files on here you'll want to do 2 things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enable &lt;em&gt;Require Password&lt;/em&gt; under  &lt;strong&gt;Sharing Security&lt;/strong&gt; and enter a password. You can put files in your Public folder and enable Public Access if you want them accessed by guests.&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Passcode&lt;/em&gt; under &lt;strong&gt;Application Lock&lt;/strong&gt; to set a code needed to access the device&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Also, if you have a PC you can use your browser to connect to your phone: just type the phone's IP address (that you found in the Air Sharing app's settings screen) in the browser's URL bar.If you like this but want to do more than just read (for example move files or sync with Mobile Me), you may want to look into the &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=312686749&amp;amp;mt=8" target="_blank"&gt;pro version&lt;/a&gt;, which does all that.&lt;/p&gt;
</description><guid>http://teebes.com/blog/8/click-by-click-how-to-browse-files-offline-on-the-iphone</guid></item><item><title>Me and my Apple TV: life after cable</title><link>http://teebes.com/blog/7/me-and-my-apple-tv-life-after-cable</link><description>&lt;p&gt;I've been meaning to document my home media setup for a while now because, well, I think it's pretty sweet and I very much enjoy the setup literally every day. Before I go into the details of how everything works, here are the key benefits of this setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I can watch 3TB worth of content (202 movies and 99 TV seasons at the moment) anytime I want at home. I don't have to get up and put anything in or out, it's just all always available. The data is protected against any single drive failure.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The computers are set up to backup over Wi-Fi without me having to even think about it&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The computers can play music on the Bose speakers by taking over the &lt;a href="http://www.apple.com/appletv/" target="_blank"&gt;Apple TV&lt;/a&gt;'s speaker output (this is an iTunes feature)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;In addition to playing games, I can use the &lt;a href="http://www.xbox.com/en-US/default.htm" target="_blank"&gt;XBox 360&lt;/a&gt; for DVDs and to stream instantly from &lt;a href="http://www.netflix.com/MemberHome" target="_blank"&gt;Netflix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I can remote control the Apple TV with my iPhone! (via the excellent &lt;a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=284417350&amp;amp;mt=8" target="_blank"&gt;Remote&lt;/a&gt; app)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="http://teebes.com/images/2009-07/multimedia-setup.png" alt="Apple TV with extra storage multimedia setup" width="492" height="390" align="middle" /&gt;&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;A couple of years ago I noticed that some of my old DVDs were no longer playing properly simply due to the disks aging, and a number of others had scratches and dents that made them glitchy. I looked around for good digital solutions, and the Apple TV caught my eye. The main problem for me was its hard-drive limitation, which at the time was around 140gigs. Given that I had a DVD library of well over 300 titles (I didn't convert them all), there was no way that was going to cut it.&lt;/p&gt;
&lt;p&gt;I was also getting to the point where I could no longer take commercial interruptions to the shows I love to watch, and I was determined to try to do something about it.&lt;/p&gt;
&lt;p&gt;I started looking around for good storage solutions, and after a lot of time spent researching, I settled on the &lt;a href="http://drobo.com/" target="_blank"&gt;Drobo&lt;/a&gt;. It's incredibly easy to set up and use, it protects you against any one of the hard-drives failing, and it's easy to expand the capacity later on. Of course the challenge was making the Apple TV use an external source for its data while still allowing me to easily manage the whole thing.&lt;img src="http://teebes.com/images/2009-07/multimedia-rack.png" border="0" alt="The multimedia setup below my TV" hspace="0" width="400" height="266" align="right" /&gt;&lt;/p&gt;
&lt;p&gt;Thankfully, the Apple TV lets you define an external iTunes instance as its main library as long as it is connected to the same network. I also knew that I wanted to have a robust solution to back up the important data on the various computers that my fiancee and I own. That's where the &lt;a href="http://www.apple.com/macmini/" target="_blank"&gt;Mac Mini&lt;/a&gt; comes in, which serves mainly as an iTunes library but also as a sharing point for the &lt;a href="http://www.maxtor.com/en/index.html" target="_blank"&gt;Maxtor&lt;/a&gt; drive (backup) and Drobo (media) to the network.&lt;/p&gt;
&lt;p&gt;Here is what each node does:&lt;/p&gt;
&lt;h3&gt;Drobo:&lt;/h3&gt;
&lt;p&gt;The Drobo has four 1TB hard-drives. Internally they are setup as a RAID 5, which means that each drive is backed up by the other three, and therefore any one of them can fail without losing any data. If one does (it's happened once already), just replace it and the data is internally re-laid out. This means that you get 3TB usable out of the 4TB capacity that you have. The really nice thing here is that the Drobo does all of this without you having to worry about any of it. Just plug it in and it works.&lt;/p&gt;
&lt;p&gt;In the interest of full disclosure, I did have some issues at first, but this was due to one of the Western Digital hard-drives, and Drobo's customer support was incredibly helpful. They actually identified via a diagnostics file I ran for them which drive was bad and generated the return forms for me. Pretty sweet.&lt;/p&gt;
&lt;h3&gt;Maxtor:&lt;/h3&gt;
&lt;p&gt;This is a 500GB external drive that I use to backup my MacBook Pro via Time Machine. My hard drive has fried once on me already and I've got to say that both this drive and Time Machine worked flawlessly. It could also be used by my fiancee for her backup needs but she prefers a system where she has a shared folder on the Drobo that she drags and drops stuff to (to each their own!).&lt;/p&gt;
&lt;p&gt;It has no RAID protection internally, but that's not a problem for me given how the odds of both it and the MacBook Pro going down are relatively small. If you want absolute peace of mind, you have to look at cloud storage. &lt;a href="http://blog.etilities.com/2008/10/remote-data-backup-the-final-component/" target="_blank"&gt;Here is an awesome article on the subject&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;h3&gt;Mac Mini:&lt;/h3&gt;
&lt;p&gt;&lt;img src="http://teebes.com/images/2009-07/mac-screensharing.png" alt="Controlling the mini's iTunes instance that the Apple TV is conected to" width="300" height="240" align="right" /&gt;It's connected via USB to the Drobo and Maxtor drive, and to the router via a regular ethernet cord. It runs iTunes (set to launch automatically on boot) and screen sharing so that it can be controlled via the MacBook Pros. It also runs uTorrent to automatically manage torrents I'm interested in as they become available. Since I know the Mini is always on, this is a good place for that to happen. I haven't set it up so I can access it from anywhere yet, but I'm definitely thinking about it. This is probably a little more expensive than it needs to be for what it does, and if you're the crafty kind you can definitely &lt;a href="http://lifehacker.com/5122782/build-a-hackintosh-for-under-240" target="_blank"&gt;build yourself a hackintosh&lt;/a&gt; for far less.&lt;/p&gt;
&lt;h3&gt;Apple TV:&lt;/h3&gt;
&lt;p&gt;The Apple TV's main purpose is to play movies and shows on the actual Samsung TV. A quick note here that Apple is really shooting itself in the foot with the naming of that thing because everyone I talk to about this always says "oh that's cool. And how big is it?" The initial impression is always that the Apple TV comes with a screen just like an iMac would.&lt;/p&gt;
&lt;p&gt;Additionally, I can rent or buy stuff directly through the Apple TV, and it gets automatically synced to the Drobo. I have it connected to the router via a physical cord, but you can also do it via Wi-Fi. It also serves the purpose of a hub between the Bose speakers and the Computers. iTunes has this really sweet option to select any speaker connected to another iTunes instance. And since the Apple TV redirects all audio output to the TV and the TV redirects all audio output to the Bose system, any one of the laptops/desktops can take over the speakers as long it's running iTunes. All you have to do is add it as a shared library. Here are some pointers if you're here trying to get this to work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt; &lt;img src="http://teebes.com/images/2009-07/apple-tv-sharing.png" alt="The Apple TV sharing itself with other iTunes library" align="right" /&gt;To manage your Apple TV's connections, go to Settings &amp;gt; Computers.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The 'Connect to iTunes' link adds your 'main library'. It should be the iTunes instance that's on the computer that will always be on. Obviously it should be a desktop that is always powered and always connected to the router.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;The 'Add Shared iTunes Library' can add any other computer. The only difference in between the main library and a shared library is that former is accessed via 'My Movies', 'My TV Shows', etc. while the latter are accessed via 'Shared Movies', 'Shared TV Shows'.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt; You can play Movies, TV Shows, Podcasts, YouTube videos, and set your own pictures as the TV's scrolling screensaver when it's idle.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;TV:&lt;/h3&gt;
&lt;p&gt;It's a &lt;a href="http://www.samsung.com/us/consumer/detail/detail.do?group=televisions&amp;amp;type=televisions&amp;amp;subtype=lcdtv&amp;amp;model_cd=LN40B530P7NXZA" target="_blank"&gt;Samsung 40" LCD 1080p HDTV&lt;/a&gt; connected to the XBox, Apple TV and speakers. Not much to say here except that it's a really great picture quality, incidentally.&lt;/p&gt;
&lt;h3&gt;XBox 360:&lt;/h3&gt;
&lt;p&gt;Of course this is mainly to get my &lt;a href="http://shoryuken.com/" target="_blank"&gt;Street Fighter IV&lt;/a&gt; fix, but it also serves nicely to play DVDs whenever someone lends us a good series and to stream instantly from Netflix online.&lt;/p&gt;
&lt;h3&gt;Speakers:&lt;/h3&gt;
&lt;p&gt;&lt;img src="http://teebes.com/images/2009-07/apple-tv-speakers.png" alt="Using iTunes to connect to remote speaker" width="289" height="203" align="right" /&gt;I use the &lt;a href="http://www.bose.com/controller?url=/shop_online/home_theater/home_theater_speakers/cinemate/index.jsp" target="_blank"&gt;Bose Cinemate&lt;/a&gt; system, it's a very nice sound quality although at some point I'll definitely want a sound-round setup. The nice thing here is that the laptops and desktops can play music on them over Wi-Fi via iTunes.&lt;/p&gt;
&lt;p&gt;Well, that about concludes it. I'm very happy with this setup, but in the interest of fairness here are its shortcomings so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I always have to wait a day or two before I can see the latest episode of a show I'm really into (for example Lost) just because they take a little bit to get onto iTunes so far. This wouldn't be that big a deal except for the fact that all of the Facebook people usually blast spoilers minutes after the end of the episode. Thankfully I don't mind spoilers too much but still.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;No access to Hulu (yet). There would be ways to set this up via Boxee on the desktop PC I have lying around but since Hulu has &lt;a href="http://lifehacker.com/5156151/hulu-blocks-boxee-tvcom-kicks-early-adopters-in-the-face" target="_blank"&gt;specifically started blocking them&lt;/a&gt; (grrrr) it's been harder to get it working.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;That's it!&lt;/li&gt;
&lt;/ul&gt;
</description><guid>http://teebes.com/blog/7/me-and-my-apple-tv-life-after-cable</guid></item><item><title>The state of the browser-os market</title><link>http://teebes.com/blog/6/the-state-of-the-browser-os-market</link><description>&lt;p&gt;Today I was going to write a long-winded post about &lt;a href="http://googleblog.blogspot.com/2009/07/introducing-google-chrome-os.html" target="_blank"&gt;Chrome OS&lt;/a&gt;, &lt;a href="http://www.engadget.com/2009/07/09/silverlight-3-out-of-beta-joins-forces-with-your-gpu-for-hd-str/" target="_blank"&gt;Silverlight&lt;/a&gt;, Firefox's support of &lt;a href="http://arstechnica.com/open-source/news/2009/07/firefox-stability-to-get-a-boost-with-multiprocess-browsing.ars" target="_blank"&gt;multiprocess browsing&lt;/a&gt;, and overall the interesting turning point we are at in terms of browsers slowly becoming operating systems in their own rights. But as I started writing I kept on having this image pop in my head, which I think illustrates my point nicely. So I hopped on Photoshop and here it is.&lt;img src="http://www.teebes.com/images/state_of_the_market.png" alt="" align="middle" /&gt;&lt;/p&gt;
</description><guid>http://teebes.com/blog/6/the-state-of-the-browser-os-market</guid></item></channel></rss>
