<?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>Sarah Mei</title>
	<atom:link href="http://www.sarahmei.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.sarahmei.com/blog</link>
	<description></description>
	<lastBuildDate>Sun, 22 Jan 2012 20:16:25 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>How Diaspora Connects Users</title>
		<link>http://www.sarahmei.com/blog/2011/09/17/how-diaspora-connects-users/</link>
		<comments>http://www.sarahmei.com/blog/2011/09/17/how-diaspora-connects-users/#comments</comments>
		<pubDate>Sat, 17 Sep 2011 23:58:14 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[diaspora]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[webfinger]]></category>
		<category><![CDATA[xml]]></category>
		<category><![CDATA[xrd]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=936</guid>
		<description><![CDATA[Note: this is the first in a series of technical posts about Diaspora’s software architecture and code, and is a slightly modified version of the original on the Diaspora blog. If you have topics you’d like to see covered in future installments, please let me know. A single installation of the Diaspora software is called [...]]]></description>
			<content:encoded><![CDATA[<p><em>Note: this is the first in a series of technical posts about Diaspora’s software architecture and code, and is a slightly modified version of <a href="http://devblog.joindiaspora.com/2012/01/22/how-diaspora-connects-users/" target="_blank">the original on the Diaspora blog</a>. If you have topics you’d like to see covered in future installments, please let me know.</em></p>
<p>A single installation of the Diaspora software is called a <strong>pod</strong>. The Diaspora distributed network is made up of hundreds of these pods, each with a set of users – sometimes just one on an individual pod, sometimes tens of thousands on a community pod. Each pod is run by a different person or organization. But no matter what pod you sign up on, you can connect with users on any other pod.</p>
<p>When you have friends on different pods, your stream seamlessly mixes updates from remote friends with updates from friends on your pod. In this way Diaspora is a <strong>distributed</strong> social network that resembles, from the user’s perspective, a <strong>centralized</strong> social network.<br />
<span id="more-936"></span><br />
<a href="http://www.sarahmei.com/blog/wp-content/uploads/2011/09/diaspora_screenshot.jpg" target="_blank"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2011/09/diaspora_screenshot.jpg" alt="" title="diaspora_screenshot" width="871" height="579" class="aligncenter size-full wp-image-953" /></a></p>
<p>But how do these users find each other? In a centralized system, all servers access the same database, so when you search for a friend, there’s only one place to look. But in the Diaspora ecosystem, each pod has its own database, inaccessible to the other pods. So how does pod A figure out who’s on pod B, or for that matter, pod C that’s never been heard from before?</p>
<p>It all starts with searching. Let’s say you’re setting up your own pod. Once you’ve downloaded the Diaspora source and gotten it running on a server accessible to the internet, you open it up, log in…and are faced with the vast emptiness of splendid isolation.</p>
<h1>Let’s get you some friends</h1>
<p>There is no central server that keeps a list of existing pods or existing users. Instead, Diaspora depends on an emerging-standard protocol called webfinger to discover users on remote pods. This all kicks off when you search for someone’s Diaspora ID in your pod’s search box.</p>
<p style="padding-left: 30px;"><strong>Aside: </strong>A Diaspora ID is made up of a username, followed by an @ sign, followed by the pod url. It looks a lot like an email address. But just like with Jabber IDs which also look like email addresses, you can’t send email to it. It’s just a unique identifier within the Diaspora ecosystem.</p>
<p>So you’ve talked to a friend who’s on a (fictional) pod called otherpod.com, and you’ve gotten her Diaspora ID – sarah@otherpod.com. You want to connect, so you put sarah@otherpod.com into the search box on your pod and hit go. A few seconds later, you see her information on the search page with a nice “Add to Aspect” button alongside.</p>
<h1>Magic!</h1>
<p>I’d love to claim that, but sadly ponycorns are in short supply around here. Here’s how it goes down behind the scenes. A detailed explanation follows the diagram.</p>
<div id="attachment_937" class="wp-caption aligncenter" style="width: 684px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2011/09/user_discovery.jpg"><img class="size-full wp-image-937" title="user_discovery" src="http://www.sarahmei.com/blog/wp-content/uploads/2011/09/user_discovery.jpg" alt="User discovery diagram" width="674" height="715" /></a><p class="wp-caption-text">How a Diaspora pod finds a user on a different pod</p></div>
<p>When it gets a search request for a Diaspora ID, the first thing your pod does is look in its local database to see if it already knows about this person. This is the grey diamond in the diagram. If it can skip all this drama and just show you the information, it does so. But because you’re on a brand new pod, the only user it knows about is you. So it prepares to retrieve the information you requested from the remote pod.</p>
<p>From here the process is:</p>
<ol>
<li>Figure out where to search</li>
<li>Search</li>
<li>Retrieve detailed information</li>
<li>Cache data locally</li>
<li>Profit!</li>
</ol>
<p>I suppose the last one is optional.</p>
<h1>1. Find out where to search</h1>
<p>Your pod extracts the pod URL from the Diaspora ID (sarah@otherpod.com becomes otherpod.com) and appends a standard location called “the host-meta route” to get this URL:</p>
<pre><code>http://otherpod.com/.well-known/host-meta</code></pre>
<p>This route is part of the webfinger standard. It’s the basic way you ask a server whether or not it supports webfinger.</p>
<p>Your pod then accesses this location and gets back a piece of XML in a format called XRD. Basically, accessing the host-meta route is the same as asking the pod, “How should I send you inquiries about users?” The XRD document that it returns tells your pod how to construct the query for the particular user you’re interested in. Here’s what it looks like:</p>
<p><script src="https://gist.github.com/1224515.js?file=gistfile1.txt"></script></p>
<p>The “template” on line 4 is the key. It tells your pod to query for the user by substituting their Diaspora ID for {uri}. So to search for your friend, your pod needs to construct need a URL like this:</p>
<pre><code>https://otherpod.com/webfinger?q=sarah@otherpod.com</code></pre>
<p style="padding-left: 30px;"><strong>Aside:</strong> All Diaspora pods accept user queries at the same location, so this step might seem redundant. But Diaspora pods also inter-operate with other, non-Diaspora social systems, and those may have different locations for querying for a user. In other words, when we get a Diaspora ID, we don’t actually know whether the pod is a Diaspora pod or something else. So we ask for the search route each time.</p>
<h1>2. Search</h1>
<p>Having figured out the right way to ask, your pod now queries for the user it wants. It accesses the query URL:</p>
<pre><code>https://otherpod.com/webfinger?q=sarah@otherpod.com</code></pre>
<p>This returns us another piece of XML – another XRD document – that gives us some basic information about the user, but mostly just tells us where to go to find more detailed info. Here’s what it looks like:</p>
<p><script src="https://gist.github.com/1224511.js?file=gistfile1.xml"></script></p>
<p style="padding-left: 30px;"><strong>Aside:</strong> The webfinger XRD document is supposed to be just links to information elsewhere. However, as you can see, Diaspora embeds some actual information, such as the person’s public key, in the document. We implemented this before we fully understood how XRD was supposed to work. We should at some point move that information to the hcard (see next section).</p>
<h1>3. Retrieve profile</h1>
<p>To fill out profile details, the pod extracts the “hcard location” from the webfinger XRD document. An hcard is a standard, structured way to represent profile data in HTML. The hcard location is on line 5 of the document above, with URL:</p>
<pre><code>https://otherpod.com/hcard/users/4cec1e372c174347b90000ad</code></pre>
<p>Your pod accesses the hcard location, and gets back a piece of HTML with additional profile details for the remote user, such as name. Here’s an excerpt of that hcard – it’s quite long.</p>
<p><script src="https://gist.github.com/1224517.js?file=gistfile1.html"></script></p>
<p>It goes on, but I think you get the idea.</p>
<h1>4. Cache data locally</h1>
<p>Finally, having searched for the user and then retrieved her hcard, your pod extracts the profile details and saves them in its local database. sarah@otherpod.com now shows up in searches you do on your pod.</p>
<h1>5. Profit!</h1>
<p>Once you start following your friend, you’ll get her updates as though she were a user local to your pod. If she also follows you, she’ll get your updates in her stream too. From there…who knows what could happen.</p>
<p>This walkthrough covered just searching and basic user discovery, which is a tiny part of how Diaspora pods interoperate. Once you get into federation of posts and other content between pods, it’s a whole different ballgame. Stay tuned for that in an upcoming installment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2011/09/17/how-diaspora-connects-users/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>CruiseControl.rb and RubyGems 1.5.2</title>
		<link>http://www.sarahmei.com/blog/2011/02/26/cruisecontrol-rb-and-rubygems-1-5-2/</link>
		<comments>http://www.sarahmei.com/blog/2011/02/26/cruisecontrol-rb-and-rubygems-1-5-2/#comments</comments>
		<pubDate>Sat, 26 Feb 2011 18:57:32 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[bundler]]></category>
		<category><![CDATA[ccrb]]></category>
		<category><![CDATA[diaspora]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rails2]]></category>
		<category><![CDATA[rails3]]></category>
		<category><![CDATA[rubygems]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=931</guid>
		<description><![CDATA[Diaspora uses CruiseControl.rb to run our continuous integration server. CC.rb is on Rails 2.3, but the applications it&#8217;s building are on Rails 3, which means I occasionally run into &#8230; weirdness. Last week, for example, I wanted to speed up our builds by upgrading Bundler to 1.0.10 and RubyGems to 1.5.2. Because of the new [...]]]></description>
			<content:encoded><![CDATA[<p>Diaspora uses <a href="https://github.com/thoughtworks/cruisecontrol.rb" target="_blank">CruiseControl.rb</a> to run our <a href="http://ci.joindiaspora.com" target="_blank">continuous integration server</a>. CC.rb is on Rails 2.3, but the applications it&#8217;s building are on Rails 3, which means I occasionally run into &#8230; weirdness.</p>
<p>Last week, for example, I wanted to speed up our builds by upgrading <a href="http://gembundler.com/" target="_blank">Bundler</a> to 1.0.10 and <a href="http://rubygems.org/" target="_blank">RubyGems</a> to 1.5.2. Because of the new partial caching of the dependency graph, the upgrades shaved two whole minutes off the build locally, and I wanted to get that on CI.<br />
<span id="more-931"></span><br />
Trouble is, once I did the upgrades, I got this upon restarting CC.rb:</p>
<p><script src="https://gist.github.com/845478.js?file=gistfile1.eclass"></script></p>
<p>It turns out that older versions of Rails 2.3 <a href="http://www.redmine.org/issues/7516" target="_blank">aren&#8217;t compatible</a> with the new version of RubyGems. To fix this, I added the following to CC.rb&#8217;s <code>config/environment.rb</code>, between the definition of <code>ABSOLUTE_RAILS_ROOT</code> and the <code>Rails::Initializer</code> block.</p>
<p><script src="https://gist.github.com/845481.js?file=gistfile1.rb"></script> </p>
<p>Both the 1.8 and 1.9 builds are now about 2 minutes faster which is awesome. Highly recommend the upgrades.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2011/02/26/cruisecontrol-rb-and-rubygems-1-5-2/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Running Cucumber Features Without a Display</title>
		<link>http://www.sarahmei.com/blog/2010/12/17/cucumber-without-a-display/</link>
		<comments>http://www.sarahmei.com/blog/2010/12/17/cucumber-without-a-display/#comments</comments>
		<pubDate>Fri, 17 Dec 2010 07:41:30 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[ci]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[diaspora]]></category>
		<category><![CDATA[selenium]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=764</guid>
		<description><![CDATA[I&#8217;ve been helping out with the Diaspora project, an open source social network that gives you control over your own data. When I first started poking around the codebase a few months ago, they&#8217;d just started writing a few cucumber selenium integration tests &#8211; which of course I want to encourage! &#8211; but they weren&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been helping out with <a href="http://joindiaspora.com" target="_blank">the Diaspora project</a>, an <a href="http://github.com/diaspora/diaspora" target="_blank">open source</a> <a href="http://www.sarahmei.com/blog/2010/07/25/safe-facebooking/" target="_blank">social network</a> that gives you control over your own data.</p>
<p>When I first started poking around the codebase a few months ago, they&#8217;d just started writing a few <a href="http://cukes.info" target="_blank">cucumber</a><a></a> <a href="http://seleniumhq.org" target="_blank">selenium</a> integration tests &#8211; which of course <a href="http://www.sarahmei.com/blog/2010/05/29/outside-in-bdd/" target="_blank">I want to encourage!</a> &#8211; but they weren&#8217;t running them on their <a href="http://ci.joindiaspora.com" target="_blank">continuous integration box</a>.  And if you aren&#8217;t running them on CI, you can&#8217;t really call them tests&#8230;</p>
<p>It&#8217;s not that the developers were lazy; it&#8217;s just that Diaspora&#8217;s CI box is an ubuntu server instance that doesn&#8217;t even have xwindows. There&#8217;s no way to attach a display, and without a display, a browser won&#8217;t run. So how can you run selenium features that have to actually open a browser window and click on stuff?<br />
<span id="more-764"></span><br />
<strong>I&#8217;m glad you asked!</strong></p>
<p>You set up a virtual framebuffer &#8211; essentially a simulated display &#8211; and run your features in there. Here&#8217;s how I got this going.</p>
<h3>Step 1: Install Crap</h3>
<p>Yeah, you need xwindows, and a bunch of other stuff. I&#8217;m not a frequent linux user, so I was surprised that a lot of these commands appeared to fail, with extremely lengthy output about how such-and-such dependency could not be installed, or compiled, or resolved, or kafloozled, or whatever. But then when I ran the programs that supposedly weren&#8217;t installed, they worked fine. &#8230;thanks, ubuntu?</p>
<p><code> </code></p>
<pre><code>sudo aptitude install xubuntu-desktop
sudo aptitude install exaile
sudo aptitude install gconf2
sudo aptitude install xvfb
sudo aptitude install firefox
</code></pre>
<p><code> </code></p>
<h3>Step 2: Verify Your Virtual Framebuffer</h3>
<p>I like the phrase &#8220;virtual framebuffer.&#8221; It&#8217;s very computer science, and using it makes me feel smart. But really, it&#8217;s just a display that gets rendered in memory, but not output to any monitor. Programs that require the ability to create windows, such as Firefox, need a display to run in &#8211; but they&#8217;ll take any old display. If you give Firefox a virtual framebuffer, it&#8217;s perfectly happy and treats it like any other display, even though no one can see it.</p>
<p>So let&#8217;s verify we have everything set up to run one. This magic incantation starts one on display port :99.</p>
<p><code> </code></p>
<pre><code> Xvfb :99 -ac -screen 0 1024x768x16
</code></pre>
<p><code> </code></p>
<p>In another shell, run this one, which opens Firefox to example.org inside the virtual display you&#8217;re running in the original shell.</p>
<p><code> </code></p>
<pre><code>DISPLAY=:99.0 firefox http://example.org
</code></pre>
<p><code> </code></p>
<p>Firefox is now open to example.org, the world&#8217;s most boring webpage. Really! Let&#8217;s prove it by taking a screenshot in a third shell.</p>
<p><code> </code></p>
<pre><code>xwd -root -display :99.0 -out xwdout
</code></pre>
<p><code> </code></p>
<p>Your screenshot is now in a file called xwdout. Sadly, this is not an image format that normal image viewers can see, so let&#8217;s convert it to a jpeg.</p>
<p><code> </code></p>
<pre><code>convert xwdout screenshot.jpg
</code></pre>
<p><code> </code></p>
<p>Using your favorite sftp program, retrieve screenshot.jpg and have a look at it. It should look something like this:</p>
<div id="attachment_894" class="wp-caption aligncenter" style="width: 1003px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/12/screenshot1.jpg"><img class="size-full wp-image-894" title="screenshot" src="http://www.sarahmei.com/blog/wp-content/uploads/2010/12/screenshot1.jpg" alt="Yup, a screenshot." width="993" height="282" /></a><p class="wp-caption-text">That boxy UI, those icons from 1995...it could only be linux.</p></div>
<p>Ta-da! You have evidence of this so-called &#8220;virtual&#8221; framebuffer. Now all we have to do is set it up so to start before we run the tests, and stop once the tests are done.</p>
<h3>Step 3: Adventures in Shell Scripting</h3>
<p>This goes in your /etc/init.d, and will give you ULTIMATE POWER, if ultimate means you can start and stop a virtual framebuffer on display port :99.</p>
<p><script src="https://gist.github.com/744561.js"></script> </p>
<p>Now you just need a rake task that starts the display, passes the port info to the tests, and then stops it.  </p>
<p><script src="https://gist.github.com/744607.js?file=gistfile1.rb"></script></p>
<p>And just in case you have to get this running with <a href="http://rvm.beginrescueend.com" target="_blank">rvm</a>, here&#8217;s the shell script that <a href="http://cruisecontrolrb.thoughtworks.com/" target="_blank">cc.rb</a> calls to kick off a Diaspora build on ruby 1.8.7.</p>
<p><script src="https://gist.github.com/744612.js?file=gistfile1.txt"></script></p>
<p>I&#8217;ve had to set this up twice, now, but there are a lot of moving parts. If you try this and notice something I forgot, please let me know.</p>
<p>Enjoy your headless cucumbers&#8230;</p>
<p><span style="color: #b2b2b2;">(I wonder what kind of google search results <strong>that</strong> will get me.)</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/12/17/cucumber-without-a-display/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Disalienation: Why Gender is a Text Field on Diaspora</title>
		<link>http://www.sarahmei.com/blog/2010/11/26/disalienation/</link>
		<comments>http://www.sarahmei.com/blog/2010/11/26/disalienation/#comments</comments>
		<pubDate>Sat, 27 Nov 2010 06:26:23 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[diaspora]]></category>
		<category><![CDATA[gender]]></category>
		<category><![CDATA[rants]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=770</guid>
		<description><![CDATA[A few months ago, I started contributing to the Diaspora project. I began by refactoring their test suite and setting up a continuous integration server. Then I installed Jasmine and started mucking around with the JavaScript. That was all pretty straightforward. A few weeks ago I made a slightly more controversial change. The &#8220;gender&#8221; field [...]]]></description>
			<content:encoded><![CDATA[<p>A few months ago, I started contributing to the <a href="http://joindiaspora.com" target="_blank">Diaspora project</a>. I began by refactoring their test suite and setting up a <a href="http://ci.joindiaspora.com" target="_blank">continuous integration server</a>. Then I installed <a href="http://pivotal.github.com/jasmine/" target="_blank">Jasmine</a> and started mucking around with the JavaScript. That was all pretty straightforward.</p>
<p>A few weeks ago I made a slightly more controversial change.<br />
<span id="more-770"></span><br />
<a href="https://github.com/diaspora/diaspora/commit/a23076387907e32215267e85c148441ff9ffc214" target="_blank"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/11/gender-is-a-textfield1.png" alt="" title="gender-is-a-textfield" width="570" height="161" class="aligncenter size-full wp-image-813" /></a></p>
<p>The &#8220;gender&#8221; field in a person&#8217;s profile was originally a dropdown menu, with three choices: blank, male, and female. My change made it an optional text field that was blank to start. A wide open frontier! Enter anything you want.</p>
<p>For a while, only a few people noticed.</p>
<p><a href="https://github.com/diaspora/diaspora/commit/a23076387907e32215267e85c148441ff9ffc214" target="_blank"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/11/this-makes-me-happy.png" alt="Screenshot of a github commit comment" title="this-makes-me-happy" width="640" height="152" class="aligncenter size-full wp-image-773" /></a></p>
<p>But now that Diaspora is in <a href="http://blog.joindiaspora.com/2010/11/23/private-alpha-released.html" target="_blank">private alpha</a>, more people have started noticing &#8211; on <a href="http://twitter.com/#!/thewavingcat/status/7734317543325696" target="_blank">twitter</a>, in the <a href="http://bugs.joindiaspora.com/issues/575" target="_blank">bug tracker</a>, and in <a href="http://getsatisfaction.com/diaspora/topics/gender_choice" target="_blank">GetSatisfaction</a>, among other places. Some folks have asked why it&#8217;s not a dropdown with two options like everywhere else. So here&#8217;s why.</p>
<h3>&#8230;what else is there?</h3>
<p>Four years ago, at my first rails job, I worked at a company with a mostly-lesbian customer base. It turns out, in that context, knowing if someone is &#8220;male&#8221; or &#8220;female&#8221; gives you almost no useful information. The lesbian community has other widely-accepted categories of gender, but the company&#8217;s internal order tracking software &#8212; a well-known package from a national vendor &#8212; offered only male or female. </p>
<p>As a result, the company didn&#8217;t even bother to ask for gender when users created accounts. </p>
<p>That was my first real-life experience with the limitations of the gender binary. It was certainly interesting, but it was essentially academic. Not long after I left that job, though, one of my closest family members told me that he&#8217;s transgender. That made the whole subject <strong>way</strong> more immediate. </p>
<h3>Now it&#8217;s personal</h3>
<p>So in the last few years I&#8217;ve tried really hard to understand what being transgender means. I&#8217;ve done a lot of reading and talking and thinking about how we construct a gender identity, and how we perceive others&#8217; gender. I&#8217;m certainly no expert, having not lived it myself. But I have discovered that my own gender identity is a bit  more fluid than I thought. And perhaps most importantly, I&#8217;ve gotten comfortable with the idea of gender as an n-dimensional space, with two big clusters and a hell of a lot of outliers.</p>
<p>Then I met Sarah Dopp at <a href="http://shesgeeky.org" target="_blank">She&#8217;s Geeky</a>, and we talked about <a target="_blank" href="http://www.sarahdopp.com/blog/2008/genders-and-drop-down-menus/">dropdown menus</a>, and it all fell into place.</p>
<h3>tl;dr</h3>
<p>I made this change to Diaspora so that I won&#8217;t alienate anyone I love before they finish signing up. </p>
<p>I made this change because gender is a beautiful and multifaceted thing that can&#8217;t be contained by a list. </p>
<p><strong>I know a lot of people aren&#8217;t there with me yet.</strong> So I also made this change to give them one momentary chance to consider other possibilities. </p>
<p>I made it to start a conversation.</p>
<p>I made it because I can.</p>
<p>And, of course, I made it so you can be a smartass.</p>
<div id="attachment_827" class="wp-caption aligncenter" style="width: 555px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/11/gender.png"><img style="max-width: 100%;" src="http://www.sarahmei.com/blog/wp-content/uploads/2010/11/gender.png" alt="a selection of the gender self-descriptions of my contacts on diaspora" title="gender" width="535" height="373" class="size-full wp-image-827" /></a><p class="wp-caption-text">a selection of the gender self-descriptions of my contacts on diaspora</p></div>
<p>Go out and have fun with it.</p>
<p><em><strong>Diaspora</strong> is an open-source social network that puts you in control of your information. As of today, November 27th, we&#8217;ve been live less than a week. Here&#8217;s <a href="http://blog.joindiaspora.com/what-is-diaspora.html" target="_blank">a quick overview of the project</a>, and if you want more news, <a href="http://blog.joindiaspora.com" target="_blank">our blog</a>. Thanks for visiting!</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/11/26/disalienation/feed/</wfw:commentRss>
		<slash:comments>350</slash:comments>
		</item>
		<item>
		<title>Speak Ruby in Japanese</title>
		<link>http://www.sarahmei.com/blog/2010/09/04/speak-ruby-in-japanese/</link>
		<comments>http://www.sarahmei.com/blog/2010/09/04/speak-ruby-in-japanese/#comments</comments>
		<pubDate>Sat, 04 Sep 2010 21:59:32 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[japan]]></category>
		<category><![CDATA[japanese]]></category>
		<category><![CDATA[language]]></category>
		<category><![CDATA[pairprogramming]]></category>
		<category><![CDATA[rubykaigi]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=678</guid>
		<description><![CDATA[I&#8217;ve studied Japanese on and off for more than ten years &#8211; mostly &#8220;off.&#8221; I took a year of language when I was in college, but since then it&#8217;s just been periodic classes at Soko Gakuen in San Francisco. I managed to pass the JLPT level 3 a few years ago, so in Japan last [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve studied Japanese on and off for more than ten years &#8211; mostly &#8220;off.&#8221; I took a year of language when I was in college, but since then it&#8217;s just been periodic classes at <a href="http://sokogakuen.org/" target="_blank">Soko Gakuen</a> in San Francisco.</p>
<p>I managed to pass the JLPT level 3 a few years ago, so in Japan last month, I was decent at ordering food and navigating the subway. But I quickly discovered that I couldn&#8217;t really talk to another programmer. None of my classes even taught me how to say &#8220;programmer,&#8221; let alone &#8220;code,&#8221; &#8220;object,&#8221; &#8220;method,&#8221; &#8220;development environment&#8221;&#8230;<span id="more-678"></span></p>
<div id="attachment_726" class="wp-caption alignleft" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/pair-programming-1.jpg"><img class="size-medium wp-image-726" title="pair-programming-1" src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/pair-programming-1-300x223.jpg" alt="" width="300" height="223" /></a><p class="wp-caption-text">@sarahmei, @t_wada, @sakuro (photo by Lee Lundrigan)</p></div>
<p>Dictionaries would normally be my next recourse, but it&#8217;s pretty hard to look this stuff up. As in the US, programmer culture in Japan has its own slang. But I had a chance to pair with some Japanese devs at the Pair Programming Cultural Exchange that <a href="http://twitter.com/t_wada" target="_blank">@t_wada</a> and I ran at RubyKaigi (see left), so I picked up a little bit. And since I&#8217;ve been back, I&#8217;ve been translating technical articles for fun. (What else would you do on a 12-hour flight?)</p>
<p>As a result, a lot of this list comes from my notes. I also added words I found in <a href="http://twitter.com/#search?q=rubykaigi" target="_blank">the RubyKaigi Twitter stream</a>, <a href="http://www.ultrasaurus.com/sarahblog/2009/12/japanese-geek-speak/">Sarah Allen&#8217;s blog</a>, and <a href="http://www.mightyverse.com/phrase_lists/pair-programing" target="_blank">Mightyverse&#8217;s pair programming phrases</a>, among other locations. Any errors are, of course, my own.</p>
<p>The list is short &#8211; there are many more things I&#8217;d like to know how to say. And I guessed at the right katakana for some of the loan words. So: <strong>please send additions and corrections</strong>[<a href="#thanks">*</a>], kkthx! I&#8217;ll add anything vaguely technical to the list.</p>
<p>I hope it provokes more cross-language Ruby discussion.</p>
<table border="0">
<tbody>
<tr>
<th>English</th>
<th>Kanji</th>
<th>Kana</th>
<th>Romaji</th>
</tr>
<tr>
<td>block</td>
<td></td>
<td>ブロック</td>
<td>burokku</td>
</tr>
<tr>
<td>blog</td>
<td></td>
<td>ブログ</td>
<td>burogu</td>
</tr>
<tr>
<td>cache</td>
<td></td>
<td>キャッシュ</td>
<td>kyasshu</td>
</tr>
<tr>
<td>character</td>
<td>文字</td>
<td>もじ</td>
<td>moji</td>
</tr>
<tr>
<td>code</td>
<td></td>
<td>コード</td>
<td>koudo</td>
</tr>
<tr>
<td>core library</td>
<td></td>
<td>コアライブラリ</td>
<td>koa raiburari</td>
</tr>
<tr>
<td>developer</td>
<td>開発者</td>
<td>かいはつしゃ</td>
<td>kaihatsusha</td>
</tr>
<tr>
<td>development</td>
<td>開発</td>
<td>かいはつ</td>
<td>kaihatsu</td>
</tr>
<tr>
<td>development environment</td>
<td>開発環境</td>
<td>かいはつかんきょう</td>
<td>kaihatsu kankyou</td>
</tr>
<tr>
<td>diary (often used in place of blog)</td>
<td>日記</td>
<td>にっき</td>
<td>nikki</td>
</tr>
<tr>
<td>digit</td>
<td>数字</td>
<td>すうじ</td>
<td>suuji</td>
</tr>
<tr>
<td>dot (as in foo.bar)</td>
<td></td>
<td>ドット</td>
<td>dotto</td>
</tr>
<tr>
<td>expected failure (of a test)</td>
<td>予想通りの失敗</td>
<td>よそうどおりのしっぱい</td>
<td>yosoudoori no shippai</td>
</tr>
<tr>
<td>feature</td>
<td>機能</td>
<td>きのう</td>
<td>kinou</td>
</tr>
<tr>
<td>flexible</td>
<td>柔軟</td>
<td>じゅうなん</td>
<td>juunan</td>
</tr>
<tr>
<td>full-width (as in character)</td>
<td>全角</td>
<td>ぜんかく</td>
<td>zenkaku</td>
</tr>
<tr>
<td>global (as in variable)</td>
<td></td>
<td>グローバル</td>
<td>guroubaru</td>
</tr>
<tr>
<td>hashrocket</td>
<td></td>
<td>ハッシュロ ケット</td>
<td>hasshuroketto</td>
</tr>
<tr>
<td>implementation</td>
<td>実装</td>
<td>じっそう</td>
<td>jissou</td>
</tr>
<tr>
<td>internal structure</td>
<td>内部構造</td>
<td>ないぶこうぞう</td>
<td>naibu kouzou</td>
</tr>
<tr>
<td>latest</td>
<td>最新</td>
<td>さいしん</td>
<td>saishin</td>
</tr>
<tr>
<td>method (on an object)</td>
<td></td>
<td>メソッド</td>
<td>mesoddo</td>
</tr>
<tr>
<td>modification</td>
<td>変更</td>
<td>へんこう</td>
<td>henkou</td>
</tr>
<tr>
<td>multibyte (as in character)</td>
<td>多バイト</td>
<td>たバイト</td>
<td>tabaito</td>
</tr>
<tr>
<td>multibyte (as in character)</td>
<td></td>
<td>マルチバイト</td>
<td>maruchibaito</td>
</tr>
<tr>
<td>object</td>
<td></td>
<td>オブジェクト</td>
<td>obujekuto</td>
</tr>
<tr>
<td>plugin</td>
<td></td>
<td>プラグイン</td>
<td>puraguin</td>
</tr>
<tr>
<td>programmer</td>
<td></td>
<td>プローグラーマ</td>
<td>purouguraama</td>
</tr>
<tr>
<td>refactoring</td>
<td></td>
<td>リファクタリング</td>
<td>rifakutaringu</td>
</tr>
<tr>
<td>refactoring</td>
<td>改善</td>
<td>かいぜん</td>
<td>kaizen</td>
</tr>
<tr>
<td>runtime</td>
<td></td>
<td>ランタイム</td>
<td>rantaimu</td>
</tr>
<tr>
<td>spec</td>
<td></td>
<td>スペック</td>
<td>supekku</td>
</tr>
<tr>
<td>statement</td>
<td></td>
<td>ステートメント</td>
<td>suteetomento</td>
</tr>
<tr>
<td>string</td>
<td>文字列</td>
<td>もじれつ</td>
<td>mojiretsu</td>
</tr>
<tr>
<td>test</td>
<td></td>
<td>テスト</td>
<td>tesuto</td>
</tr>
<tr>
<td>test framework</td>
<td></td>
<td>テストフレームワーク</td>
<td>tesuto fureemuwaaku</td>
</tr>
<tr>
<td>threadsafe</td>
<td></td>
<td>スレッドセーフ</td>
<td>sureddoseefu</td>
</tr>
<tr>
<td>tool</td>
<td></td>
<td>ツール</td>
<td>tsuuru</td>
</tr>
<tr>
<td>tutorial</td>
<td></td>
<td>チュートリアル</td>
<td>chuutoriaru</td>
</tr>
<tr>
<td>ugly (as in code)</td>
<td>かっこ悪い</td>
<td>かっこわるい</td>
<td>kakko warui</td>
</tr>
<tr>
<td>usage</td>
<td>使い</td>
<td>つかい</td>
<td>tsukai</td>
</tr>
<tr>
<td>variable</td>
<td>変数</td>
<td>へんすう</td>
<td>hensuu</td>
</tr>
</tbody>
</table>
<p><a name="thanks">[*]</a> ありがとう:</p>
<ul>
<li><a href="http://twitter.com/threedaymonk" target="_blank">@threedaymonk</a> for a correction to multibyte digit.</li>
<li>Nobuyoshi Nakada for corrections to variable and multibyte character/digit, and addition of full-width character/digit.</li>
<li><a href="http://twitter.com/boblet" target="_blank">@boblet</a> and <a href="http://karmag.dreamwidth.org/" target="_blank">karmag</a> for a correction to development environment.
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/09/04/speak-ruby-in-japanese/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Ruby Kaigi</title>
		<link>http://www.sarahmei.com/blog/2010/09/02/ruby-kaigi/</link>
		<comments>http://www.sarahmei.com/blog/2010/09/02/ruby-kaigi/#comments</comments>
		<pubDate>Fri, 03 Sep 2010 04:54:11 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[asakusarb]]></category>
		<category><![CDATA[jasmine]]></category>
		<category><![CDATA[origami]]></category>
		<category><![CDATA[rubykaigi]]></category>
		<category><![CDATA[tdiary]]></category>
		<category><![CDATA[_why]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=659</guid>
		<description><![CDATA[Apart from attending Ruby meetups, my main reason for visiting Japan last month was RubyKaigi 2010. I wasn&#8217;t sure what to expect. Would it be all men in button-down dress shirts and pleated pants? Would I give my talk to a room full of blank looks? Would I be the one with the blank look [...]]]></description>
			<content:encoded><![CDATA[<p>Apart from <a href="http://www.sarahmei.com/blog/2010/09/01/asakusa-rb/" target="_blank">attending Ruby meetups</a>, my main reason for visiting Japan last month was <a href="http://rubykaigi.com/2010/en" target="_blank">RubyKaigi 2010</a>. </p>
<div id="attachment_670" class="wp-caption alignright" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/lee-sarah-ruby-kaigi-why-not.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/lee-sarah-ruby-kaigi-why-not-300x199.jpg" alt="" title="lee-sarah-ruby-kaigi-why-not" width="300" height="199" class="size-medium wp-image-670" /></a><p class="wp-caption-text">Why not, indeed? (photo by Lee Lundrigan)</p></div>
<p>I wasn&#8217;t sure what to expect. Would it be all men in button-down dress shirts and pleated pants? Would I give my talk to a room full of blank looks? Would I be the one with the blank look when I went to a talk in Japanese? WOULD THERE BE FAN SERVICE??<br />
<span id="more-659"></span><br />
No, no, no, and no. Thank goodness. It was one of the best conferences I&#8217;ve ever attended. Most of the talks were in Japanese, but they all had simultaneous translation in IRC (and IRC was projected onto screens at the side of the stage). My talk was translated into Japanese in IRC as I gave it, and <a href="http://bit.ly/aaEr2h" target="_blank">the video</a> was up the next day. </p>
<p>And people were interested in <a href="http://pivotal.github.com/jasmine/" target="_blank">Jasmine</a> and JavaScript testing (the subject of my talk), beyond just politeness. I helped <a href="http://twitter.com/machu" target="_blank">Kohei Matsuoka (@machu)</a>, one of the contributors to <a href="http://www.tdiary.org/" target="_blank">tdiary</a>, get going with Jasmine testing for their JavaScript. tdiary (also <a href="http://github.com/tdiary" target="_blank">on github</a>) is an open-source Ruby blogging system built on CGI.rb. It is OLD SKOOL. They are just now formulating a testing strategy (it&#8217;s in <a href="http://github.com/tdiary/tdiary-core" target="_blank">a branch of tdiary/tdiary-core</a>, to be merged in soon according to <a href="http://twitter.com/hsbt" target="_blank">@hsbt</a>). It&#8217;ll be fun to watch it shake out.</p>
<div id="attachment_665" class="wp-caption alignleft" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/origiami-ruby.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/origiami-ruby-300x224.jpg" alt="" title="origiami-ruby" width="300" height="224" class="size-medium wp-image-665" /></a><p class="wp-caption-text">Lightly crushed</p></div>
<p>The extra-curriculars were also fun. I clumsily made an origami ruby, with much help and encouragement from the instructor, who could clearly do more complex pieces. One of the Asakusa.rb guys gave me a fan from last year&#8217;s Ruby Kaigi that has an illustration from _why on it. I think it&#8217;s my favorite souvenir. I drank a lot, and went to a matsuri (street fair) where I saw weird dancing robots. I ate a lot of food that I probably would have refused had I known what it was. Most of it was great, though I did manage to go to the TGIFriday&#8217;s of Japanese food one night. </p>
<p>Oh &#8211; and there were other talks. They were great! I particularly enjoyed <a href="http://rubykaigi.tdiary.net/20100828.html#p08" target="_blank">Yasuko Ohba&#8217;s talk</a> on <a href="http://www.slideshare.net/nay/the-basis-of-making-dsl-with-ruby" target="_blank">writing DSLs in Ruby</a>. You can get the <a href="http://rubykaigi.tdiary.net/20100828.html" target="_blank">full set of videos</a> &#8211; most of them are quite good. There were quite a few talks on scientific computing and numeric libraries in Ruby. I&#8217;m glad I don&#8217;t have to work with them, but I&#8217;m glad someone&#8217;s doing them. </p>
<p><strong>Geek culture</strong></p>
<p>I saw a lot of threadless tshirts and baggy jeans &#8211; geeks the world over dress the same. That goddamned &#8220;#shirt&#8221; shirt was everywhere too. I saw at least one Ruby committer packing his own (large) bottle of sake. I did a pair programming subevent, and was able to pair with a couple of Japanese programmers. It pretty much felt like pairing at Pivotal (that&#8217;s a good thing). Smart people, it seems, can communicate with each other even when there&#8217;s a language barrier. Particularly when there&#8217;s a common (formal) language.</p>
<p>I am already making plans to attend RubyKaigi next year. Thank you very much to <a href="http://twitter.com/takahashim" target="_blank">Masayoshi Takahashi</a> and <a href="http://twitter.com/kakutani" target="_blank">Shintaro Kakutani</a>, and all the other organizers and volunteers. Fabulous event.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/09/02/ruby-kaigi/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Asakusa.rb</title>
		<link>http://www.sarahmei.com/blog/2010/09/01/asakusa-rb/</link>
		<comments>http://www.sarahmei.com/blog/2010/09/01/asakusa-rb/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 05:21:01 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[asakusarb]]></category>
		<category><![CDATA[japan]]></category>
		<category><![CDATA[rubykaigi]]></category>
		<category><![CDATA[sfruby]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=600</guid>
		<description><![CDATA[A few days after we arrived in Japan, Sarah Allen and I went to the weekly hack night put on by Asakusa.rb, a meetup group in Tokyo founded by Akira Matsuda. (She blogged about it here.) Asakusa is the neighborhood in Tokyo where we were staying. It was a fortuitous choice. The first night we [...]]]></description>
			<content:encoded><![CDATA[<p>A few days after we arrived in Japan, Sarah Allen and I went to the weekly hack night put on by <a href="http://qwik.jp/asakusarb/" target="_blank">Asakusa.rb</a>, a meetup group in Tokyo founded by <a href="http://twitter.com/a_matsuda" target="_blank">Akira Matsuda</a>. <span id="more-600"></span>(<a href="http://www.ultrasaurus.com/sarahblog/2010/08/ruby-meetup-in-tokyo-asakusa-rb/" target="_blank">She blogged about it here</a>.) </p>
<div id="attachment_602" class="wp-caption alignleft" style="width: 234px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/asakusa-gate.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/asakusa-gate-224x300.jpg" alt="" title="asakusa-gate" width="224" height="300" class="size-medium wp-image-602" /></a><p class="wp-caption-text">went out for groceries, found this.</p></div>
<p><a href="http://en.wikipedia.org/wiki/Asakusa" target="_blank">Asakusa</a> is the neighborhood in Tokyo where we were staying. It was a fortuitous choice. </p>
<p>The first night we were there, my friend Iku and I walked to a sento &#8211; and old-style Japanese bath. (Note: bathhouses in Japan don&#8217;t have the same <a href="http://en.wikipedia.org/wiki/Gay_bathhouse" target="_blank">connotation</a>  that they do in the U.S.) It was incredibly relaxing, after 12 hours on the plane, to wash and sit in the hot water. According to Iku, who&#8217;s Japanese, traditional sentos are hard to find in Japan these days. Asakusa is evidently one of the few places in Tokyo you still can. </p>
<p>So I liked it a lot. The rest of Tokyo sometimes felt like an entire city made out of Times Square. Asakusa, on the other hand, is wonderful mix of residential zones, old-style buildings, and modern-ADD-blinky commercial areas. </p>
<p>The second afternoon we were there, I went out to find a grocery store and randomly stumbled across this gigantic gate, called the Kaminarimon. I did also eventually find the grocery store with its $5-a-piece peaches. </p>
<p>I posted the picture to Twitter, and <a href="http://twitter.com/kakutani" target="_blank">Shintaro Kakutani</a> saw it, recognized it, and invited me to their meetup. So Sarah and I took the subway a few stops down on Tuesday night. I brought my laptop, prepared to hack, but the schedule was more oriented towards the &#8220;meet&#8221; part of meetup.</p>
<ul>
<li>19:45 Introductions</li>
<li>20:15 Drinking</li>
</ul>
<p>Here&#8217;s the aftermath:</p>
<table border=0>
<tr>
<td>
<div id="attachment_622" class="wp-caption alignleft" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-3.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-3-300x224.jpg" alt="Akira Matsuda, Shintaro Kakutani" title="asakusarb-a_matsuda-kakutani" width="300" height="224" class="size-medium wp-image-622" /></a><p class="wp-caption-text">Akira Matsuda &#038; Shintaro Kakutani</p></div></p>
<p><div id="attachment_621" class="wp-caption alignleft" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-4.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-4-300x224.jpg" alt="" title="beer-beer-beer" width="300" height="224" class="size-medium wp-image-621" /></a><p class="wp-caption-text">BEER BEER BEER MEAT BEER</p></div>
</td>
</tr>
<tr>
<td>
<div id="attachment_641" class="wp-caption alignleft" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-1.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-1-300x224.jpg" alt="" title="yakitori-3" width="300" height="224" class="size-medium wp-image-641" /></a><p class="wp-caption-text">You guessed it...</p></div></p>
<p><div id="attachment_640" class="wp-caption alignleft" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-2.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/yakitori-2-300x224.jpg" alt="" title="yakitori-4" width="300" height="224" class="size-medium wp-image-640" /></a><p class="wp-caption-text">MOAR BEER</p></div>
</td>
</tr>
</table>
<p>It was fantastic. I am told there is usually more hacking, but beer is a great social lubricant. I&#8217;m incredibly self-conscious about speaking Japanese, because I know I&#8217;m not very good. But somehow, when I have beer, I don&#8217;t care as much. </p>
<p>Aaron Patterson, in <a href="http://rubykaigi.tdiary.net/20100828.html#p16" target="_blank">his RubyKaigi talk</a>, said that people are interpreters with very forgiving parsers. As it turns out, he&#8217;s right! So even though I speak horrible Japanese, my meaning got through most of the time.</p>
<p>I&#8217;m eternally grateful to all the Asakusa.rb folks for not outright laughing at me whenever I opened my mouth and said something inappropriate or totally nonsensical. Which I&#8217;m sure I did often. <img src='http://www.sarahmei.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Sarah and I had such a good time, there and at RubyKaigi later in the week, that we decided to ask Asakusa.rb to be officially friendly with <a href="http://www.meetup.com/sfruby" target="_blank">SF Ruby</a>. Akira sort of had to say yes. After all, we gave him Ghiradelli chocolate.</p>
<div id="attachment_625" class="wp-caption alignright" style="width: 310px"><a href="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/asakusa-chocolate.jpg"><img src="http://www.sarahmei.com/blog/wp-content/uploads/2010/09/asakusa-chocolate-300x199.jpg" alt="" title="asakusa-chocolate" width="300" height="199" class="size-medium wp-image-625" /></a><p class="wp-caption-text">CHOCO</p></div>
<p>So now we are sister meetups, and Sarah and I are official members of Asakusa.rb (we&#8217;re even on their <a href="http://twitter.com/a_matsuda/asakusa-rb" target="_blank">twitter list</a>). Next time: hacking and beer together. Hopefully in <a href="http://rubyconf.org" target="_blank">New Orleans</a>.</p>
<p>Until then, ありがとうございます Asakusa.rb, for making me feel so welcome in a place that otherwise felt a little alien. Geek culture really does transcend the language barrier. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/09/01/asakusa-rb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Safe Facebooking</title>
		<link>http://www.sarahmei.com/blog/2010/07/25/safe-facebooking/</link>
		<comments>http://www.sarahmei.com/blog/2010/07/25/safe-facebooking/#comments</comments>
		<pubDate>Sun, 25 Jul 2010 23:48:21 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[paranoia]]></category>
		<category><![CDATA[rants]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=554</guid>
		<description><![CDATA[I have a Facebook account on which I have duly locked down the privacy controls (several times, it feels like). In theory, no one can get at my information unless we become Facebook friends. In practice, I&#8217;ve discovered, it&#8217;s another story entirely. After spending the better part of ten days, recently, integrating Facebook into another [...]]]></description>
			<content:encoded><![CDATA[<p>I have a Facebook account on which I have duly locked down the privacy controls (several times, it feels like). In theory, no one can get at my information unless we become Facebook friends.</p>
<p>In practice, I&#8217;ve discovered, it&#8217;s another story entirely. After spending the better part of ten days, recently, integrating Facebook into another website, I have new rules for how I use Facebook. I realize they sound a little tin-foil-hat-style crazy, so after the rules I&#8217;ll explain a bit about why I adopted them.<br />
<span id="more-554"></span></p>
<div id="attachment_561" class="wp-caption alignright" style="width: 268px"><a href="http://www.flickr.com/photos/8188326@N02/3248980358/" target="_blank"><img class="size-full wp-image-561" title="tin-foil-hat" src="http://www.sarahmei.com/blog/wp-content/uploads/2010/07/3248980358_3cc305c96a_o.jpg" alt="Image by CycleDog" width="258" height="300" /></a><p class="wp-caption-text">Image by CycleDog</p></div>
<p><strong>Rule 0: I&#8217;m not closing my Facebook account.</strong> I know a few people who have gotten off Facebook entirely, recently, but Facebook is the only place I&#8217;m in touch with my cousins who live out of state, my best friends from elementary school who are scattered to the wind, and my husband&#8217;s family who live on the east coast. These people aren&#8217;t going to get on Twitter, and I do want to hear about their lives.</p>
<p><strong>Rule 1: I always browse Facebook in a separate browser.</strong> If I&#8217;m doing my random web browsing in Firefox, then I open Facebook in Chrome (or less frequently, Safari). It&#8217;s not sufficient to open Facebook in a different window of the same browser, or a different tab of the same browser. It has to be a <em>different</em> browser. (If you only have one browser right now, you can <a href="http://www.mozilla.com/en-US/firefox/firefox.html" target="_blank">install Firefox here</a>, or <a href="http://www.google.com/chrome" target="_blank">Chrome over here</a>.)</p>
<p><strong>Rule 2: I make sure my non-Facebook browser has no residual FB cookies.</strong> I used to just leave a Facebook tab open while I browsed random web sites in other tabs, but that&#8217;s incredibly dangerous. If I am logged in to FB, any of those third-party sites could be silently collecting my Facebook information without notifying me. Once I decided to separate my browsing, I deleted all cookies in my non-Facebook browser. As long as I don&#8217;t log in to FB again in that browser, other sites won&#8217;t be able to access my Facebook information.</p>
<p><strong>Rule 3: I never browse anywhere else in the Facebook browser.</strong> I use Chrome for Facebook, so I don&#8217;t use Chrome for anything else. Any external links I want to click on from Facebook, I open in Firefox. This can be a pain in the ass because links on FB usually redirect you through another FB page. So in FB, I right click the link, select &#8220;Copy link location&#8221;, switch to Firefox, paste the link in, edit it to remove the Facebook prefix, and then hit return to go there.</p>
<p>In practice, I do use Chrome for other stuff, but I log out of Facebook and clear all my cookies first. Which leads to&#8230;</p>
<p><strong>Rule 4: I always log out of Facebook.</strong> They&#8217;ve hidden the log out option; it&#8217;s at the top right, the last option under &#8220;Account.&#8221; It&#8217;s not sufficient to close the Facebook window, or even to quit the browser you&#8217;re using for Facebook. In either case you leave behind a set of &#8220;logged in&#8221; FB cookies that other sites can read. I always explicitly log out of FB when I&#8217;m done.</p>
<p><strong>Rule 5:</strong> There is no rule 5.</p>
<p><strong>Rule 6: I never use Facebook to log in to another web site.</strong> Any web site can use Facebook as their log in system, instead of (or in addition to) letting visitors create accounts. Most of these sites are not officially affiliated with Facebook. It&#8217;s convenient to use FB for this, sometimes, instead of creating yet another username and password to remember.</p>
<p>But it&#8217;s also dangerous. When I authorize a site to use Facebook to log me in, that site can then access my name, my email address, all the schools I went to, all my employers past and present, my interests and hobbies, my pictures, my wall, my messages, and my friends. Among other things. And the site may hold on to that data, so even if I change it or hide it in Facebook, it&#8217;ll be in their database in perpetuity. Since the site isn&#8217;t affiliated with Facebook, it&#8217;s not bound by their privacy policy, so who knows what it will do with the information? It&#8217;s way better to just set up another account with a throw-away username and password.</p>
<h2>Feeling crazy yet?</h2>
<p>I am. Or maybe &#8220;paranoid&#8221; is a better word. Before I started working with the Facebook API, I had no idea how much information was available to third-party web sites. Things I don&#8217;t think of as public &#8211; including my email address &#8211; are available by default. </p>
<p>There are, of course, terms of service that those sites have agreed to, but nobody checks or audits them. It&#8217;s trivially easy to sign up for a developer account, create a Facebook application, stick some Javascript on a site, and start collecting the data of unwary FB members who visit you. </p>
<h2>The public/private dilemma</h2>
<p>Honestly, most of the information I have on Facebook is public knowledge anyway, including my email address. But it doesn&#8217;t sit well with me that FB lets third-party sites access information that non-friends can&#8217;t see. What else might they decide to share someday &#8211; the links I&#8217;ve clicked on? The groups I&#8217;ve visited but not joined? The exes I&#8217;ve searched for?</p>
<p>By keeping Facebook quarantined, I hope to contain the fallout of any future &#8220;experience enhancements.&#8221; </p>
<p>And now, if you&#8217;ll excuse me, I need to go use my hat to make dinner.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/07/25/safe-facebooking/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>RailsConf Slides &#8211; Beyond (No)SQL</title>
		<link>http://www.sarahmei.com/blog/2010/06/09/railsconf-slides/</link>
		<comments>http://www.sarahmei.com/blog/2010/06/09/railsconf-slides/#comments</comments>
		<pubDate>Wed, 09 Jun 2010 16:26:54 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[databases]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[railsconf]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=544</guid>
		<description><![CDATA[Preliminary slides for my RailsConf talk I&#8217;m giving this afternoon on conceptual tools for evaluating databases. Contains some profanity at the beginning. Slides are subject to change since I haven&#8217;t actually given the talk yet! Beyond (No)SQL View more presentations from Sarah Mei.]]></description>
			<content:encoded><![CDATA[<p>Preliminary slides for my RailsConf talk I&#8217;m giving this afternoon on conceptual tools for evaluating databases. <strong>Contains some profanity at the beginning.</strong> Slides are subject to change since I haven&#8217;t actually given the talk yet!<br />
<span id="more-544"></span></p>
<div style="width:425px" id="__ss_4453924"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/sarahmei/beyond-nosql" title="Beyond (No)SQL">Beyond (No)SQL</a></strong><object id="__sse4453924" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=beyond-no-sql-100609112040-phpapp02&#038;stripped_title=beyond-nosql" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse4453924" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=beyond-no-sql-100609112040-phpapp02&#038;stripped_title=beyond-nosql" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/sarahmei">Sarah Mei</a>.</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/06/09/railsconf-slides/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Outside-In BDD: How?!</title>
		<link>http://www.sarahmei.com/blog/2010/05/29/outside-in-bdd/</link>
		<comments>http://www.sarahmei.com/blog/2010/05/29/outside-in-bdd/#comments</comments>
		<pubDate>Sun, 30 May 2010 03:14:55 +0000</pubDate>
		<dc:creator>sarahmei</dc:creator>
				<category><![CDATA[ruby]]></category>
		<category><![CDATA[bdd]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[rspec]]></category>

		<guid isPermaLink="false">http://www.sarahmei.com/blog/?p=443</guid>
		<description><![CDATA[I use rspec on every project, and I&#8217;ve started adding cucumber to all my projects in the last few months. There&#8217;s lots of information out there about how to set up and use cucumber, but there isn&#8217;t much covering your developer workflow when you&#8217;re using these tools. How do you start, and how do you [...]]]></description>
			<content:encoded><![CDATA[<p>I use <a href="http://rspec.info/">rspec</a> on every project, and I&#8217;ve started adding <a href="http://cukes.info">cucumber</a> to all my projects in the last few months. There&#8217;s lots of information out there about how to set up and use cucumber, but there isn&#8217;t much covering your developer workflow when you&#8217;re using these tools.</p>
<p>How do you start, and how do you know you&#8217;re finished? What do you test, and where? These questions can be answered hundreds of different ways, but here&#8217;s my way.<br />
<span id="more-443"></span></p>
<h2>The first code I write: a feature</h2>
<p>As a developer, rather than a designer, I&#8217;m always tempted to start with unit tests and work out towards a cucumber feature (&#8220;inside-out&#8221; testing). But that approach gets me into no end of trouble. I usually end up writing and testing stuff on the model that I don&#8217;t ultimately need. Plus once I&#8217;m down in the weeds coding, I lose track of the big picture.</p>
<p>So I like to do outside-in testing instead. I start each story I get from <a href="http://pivotaltracker.com">tracker</a> with a cucumber feature that expresses how the PM will be able to accept it when I&#8217;m done. The feature helps me frame the problem properly, and focus on doing exactly what I need to make it work. Since I come back to it periodically while I&#8217;m coding, I keep focused on the higher-level goal. And finally &#8211; if I write it first, I can&#8217;t skip writing it once I&#8217;m done.</p>
<h2>Before we get going&#8230;</h2>
<p>There are certain types of tests I don&#8217;t write in this example (and in some cases, at all). Let&#8217;s get those out of the way so you don&#8217;t have to come up with a scathing comment at the bottom of the post.</p>
<ul>
<li><strong>Model tests.</strong> In this example, my model doesn&#8217;t do anything other than default ActiveRecord behavior, so it doesn&#8217;t need any tests. <strong>Don&#8217;t test rails internals.</strong> Once my model has custom behavior, it will have specs, too.</li>
<li><strong>View tests.</strong> I have no tests that verify that my markup is what I expect. That&#8217;s because they&#8217;re a waste of time. Yes, even with complex views. Verify behavior with cucumber tests, unit-test Javascript with <a href="http://github.com/pivotal/jasmine">jasmine</a>, and leave the rest to the humans. You&#8217;ll waste more developer time maintaining them than it would take humans to verify them. Verifiers are a whole lot cheaper than developers.</li>
<li><strong>Error case tests.</strong> In this example, there are no error cases. The model has no validations, and the table has no constraints. Once there are error cases, I generally put those in the model if I can, in the controller when I have to, and never in the cucumber tests. The latter is mostly a suite-speed consideration &#8211; cucumber tests run much more slowly than rspec. Cucumber&#8217;s great for for happy path tests; I leave the rest to rspec.</li>
</ul>
<p>Let&#8217;s get going!</p>
<h2>The first feature</h2>
<p>Say I&#8217;m doing a library app and the first story is &#8220;User can enter a new book into the system.&#8221; Before I write any other code, I write this feature:</p>
<pre lang="RUBY">
Feature: User manages books
  Scenario: User adds a new book
    Given I go to the new book page
    And I fill in "Name" with "War &#038; Peace"
    And I fill in "Description" with "Long Russian novel"
    When I press "Create"
    Then I should be on the book list page
    And I should see "War &#038; Peace"
</pre>
<h2>Starting the fail-fix cycle</h2>
<p>I run it using <code>cucumber features</code>, and it fails on the first line &#8211; <code>Given I go to the new book page</code> &#8211; because cucumber doesn&#8217;t know where the &#8220;new book page&#8221; is. So I add that to the cucumber paths helper.</p>
<pre lang="RUBY">
    when /the new book page/
      new_book_path
</pre>
<p>Now when I run cucumber, it fails because it can&#8217;t find <code>new_book_path</code>. So I add that to <code>routes.rb</code>:</p>
<pre lang="RUBY">
  map.resources :books, <img src='http://www.sarahmei.com/blog/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> nly => [:new]
</pre>
<p>Now when I run cucumber, it complains that it can&#8217;t find the BooksController. That means it&#8217;s time to dive down to rspec controller tests. </p>
<h2>My first spec experience</h2>
<p>I create <code>books_controller_spec.rb</code> in spec/controllers, and add a test for the <code>new</code> method:</p>
<pre lang="RUBY">
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper.rb'))
describe BooksController do
  describe "#new" do
    it "should be successful" do
      get :new
      response.should be_success
    end
  end
end
</pre>
<p>When I run this spec, it complains that there is no BooksController. Fixed:</p>
<pre lang="RUBY">
class BooksController < ApplicationController
end
</pre>
<p></code><br />
I re-run the spec and get "no action responded to new." So I add the <code>new</code> method.</p>
<pre lang="RUBY">
class BooksController < ApplicationController
  def new
  end
end
</pre>
<p>Now the spec passes! Time to check back with cucumber.</p>
<h2>Getting past the first line</h2>
<p>I read through my cucumber feature again:</p>
<pre lang="RUBY">
Feature: User manages books
  Scenario: User adds a new book
    Given I go to the new book page
    And I fill in "Name" with "War &#038; Peace"
    And I fill in "Description" with "Long Russian novel"
    When I press "Create"
    Then I should be on the book list page
    And I should see "War &#038; Peace"
</pre>
<p>Last time I ran it, it failed on the first line because it couldn't find the BooksController. This time, same location, but it says it can't find the view. So whiny! To placate it, I create an empty view called <code>new.html.erb</code> and run it again.</p>
<p>Now cucumber gets past line 1 (huzzah!!) and fails on line 2 (<code>And I fill in "Name" with "War &amp; Peace"</code>) with the message that it can't find a field called Name to fill in. So I add a standard rails form to the view.</p>
<pre lang="RUBY">
<%- form_for @book do |f| -%>
    <%= f.label :name %>
    <%= f.text_field :name %>
    <%= f.label :description %>
    <%= f.text_area :description %>
    <%= f.submit "Create" %>
<%- end -%>
</pre>
<p>Uh oh. Cucumber is mad at me because there is no <code>@book</code> object. Back to rspec for me!</p>
<h2>rspec: The Return</h2>
<p>In my controller's <code>new</code> method, I need to create a book object that the form will use. I first add a test for that in the controller spec:</p>
<pre lang="RUBY">
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper.rb'))
describe BooksController do
  describe "#new" do
    before do
      get :new
    end
    it "should be successful" do
      response.should be_success
    end
    it "should create a book object" do
      assigns(:book).should_not be_nil
    end
  end
end
</pre>
<p>This fails the right way - it says assigns(:book) is nil. So then I add the creation of the book object to the controller.</p>
<pre lang="RUBY">
class BooksController < ApplicationController
  def new
    @book = Book.new
  end
end
</pre>
<p>Now the spec fails, saying it can't find the Book class. It has a point - I haven't created the model yet. Fixed:</p>
<pre lang="RUBY">
class Book < ActiveRecord::Base
end
</pre>
<p>Now it fails saying it can't find the books table. So I write a migration that creates that.</p>
<pre lang="RUBY">
class CreateBooksTable < ActiveRecord::Migration
  def self.up
    create_table :books do |t|
      t.string :name
      t.text :description
    end
  end
  def self.down
    drop_table :books
  end
end
</pre>
<p>Once I do <code>rake db:migrate</code> and <code>rake db:test:prepare</code>, I re-run my controller spec....and it passes! Back to the cucumber feature!</p>
<h2>Cucumber...again.</h2>
<p>In our last episode, cucumber was visibly annoyed because there was no <code>@book</code> object for the form to operate on. I run it again to see if it's still sulking.</p>
<p>Yep. This time it tells me that it can't find books_path. <code>form_for</code> tries to submit to the create path by default, which I haven't added yet. I add it to the routes.</p>
<pre lang="RUBY">
  map.resources :books, <img src='http://www.sarahmei.com/blog/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> nly => [:new, :create]
</pre>
<p>This time, when I run cucumber, it gets through the first three lines (woo hoo!) and fails on the 4th, saying no action responded to create. Back to the rspec-cave, batman!</p>
<h2>rspec: The Sequel to The Return</h2>
<p>I add a controller spec for the <code>create</code> method.</p>
<pre lang="RUBY">
  describe "#create" do
    it "should create a new book" do
      post :create, "book" => {"name" => "Jane Eyre", "description" => "Something Victorian"}
      assigns(:book).should_not be_nil
      assigns(:book).name.should == "Jane Eyre"
    end
  end
</pre>
<p>When I run it, I get the same message as in cucumber: no action responded to create. So I create the create:</p>
<pre lang="RUBY">
class BooksController < ApplicationController
  def new
    @book = Book.new
  end
  def create
  end
end
</pre>
<p>Now when I re-run the spec, it fails saying that assigns(:book) is nil, which makes sense. I put in the guts of <code>create</code> to make that pass.</p>
<pre lang="RUBY">
  def create
    @book = Book.new(params[:book])
    @book.save
  end
</pre>
<p>Now rspec passes! Back to cucumber. </p>
<h2>So...cucumber. We meet again.</h2>
<p>When I re-run the feature, it says I'm missing a template for create, which is correct. However, in this case, I don't want to make a template for create - I want to redirect to the book list page. So once again, I'm back with rspec.</p>
<h2>rspec: Back so soon?</h2>
<p>I add that expectation to the controller spec for <code>create</code>.</p>
<pre lang="RUBY">
    it "should redirect to the book list page" do
      response.should redirect_to books_path
    end
</pre>
<p>It fails saying there's no redirect. So to make it pass, I add a redirect to the controller code.</p>
<pre lang="RUBY">
  def create
    @book = Book.new(params[:book])
    if @book.save
      redirect_to books_path
    end
  end
</pre>
<p>Now my controller specs pass. Cucumber, I'm coming for you!</p>
<h2>Oh, you again.</h2>
<p>Last time, we got through the first 3 lines of the feature and failed on line 4 (<code>When I press "Create"</code>). When I run it this time, it gets through the same 3 lines and then fails in the same place again, saying that no action responded to index. I add <code>index</code> to the routes.</p>
<pre lang="RUBY">
  map.resources :books, <img src='http://www.sarahmei.com/blog/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> nly => [:new, :create, :index]
</pre>
<p>I re-run the feature and get the same error message. WTF, cucumber?! It turns out that rails' implementation of REST uses the same path helper for create and index, so the path helper for <code>index</code> already exists, even though the method does not. A little strange, I know. But we need an <code>index</code> method, so it's back to rspec.</p>
<h2>rspec: For the first time, for the last time...</h2>
<p>I write a spec for the <code>index</code> method.</p>
<pre lang="RUBY">
  describe "#index" do
    it "should be successful" do
      get :index
      response.should be_success
    end
  end
</pre>
<p>I still get no action responded to index. So l add the method in BooksController, empty to start.</p>
<pre lang="RUBY">
  def index
  end
</pre>
<p>Specs pass, back to cucumber!</p>
<h2>How can I miss you if you won't go away?</h2>
<p>Cucumber tells me there's no template for index. So I create an empty one, and re-run. This run, for the first time, I pass line 4 (yaaaaay) but then it fails on line 5 (<code>Then I should be on the book list page</code>) because it can't figure out what I mean by "the book list page." That goes in the cucumber path helper.</p>
<pre lang="RUBY">
    when /the book list page/
      books_path
</pre>
<p>OMG five out of six steps pass! Now cucumber says it can't find "War &amp; Peace" on the page, so let's make the index view list the existing books. Back to rspec...</p>
<h2>Don't go away mad...just go away.</h2>
<p>I add the following <code>it</code> block to the spec for <code>index</code>.</p>
<pre lang="RUBY">
    it "should assign a list of existing books" do
      Book.create!(:name => "Endymion", :description => "weird")
      get :index
      assigns(:books).should_not be_nil
      assigns(:books).length.should == 1
    end
</pre>
<p>It fails because I'm not creating @books in the controller, so I fix that.</p>
<pre lang="RUBY">
  def index
    @books = Book.all
  end
</pre>
<p>Now the specs pass - back to cucumber. </p>
<h2>We really have to stop seeing each other like this.</h2>
<p>Cucumber still says it can't find War &amp; Peace, because I haven't added printing out the books to the index view. I'll fix that.</p>
<pre lang="RUBY">
<%- @books.each do |book| -%>
    <strong><%= h book.name %></strong>
    <%= h book.description %>
<%- end -%>
</pre>
<p>Re-run cucumber and ... ta-da! The feature passes! I've done everything I need to call the story done. I have the minimum amount of code I need, because all the code I wrote was driven by the feature. Story: <strong>delivered</strong>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.sarahmei.com/blog/2010/05/29/outside-in-bdd/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
	</channel>
</rss>

