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

<channel>
	<title>SQUARISM</title>
	<atom:link href="http://squarism.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://squarism.com</link>
	<description>addicted to pixels</description>
	<lastBuildDate>Tue, 20 Jul 2010 15:12:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Fixing IRB autocompletion on OSX</title>
		<link>http://squarism.com/2010/07/10/fixing-irb-autocompletion-on-osx/</link>
		<comments>http://squarism.com/2010/07/10/fixing-irb-autocompletion-on-osx/#comments</comments>
		<pubDate>Sat, 10 Jul 2010 16:25:44 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=583</guid>
		<description><![CDATA[Using macports version of ruby (and irb), I noticed that the lovely and wonderful tab autocompletion wasn't working: $ irb --simple-prompt >> "foo".cap (The tab key! It does nothing!) So you can see that I was hitting tab expecting to see .cap resolve to .capitalize but it did nothing. I'm not quite sure if this [...]]]></description>
			<content:encoded><![CDATA[<p>Using macports version of ruby (and irb), I noticed that the lovely and wonderful tab autocompletion wasn't working:<br />
<code><br />
$ irb --simple-prompt<br />
>> "foo".cap  (The tab key!  It does nothing!)<br />
</code></p>
<p>So you can see that I was hitting tab expecting to see .cap resolve to .capitalize but it did nothing.  I'm not quite sure if this has been broken for a long time or forever.  On Linux, irb seems to just work.  So I found a fix out on the 'tubes.</p>
<p><code>sudo gem install bond<br />
$ cat ~/.irbrc<br />
require 'rubygems';<br />
require 'bond';<br />
Bond.start</code></p>
<p>And now the tab is delicious.</p>
<p><code>>> "foo".cap   [tab]<br />
>> "foo.capitalize<br />
=> "Foo"</code></p>
<p>Now while you're at it, might as well get the color going with wirble:<br />
<code>sudo gem install wirble</code><br />
Add some more lines to .irbrc.  Now our .irbrc looks like this:<br />
<code>$ cat ~/.irbrc<br />
require 'rubygems'<br />
require 'wirble'<br />
require 'bond'<br />
Wirble.init<br />
Wirble.colorize<br />
Bond.start</code></p>
<p><img src="http://squarism.com/wp-content/uploads/2010/07/wirble_bond.png" alt="" title="wirble_bond" width="141" height="26" class="alignright size-full wp-image-587" />Irb will have color and [tab] now which you can see in the screenshot right there.  The colors can be customized in wirble but it seems to be limited to ANSI terminal colors.  I wish the terminal would support RGB #html_codes, you could really go crazy and port some of the awesome Textmate themes over to the irb term.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/07/10/fixing-irb-autocompletion-on-osx/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Fit-PC2 and Ubuntu 10.04</title>
		<link>http://squarism.com/2010/07/10/fit-pc2-and-ubuntu-10-04/</link>
		<comments>http://squarism.com/2010/07/10/fit-pc2-and-ubuntu-10-04/#comments</comments>
		<pubDate>Sat, 10 Jul 2010 14:51:02 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Blog]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=619</guid>
		<description><![CDATA[I had Ubuntu 9.04 running on my Fit-PC2 and I wanted to move up to 10.04. I ran: sudo apt-get update; sudo apt-get dist-upgrade and it chugged along doing the update. Install went fine and I was impressed that you can jump major revs like that. Although there was one bit of weirdness: when I [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://squarism.com/wp-content/uploads/2010/07/fitpc2.jpg" alt="" title="fitpc2" width="620" height="382" class="aligncenter size-full wp-image-620" /></p>
<p>I had Ubuntu 9.04 running on my Fit-PC2 and I wanted to move up to 10.04.  I ran: <code>sudo apt-get update; sudo apt-get dist-upgrade</code> and it chugged along doing the update.  Install went fine and I was impressed that you can jump major revs like that.</p>
<p>Although there was one bit of weirdness: when I did the dist-upgrade I noticed that it was going to upgrade openoffice.  I didn't want openoffice at all on there so I was going to remove it before it upgraded.  So I hit Ctrl-C which promptly locked the whole box.  I thought it was heat related and so I got an ice pack and wrapped the Fit in ice.  I tried it again, Ctrl-C locks the box.  Like, the whole box.  Caps lock dead.  Anyway, I've never seen that before.  When I let the dist-upgrade go, it worked fine.</p>
<p>When it booted 10.04, my display was set to 1280x1024 and was running slow.  It was running the vesa driver.  So I downloaded the paulsbo driver for the GMA500 (the 'gpu' that the Fit-PC2 uses) and although the module built fine, it really screwed up everything.  The screen would blank, X would be trying to start, there'd be video garbage doing all kinds of weird things.</p>
<p>So I wiped the whole thing and installed 9.10 and followed the 9.10 installation instructions on the Fit wiki.  I installed the netbook-remix distro (i386) and the install instructions still work.  Now I just need to get rid of the netbook-remix gui.  Too bad they don't have desktop-switcher anymore.</p>
<p>Also, I was playing around with <a href="http://xmonad.org/">Xmonad</a> which is interesting if not a bit understated.  It reminds me of when I used Enlightenment back in 1999.  The styles are different but the amount of config editing is the same.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/07/10/fit-pc2-and-ubuntu-10-04/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>IRB return trick</title>
		<link>http://squarism.com/2010/07/09/irb-return-trick/</link>
		<comments>http://squarism.com/2010/07/09/irb-return-trick/#comments</comments>
		<pubDate>Sat, 10 Jul 2010 03:13:56 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=604</guid>
		<description><![CDATA[Irb, like ruby and many other languages, returns things to the left. When you say: x = 5 5 gets sent to x and x gets sent to the console or to the bit bucket if nothing is watching for it. Irb with Ruby will tell you this explicitly which is great for debugging and [...]]]></description>
			<content:encoded><![CDATA[<p>Irb, like ruby and many other languages, returns things to the left.  When you say:</p>
<p><code>x = 5</code></p>
<p>5 gets sent to x and x gets sent to the console or to the bit bucket if nothing is watching for it.  Irb with Ruby will tell you this explicitly which is great for debugging and developing.</p>
<p><code>>> x = 5<br />
=> 5</code></p>
<p>But sometimes it's annoying.  Like when you have a massive array of objects and you're iterating through them.  A quick trick is to add another meaningless return on the end like this:</p>
<p><code>>> x = 5; 1<br />
=> 1</code></p>
<p>If x=5 was a huge database result set or a large array, it'd be hard to read.  But 1 is easy to ignore.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/07/09/irb-return-trick/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Technology Knowledge Debt, Part One</title>
		<link>http://squarism.com/2010/07/03/tech-knowledge-debt/</link>
		<comments>http://squarism.com/2010/07/03/tech-knowledge-debt/#comments</comments>
		<pubDate>Sat, 03 Jul 2010 19:44:20 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Systems]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=539</guid>
		<description><![CDATA[As a follow-up to my previous post where I psuedo-whined about common knowledge gaps in technology projects, I thought I'd try to contribute a solution to the problem. I'll list out each of the areas that I previously posted about; the areas I think people are behind on. It's knowledge debt. When you don't spend [...]]]></description>
			<content:encoded><![CDATA[<p>As a follow-up to my <a href="http://squarism.com/2010/05/31/dearth-patterns/">previous post</a> where I psuedo-whined about common knowledge gaps in technology projects, I thought I'd try to contribute a solution to the problem.  I'll list out each of the areas that I previously posted about; the areas I think people are behind on.  It's knowledge debt.  When you don't spend the time to catch up, you're in knowledge debt.</p>
<h5>Troubleshooting</h5>
<p>Troubleshooting is a learned skill and it's hard to "train".  A lot of effectiveness in troubleshooting is related to the specific skill or expertise domain so a novice is not going to magically become effective at problem-solving just by knowing how to troubleshoot generically.  Let's instead try to focus on some specific examples and talk about common patterns and strategies.  The easiest examples revolve around so-called "computer problems" which really mean desktop, networking, environmental and software corruption problems.  I'll stay away from debugging which is a much more precise process where you have the advantage of memory inspection, breakpoints and god-like control over the problem unless you hit external resources which would land you back in the "computer problems" category.</p>
<p>Let's say a developer is designing a web application on their laptop.  Everything works as they designed.  Now it's time to deploy to the test server for other team members to integrate/play with.  Ok, let's scp our .zip/.war/installer/.tar/whatever up to the server and then see if it works.  A near-infinite list of things can go wrong but here are a few:</p>
<ul>
<li>You can't scp to the server.</li>
<ul>
<li>If you got a network error, can you ping the server?  If you can ping, can you telnet to port 22?  Ping just hits the IP.  Telnet will hit the IP plus the port.  If the IP works and the port doesn't then something is filtering the port (firewall) or SSH on the server is not listening on 0.0.0.0 (every interface) but on localhost or another interface.</li>
<li>If you are getting a login type error after you connect then try just ssh'ing to the box first.  Maybe your shell is messed up.  Are you overriding the shell with WinSCP?  Are you trying to start in a directory that you don't have +x on?  Maybe your account is locked.  The idea here is to ignore network problems.  You've got a socket and a login prompt.  It's OS/shell related at this point.</li>
<li>Understand that putty's configuration is not shared by WinSCP but pscp is.  Try pscp'ing with the -load switch.  For example pscp -load testserver-saved-sessions file user@testserver:/tmp will load the "testserver-saved-session" putty config and SCP a file named "file" to the server.  If you have proxy settings or other parameters that are needed, you can use pscp instead of reconfiguring WinSCP.</li>
</ul>
<li>The webapp won't start.</li>
<ul>
<li>Obviously, check logs.  You don't have logs?  Are you on Linux?  Try strace.  If you're running a java webapp, strace is going to be impossible to follow.  On Solaris, run truss.  Both of these programs will give low level C system calls that can add up if you're using a java container.  Even rails or php can create a crapton of logs.  This is not the first place to start but a fallback when your more exact logs fail you.</li>
<li>What is different between your laptop and the test box?  If you're moving from Linux to Solaris, you need to understand the environmental differences between the two OS's.  This is especially true from Windows to Unix.  Your middleware stack might be completely different.</li>
<li>What is different between your laptop's network and interaction between systems and your test network?  If you have all software on a single box, moving to a distributed test environment will most likely break stuff.  You should abstract away hostnames and services to local names that can be configured in DNS or in a hosts file.  Instead of pointing to a database called "DBSVR001_L2".  You should have an alias called "webapp-db".  Then configure your app to use the alias and not the box name.  /etc/hosts files can have 8 aliases per line.</li>
</ul>
</ul>
<p>The list of problems can go on from "the webapp behaves differently" to "you can't get to the webapp at all".  And this is just deploying to the test environment!  In any case, the common pattern should be:</p>
<ul>
<li>Is the problem reproducible?  Is it intermittent?  This answer should help narrow down the problem scope.  Intermittent problems could be external, timing issues, race conditions, memory management, resource limits, functional bugs, synchronization and temporal problems like this.</li>
<li>Log mentally or in a journal what each finding means.  You should be thinking critically and expecting results as you try things: "if the following test does A then it means X.  If it doesn't then it means Y".  See this <a href="http://blog.ksplice.com/2010/06/attack-of-the-cosmic-rays/">fantastic single-bit-flip troubleshooting session here</a> where the author systematically dives deeper and deeper into a segfault until he proves that a system binary <a href="http://en.wikipedia.org/wiki/RAM_parity">bit-flipped</a> while in memory.</li>
<li>Change one thing at a time.</li>
<li>Try to break the problem into levels if it's related to lifecycle or something serial.  Start high-level and work lower (ie: trace the network cable before setting up debugger breakpoints).  Once you've proved a level is working, ignore problems on that level.  Unless you're doubting your sanity, if ping works, IP is working.</li>
<li>Assume it's your code and your problem forever.  When it's not your code's problem, assume it is.  Try typing up an angry email to the author, corporation or project and blame them with vigorous detail about why you think this is their problem and not your fault.  Do not send it.  By the end of your rant, you might have thought of things you haven't tested or tried enough when proposing what their problem is.</li>
<li>Break down the problem and prove hypotheses.  Can you break the app even more?  If you fix it, can you break it again to prove your sanity?</li>
</ul>
<p>Even though many of my examples are deployment and networking related, the same process can be used for development and software.  Since so many abstraction layers exist in software, it's very similar to a system or a network stack.  Learning these troubleshooting strategies can be a valuable skill to learn and might avoid just giving up or asking a coworker for help.  I've met people that have no domain experience in a problem area but have enough previous experience and troubleshooting skill that they seem magically able to figure out a problem (and suddenly are regarded as experts).</p>
<h5>VNC</h5>
<p>Understanding VNC can be difficult because many devs/admins/dbas/whatever are used to other things.  Maybe they are even used to VNC but don't understand how powerful it can be or why it's being used in the first place.  So let's talk about the basics and maybe this will clear up what VNC is and what it isn't.</p>
<p>First, VNC is cross platform and open source.  That means I can use a Windows VNC client to connect to Windows, Linux, Solaris, Mac, iPhone or whatever.  Open source means not so much that VNC is free but that there are many tools available because the spec is open.  I can do "many-to-many" client to server.  Remote desktop is Microsoft only.  VNC can compress remote sessions for slow WAN links.  VNC can be faster than X11 if you use it right.  VNC can keep a GUI process/installer/program running while you disconnect.  VNC can let you broadcast your screen to a classroom of students.  VNC can let you bounce around a network when a firewall or network layout prevents you from connecting directly to a server.  It's flexible, familiar to many and open source.</p>
<p>Let's talk about what VNC isn't.  VNC is an onion skin, it's not the root window.  If you move your mouse in VNC, someone who walks over to the server won't see the mouse moving.  It's not SSH, you can't necessarily copy files over VNC, you won't automatically get a shell either.  It's not secure.  It's not remote desktop.  It's not X11.  It's different on Windows than it is on UNIX, UNIX lets you pick your window manager (Gnome,KDE, twm).  VNC has a separate password than your OS user account.</p>
<p>When you start vncserver, it reads your ~/.vnc/xstartup file and launches whatever is in there and fires up a port to listen on for your user.  So you can fire up multiple servers if you want but each time you do, everything in ~/.vnc/xstartup is duplicated.  If you use Gnome in your xstartup, this might make Gnome mad because it's not good at running twice.</p>
<p>Once you are connected to your vncserver with a client, you stay logged in.  Let's say you fire up vncserver with Gnome configured for xstartup.  So your ~/.vnc/xstartup looks like this:<br />
<code><br />
#!/bin/sh</p>
<p># Uncomment the following two lines for normal desktop:<br />
# unset SESSION_MANAGER<br />
# exec /etc/X11/xinit/xinitrc</p>
<p>[ -x /etc/vnc/xstartup ] &#038;& exec /etc/vnc/xstartup<br />
[ -r $HOME/.Xresources ] &#038;& xrdb $HOME/.Xresources<br />
xsetroot -solid grey<br />
vncconfig -iconic &#038;<br />
xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &#038;<br />
gnome-session &#038;<br />
</code></p>
<p>This is going to open xterm everytime your log in and you'll have a Gnome desktop.  That's because the process stack looks something like this:</p>
<p><code><br />
vncserver (includes the X11 server)<br />
|-- X11 Desktop (ie: Gnome as a gnome-session process)<br />
|---- vncconfig (a vnc utility dialog box app, not needed most of the time)<br />
|---- gnome-terminal (one of many windows within your vnc window)<br />
|------ bash (unix shell within gnome-terminal)<br />
</code></p>
<p>I see people make a common mistake when using VNC.  They'll log out of Gnome instead of closing their client window.  So the vncserver process lives but there's no desktop shell.  So when they log back in, they just have a blank blue (or whatever color) screen and they're like "IT'S BROKEN!".  Look at the process stack above.  If you log out of the X11 desktop (clicking System->Logout in Gnome) then your process stack looks like this:<br />
<code><br />
vncserver<br />
|-- nothing<br />
</code></p>
<p>Everything is gone now except the vncserver and X11 server which aren't running anything interesting.  So you can connect but you can't do anything.  So don't log out with the Log Out menu.  Just close the VNC client window.</p>
<p>VNC is really good at WAN traffic (like from home to your office).  Well, you can configure it that way.  There are many types of clients out there but many of them either have a preset for slow links or let you set the compression to fast crappy quality so that you can work over a WAN.  From more of a CS perspective, I like to think of VNC as local CPU-hit compression and then a smaller network packet and blown up on the client side.  Since you're network-bound on a WAN, this makes this easier on the WAN and the experience is faster.</p>
<h5>X11</h5>
<p>X11 is called the X Server because clients actually connect to it to display GUIs like a client-server model.  It's designed this way to allow for flexible displaying of GUIs.  Because X11 is a client server model, you can forward X11 over SSH for example and have X11 apps pop up on your local desktop X11 server like magic.  Or you can have VNC display your apps instead, which is how the Xvnc process works and no Xorg process is needed.</p>
<p>So X11 on Linux kicks off in init level 5.  The root window, which you'll see if you walk over to a server, is running as the root user and will let you log in as whatever user on the box.  Here's an example process:<br />
<code><br />
$ ps auxww|grep Xorg<br />
root      3320  0.0  0.6  34220 27721 tty7     Ss+  Jun24   1:06 /usr/bin/Xorg :0 -br -audit 0 -auth /var/gdm/:0.Xauth -nolisten tcp vt7<br />
</code></p>
<p>From there, your desktop shell (like Gnome) is kicked off as the user you logged in as which displays back to the X server.  If you kill X11 (with Ctrl-Alt-Backspace) then you lose everything within the X server.  But VNC is not using the Xorg process, so you won't kill off anyone's VNC sessions or anything contained within them.  You also won't kill people that are forwarding X11 apps back to their laptop or what-not.  Because they are running a local X11 server such as Xming or Cygwin.  Because the desktop shell is running as the current user, it's usually a bad idea to log into the console as root.  It's unnecessary.  If you need root, log in as a normal user, open a terminal and sudo or su -.  This will prevent you accidentally doing something bad as root with the file browser, prevent hackers from forwarding malicious apps to you (if you do a xhost + for example) and in general just a better "least privilege" habit to get into.  Of course, most people just log in as root to the server because it's easy (boo).</p>
<p>The DISPLAY variable.  You set the DISPLAY variable to an X11 server hostname and port.  If you log into the GUI console, you'll see this:<br />
<code>$ echo $DISPLAY<br />
:0.0</code></p>
<p>The :0.0 is a special value which signifies the root window.  If you
<pre>export DISPLAY=localhost:0.0</pre>
<p> and<br />
<code>$ gnome-terminal<br />
Failed to parse arguments: Cannot open display:</code><br />
It won't work.  You can't unset it either.  You have to set it to ":0.0" if you're actually on the box.  When you forward X11 over SSH, the variable will look like this:<br />
<code>$ echo $DISPLAY<br />
localhost:10.0</code></p>
<p>X11 isn't encrypted <a href="http://en.wikipedia.org/wiki/X_Window_System#Network">by design</a>.  So use SSH forwarding.</p>
<p>X11 can be slow.  It sends the whole damn GUI object uncompresed over the network.  So if you're on a WAN, use VNC.  </p>
<p>X11 can be buggy with Java swing.  VNC can be used as a workaround.</p>
<h5>Version control</h5>
<p>Let's say you're working on a boring spreadsheet full of team members and their skills.  You've put a lot of time into this spreadsheet and you don't want to lose what you've done.  At the same time, you want to completely reorganize it by skill type and seniority.  Also at the same time, your boss likes to refer to your spreadsheet to see what kind of people you have available on your team.  It's going to be a lot of work to rework the spreadsheet and it'd be bad if your boss couldn't access it if you saved a crappy/corrupt version to so you save a copy to the fileshare as "Team_Version_2.xls".</p>
<p>Great.  Except what does Version 2 mean?  Also, if "Team.xls" is out there and "Team_Version_2.xls" is out there, which version is the "real" one?  Maybe you tell your boss "just use the one with the most recent last modified date", which is Team.xls right now.  But then Rick from sales changes the font color in Team_Version_2.xls and then your boss is checking that for updates because that's been updated last.  Your boss is all confused now because he's been checking the wrong one and he hired a bunch of DBAs that you already had.  Argh!  What is going on here?!</p>
<p><img src="http://squarism.com/wp-content/uploads/2010/06/branching_example.png" alt="" title="branching_example" width="220" height="540" class="alignright size-full wp-image-555" /><br />
The problem is, you are trying to version control a spreadsheet using a fileshare.  You're trying to create different versions using a filename convention.  This problem has already been solved using software called Version Control Software (VCS) like CVS, SVN (Subversion), Git and so on.  At the very least you should be using Sharepoint if you're in love with MS Office, if you're on a software project, you should be using a VCS.  Even if you're using VCS, you might be using it wrong (like trying to version control with filenames inside a version control repository).</p>
<p>First, let me explain extremely briefly what each of CVS/SVN/Git is all about.  CVS is old.  SVN replaced CVS.  Git is sort of replacing SVN but also changing many things along the way.  SVN has the most mature toolset, Git has very few GUIs available although XCode 4 looks really nice.  Short answer, at least use SVN.  If you can hack it, use Git.</p>
<p>So in our example, assuming you are using SVN, you'd create a root hierarchy in SVN called<br />
<code><br />
/trunk<br />
/branches<br />
/tags<br />
/releases<br />
</code></p>
<p>We'd commit a file under /trunk/Team.xls.  This file will be the only file that anyone (like our boss) will look at for "the place to go to get our team layout".  When it comes time to make a major change, we'll do an SVN copy to /branches/omg_major_team_reorg/Team.xls and create a branch.  We'll go to town on our branched version and make major changes.  Later, when everyone has been editing the trunk version of Team.xls, we'll merge our branched version back in and everybody's edits will be lumped together intelligently.  Recent versions of Subversion even has a diff viewer for Office docs.</p>
<p>So how does branching actually work?  This took me a while to see when and why I'd use branching.  There's a simple diagram to the right to help visualize the process.  I think it helps to just try it out a few times when you think you have a need for it and let yourself get used to the process.  Without a concrete need or example, you might think it's foreign and unnecessary.  When it works for you, it'll be more familiar and useful.</p>
<p>In Git, you should be branching and merging a lot.  There's a great book called <a href="http://progit.org/book/">Pro Git</a> which <a href="http://progit.org/book/ch3-1.html">has this</a> and more explanation.  Git really changed the way I thought about developing.  I wouldn't say I'm an expert yet.  I still need to learn to commit more often and branch more.  But I'm ok with these future optimization goals, I'm not ok with manual version control on a file share.  :)</p>
<h5>Sudo and cron</h5>
<p>Sudo is a program that lets you run something as root.  But more importantly, sudo can look at a file called sudoers to define a list of people and programs.  You can let sally run `reboot' or you can let dave run /etc/init.d/apache.  Sudo lets you delegate to normal user accounts without everyone knowing the root password.  Sudo also allows for actual, real and useful system logging.  When everyone logs in as root, no one is accountable and logging is basically useless.  If everyone has root and you need to find out who screwed up the server or even who is doing the most amount of good on a server, you can't identify people because the logs will tell you that root did it and not dave or sally.  So sudo allows for finer grained access control and more accountable logging.  Sudo is not "su".  Also, the sudo concept is not limited to Unix.  Windows Vista and 7 have UAC which is a very similar concept.  UAC prompts for your password and temporarily elevates your access.  They took the popup concept from OSX which actually uses Unix sudo.  Ubuntu followed this design idea too.  XP doesn't have this ability and thusly, most people run as administrator all the time.</p>
<p>Unrelated to sudo is a scheduling utility called cron.  Cron runs things at a certain time and most people get that.  But there are multiple places for cron jobs to be run from.  There's a global /etc/crontab which can only be edited by root and there are individual user crontab files that normal users can edit with crontab -e.  But some distros of Linux (like Ubuntu) have some special directories for cron jobs:</p>
<pre>
$ ls -ld /etc/cron*
drwxr-xr-x 2 root root 4096 2010-04-29 05:41 /etc/cron.d
drwxr-xr-x 2 root root 4096 2010-06-16 15:06 /etc/cron.daily
drwxr-xr-x 2 root root 4096 2010-04-29 05:21 /etc/cron.hourly
drwxr-xr-x 2 root root 4096 2010-04-29 05:41 /etc/cron.monthly
-rw-r--r-- 1 root root  724 2010-04-14 23:51 /etc/crontab
drwxr-xr-x 2 root root 4096 2010-04-29 05:43 /etc/cron.weekly
</pre>
<p>Ok so this is pretty basic sysadmin stuff but I wanted to touch on a more important idea of cron: <a href="http://en.wikipedia.org/wiki/Extract,_transform,_load">ETL</a>.  ETL is extract, transform and load.  But don't think in terms of data warehousing or anything specific.  Think of cron and ETL as polling.  Cron is stupid polling.  It's not event driven.  It's going to run whatever you say every interval you specify.  If you have a "send forgotten passwords emailer" cron job set for 15 minute intervals, your users are going to be waiting up to 15 minutes for their email that lets them get into the system.  This can be good or this can be a flawed design.  On the other hand are events/callbacks/hooks/triggers etc.  Events are smarter, more exact and better to use if you can.  However integrating events all the way down to the OS layer is nearly impossible.  It's hard to 'hook' into an application stack from the bare OS level.  Many times, polling is the only solution.</p>
<p>Polling vs events.  Very important pattern to look out for.</p>
<h5>Next</h5>
<p>In the next part, we'll wrap up with the other dearthy areas and put this topic to pasture.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/07/03/tech-knowledge-debt/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dearth Patterns</title>
		<link>http://squarism.com/2010/05/31/dearth-patterns/</link>
		<comments>http://squarism.com/2010/05/31/dearth-patterns/#comments</comments>
		<pubDate>Mon, 31 May 2010 16:36:29 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Systems]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=411</guid>
		<description><![CDATA[There seems to be a lack of understanding and experience in the same areas from project to project, company to company and sector to sector. It's nothing specific to any one company. I've seen this in small .com companies to large contractors. It's very odd to me. Even if everyone can't know everything, it seems [...]]]></description>
			<content:encoded><![CDATA[<p>There seems to be a lack of understanding and experience in the same areas from project to project, company to company and sector to sector.  It's nothing specific to any one company.  I've seen this in small .com companies to large contractors.  It's very odd to me.  Even if everyone can't know everything, it seems certain things are never taught or learned.</p>
<p>Some of these things are high-level skills and some are very specific.  I think the specific ones are weirder.  Osmosis, you'd think, would have some people learn certain tools or languages or whatever.</p>
<p><strong>Troubleshooting</strong><br />
Basic troubleshooting skills.  Like, change one thing at a time.  Or, turn on debugging.  Or what the likely culprit is versus chasing after every possibility.  Or, feeling overwhelmed and not trying anything: it's just broken.  Broken how?  What changed last?  What is persisted?  Is there a tmp file?  Is there a cache?  Can you repeat this every time?  If not, what are the variables?</p>
<p><strong>VNC</strong><br />
No one seems to know how VNC works.  But everyone uses remote desktop just fine.  With VNC, it's an effort to get everyone connected and it's a hand-holding operation.  I understand it's tricky.  But VNC has been around for almost a decade and it hasn't really changed.  I figured osmosis would kick in.</p>
<p><strong>X11</strong><br />
Cygwin, Xming, putty and getting X11 working.  Or how X11 isn't encrypted.  Or security problems with xhost.  Or running X as root.  Or how X11 is different than the Windows display.  Or how X11 is separate from the X11 shell.  How to forward X11 over ssh.  All these X concepts don't come from the Windows world but are in every other OS: Solaris, OSX (X11 app), Linux.</p>
<p><strong>Version control</strong><br />
Conflicts, merging, branching.  Why SVN is different from SharePoint is different than git is different than just calling files .year.mon.day in a file share.  Even 'senior developers' that don't have any high-level knowledge of CVS, a program that was released in 1990.  I've seen this everywhere.</p>
<p><strong>Sudo and cron</strong><br />
Even if in a Windows environment, what the UAC momentary elevation of privileges is like (also see OSX ignorance).  Or how `at` works on Windows.  At is also a program in UNIX.</p>
<p><strong>Myth of supportability</strong><br />
Everything has to be written in the language that's delivered.  If we're delivering a COTS program that runs on C# then every script has to be C# because that's what we know.  If you offered to write a utility in a higher level language, then all the Java devs would cry foul because it's too foreign.  So everything is a hammer problem even though all tech changes in a few years.  I wish someone would add up all the "this is going to save you so much money" promises of middleware.</p>
<p>Regarding COTS, Mike Taylor's <a href="http://reprog.wordpress.com/2010/03/03/whatever-happened-to-programming/">Whatever happened to programming?</a> is a good read.  I think of his libraries as my COTS/middleware.</p>
<p><strong>Copy and paste</strong><br />
How to paste in X11 (middle click).  How to paste into putty (right click).  How to copy in X11 (select), how to copy in putty (select).  How to copy and paste across VNC (enable clipboard support).  How to strip out formatting (paste to notepad, start->Run, anything plain text).</p>
<p><strong>Hierarchies in Software Architecture</strong><br />
How many nodes make up an X?  How many X's make up a Y?  Can I have multiple Y's per Z?  What's shared between Z and foo?  Do I have to create foo first?  Enough of the contrived examples.  Let me give you some real examples: </p>
<p>I have a bunch of disks I'm going to share out for a database.</p>
<ol>
<li>Combine one or more disks into raid or jbod array on SAN</li>
<li>Slice up SAN into one or more LUNs</li>
<li>Create volumes or filesystems with one or more partitions</li>
<li>Organize partisions into one or more diskgroups</li>
<li>Database is spread across one or more diskgroups</li>
<li>Database has one or more schemas</li>
<li>Schema has one or more tables</li>
<li>Tables can have one or more data partitions</li>
</ol>
<p>And so on.  It can keep going like this for quite a while.  In terms of performance, if the partitions go to the same busy disks then there's not a whole lot of point in sharding.  In terms of design and planning, obviously I have to do step 1 before I can do step 5 and often undoing step 1 will cause the house of cards to tumble.</p>
<p>That's not really a software thing like I said, ok.  I can have multiple listeners in Oracle DB but I can't have multiple listeners on the same port.  I can have multiple reverse HTTP proxies on different boxes point back to the same web server on a single port though.  I can have multiple IPs on a box but not multiple programs using a port on a single IP.  I can have multiple virtualhosts against one IP in Apache but I can't have name based virtualhosts against one IP with ssh.  This hierarchy is especially important when load balancing webapps.  Even the F5 has an internal hierarchy to make the configuration flexible.</p>
<p>Ok, ok.  That's still not software enough.  I can have multiple sessions in a webapp but only one identity.  The identity is in the persistence layer of the above SAN example and my webapp can scale out to the horizon with shared-nothing or whatever I want.  I'm still really logging in only once even if I bounce between 500 boxes.  The session could be just an instance of my identity.</p>
<p><strong>Webapp vs webpage</strong><br />
Website is to webserver as webapp is to application server.  HTML is a webpage/website.  JSP/PHP/CF/ASP is a webapp.  This is simplified but so many people don't know the difference even when their job revolves around integrating or even developing 'webpages'.</p>
<p><strong>Plain-text</strong><br />
Many protocols, passwords and network traffic can be viewed in plaintext.  There's some protocols you can interact with just by typing text to an open telnet session.  You can do IRC with telnet.  You can do SMTP with telnet.  You can do a HTTP GET.</p>
<p><strong>Hands-On Security</strong><br />
Policy makers vs whitehats.  Like actually checking Sans for actual 0-day remote root-level ultra-bad exploit on foo software.  Security is always a huge blanket statement with very few actual experts.  I'm certainly not one but I have met very few that I thought were teaching me something.  I want the guy who actually knows what a XSS attack or a buffer overflow is.  This might just be a reflection on the projects I've been on.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/05/31/dearth-patterns/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Changing DroboFS passwords</title>
		<link>http://squarism.com/2010/05/24/changing-drobofs-passwords/</link>
		<comments>http://squarism.com/2010/05/24/changing-drobofs-passwords/#comments</comments>
		<pubDate>Tue, 25 May 2010 04:30:19 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Hardware]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=515</guid>
		<description><![CDATA[First off, this DroboFS is awesome. However some of the apps are a little thin on the docs and there were a few things I had to figure out. So first a few assumptions, you didn't change your drobo's name. I actually did and that's ok, just in this post, it's called drobo-fs which affects [...]]]></description>
			<content:encoded><![CDATA[<p>First off, this DroboFS is awesome.  However some of the apps are a little thin on the docs and there were a few things I had to figure out.</p>
<p>So first a few assumptions, you didn't change your drobo's name.  I actually did and that's ok, just in this post, it's called drobo-fs which affects mount paths and some of the commands.  So just watch for "drobo-fs" below.</p>
<p>To get apps on the drobo, you have to install apache first, reboot and then install drobotools and reboot again.  After that you can one-click install from the web admin.  Just follow the official PDF docs.</p>
<p>Ok, if you install dropbear, we have the first password to reset: ssh root.</p>
<h5>1. Change ssh root</h5>
<p>ssh root@drobo-fs<br />
(yes)<br />
# passwd<br />
(ignore error about /etc/shadow)</p>
<h5>2. Change apache's root</h5>
<p>cd /mnt/Drobo-FS/Shares/DroboApps/droboadmin</p>
<p>Find a box with htpasswd on it (like a Linux box).  There are ones <a href="http://www.htaccesstools.com/htpasswd-generator/">online</a> if you don't like that.  You'll get a hash like:<br />
<code>root:$apr1$UNZ3DFk3$tspu/3z5Pkkn.h.TUytUl1</code></p>
<p>Backup your htpasswd:</p>
<ol>
<li>cp htpasswd htpasswd.orig
<li>vi htpasswd
<li>Replace the root line: d,d,i,[paste hash]
<li>Write/save: [colon], w, x, [enter]
</ol>
<p>Close your browser (clear the session) and try to go to:</p>
<p>http://drobo:8080/droboadmin/</p>
<p>The login should work now without restarting.  If it doesn't restore your htpasswd file and hit the web.</p>
<p>Looks like this thing has 128MB of RAM.  It has some basic busybox commands but no bash, nano or even users.  I haven't figure out ctorrent yet.  Also, big note: the root password resets on reboot.  Run /mnt/DroboFS/Shares/DroboApps/dropbear/root_passwd to make the password change permanent.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/05/24/changing-drobofs-passwords/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Watchr Unit Tests + Growl + Doomguy</title>
		<link>http://squarism.com/2010/05/23/watchr-unit-tests-growl-doomguy/</link>
		<comments>http://squarism.com/2010/05/23/watchr-unit-tests-growl-doomguy/#comments</comments>
		<pubDate>Sun, 23 May 2010 16:57:06 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=489</guid>
		<description><![CDATA[I read this post about setting up autotest with growl. The little doomguy was a nice effect and got a literal lol out of me (llol?). However, the post was from 2007 and apparently autotest has some problems with 1.9. I'm not running 1.9 but on the eve of rails3, I probably will be. Ok, [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://squarism.com/wp-content/uploads/2010/05/doomguy_passed-580x150.jpg" alt="" title="doomguy_passed" width="580" height="150" class="aligncenter size-large wp-image-496" /><br />
I read <a href="http://szeryf.wordpress.com/2007/07/30/way-beyond-cool-autotest-growl-doomguy/">this post about setting up autotest with growl</a>.  The little doomguy was a nice effect and got a literal lol out of me (llol?).  However, the post was from 2007 and apparently autotest has some problems with 1.9.  I'm not running 1.9 but on the eve of rails3, I probably will be.  Ok, <a href="http://www.google.com/search?hl=en&#038;source=hp&#038;q=%22better+than+autotest%22&#038;aq=f&#038;aqi=&#038;aql=f&#038;oq=&#038;gs_rfai=">what's cooler than autotest</a>?  Apparently not much, three hits on google.</p>
<h5>Install a gem</h5>
<p>I found <a href="http://github.com/mynyml/watchr">Watchr</a>.  It's not too hard to set up.  <code>sudo gem install watchr</code> to get the gem.  If you're on *nix, grab the filesystem event gem: <code>sudo gem install rev</code>.  Now we have watchr up but we need a config.</p>
<h5>Create the watchr config</h5>
<p>The docs tell you to edit a config/watchr.rb file.  Mine came from <a href="http://wiki.github.com/mynyml/watchr/prepackaged-scripts">the examples on their wiki</a>.  It worked out of the box.  It's posted <a href="http://squarism.com/files/watchr_doom/watchr.rb">here</a>.  It's a basic rails testing config that I haven't had to edit.  It's very readable if you need to futz with it.</p>
<h5>Create a test case</h5>
<p>Ok so watchr just runs `rake test` which you can do in your rails app root.  But perhaps you don't have any test cases created.  The generators will create them for you.  But assuming you don't have one, create a file named [rails app root]/test/functional/hello_test.rb.</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'test_helper'</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> HelloTest <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveSupport::TestCase</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> test_hello
    s = <span style="color:#996600;">&quot;Hello&quot;</span>
    assert_equal<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;Hello&quot;</span>, s<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Obviously this is a crappy assertion.</p>
<h5>Get Growlnotify</h5>
<p>Obviously, you'll need growl installed.  But you also need the growlnotify binary.  It's good to have anyway (I use it in Automator).  I didn't have it by default on two of my machines so I assume that the normal Growl install doesn't install it.  It's on the Growl 1.2 dmg under extras.  You just have to copy it to /usr/local/bin.  Download the <a href="http://growl.info/">growl dmg</a>, open the dmg.  You don't need to reinstall growl.<br />
<code>sudo cp /Volumes/Growl-1.2/Extras/growlnotify/growlnotify /usr/local/bin</code></p>
<p>You can test it with:<br />
<code>growlnotify -t "Title" -m "Message"</code></p>
<h5>Make doomguy happen</h5>
<p>Create a directory for the growl icons: <code>mkdir ~/.watchr_images</code>.<br />
Copy <a href="http://squarism.com/files/watchr_doom/failed.png">these</a> <a href="http://squarism.com/files/watchr_doom/passed.png">two</a> doomguy faces to ~/.watchr_images/.  On mac, hit Shift+Cmd+G and go to the hidden folder.<br />
<img src="http://squarism.com/wp-content/uploads/2010/05/doomguy_go.png" alt="" title="doomguy_go" width="433" height="127" class="aligncenter size-full wp-image-490" /></p>
<p>Now we should have two pngs in our hidden folder.<br />
<img src="http://squarism.com/wp-content/uploads/2010/05/doomguy_pngs.png" alt="" title="doomguy_pngs" width="217" height="141" class="aligncenter size-full wp-image-491" /></p>
<p>The watchr.rb file references these images.  If you want to put it somewhere else (like maybe under ~/Pictures), you can change the .rb.</p>
<h5>Ok Go!</h5>
<p>Launch with: watchr config/watchr.rb from your rails app root.  Watchr should be in your $PATH because you installed the gem.  You should have a blank screen now where watchr is waiting.</p>
<p>When you save a file, watchr will fire.  Depending on the file you saved, a different test will run.  If you save a controller, for example, the functional test for that controller will fire.  If you save a model object, a unit test for that model object will fire.  If you want to force a whole test suite to run hit Ctrl+\ in the watchr window.</p>
<p>When things pass:<br />
<img src="http://squarism.com/wp-content/uploads/2010/05/watchr_growl_overlay.png" alt="" title="watchr_growl_overlay" width="326" height="81" class="aligncenter size-full wp-image-492" /></p>
<p>When things fail:<br />
<img src="http://squarism.com/wp-content/uploads/2010/05/watchr_growl_overlay_fail.png" alt="" title="watchr_growl_overlay_fail" width="325" height="100" class="aligncenter size-full wp-image-493" /></p>
<p>The growl display setting is "Music Video".  I have it popping up on my 3rd monitor which is less distracting but still visible.  However if you put it on your main monitor, the overlay won't interfere with mouse clicks, which is nice.  It makes for a killer setup where I don't have to test my app with a browser or with contrived/non-automated tests.  Now I just need to write more tests.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/05/23/watchr-unit-tests-growl-doomguy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Downloading en mass</title>
		<link>http://squarism.com/2010/05/15/downloading-en-mass/</link>
		<comments>http://squarism.com/2010/05/15/downloading-en-mass/#comments</comments>
		<pubDate>Sun, 16 May 2010 03:08:27 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=474</guid>
		<description><![CDATA[Railscast has a podcast on iTunes which lets you download all past episodes and thus this post is moot. But, I thought, what if it wasn't? What if I found a cool site with a bunch of media files I want to download? I knew wget can do this but it's been a while. So [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://squarism.com/wp-content/uploads/2010/05/txt.png" alt="" title="txt" width="103" height="130" class="alignright size-full wp-image-479" /><br />
<a href="http://railscasts.com/">Railscast</a> has a podcast on iTunes which lets you download all past episodes and thus this post is moot.  But, I thought, what if it wasn't?  What if I found a cool site with a bunch of media files I want to download?  I knew wget can do this but it's been a while.  So here's what I did.</p>
<h5>Get the links</h5>
<p>First I downloaded the <a href="http://railscasts.com/episodes/archive">archive page</a>.  It's full of links to other pages which have the .mov media files on it.  But I really needed a list of those pages within the archive page.  Ok, I wget'd the archive html source page in a file named archive.txt:<br />
<code>curl -o archive.txt http://railscasts.com/episodes/archive</code></p>
<p>However this page is full of html source and all I need is the list of episodes.  So a simple:<br />
<code>grep href urls.txt|grep episodes > urls_href.txt</code> gets me that.  But it's really full of text that looks like this: <code>&lt;a href="/episodes/195-my-favorite-web-apps-in-2009"&gt;My Favorite Web Apps in 2009&lt;/a&gt;</code></p>
<h5>Clean the links</h5>
<p> Ok now I just need to trim all this crap out.  First, there's a bunch of whitespace out front in the links.  Let's use sed inline.  This works on OSX and Linux but won't work on Solaris (inline boo).<br />
<code>sed -ie 's/^[ ]*//' urls_href.txt</code>.  Now we're missing a those spaces out front.</p>
<p>Ok, now we need to trim down to the relative link to the episode.  We want a URL that looks like:<br />
<code>http://railscasts.com/episodes/30-pretty-page-title</code></p>
<p>So at this point sed was failing me because the regex syntax is different than I'm used to.  So let's switch to perl.<br />
<code>cat urls_href.txt | perl -e 'while(&lt;&gt;) { s/\&lt;a\s*(.*)\&gt;(.*)\&lt;\/a\&gt;/$1/; print}' &gt; urls_href_clean.txt</code></p>
<p>We're almost there.  We have URLs that look like this:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&quot;/episodes/17-habtm-checkboxes&quot;
&quot;/episodes/16-virtual-attributes&quot;
&quot;/15-fun-with-find-conditions&quot;</pre></div></div>

<p>We need a prefix of the domain and to get rid of those quotes:<br />
cat urls_href_clean.txt | perl -e 'while(<>) { s/href\=/http\:\/\/railscasts.com/; s/\"//; s/\"//; print}' > urls_href_super_clean.txt</p>
<p>Despite our horribly unmaintainable "super" naming convention, we now have a text file full of URLs that looks like this:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">http://railscasts.com/episodes/17-habtm-checkboxes
http://railscasts.com/episodes/16-virtual-attributes
http://railscasts.com/episodes/15-fun-with-find-conditions</pre></div></div>

<h5>Scrape</h5>
<p>Fire wget using our text file as input (-i).  Recurse (-r), go only two levels deep (-l), don't download the file if we have one that's newer and use timestamps to make this possible (-Nc), span hosts (-H), no directories (-nd), disrespect the robots.txt file (-erobots=off) and look for only .mov files (-A).<br />
<code>wget -r -erobots=off -l2 -H -Nc -nd -A.mov -i urls_href_super_clean.txt</code></p>
<p>You might want to create a working directory for this before you run it.  And probably run it under "screen" if you have that command.  After you do, you'll eventually you'll have a directory full of railscasts.  Or, you could just subscribe to <a href="http://itunes.apple.com/us/podcast/railscasts/id218282043">their feed on iTunes</a>.  Getting it through iTunes is a lot easier but that wasn't <a href="http://en.wikipedia.org/wiki/Knowledge_by_acquaintance">the point</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/05/15/downloading-en-mass/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>iPhone Developer Activation Problem</title>
		<link>http://squarism.com/2010/05/10/iphone-developer-activation-problem/</link>
		<comments>http://squarism.com/2010/05/10/iphone-developer-activation-problem/#comments</comments>
		<pubDate>Mon, 10 May 2010 22:58:06 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[ObjC]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=461</guid>
		<description><![CDATA[I went to renew my membership on my iPhone developer account and although the membership is $99/year, my expiration date was only extended out for 3 months. I opened a ticket and it took about a month or two to get this thing resolved. I had to send screenshots, steps and evidence that I was [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://squarism.com/wp-content/uploads/2010/05/iphone_portal_expiration.png" alt="" title="iphone_portal_expiration" width="264" height="95" class="alignright size-full wp-image-466" /><br />
I went to renew my membership on my iPhone developer account and although the membership is $99/year, my expiration date was only extended out for 3 months.  I opened a ticket and it took about a month or two to get this thing resolved.  I had to send screenshots, steps and evidence that I was doing the right thing (ok, fine, tier 1 support).  Eventually I got a person that had a trick up their sleeve but didn't quite get it right.  I'm blogging this in case this is happening to someone else.</p>
<p>First, the symptoms.  Let's say your dev account is about to expire in January 2010.  You want to renew for another year.  You add a year subscription to your cart, pay for it.  You'd expect your new expire date to be Jan-2011.  But in your profile and in the "thanks for ordering" activation page it says March 2010 or something else wrong.  Support send me activation links basically to the same page that wouldn't activate any differently.  Each time I checked out, it would say March 2010.</p>
<p>The trick was to click on renew membership (as if paying again), click the checkbox to select which membership you want to buy ($99 year) and hit continue.  Now you're on a "Review your purchase" page.  Click continue here.  Finally, it says "Proceed to your country's Apple Online Store to purchase" and has an add to cart button.  Stop here.</p>
<p>Click the following link: <a href="http://developer.apple.com/iphone/enroll/activate.action">http://developer.apple.com/iphone/enroll/activate.action</a>.  It should bring you to a page with a single textbox for your activation code (the one you've been trying to use all along).  Put this in and hit continue.  Your expire date should be correct now.  If not, try using a link formatted like this:<br />
<code>http://developer.apple.com/iphone/enroll/activate.action?activationCode=YOURCODEHERE</code></p>
<p>Seems like they have some bug in the dev store.  Please comment if this helped you so it's documented.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/05/10/iphone-developer-activation-problem/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Reversing sentences with Ruby.</title>
		<link>http://squarism.com/2010/05/01/reversing-sentences-with-ruby/</link>
		<comments>http://squarism.com/2010/05/01/reversing-sentences-with-ruby/#comments</comments>
		<pubDate>Sat, 01 May 2010 19:01:28 +0000</pubDate>
		<dc:creator>Dillon</dc:creator>
				<category><![CDATA[Blog]]></category>

		<guid isPermaLink="false">http://squarism.com/?p=433</guid>
		<description><![CDATA[A question came up today about how to reverse a sentence. Word by word, preserving periods etc. I've done stuff like this before but I really saw an opportunity to solve this contrived quiz type question with TDD. It's perfect really. Tedious string checking? Screw it. Let my test tell me when I'm done. Ok [...]]]></description>
			<content:encoded><![CDATA[<p>A question came up today about how to reverse a sentence.  Word by word, preserving periods etc.  I've done stuff like this before but I really saw an opportunity to solve this <em>contrived</em> quiz type question with <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a>.  It's perfect really.  Tedious string checking?  Screw it.  Let my test tell me when I'm done.</p>
<p>Ok so I could have probably done this more comfortably in Java but it would have been more lines.  I banged this out in Ruby, including "learning" test cases (it's really easy) in about an hour.  At one point I realized how easy string manipulation is in Ruby for this and I literally said, "holy shit Ruby is amazing".  And then the edge cases started happening.  I was only handling periods and my algorithm fell over flat on three sentences because I was trying to do a string[start, end] when it's really string[start, length].  I fixed it with learning the String#slice syntax.</p>
<p>Ok enough fanboi service.  There's two files.  One's the test and one's the class.  Run the test and not the class (ie: ruby tc_reverser.rb).</p>
<p>tc_reverser.rb:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'test/unit'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'reverser'</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Be wary of newlines in the test and expected trings.</span>
  <span style="color:#008000; font-style:italic;"># Do not try to word wrap with escapes.  It's very picky.</span>
<span style="color:#9966CC; font-weight:bold;">class</span> TestReverser <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">Test::Unit::TestCase</span>
  <span style="color:#008000; font-style:italic;"># Optional</span>
  <span style="color:#008000; font-style:italic;">#def setup</span>
  <span style="color:#008000; font-style:italic;">#end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> teardown
    <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#0066ff; font-weight:bold;">@test_passed</span> <span style="color:#9966CC; font-weight:bold;">then</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\n</span> #{@r.reverse}&quot;</span>  <span style="color:#008000; font-style:italic;"># more verbose for successful passes</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;#{@method_name.upcase} OK.<span style="color:#000099;">\n</span><span style="color:#000099;">\n</span>&quot;</span>
    <span style="color:#9966CC; font-weight:bold;">else</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;#{@method_name.upcase} FAIL.<span style="color:#000099;">\n</span><span style="color:#000099;">\n</span>&quot;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># simple test</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> test_reverser
    test = <span style="color:#996600;">&quot;You space bastard.  You killed my pine.&quot;</span>
    expected = <span style="color:#996600;">&quot;Bastard space you.  Pine my killed you.&quot;</span>
    <span style="color:#0066ff; font-weight:bold;">@r</span> = Reverser.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>test<span style="color:#006600; font-weight:bold;">&#41;</span>
    assert_equal<span style="color:#006600; font-weight:bold;">&#40;</span>expected, <span style="color:#0066ff; font-weight:bold;">@r</span>.<span style="color:#9900CC;">reverse</span>, <span style="color:#996600;">&quot;Fail.&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>        
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Test multiple sentence delimiters  </span>
  <span style="color:#9966CC; font-weight:bold;">def</span> test_multiple_sentence_delimiters
    test = <span style="color:#996600;">&quot;Shape up, man.  You're a slacker.  Do you want to be a slacker for the rest of your life?&quot;</span>
    expected = <span style="color:#996600;">&quot;Man, up shape.  Slacker a you're.  Life your of rest the for slacker a be to want you do?&quot;</span>
    <span style="color:#0066ff; font-weight:bold;">@r</span> = Reverser.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>test<span style="color:#006600; font-weight:bold;">&#41;</span>
    assert_equal<span style="color:#006600; font-weight:bold;">&#40;</span>expected, <span style="color:#0066ff; font-weight:bold;">@r</span>.<span style="color:#9900CC;">reverse</span>, <span style="color:#996600;">&quot;Fail.&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Test no periods, question mark and commas</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> test_question_comma
    test = <span style="color:#996600;">&quot;Then tell me, future boy, huh, who's president of the United States in 1985?&quot;</span>
    expected = <span style="color:#996600;">&quot;1985 in States United the of president who's, huh, boy future, me tell then?&quot;</span>
    <span style="color:#0066ff; font-weight:bold;">@r</span> = Reverser.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>test<span style="color:#006600; font-weight:bold;">&#41;</span>
    assert_equal<span style="color:#006600; font-weight:bold;">&#40;</span>expected, <span style="color:#0066ff; font-weight:bold;">@r</span>.<span style="color:#9900CC;">reverse</span>, <span style="color:#996600;">&quot;Fail.&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Big test  </span>
  <span style="color:#9966CC; font-weight:bold;">def</span> test_everything
    test = <span style="color:#996600;">&quot;Our first television set. Dad just picked it up today. Do you have a television? Well, yeah, you know, we have two of them. Wow! You must be rich. Oh, honey, he's teasing you. Nobody has two television sets.&quot;</span>
    expected = <span style="color:#996600;">&quot;Set television first our.  Today up it picked just dad.  Television a have you do?  Them of two have we, know you, yeah, well.  Wow!  Rich be must you.  You teasing he's, honey, oh.  Sets television two has nobody.&quot;</span>
    <span style="color:#0066ff; font-weight:bold;">@r</span> = Reverser.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>test<span style="color:#006600; font-weight:bold;">&#41;</span>
    assert_equal<span style="color:#006600; font-weight:bold;">&#40;</span>expected, <span style="color:#0066ff; font-weight:bold;">@r</span>.<span style="color:#9900CC;">reverse</span>, <span style="color:#996600;">&quot;Fail.&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>I have to apologize for the scrollbars in the above posted code.  I tried many different ways of escaping carriage returns for better formatting but it would have required a lot of changes to deal with the \n and so on in the tests.  I had something nicely formatted working but the tabs and spaces for alignment then screwed the test and broke the pretty formatting a different way.</p>
<p>reverser.rb:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># A class that reverses sentences.</span>
<span style="color:#008000; font-style:italic;"># &quot;My dog has fleas.&quot; --&gt; &quot;Fleas has dog my.&quot;</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> Reverser
&nbsp;
  <span style="color:#008000; font-style:italic;"># tasty constructor, full of taste</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> initialize<span style="color:#006600; font-weight:bold;">&#40;</span>text<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@text</span> = text
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> reverse
    delim_location = <span style="color:#006666;">0</span>  <span style="color:#008000; font-style:italic;"># char by char, remember !?. for sentence determination</span>
    delim_last_location = <span style="color:#006666;">0</span>  <span style="color:#008000; font-style:italic;"># allows us to move on to the next sentence</span>
    delim_i = <span style="color:#006666;">0</span>  <span style="color:#008000; font-style:italic;"># an iterator</span>
&nbsp;
    new_sentence = <span style="color:#996600;">&quot;&quot;</span>  <span style="color:#008000; font-style:italic;"># inits an empty string for string methods to work</span>
&nbsp;
    new_sentence_array = <span style="color:#CC0066; font-weight:bold;">Array</span>.<span style="color:#9900CC;">new</span>
    punc_array = <span style="color:#CC0066; font-weight:bold;">Array</span>.<span style="color:#9900CC;">new</span>  <span style="color:#008000; font-style:italic;"># list of punctuation marks in order</span>
    <span style="color:#0066ff; font-weight:bold;">@text</span>.<span style="color:#9900CC;">each_char</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>char<span style="color:#006600; font-weight:bold;">|</span>
      delim_i <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#006666;">1</span>  <span style="color:#008000; font-style:italic;"># iterator</span>
      <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>char == <span style="color:#996600;">&quot;.&quot;</span> <span style="color:#006600; font-weight:bold;">||</span> char == <span style="color:#996600;">&quot;?&quot;</span> <span style="color:#006600; font-weight:bold;">||</span> char == <span style="color:#996600;">&quot;!&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
        <span style="color:#008000; font-style:italic;"># we hit a deliminter (?!.), remember it</span>
        sentence = <span style="color:#0066ff; font-weight:bold;">@text</span>.<span style="color:#9900CC;">slice</span><span style="color:#006600; font-weight:bold;">&#40;</span>delim_last_location..<span style="color:#9900CC;">delim_i</span><span style="color:#006600; font-weight:bold;">&#41;</span> 
        sentence.<span style="color:#9900CC;">strip</span>!
        new_sentence_array.<span style="color:#9900CC;">push</span><span style="color:#006600; font-weight:bold;">&#40;</span>sentence<span style="color:#006600; font-weight:bold;">&#41;</span>
        punc_array.<span style="color:#9900CC;">push</span><span style="color:#006600; font-weight:bold;">&#40;</span>char<span style="color:#006600; font-weight:bold;">&#41;</span>             
        delim_last_location = delim_i  <span style="color:#008000; font-style:italic;"># remember our substring position</span>
      <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    i = <span style="color:#006666;">0</span>
    new_sentence_array.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>s<span style="color:#006600; font-weight:bold;">|</span>
        returned_sentence = do_reverse<span style="color:#006600; font-weight:bold;">&#40;</span>s<span style="color:#006600; font-weight:bold;">&#41;</span>  <span style="color:#008000; font-style:italic;"># reverse words sentence by sentence</span>
        new_sentence <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#996600;">&quot;#{returned_sentence}#{punc_array[i]}&quot;</span>  <span style="color:#008000; font-style:italic;"># append the delimiter back</span>
&nbsp;
        <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>i<span style="color:#006600; font-weight:bold;">+</span><span style="color:#006666;">1</span> != new_sentence_array.<span style="color:#9900CC;">length</span><span style="color:#006600; font-weight:bold;">&#41;</span>  <span style="color:#008000; font-style:italic;"># look ahead for last sentence</span>
          new_sentence <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#996600;">&quot;  &quot;</span>  <span style="color:#008000; font-style:italic;"># only append spaces if this is not the last sentence</span>
        <span style="color:#9966CC; font-weight:bold;">end</span>
        i <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#006666;">1</span>
&nbsp;
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
&nbsp;
    new_sentence  <span style="color:#008000; font-style:italic;"># return new_sentence</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> do_reverse<span style="color:#006600; font-weight:bold;">&#40;</span>s<span style="color:#006600; font-weight:bold;">&#41;</span>
    s.<span style="color:#CC0066; font-weight:bold;">chop!</span>  <span style="color:#008000; font-style:italic;"># trim delimiter</span>
    text_array = s.<span style="color:#CC0066; font-weight:bold;">split</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot; &quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>  <span style="color:#008000; font-style:italic;"># split on spaces to array</span>
    text_array<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#93;</span>.<span style="color:#9900CC;">downcase</span>!  <span style="color:#008000; font-style:italic;"># downcase first word</span>
    text_array.<span style="color:#9900CC;">reverse</span>!  <span style="color:#008000; font-style:italic;"># destructive reverse</span>
&nbsp;
    <span style="color:#008000; font-style:italic;"># need to move commas back</span>
    text_array_i = <span style="color:#006666;">0</span>
    text_array.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>e<span style="color:#006600; font-weight:bold;">|</span>
       text_array_i <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#006666;">1</span>
        <span style="color:#9966CC; font-weight:bold;">if</span> e.<span style="color:#9966CC; font-weight:bold;">include</span>? <span style="color:#996600;">&quot;,&quot;</span>
          e.<span style="color:#CC0066; font-weight:bold;">chop!</span>   <span style="color:#008000; font-style:italic;"># modify original array</span>
          text_array<span style="color:#006600; font-weight:bold;">&#91;</span>text_array_i <span style="color:#006600; font-weight:bold;">-</span> <span style="color:#006666;">2</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#996600;">&quot;,&quot;</span>
        <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    text_array<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#93;</span>.<span style="color:#9900CC;">capitalize</span>!
    text_array = text_array.<span style="color:#9900CC;">join</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot; &quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>  <span style="color:#008000; font-style:italic;"># destructive join</span>
    text_array.<span style="color:#9900CC;">to_s</span>  <span style="color:#008000; font-style:italic;"># return reversed array</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<p>When you run the test case it looks like this:</p>
<blockquote><p>Loaded suite ~/src/ruby/tc_reverser<br />
Started</p>
<p> Set television first our.  Today up it picked just dad.  Television a have you do?  Them of two have we, know you, yeah, well.  Wow!  Rich be must you.  You teasing he's, honey, oh.  Sets television two has nobody.<br />
TEST_EVERYTHING OK!</p>
<p>.<br />
 Man, up shape.  Slacker a you're.  Life your of rest the for slacker a be to want you do?<br />
TEST_MULTIPLE_SENTENCE_DELIMITERS OK!</p>
<p>.<br />
 1985 in States United the of president who's, huh, boy future, me tell then?<br />
TEST_QUESTION_COMMA OK!</p>
<p>.<br />
 Bastard space you.  Pine my killed you.<br />
TEST_REVERSER OK!</p>
<p>.<br />
Finished in 0.003567 seconds.<br />
<font color="green">4 tests, 4 assertions, 0 failures, 0 errors</font>
</p></blockquote>
<p>Marty McFly would be proud.  So some limitations.  First, I don't handle elipsis (...) characters at all.  It splits on sentence delimiters which one is a period.  The eplisis would cause major weirdness.  I also don't handle recapitalizing the word "Dad" as you can see in the "test_everything" test.  This would require language parsing or a massive pick list.  I also don't handle slang or abbreviations.  For example, "You told 'em?"  would turn into "'em told you?" which might not be exactly right.</p>
<p>There are many other edge cases that this thing would fall flat on.  My test case tests what functionality I wanted and nothing more.</p>
<p>Ruby's String#slice was super handy on this as well as capitalize, reverse and even a regex split that I didn't use (amazing!).  You can see it here.  It would split text into an array really easily:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;">sentence_array = <span style="color:#0066ff; font-weight:bold;">@text</span>.<span style="color:#CC0066; font-weight:bold;">split</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">/</span><span style="color:#006600; font-weight:bold;">&#91;</span>\.\!\?<span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">/</span><span style="color:#006600; font-weight:bold;">&#41;</span>  <span style="color:#008000; font-style:italic;"># split with regular expression</span>
&nbsp;
new_sentence_array = <span style="color:#CC0066; font-weight:bold;">Array</span>.<span style="color:#9900CC;">new</span>
sentence_array.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>s<span style="color:#006600; font-weight:bold;">|</span>
    new_sentence_array.<span style="color:#9900CC;">push</span><span style="color:#006600; font-weight:bold;">&#40;</span>do_reverse<span style="color:#006600; font-weight:bold;">&#40;</span>s<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#CC0066; font-weight:bold;">puts</span> sentence_array<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006666;">2</span><span style="color:#006600; font-weight:bold;">&#93;</span>
<span style="color:#0000FF; font-weight:bold;">return</span></pre></div></div>

<p>But then I'd lose the delimiter character (!?. etc) and would have to save it, search it or some other nonsense.  Still, really neat that you can split on a regex.</p>
]]></content:encoded>
			<wfw:commentRss>http://squarism.com/2010/05/01/reversing-sentences-with-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
