<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>This is a tumblr. I don’t know what it is for.</description><title>mlcastle</title><generator>Tumblr (3.0; @mlcastle)</generator><link>http://tumble.mlcastle.net/</link><item><title>The Five Boro Bike Tour this morning was fine (though apparently not if you weren&amp;#8217;t in it),...</title><description>&lt;p&gt;The Five Boro Bike Tour this morning &lt;a href="http://app.strava.com/rides/8000101"&gt;was fine&lt;/a&gt; (though &lt;a href="https://twitter.com/n8han/status/199273911999930369"&gt;apparently not&lt;/a&gt; if you weren&amp;#8217;t in it), but the best moment was the fleeting one after the ride, when hundreds of cyclists at a time got off the ferry and momentarily outnumbered cars in lower Manhattan, not interfering with but &lt;em&gt;becoming&lt;/em&gt; the traffic of Nieuw-Amsterdam. This is what I want New York to look like every day; motorists, as far as I&amp;#8217;m concerned, can keep the BQE.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/22557848052</link><guid>http://tumble.mlcastle.net/post/22557848052</guid><pubDate>Sun, 06 May 2012 21:00:00 -0400</pubDate><category>new york</category><category>bikenyc</category><category>bike</category><category>5bbt</category></item><item><title>I gave a brief talk on how Android uses images, and how to deal...</title><description>&lt;img src="http://25.media.tumblr.com/tumblr_m2yhrsXUOU1roioaqo1_r1_500.png"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;I gave a brief talk on how Android uses images, and how to deal with the variety of device sizes and resolutions in the Android ecosystem. I made sure to give the talk as boring a title as possible.&lt;/p&gt;

&lt;p&gt;The target audience was a group of experienced web developers just starting to learn about mobile development, mostly focusing (except for this “guest lecture”) on iOS. &lt;a href="http://assets.vermicel.li/android-resources.pdf"&gt;Have a look at my slides&lt;/a&gt; if you like.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/21678571233</link><guid>http://tumble.mlcastle.net/post/21678571233</guid><pubDate>Mon, 23 Apr 2012 19:41:00 -0400</pubDate><category>android</category><category>resources</category><category>images</category><category>presentation</category><category>pdf</category></item><item><title>Why gender parity in computing matters</title><description>&lt;a href="https://www.nytimes.com/2012/04/03/science/giving-women-the-access-code.html?hpw=&amp;pagewanted=all"&gt;Why gender parity in computing matters&lt;/a&gt;: &lt;p&gt;&lt;blockquote&gt;Dr. Klawe and others say the underrepresentation of women in the field is detrimental in a larger sense. Computer science, they say, is as vital to propelling society forward in the digital era as mechanical engineering was in the industrial age.&lt;/blockquote&gt;&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/20406623808</link><guid>http://tumble.mlcastle.net/post/20406623808</guid><pubDate>Tue, 03 Apr 2012 09:26:02 -0400</pubDate></item><item><title>Ride Report: Manhattan to Staten Island via George Washington and Bayonne Bridges</title><description>&lt;p&gt;I &lt;a href="http://app.strava.com/rides/5720036" title="GPS trace and other ride data on Strava"&gt;rode&lt;/a&gt; this weekend from my home in lower Manhattan to Staten Island. The only bike-accessible route for doing this involves riding up Manhattan (the &lt;a href="https://secure.flickr.com/photos/mlcastle/7015320567/"&gt;cherry blossoms&lt;/a&gt; were in bloom!), across the George Washington Bridge, down through New Jersey along the west bank of the Hudson River, and across the Bayonne Bridge to Staten Island. (Conversely, if you&amp;#8217;re actually interested for some reason in &lt;em&gt;going to Staten Island&lt;/em&gt;, it&amp;#8217;s much quicker just to take the ferry.)&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m1okkjbH1x1r6lj47.png" alt="map"/&gt;&lt;/p&gt;

&lt;p&gt;A friend then &lt;a href="https://twitter.com/#!/dearnate/status/185325045189591040"&gt;asked&lt;/a&gt; how the ride was; in case my advice is useful to anyone else, here it is.&lt;/p&gt;

&lt;p&gt;The ride is totally doable, but&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Much of the route is on this weird walkway along the Hudson which is 
mostly bricked rather than asphalted. I think bikes are allowed there 
(and anyhow no one tried to stop me) but am not totally certain. It 
seems like it is also still being constructed in parts. Anyway you 
shouldn&amp;#8217;t expect to go particularly fast on it, if that&amp;#8217;s a concern.&lt;/li&gt;
&lt;li&gt;The walkway disappears and reappears, and unless you have a map, you 
might not know from the road when it is reappearing, as you often have 
to ride through a parking lot or something to get there.&lt;/li&gt;
&lt;li&gt;When you have to be on a road, the road is full of New Jersey 
drivers.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://g.co/maps/2x52d"&gt;This&lt;/a&gt; is apparently New Jersey&amp;#8217;s idea of a designated signed but not 
striped &amp;#8220;bike route.&amp;#8221;&lt;/li&gt;
&lt;li&gt;As far as I could tell, the easiest way to get around the Hoboken 
train station is to get off the bike and just walk it through the 
station.&lt;/li&gt;
&lt;li&gt;There are stairs on the NJ side of the Bayonne Bridge, and 
technically you&amp;#8217;re supposed to walk your bike across, but seriously I 
saw one pedestrian the whole way across and it wasn&amp;#8217;t a huge problem to 
avoid hitting him while riding.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Good news:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Except for the approach to and from the GWB, it&amp;#8217;s pretty much flat&lt;/li&gt;
&lt;li&gt;There&amp;#8217;s a &lt;a href="http://g.co/maps/m82mg"&gt;very nice segragated cycleway&lt;/a&gt; for four whole blocks of 
Hoboken&lt;/li&gt;
&lt;li&gt;The southern part of Jersey City and all of Bayonne and S.I. are on 
normal roads with cars on them, but reasonably narrow roads which felt 
reasonably safe.&lt;/li&gt;
&lt;li&gt;Free ferry back to Manhattan.&lt;/li&gt;
&lt;/ul&gt;</description><link>http://tumble.mlcastle.net/post/20150604682</link><guid>http://tumble.mlcastle.net/post/20150604682</guid><pubDate>Thu, 29 Mar 2012 22:13:00 -0400</pubDate><category>bike</category><category>staten island</category><category>new jersey</category><category>bayonne</category><category>ride</category></item><item><title>The Tumblr of the moment to follow is geraldoinahoodie. Have a...</title><description>&lt;img src="http://24.media.tumblr.com/tumblr_m1coejpprP1rssd6zo1_400.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;The Tumblr of the moment to follow is &lt;a href="http://geraldoinahoodie.tumblr.com/"&gt;geraldoinahoodie&lt;/a&gt;. Have a look if you like to look at hypocritical Fox News talking heads.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/19813114772</link><guid>http://tumble.mlcastle.net/post/19813114772</guid><pubDate>Fri, 23 Mar 2012 22:18:13 -0400</pubDate></item><item><title>makingmeetup:

We noticed some interesting contrasts in the...</title><description>&lt;img src="http://25.media.tumblr.com/tumblr_m0zzgmMg5E1qerlgao1_500.png"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;&lt;a href="http://making.meetup.com/post/19416945851/weekend-app-users" class="tumblr_blog"&gt;makingmeetup&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;We noticed some interesting contrasts in the weekday usage of Meetup on different platforms.&lt;/p&gt;

&lt;p&gt;Saturday is the biggest day for Meetups to take place. Since people are away from their desks, they use our &lt;a href="https://play.google.com/store/apps/details?id=com.meetup"&gt;mobile&lt;/a&gt; &lt;a href="http://itunes.apple.com/us/app/meetup/id375990038?mt=8"&gt;apps&lt;/a&gt; and mobile web site to find their way — but visits to &lt;a href="http://www.meetup.com/"&gt;Meetup.com&lt;/a&gt; fall off.&lt;/p&gt;

&lt;p&gt;On Monday, however, people return to their desks and start planning their week on the website, while usage of the mobile platforms is lower.&lt;/p&gt;

&lt;p&gt;(Data is from the past nine weeks, normalized so that the average day for each type of access is equal to 1.0.)&lt;/p&gt;&lt;/blockquote&gt;</description><link>http://tumble.mlcastle.net/post/19417496195</link><guid>http://tumble.mlcastle.net/post/19417496195</guid><pubDate>Fri, 16 Mar 2012 18:43:43 -0400</pubDate></item><item><title>"When you transfer Subscriber Content to Tumblr through the Services, you give Tumblr a..."</title><description>“&lt;p&gt;When you transfer Subscriber Content to Tumblr through the Services, you give Tumblr a non-exclusive, worldwide, royalty-free, sublicensable, transferable right and license to use, host, store, cache, reproduce, publish, display (publicly or otherwise), perform (publicly or otherwise), distribute, transmit, modify, adapt (including, without limitation, in order to conform it to the requirements of any networks, devices, services, or media through which the Services are available), and create derivative works of (including, without limitation, by Reblogging, as defined below), such Subscriber Content.&lt;/p&gt;

&lt;p&gt;Note also that this license to your Subscriber Content continues even if you stop using the Services…&lt;/p&gt;”&lt;br/&gt;&lt;br/&gt; - &lt;em&gt;&lt;a href="https://github.com/tumblr/policy/blob/38f37e7680146442b3eab79080a73e3a896d8ad0/terms-of-service.txt#L139"&gt;Tumblr Terms of Service&lt;/a&gt;&lt;/em&gt;</description><link>http://tumble.mlcastle.net/post/18685272763</link><guid>http://tumble.mlcastle.net/post/18685272763</guid><pubDate>Sat, 03 Mar 2012 16:56:29 -0500</pubDate><category>legalese</category><category>tumblr</category><category>tos</category></item><item><title>Android Photo Spy</title><description>&lt;p&gt;The &lt;em&gt;New York Times&lt;/em&gt; published &lt;a href="http://bits.blogs.nytimes.com/2012/02/28/tk-ios-gives-developers-access-to-photos-videos-location/?hp"&gt;an article&lt;/a&gt; noting that iOS applications can access your photo library if they request permission to know your device&amp;#8217;s location. They noted that “Google declined to comment on how its Android operating system for mobile devices handles this issue.”&lt;/p&gt;

&lt;p&gt;Of course Google declined to comment: Android apps can access your photos with &lt;strong&gt;no special permissions whatsoever&lt;/strong&gt;. This is, apparently, &lt;em&gt;by design&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So, I wrote a quick demonstration app which gets a list of your photos (and their locations, if available) and lets you look at the photos if you like:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m059pf6vbe1r6lj47.png" alt="screenshot"/&gt;&lt;/p&gt;

&lt;p&gt;Just to say it again: the app requires &lt;strong&gt;no special permissions whatsoever&lt;/strong&gt;. In fact, because it doesn&amp;#8217;t have &lt;a href="http://developer.android.com/reference/android/Manifest.permission.html#INTERNET"&gt;&lt;code&gt;INTERNET&lt;/code&gt; permission&lt;/a&gt;, it can&amp;#8217;t send your photos anywhere and so is not a true “spy,” but of course almost all real apps have &lt;code&gt;INTERNET&lt;/code&gt; permission, and so this is not a limitation in actual practice.&lt;/p&gt;

&lt;p&gt;You can download the app and look at the source code &lt;a href="https://github.com/mlc/android-photo-spy"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For further Android privacy leaks, see packetlss&amp;#8217;s &lt;a href="https://github.com/packetlss/android-locdump"&gt;android-locdump&lt;/a&gt; project, which reads your phone&amp;#8217;s location cache and which I used to make &lt;a href="http://www.flickr.com/photos/mlcastle/5641787212/"&gt;a map&lt;/a&gt; of my travels. (This map was also &lt;a href="http://www.spiegel.de/netzwelt/netzpolitik/0,1518,758775,00.html"&gt;published&lt;/a&gt; by &lt;em&gt;Spiegel Online&lt;/em&gt;.)&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/18493500035</link><guid>http://tumble.mlcastle.net/post/18493500035</guid><pubDate>Wed, 29 Feb 2012 08:58:05 -0500</pubDate><category>android</category><category>spyware</category></item><item><title>HTML-like Tags in Android String Resources</title><description>&lt;p&gt;It&amp;#8217;s not a secret that you can put some HTML-like tags in Android &lt;a href="http://developer.android.com/guide/topics/resources/string-resource.html"&gt;string resources&lt;/a&gt; files. The &lt;a href="http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling"&gt;documentation&lt;/a&gt; lists only three such supported tags: &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;u&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, reading the source code reveals that some other tags are also supported! According to the Android source code at &lt;code&gt;android.content.res.StringBlock#applyStyles&lt;/code&gt;, the complete list of supported tags is:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; creates boldface text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; creates italicized text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;u&amp;gt;&lt;/code&gt; creates an underline&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;tt&amp;gt;&lt;/code&gt; creates text in a monospaced font&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;big&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;small&amp;gt;&lt;/code&gt; increase and decrease the font size&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;sub&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;super&amp;gt;&lt;/code&gt; create superscripts and subscripts&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;strike&amp;gt;&lt;/code&gt; creates a strikethrough&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; creates a paragraph with a bullet next to it&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt; creates a span with &lt;code&gt;&lt;a href="http://developer.android.com/reference/android/text/TextUtils.TruncateAt.html#MARQUEE"&gt;TruncateAt.MARQUEE&lt;/a&gt;&lt;/code&gt; set.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;font&amp;gt;&lt;/code&gt; tag supports a few attributes:      
&lt;ul&gt;&lt;li&gt;&lt;code&gt;height&lt;/code&gt; accepts an integer and sets the &lt;a href="http://developer.android.com/reference/android/text/style/LineHeightSpan.WithDensity.html"&gt;line height&lt;/a&gt; accordingly&lt;/li&gt;
&lt;li&gt;&lt;code&gt;size&lt;/code&gt; accepts an integer, setting the size of the enclosed text in &lt;a href="http://developer.android.com/guide/topics/resources/more-resources.html#Dimension"&gt;dip&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fgcolor&lt;/code&gt; and &lt;code&gt;bgcolor&lt;/code&gt; set the foreground and background color. Note that these attributes accept only integer arguments, so you need to say “&lt;code&gt;#ff00ff00&lt;/code&gt;”, not “&lt;code&gt;green&lt;/code&gt;”.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag accepts the &lt;code&gt;href&lt;/code&gt; attribute and creates a &lt;a href="http://developer.android.com/reference/android/text/style/URLSpan.html"&gt;link&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;annotation&amp;gt;&lt;/code&gt; tag accepts arbitrary attributes, creating arbitrary &lt;code&gt;&lt;a href="http://developer.android.com/reference/android/text/Annotation.html"&gt;Annotation&lt;/a&gt;&lt;/code&gt; spans.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Don&amp;#8217;t be afraid to use the source or to experiment in order to get further details.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17700903735</link><guid>http://tumble.mlcastle.net/post/17700903735</guid><pubDate>Thu, 16 Feb 2012 00:54:46 -0500</pubDate></item><item><title>makingmeetup:

We’re refactoring some of the user interface in...</title><description>&lt;img src="http://24.media.tumblr.com/tumblr_lyu10ygrff1qerlgao1_500.png"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;&lt;a href="http://making.meetup.com/post/16985557629/were-refactoring-some-of-the-user-interface-in" class="tumblr_blog"&gt;makingmeetup&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;We’re refactoring some of the user interface in our Android app. As you can see, we want event comments to feel more like a conversation, so we’re making them feel like the native Android Messaging app.&lt;/p&gt;

&lt;p&gt;As we do this, we’re trying to take some of the new Android ICS &lt;a href="http://developer.android.com/design/index.html"&gt;design guidance&lt;/a&gt; into account; you can see in particular the &lt;a href="http://developer.android.com/design/patterns/actionbar.html"&gt;action bar&lt;/a&gt; pattern here. We’re using &lt;a href="http://actionbarsherlock.com/"&gt;Action Bar Sherlock&lt;/a&gt; so that we remain compatible with Android 2.&lt;i&gt;x&lt;/i&gt; devices.&lt;/p&gt;

&lt;p&gt;And, finally, the reason we have two screenshots above is that we’re taking care to use UI elements that will be familiar to our users, depending on the version of the Android platform that they’re using. In particular, the share icon, add-a-comment text field, and send button are set to match the usual platform style: Gingerbread on the left and Ice Cream Sandwich on the right.&lt;/p&gt;&lt;/blockquote&gt;</description><link>http://tumble.mlcastle.net/post/16985695378</link><guid>http://tumble.mlcastle.net/post/16985695378</guid><pubDate>Fri, 03 Feb 2012 14:41:48 -0500</pubDate></item><item><title>Tumblrs, how do they work?</title><description>&lt;p&gt;Tumblrs, how do they work?&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/16966574023</link><guid>http://tumble.mlcastle.net/post/16966574023</guid><pubDate>Fri, 03 Feb 2012 02:10:33 -0500</pubDate></item><item><title>Purchasing 4′33″ Outside the UK</title><description>&lt;p&gt;The &lt;a href="http://www.catm.co.uk"&gt;Cage Against the Machine&lt;/a&gt; campaign aims to make John Cage’s &lt;a href="http://en.wikipedia.org/wiki/4%E2%80%B233%E2%80%B3"&gt;&lt;cite&gt;4′33″&lt;/cite&gt;&lt;/a&gt; the number one single in the &lt;abbr title="United Kingdom"&gt;UK&lt;/abbr&gt; this Christmas. This is a campaign which I support wholeheartedly, and I wished to participate.&lt;/p&gt;
        &lt;p&gt;However, UK digital music vendors limit sales to computers with IP addresses actually in the UK. Here is a summary of the steps I took to purchase the album anyway. (Note that the operative word here is “summary”; some degree of technical skill may be needed to follow this procedure.)&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: Even though this post explains how to &lt;em&gt;purchase&lt;/em&gt; music, not how to &lt;em&gt;pirate&lt;/em&gt; it, it is entirely possible—maybe even likely—that following the steps described here violates some terms of service agreement or is otherwise illegal.&lt;/p&gt;
        &lt;h2&gt;Procedure&lt;/h2&gt;
        &lt;p&gt;Here are the steps I followed. You may need to vary them somewhat to suit your taste.&lt;/p&gt;
        &lt;ol&gt;&lt;li&gt;Download and install &lt;a href="http://torproject.org"&gt;Tor&lt;/a&gt; if you haven’t done so already. Set it up in such a way that you can browse the web anonymously. (Note that steps taken later in this document will completely break your anonymity.)&lt;/li&gt;
                &lt;li&gt;Use &lt;a href="http://www.torproject.org/projects/vidalia.html.en"&gt;Vidalia&lt;/a&gt;, &lt;a href="http://torstatus.blutmagie.de"&gt;Tor Status&lt;/a&gt;, or some other means to gather the fingerprints of a few reasonably fast exit nodes in the UK.&lt;/li&gt;
                &lt;li&gt;Edit your &lt;code&gt;torrc&lt;/code&gt; file to contain the following lines, and restart tor to make your changes take effect.&lt;br/&gt;&lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Variable"&gt;ExitNodes&lt;/span&gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;fingerprint1&lt;/span&gt;, &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;fingerprint2&lt;/span&gt;, ...
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; &lt;span class="Variable"&gt;StrictExitNodes&lt;/span&gt; &lt;span class="Constant"&gt;1&lt;/span&gt;&lt;/pre&gt;
        &lt;/div&gt;
        &lt;/li&gt;
                &lt;li&gt;Have your browser use tor, and verify that your HTTP connections are indeed coming from the UK.&lt;/li&gt;
                &lt;li&gt;Visit 7digital’s &lt;a href="http://www.7digital.com/artists/john-cage/cage-against-the-machine"&gt;Cage Against the Machine&lt;/a&gt; page and follow the steps there to purchase the single or the 8-track collection of remixes.&lt;/li&gt;
                &lt;li&gt;You can sign up for an account using your usual contact details. On the payment page, make sure to use a credit card. It’s apparently okay if you leave the country on “United Kingdom”; just enter your US ZIP code (or other foreign postal code) in the postcode box. Do not attempt to pay with PayPal—logging in to PayPal through tor will apparently cause your account to be locked.&lt;/li&gt;
                &lt;li&gt;After you have paid, you will be taken to a page with links to download your purchase. After you have arrived at this page, but before you click on the download link, disconnect your browser from tor.&lt;/li&gt;
                &lt;li&gt;Now you can download the files without tor, conserving the bandwidth of the tor network and allowing your download to complete in a reasonable amount of time. (You’ve already completely destroyed your anonymity by providing your credit card number.)&lt;/li&gt;
                &lt;li&gt;Remove the lines you added to your &lt;code&gt;torrc&lt;/code&gt; and restart tor again, so that you will use any nodes in the network next time you use tor.&lt;/li&gt;
        &lt;/ol&gt;</description><link>http://tumble.mlcastle.net/post/17704287465</link><guid>http://tumble.mlcastle.net/post/17704287465</guid><pubDate>Mon, 13 Dec 2010 14:25:00 -0500</pubDate><category>4′33″</category><category>music</category><category>commerce</category></item><item><title>Cooking Roasted Vermicelli</title><description>&lt;p&gt;When I began freelancing and formed a company, I called it “Roasted Vermicelli” more-or-less at random: I had previously purchased the domain name &lt;code&gt;vermicel.li&lt;/code&gt;, and sticking the word “roasted” in front was required to make the business name unique within New York State.&lt;/p&gt;
        &lt;p&gt;After some years of this—plus a blog tagline which promises musings on pasta but has yet to deliver any—when I cooked dinner the other day, it was time to cook using some roasted vermicelli.&lt;/p&gt;
        &lt;h2&gt;Roasting&lt;/h2&gt;
        &lt;p&gt;The actual process of roasting the vermicelli is quite simple: heat a little bit of fat (oil or &lt;a href="http://en.wikipedia.org/wiki/Ghee"&gt;ghee&lt;/a&gt;) in a pan, put the uncooked vermicelli in, and toast for a few minutes, stirring constantly, then remove from the heat. The result looks something like this:&lt;br/&gt;&lt;a href="http://www.flickr.com/photos/mlcastle/4819948120/" title="Roasted Vermicelli by mlcastle, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4141/4819948120_779a563303.jpg" width="500" height="335" alt="Roasted Vermicelli"/&gt;&lt;/a&gt;&lt;/p&gt;
        &lt;p style="clear: both;"&gt;If this is too much work, it’s probably just as well to skip making dinner altogether, but my &lt;a href="http://www.dualspecialty.com/"&gt;local Indian market&lt;/a&gt; does stock pre-roasted vermicelli.&lt;/p&gt;
        &lt;h2&gt;Then What?&lt;/h2&gt;
        &lt;p&gt;There are a few different recipes one can then make using the roasted vermicelli. &lt;a href="http://www.chef2chef.net/recipes/recipe-archive/26/141532.shtml"&gt;Semia payasam&lt;/a&gt; is a dessert with condensed milk, sugar, and nuts. I, however, made vermicelli upma, which is a savory dish with lentils, nuts, and vegetables. Apparently it’s normally served for breakfast, but I ate it for dinner and didn’t get struck down by any gods of propriety.&lt;/p&gt;
        &lt;p&gt;The &lt;a href="http://showmethecurry.com/odds-ends/sevia-vermicelli-upma-south-indian-recipe.html"&gt;recipe I followed&lt;/a&gt; is from a site called “Show Me the Curry”, and the result looked like this:&lt;br/&gt;&lt;a href="http://www.flickr.com/photos/mlcastle/4819948944/" title="Vermicelli Upma by mlcastle, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4096/4819948944_94ffa32cea.jpg" width="500" height="335" alt="Vermicelli Upma"/&gt;&lt;/a&gt;&lt;/p&gt;
        &lt;p style="clear: both;"&gt;It tasted good, and I was able to take some photographs for my next batch of &lt;a href="http://us.moo.com/en/products/business_cards.php"&gt;business cards&lt;/a&gt;. A successful project!&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17704287357</link><guid>http://tumble.mlcastle.net/post/17704287357</guid><pubDate>Mon, 26 Jul 2010 15:28:00 -0400</pubDate><category>food</category><category>pasta</category><category>vermicelli</category><category>upma</category></item><item><title>Queueing async_observer tasks from the command line</title><description>&lt;p&gt;For one of our projects, we’re using &lt;a href="http://kr.github.com/beanstalkd/"&gt;&lt;code&gt;beanstalkd&lt;/code&gt;&lt;/a&gt; in combination with the &lt;a href="http://async-observer.rubyforge.org/"&gt;&lt;code&gt;async_observer&lt;/code&gt;&lt;/a&gt; plugin for Rails to run slow tasks in the background.&lt;/p&gt;
        &lt;p&gt;This is a nice setup. Although we’re not using all of its more advanced features, &lt;code&gt;beanstalkd&lt;/code&gt; is a very powerful and efficient queueing system, and &lt;code&gt;async_observer&lt;/code&gt; provides a clean integration with Rails. (On other projects, I’ve happily used &lt;a href="http://github.com/tobi/delayed_job"&gt;&lt;code&gt;delayed_job&lt;/code&gt;&lt;/a&gt; for similar purposes, but it was not suitable for this project due to its heavy dependency on a &lt;code&gt;ActiveRecord&lt;/code&gt; and a SQL database. By contrast, &lt;code&gt;async_observer&lt;/code&gt;’s support for &lt;code&gt;ActiveRecord&lt;/code&gt; was quite easy to rip out.)&lt;/p&gt;
        &lt;p&gt;However, we would like to be able to queue &lt;code&gt;async_observer&lt;/code&gt; jobs from cron or the command line. Why launch a new process with a copy of the Rails stack when there’s already a long-lived one running? So, I wrote a ruby script with minimal dependencies which can stick tasks in &lt;code&gt;beanstalkd&lt;/code&gt; for our &lt;code&gt;async_observer&lt;/code&gt; worker to run.&lt;/p&gt;
        &lt;h3&gt;The Script&lt;/h3&gt;
        &lt;p&gt;Here’s the script I wrote. Stick it in &lt;code&gt;RAILS_ROOT/script/beanstalk-queue&lt;/code&gt;.&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;!/usr/bin/env ruby&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; == Synopsis&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; This is a replacement for script/runner which avoids loading up the&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; rails environment and instead queues things for async_observer to&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; do. Note that, unlike script/runner, a filename is not accepted; you&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; must provide the code to run on the command-line.&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; == Usage&lt;/span&gt;
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; beanstalk-queue [OPTION] ... 'CODE'&lt;/span&gt;
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; -d DELAY:&lt;/span&gt;
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;   amount of delay before the job is run (default 0 seconds)&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; -e ENVIRONMENT:&lt;/span&gt;
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;   set the rails environment (falls back to ENV['RAILS_ENV'] or&lt;/span&gt;
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;   'development' if unset)&lt;/span&gt;
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; -p PRIORITY:&lt;/span&gt;
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;   set the job priority (default 65536)&lt;/span&gt;
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; -t TTR:&lt;/span&gt;
&lt;span class="line-numbers"&gt;  26 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;   set the job's time to run (default 120 seconds)&lt;/span&gt;
&lt;span class="line-numbers"&gt;  27 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  28 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; -v, --verbose:&lt;/span&gt;
&lt;span class="line-numbers"&gt;  29 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;   be verbose&lt;/span&gt;
&lt;span class="line-numbers"&gt;  30 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  31 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; -h, --help:&lt;/span&gt;
&lt;span class="line-numbers"&gt;  32 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;   display this help&lt;/span&gt;
&lt;span class="line-numbers"&gt;  33 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  34 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt; CODE: the ruby code to be called asynchronously&lt;/span&gt;
&lt;span class="line-numbers"&gt;  35 &lt;/span&gt; &lt;span class="Comment"&gt;&lt;span class="Comment"&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  36 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  37 &lt;/span&gt; &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;rubygems&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  38 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  39 &lt;/span&gt; &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;beanstalk-client&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  40 &lt;/span&gt; &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;getoptlong&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  41 &lt;/span&gt; &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;rdoc/usage&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  42 &lt;/span&gt; &lt;span class="Keyword"&gt;require&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;yaml&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  43 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  44 &lt;/span&gt; &lt;span class="Variable"&gt;RAILS_ROOT&lt;/span&gt; &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;expand_path&lt;/span&gt;(&lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;join&lt;/span&gt;(&lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;dirname&lt;/span&gt;(&lt;span class="Variable"&gt;__FILE__&lt;/span&gt;), &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;..&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;))
&lt;span class="line-numbers"&gt;  45 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  46 &lt;/span&gt; opts &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;GetoptLong&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(
&lt;span class="line-numbers"&gt;  47 &lt;/span&gt;   [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;--help&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-h&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Support"&gt;GetoptLong&lt;/span&gt;::&lt;span class="Entity"&gt;NO_ARGUMENT&lt;/span&gt;],
&lt;span class="line-numbers"&gt;  48 &lt;/span&gt;   [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-d&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Support"&gt;GetoptLong&lt;/span&gt;::&lt;span class="Entity"&gt;REQUIRED_ARGUMENT&lt;/span&gt;],
&lt;span class="line-numbers"&gt;  49 &lt;/span&gt;   [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-e&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Support"&gt;GetoptLong&lt;/span&gt;::&lt;span class="Entity"&gt;REQUIRED_ARGUMENT&lt;/span&gt;],
&lt;span class="line-numbers"&gt;  50 &lt;/span&gt;   [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-p&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Support"&gt;GetoptLong&lt;/span&gt;::&lt;span class="Entity"&gt;REQUIRED_ARGUMENT&lt;/span&gt;],
&lt;span class="line-numbers"&gt;  51 &lt;/span&gt;   [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-t&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Support"&gt;GetoptLong&lt;/span&gt;::&lt;span class="Entity"&gt;REQUIRED_ARGUMENT&lt;/span&gt;],
&lt;span class="line-numbers"&gt;  52 &lt;/span&gt;   [&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;--verbose&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-v&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class="Support"&gt;GetoptLong&lt;/span&gt;::&lt;span class="Entity"&gt;NO_ARGUMENT&lt;/span&gt;]
&lt;span class="line-numbers"&gt;  53 &lt;/span&gt; )
&lt;span class="line-numbers"&gt;  54 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  55 &lt;/span&gt; pool &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Beanstalk&lt;/span&gt;::&lt;span class="Entity"&gt;Pool&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;%w(&lt;/span&gt;localhost:11300&lt;span class="String"&gt;)&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  56 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  57 &lt;/span&gt; tube &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;default&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  58 &lt;/span&gt; delay &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;0&lt;/span&gt;
&lt;span class="line-numbers"&gt;  59 &lt;/span&gt; environment &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;ENV&lt;/span&gt;[&lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;RAILS_ENV&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;] &lt;span class="Keyword"&gt;||&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;development&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  60 &lt;/span&gt; priority &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;65536&lt;/span&gt;
&lt;span class="line-numbers"&gt;  61 &lt;/span&gt; ttr &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;120&lt;/span&gt;
&lt;span class="line-numbers"&gt;  62 &lt;/span&gt; verbose &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;false&lt;/span&gt;
&lt;span class="line-numbers"&gt;  63 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  64 &lt;/span&gt; opts.&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;opt&lt;/span&gt;,&lt;span class="Variable"&gt;arg&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  65 &lt;/span&gt;   &lt;span class="Keyword"&gt;case&lt;/span&gt; opt
&lt;span class="line-numbers"&gt;  66 &lt;/span&gt;   &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;--help&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  67 &lt;/span&gt;     &lt;span class="Variable"&gt;RDoc&lt;/span&gt;::&lt;span class="Entity"&gt;usage&lt;/span&gt;
&lt;span class="line-numbers"&gt;  68 &lt;/span&gt;   &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-d&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  69 &lt;/span&gt;     delay &lt;span class="Keyword"&gt;=&lt;/span&gt; arg.&lt;span class="Entity"&gt;to_i&lt;/span&gt;
&lt;span class="line-numbers"&gt;  70 &lt;/span&gt;   &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-e&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  71 &lt;/span&gt;     environment &lt;span class="Keyword"&gt;=&lt;/span&gt; arg
&lt;span class="line-numbers"&gt;  72 &lt;/span&gt;   &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-p&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  73 &lt;/span&gt;     priority &lt;span class="Keyword"&gt;=&lt;/span&gt; arg.&lt;span class="Entity"&gt;to_i&lt;/span&gt;
&lt;span class="line-numbers"&gt;  74 &lt;/span&gt;   &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;-t&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  75 &lt;/span&gt;     ttr &lt;span class="Keyword"&gt;=&lt;/span&gt; arg.&lt;span class="Entity"&gt;to_i&lt;/span&gt;
&lt;span class="line-numbers"&gt;  76 &lt;/span&gt;   &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;--verbose&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  77 &lt;/span&gt;     verbose &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="line-numbers"&gt;  78 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  79 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  80 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  81 &lt;/span&gt; &lt;span class="Keyword"&gt;if&lt;/span&gt; &lt;span class="Variable"&gt;ARGV&lt;/span&gt;.&lt;span class="Entity"&gt;length&lt;/span&gt; &lt;span class="Keyword"&gt;!=&lt;/span&gt; &lt;span class="Constant"&gt;1&lt;/span&gt;
&lt;span class="line-numbers"&gt;  82 &lt;/span&gt;   &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;$&lt;/span&gt;stderr&lt;/span&gt;.&lt;span class="Entity"&gt;puts&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;missing code to run (try &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;File&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;basename&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="Variable"&gt;__FILE__&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; --help)&lt;span class="Constant"&gt;\n&lt;/span&gt;&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  83 &lt;/span&gt;   exit &lt;span class="Constant"&gt;1&lt;/span&gt;
&lt;span class="line-numbers"&gt;  84 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  85 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  86 &lt;/span&gt; code &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;ARGV&lt;/span&gt;[&lt;span class="Constant"&gt;0&lt;/span&gt;]
&lt;span class="line-numbers"&gt;  87 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  88 &lt;/span&gt; &lt;span class="Keyword"&gt;case&lt;/span&gt; environment
&lt;span class="line-numbers"&gt;  89 &lt;/span&gt; &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;production&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  90 &lt;/span&gt;   &lt;span class="Keyword"&gt;if&lt;/span&gt; &lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;exist?&lt;/span&gt;(&lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;join&lt;/span&gt;(&lt;span class="Variable"&gt;RAILS_ROOT&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;REVISION&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;))
&lt;span class="line-numbers"&gt;  91 &lt;/span&gt;     tube &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;read&lt;/span&gt;(&lt;span class="Support"&gt;File&lt;/span&gt;.&lt;span class="Entity"&gt;join&lt;/span&gt;(&lt;span class="Variable"&gt;RAILS_ROOT&lt;/span&gt;, &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;REVISION&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;)).&lt;span class="Entity"&gt;strip&lt;/span&gt;
&lt;span class="line-numbers"&gt;  92 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  93 &lt;/span&gt; &lt;span class="Keyword"&gt;when&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;development&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  94 &lt;/span&gt;   tube &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;ourproj-development&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  95 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  96 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  97 &lt;/span&gt; puts &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;using tube &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;tube&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;if&lt;/span&gt; verbose
&lt;span class="line-numbers"&gt;  98 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  99 &lt;/span&gt; pool.&lt;span class="Entity"&gt;connect&lt;/span&gt;
&lt;span class="line-numbers"&gt; 100 &lt;/span&gt; pool.&lt;span class="Entity"&gt;use&lt;/span&gt;(tube)
&lt;span class="line-numbers"&gt; 101 &lt;/span&gt; job &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;YAML&lt;/span&gt;.&lt;span class="Entity"&gt;dump&lt;/span&gt;({&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;type&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;rails&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;code&lt;/span&gt; =&amp;gt; code})
&lt;span class="line-numbers"&gt; 102 &lt;/span&gt; 
&lt;span class="line-numbers"&gt; 103 &lt;/span&gt; puts &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;job:&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;, job &lt;span class="Keyword"&gt;if&lt;/span&gt; verbose
&lt;span class="line-numbers"&gt; 104 &lt;/span&gt; 
&lt;span class="line-numbers"&gt; 105 &lt;/span&gt; job_id &lt;span class="Keyword"&gt;=&lt;/span&gt; pool.&lt;span class="Entity"&gt;put&lt;/span&gt;(job, priority, delay, ttr)
&lt;span class="line-numbers"&gt; 106 &lt;/span&gt; 
&lt;span class="line-numbers"&gt; 107 &lt;/span&gt; puts &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;job &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;job_id&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; sent to &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;pool&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;last_server&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;if&lt;/span&gt; verbose&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;&lt;a href="http://assets.vermicel.li/code/beanstalk-queue.rb"&gt;Download this code.&lt;/a&gt;&lt;/p&gt;
        &lt;h4&gt;Usage&lt;/h4&gt;
        &lt;p&gt;Because this code does not load Rails or your application, it is unfortunately unable to determine how you have configured your application. Therefore, you must configure the script itself.&lt;/p&gt;
        &lt;p&gt;First, if you’re not using the default &lt;code&gt;beanstalkd&lt;/code&gt; configuration of one server running on &lt;code&gt;localhost:11300&lt;/code&gt;, edit line 55 appropriately. Next, you’re probably not using the same &lt;code&gt;beanstalkd&lt;/code&gt; tube naming convention as we are, so edit lines 88–95 to properly map rails environment names to &lt;code&gt;beanstalkd&lt;/code&gt; tube names.&lt;/p&gt;
        &lt;p&gt;Full documentation for running the script is at the top of the file, or run &lt;code&gt;script/beanstalk-queue --help&lt;/code&gt;, but basically it works like &lt;code&gt;script/runner&lt;/code&gt; with some extra optional parameters for controlling the queueing:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;$ ./script/beanstalk-queue -e production &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;MyModel.some_long_task&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
        &lt;/div&gt;
&lt;h3&gt;cron and whenever integration&lt;/h3&gt;
        &lt;p&gt;If you use &lt;a href="http://github.com/javan/whenever"&gt;&lt;code&gt;whenever&lt;/code&gt;&lt;/a&gt; for dealing with programatically your &lt;code&gt;crontab&lt;/code&gt; file, here’s a bit of code to define a new &lt;code&gt;beanstalk&lt;/code&gt; job type, analogous to the &lt;code&gt;runner&lt;/code&gt; job type:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Keyword"&gt;module&lt;/span&gt; ::&lt;span class="Entity"&gt;Whenever&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   &lt;span class="Keyword"&gt;module&lt;/span&gt; &lt;span class="Entity"&gt;Job&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;     &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;Beanstalk&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; Whenever::Job::Default&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;       &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;output&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;         path_required
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;         &lt;span class="String"&gt;&lt;span class="String"&gt;%Q(&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;File&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;join&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;path&lt;/span&gt;&lt;span class="String"&gt;,&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;script&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;,&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;beanstalk-queue&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; -e &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;environment&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;task&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;inspect&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;       &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;   &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;JobList&lt;/span&gt;
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;     &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;beanstalk&lt;/span&gt;(&lt;span class="Variable"&gt;task&lt;span class="Variable"&gt;,&lt;/span&gt; options &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Variable"&gt;{&lt;/span&gt;&lt;span class="Variable"&gt;}&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;       options.&lt;span class="Entity"&gt;reverse_merge!&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;environment&lt;/span&gt; =&amp;gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;environment&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;path&lt;/span&gt; =&amp;gt; &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;path&lt;/span&gt;)
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;       options[&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;class&lt;/span&gt;] &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Whenever&lt;/span&gt;::&lt;span class="Entity"&gt;Job&lt;/span&gt;::&lt;span class="Entity"&gt;Beanstalk&lt;/span&gt;
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;       &lt;span class="Entity"&gt;command&lt;/span&gt;(task, options)
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;&lt;a href="http://assets.vermicel.li/code/beanstalk-whenever.rb"&gt;Download this code.&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;I just stuck this code near the top of my &lt;code&gt;config/schedule.rb&lt;/code&gt; file, though a possibly cleaner approach would be to put it in its own file and then &lt;code&gt;require&lt;/code&gt; that file from &lt;code&gt;schedule.rb&lt;/code&gt;.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17704454556</link><guid>http://tumble.mlcastle.net/post/17704454556</guid><pubDate>Thu, 22 Apr 2010 18:27:00 -0400</pubDate><category>async_observer</category><category>beanstalkd</category><category>ruby</category><category>rails</category></item><item><title>Converting a Rails Database From MySQL to PostgreSQL</title><description>&lt;p&gt;For a number of reasons, we want to convert one of ours Rails applications so that it is backed by PostgreSQL rather than MySQL. Although Rails and ActiveRecord abstract many of the differences between the two database engines, there are still some issues to be aware of and no one-size-fits-all solution to the problem. Here are some of the challenges we encountered in making the switch.&lt;/p&gt;
        &lt;h3&gt;Converting the schema&lt;/h3&gt;
        &lt;p&gt;The first thing in the conversion process is converting the schema. Fortunately, if you have a fairly simple schema, then Rails makes this relatively easy.&lt;/p&gt;
        &lt;p&gt;Look in &lt;code&gt;config/environment.rb&lt;/code&gt; and make sure that &lt;code&gt;config.active_record.schema_format&lt;/code&gt; is unset or set to &lt;code&gt;:ruby&lt;/code&gt;. This is the default, but some projects change it to &lt;code&gt;:sql&lt;/code&gt; for various reasons.&lt;/p&gt;
        &lt;p&gt;Then do &lt;code&gt;rake db:schema:dump&lt;/code&gt; to generate &lt;code&gt;db/schema.rb&lt;/code&gt; with your current database schema. Edit &lt;code&gt;config/database.yml&lt;/code&gt; to use a PostgreSQL database instead of a MySQL one and do &lt;code&gt;rake db:schema:load&lt;/code&gt; to load the schema in.&lt;/p&gt;
        &lt;p&gt;If your database migrations are simple, you might instead be able to do &lt;code&gt;rake db:migrate&lt;/code&gt; to construct the Postgres database from scratch, but in my experience most Rails projects eventually end up with some kind of database-specific SQL in at least one migration.&lt;/p&gt;
        &lt;p&gt;If, on the other hand, your database schema is so complex that ActiveRecord’s schema dumps can’t handle it, you might consider looking at &lt;a href="http://sqlfairy.sourceforge.net/"&gt;SQLFairy&lt;/a&gt; to help you manage the transition.&lt;/p&gt;
        &lt;h3&gt;Converting the code&lt;/h3&gt;
        &lt;p&gt;This is likely to be the trickiest part of the conversion process. You must go through your code and replace anything MySQL-specific with something PostgreSQL-specific or (better) database-agnostic.&lt;/p&gt;
        &lt;p&gt;Good test coverage of your application is definitely a huge help here: if you can run tests and fix bugs as they appear, you dramatically increase the likelihood of catching everything.&lt;/p&gt;
        &lt;p&gt;Here are some of the issues that we encountered in our codebase; of course, different applications and coding styles may reveal other issues.&lt;/p&gt;
        &lt;h4&gt;Differences in quoting&lt;/h4&gt;
        &lt;p&gt;MySQL allows you to quote table and column names with backquotes; PostgreSQL requires double-quotes. One of our tables has a column in it called &lt;code&gt;when&lt;/code&gt;, which must be quoted whenever we use it.&lt;/p&gt;
        &lt;p&gt;Rails will of course handle the quoting for you if you do something like &lt;code&gt;OurModel.find_by_when(Time.now)&lt;/code&gt;. But if you are constructing your own SQL or conditions, you need to handle the quoting yourself. Given MySQL-compatible code like this:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; [&lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;`when` &amp;lt; ?&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;yesterday&lt;/span&gt;])&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;You need to either use Postgres-specific code like this:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; [&lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;&lt;span class="Constant"&gt;\"&lt;/span&gt;when&lt;span class="Constant"&gt;\"&lt;/span&gt; &amp;lt; ?&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;yesterday&lt;/span&gt;])&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;Or, better, convert it to something database-agnostic:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; [&lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;&lt;span class="Support"&gt;OurModel&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;connection&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;quote_column_name&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;(&lt;/span&gt;&lt;span class="Keyword"&gt;when&lt;/span&gt;&lt;span class="String"&gt;)&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt; &amp;lt; ?&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;yesterday&lt;/span&gt;])&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;In addition to &lt;code&gt;quote_column_name&lt;/code&gt;, ActiveRecord also provides a &lt;code&gt;quote_table_name&lt;/code&gt; function for quoting table names and, crucially, a &lt;code&gt;quote&lt;/code&gt; function for quoting the data you actually store in your database. These functions are your friends in database-agnostic programming—be sure to use them!&lt;/p&gt;
        &lt;h4&gt;Booleans&lt;/h4&gt;
        &lt;p&gt;MySQL lacks a native &lt;code&gt;BOOLEAN&lt;/code&gt; type, so if you create a &lt;code&gt;boolean&lt;/code&gt; column in Rails, you will end up with a &lt;code&gt;TINYINT(1)&lt;/code&gt; column which has values of &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; for &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;true&lt;/code&gt; respectively. There’s no particular need to be aware of this fact—except that most Rails developers &lt;strong&gt;are&lt;/strong&gt; aware of this fact and so often use &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; somewhat interchangeably. PostgreSQL has a native &lt;code&gt;BOOLEAN&lt;/code&gt; type, so these shenanigans will no longer fly. If &lt;code&gt;important&lt;/code&gt; is a boolean column, then code like this will work in MySQL-backed rails but not under PostgreSQL:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;m &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;important&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;1&lt;/span&gt;)
m.&lt;span class="Entity"&gt;save&lt;/span&gt;&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;Instead, be sure to use &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; where you mean to be using booleans:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;m &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;important&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;true&lt;/span&gt;)
m.&lt;span class="Entity"&gt;save&lt;/span&gt;&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;Similarly, if you are using booleans in your conditions, this will work in MySQL:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;important = 1&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;)&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;Instead, take advantage of ActiveRecord’s helpful quoting capabilities and replace your code with the database-agnostic equivalent:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; [&lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;important = ?&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="Constant"&gt;true&lt;/span&gt;])&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;You may also be tripped up by this problem in your fixtures; replace &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; with &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;true&lt;/code&gt; in your YAML files: they’ll then work in both MySQL and PostgreSQL.&lt;/p&gt;
        &lt;h4&gt;Other differences&lt;/h4&gt;
        &lt;p&gt;There are other differences which we encountered. In one part of our code, we need to select rows from the database at random. In MySQL, this is expressed as &lt;code&gt;ORDER BY RAND()&lt;/code&gt;, but in PostgreSQL, the function is called &lt;code&gt;RANDOM()&lt;/code&gt;. Since there is only one place in the code where this occurs, we did not bother to implement any kind of sophisticated database-agnostic function. Instead, we took advantage of the &lt;code&gt;connection.adapter_name&lt;/code&gt; function to be able to write this:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;find&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; [&lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;value &amp;lt; ?&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span class="Variable"&gt;&lt;span class="Variable"&gt;@&lt;/span&gt;max_wanted_value&lt;/span&gt;],
              &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;order&lt;/span&gt; =&amp;gt; &lt;span class="Support"&gt;OurModel&lt;/span&gt;.&lt;span class="Entity"&gt;connection&lt;/span&gt;.&lt;span class="Entity"&gt;adapter_name&lt;/span&gt; &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;PostgreSQL&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="Keyword"&gt;?&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;RANDOM()&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt; : &lt;span class="String"&gt;&lt;span class="String"&gt;'&lt;/span&gt;RAND()&lt;span class="String"&gt;'&lt;/span&gt;&lt;/span&gt;,
	      &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;limit&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;50&lt;/span&gt;)&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;Additionally, we had one unexpected issue: MySQL &lt;code&gt;DATETIME&lt;/code&gt; columns store their data with integer second precision, but PostgreSQL &lt;code&gt;timestamp without time zone&lt;/code&gt; columns are based on floating point numbers. We had to modify our code in a couple of places to round times to the nearest second before storing them in the database in order to get certain equality comparisons to work properly.&lt;/p&gt;
        &lt;h3&gt;Copying the data&lt;/h3&gt;
        &lt;p&gt;Once we had the application more-or-less working as we wanted, it was time to copy the data from the MySQL database into Postgres. Naïve approaches actually do not work well here; due to the difference in quoting rules, the differing storage for booleans mentioned above, and a couple of issues, calling &lt;code&gt;mysqldump&lt;/code&gt; and then attempting to read the output into Postgres fails.&lt;/p&gt;
        &lt;p&gt;Instead, we did the data transfer taking advantage of ActiveRecord’s knowledge of databases. We based the method on &lt;a href="http://myutil.com/2008/8/31/rake-task-transfer-rails-database-mysql-to-postgres"&gt;this one provided by Rama McIntosh&lt;/a&gt; but we had to make a couple of changes. We modified the code to manually generate &lt;code&gt;INSERT&lt;/code&gt; statements rather than constructing &lt;code&gt;ActiveRecord&lt;/code&gt; objects. This has the chief advantage of working with &lt;code&gt;has_and_belongs_to_many&lt;/code&gt;-style join tables without &lt;code&gt;id&lt;/code&gt; columns and the secondary advantage of being somewhat faster. We also modified the code to automatically update the PostgreSQL sequence objects so as not to break future inserts into the table.&lt;/p&gt;
        &lt;p&gt;The resultant code has been &lt;a href="http://github.com/mlc/rails_db_convert_using_adapters/blob/master/convert.rake"&gt;checked into github&lt;/a&gt;. Feel free to use it and modify it further if you like.&lt;/p&gt;
        &lt;p&gt;If you are working on a similar conversion project, good luck!&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17704287267</link><guid>http://tumble.mlcastle.net/post/17704287267</guid><pubDate>Thu, 04 Feb 2010 00:55:00 -0500</pubDate><category>rails</category><category>programming</category><category>database</category><category>postgresql</category></item><item><title>timemachine-fu, a Timemachine plugin for Rails</title><description>&lt;p&gt;&lt;a href="http://timemachine.6x.to/"&gt;Tobis Timemachine&lt;/a&gt; is a very useful tool for bringing websites back to “the amateur web of 1996.”&lt;/p&gt;
        &lt;p&gt;However, there is is some overhead with the Timemachine: it requires your users to have already installed it in order to be useful, and those visitors need to be using a compatible version of Firefox. What if you want to make a 1996-era website for all your site’s visitors?&lt;/p&gt;
        &lt;p&gt;Thus, &lt;code&gt;timemachine-fu&lt;/code&gt;, the Timemachine plugin for Rails.&lt;/p&gt;
        &lt;h2&gt;Demonstration&lt;/h2&gt;
        &lt;p&gt;&lt;a href="http://vermicel.li/blog/2009/02/27/pbcore-cataloging-tool-released-under-gpl.html"&gt;PBCore Database&lt;/a&gt; &lt;strong&gt;before&lt;/strong&gt; &lt;code&gt;timemachine-fu&lt;/code&gt;:&lt;br/&gt;&lt;a style="border: none;" href="http://www.flickr.com/photos/mlcastle/4099404122/" title="PBCore Database by mlcastle, on Flickr"&gt;&lt;img style="float: none; margin: 0px; padding: 0px;" src="http://farm3.static.flickr.com/2726/4099404122_21f39f8e46.jpg" width="500" height="388" alt="PBCore Database"/&gt;&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;and &lt;strong&gt;after&lt;/strong&gt;:&lt;br/&gt;&lt;a style="border: none;" href="http://www.flickr.com/photos/mlcastle/4098648951/" title="PBCore Database + timemachine-fu by mlcastle, on Flickr"&gt;&lt;img style="float: none; margin: 0px; padding: 0px;" src="http://farm3.static.flickr.com/2539/4098648951_42f1034fe9.jpg" width="500" height="388" alt="PBCore Database + timemachine-fu"/&gt;&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;Note that this static demonstration does not really show the full power of &lt;code&gt;timemachine-fu&lt;/code&gt;: in actual installation, many of the images will be animated for further effect, and there are three stylesheets, one of which will be randomly selected for each page load. In addition, positioning of some of the images will be randomly determined anew on each page load, and one of the themes includes a &lt;acronym title="Musical Instrument Digital Interface"&gt;MIDI&lt;/acronym&gt; file for your listening pleasure.&lt;/p&gt;
        &lt;h2&gt;Use it in your own projects&lt;/h2&gt;
        &lt;p&gt;&lt;code&gt;timemachine-fu&lt;/code&gt; is provided as an easy-to-install Rails plugin. It is compatible with either Prototype, as provided by default with Rails, or with JQuery, if you’re using &lt;a href="http://github.com/aaronchi/jrails"&gt;&lt;code&gt;jrails&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
        &lt;p&gt;You can get the code, see installation instructions, and do whatever else you want &lt;a href="http://github.com/mlc/timemachine-fu"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17704287087</link><guid>http://tumble.mlcastle.net/post/17704287087</guid><pubDate>Thu, 12 Nov 2009 18:08:00 -0500</pubDate><category>rails</category><category>timemachine</category><category>aweseomeness</category></item><item><title>A Lightweight “Migrations” System for CouchDB</title><description>&lt;p&gt;As I’ve &lt;a href="/blog/2009/06/16/efficiently-serving-couchdb-attachments.html"&gt;mentioned before&lt;/a&gt;, I’m working on a Rails project where we’ve dropped &lt;code&gt;ActiveRecord&lt;/code&gt; in favor of a CouchDB database.&lt;/p&gt;
        &lt;p&gt;One thing I miss from &lt;code&gt;ActiveRecord&lt;/code&gt;, though, is the ability to do migrations. While SQL migrations in their most conventional use—to modify the schema of our database—are of course not needed in schemaless CouchDB databases, there is still sometimes a need to have a block of code which runs exactly once on every installation of our codebase. For example, we might have a key in our documents which becomes so important that we want to break its values off into new documents, or vice versa, we might have a bunch of documents which should all get combined into attributes of one master document.&lt;/p&gt;
        &lt;h3&gt;What we aren’t doing&lt;/h3&gt;
        &lt;p&gt;The correct way to solve this problem, &lt;a href="http://stackoverflow.com/questions/130092/couchdb-document-model-changes/410840#410840"&gt;some people argue&lt;/a&gt;, is to simply put some smarts into our model and support versioning there:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Keyword"&gt;class&lt;/span&gt; &lt;span class="Entity"&gt;MyClass&lt;span class="Superclass"&gt; &lt;span class="Superclass"&gt;&amp;lt;&lt;/span&gt; CouchRestRails::Document&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;   attribute &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;version&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;   &lt;span class="Keyword"&gt;def&lt;/span&gt; &lt;span class="Entity"&gt;stuff&lt;/span&gt;
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;     &lt;span class="Keyword"&gt;if&lt;/span&gt; &lt;span class="Variable"&gt;self&lt;/span&gt;.&lt;span class="Entity"&gt;version&lt;/span&gt; &lt;span class="Keyword"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="Variable"&gt;self&lt;/span&gt;.&lt;span class="Entity"&gt;version&lt;/span&gt; &lt;span class="Keyword"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="Constant"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;       &lt;span class="Support"&gt;Stuff&lt;/span&gt;.&lt;span class="Entity"&gt;by_parent_id&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;key&lt;/span&gt; =&amp;gt; this.&lt;span class="Entity"&gt;id&lt;/span&gt;)
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;     &lt;span class="Keyword"&gt;else&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;       &lt;span class="Variable"&gt;self&lt;/span&gt;[&lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;stuff&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;]
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;
        &lt;/div&gt;
&lt;h3&gt;No thanks&lt;/h3&gt;
        &lt;p&gt;But that seems like a lot of work to maintain. If our system had millions of models in it and converting them was expensive, we might have no choice but to do it, but I’d rather just convert all existing models to our new schema (or, rather, our new but still schemaless way of being) and write all our code to use the updated way of thinking.&lt;/p&gt;
        &lt;p&gt;So, I designed a simple, lightweight “migrations” system. It lives entirely in &lt;a href="http://rake.rubyforge.org/"&gt;Rake&lt;/a&gt; and is only a couple dozen line of code.&lt;/p&gt;
        &lt;p&gt;Here it is, in its entirety:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; desc &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;Perform unperformed CouchDB migrations&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; task &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;migrate&lt;/span&gt; =&amp;gt; &lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;environment&lt;/span&gt; &lt;span class="Keyword"&gt;do&lt;/span&gt;
&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;   schema &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Keyword"&gt;begin&lt;/span&gt;
&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;              &lt;span class="Support"&gt;CouchRestRails&lt;/span&gt;::&lt;span class="Entity"&gt;Document&lt;/span&gt;.&lt;span class="Entity"&gt;get&lt;/span&gt;(&lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;migration-list&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;            &lt;span class="Keyword"&gt;rescue&lt;/span&gt; &lt;span class="Support"&gt;RestClient&lt;/span&gt;::&lt;span class="Entity"&gt;ResourceNotFound&lt;/span&gt;
&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;              &lt;span class="Support"&gt;CouchRestRails&lt;/span&gt;::&lt;span class="Entity"&gt;Document&lt;/span&gt;.&lt;span class="Entity"&gt;new&lt;/span&gt;(&lt;span class="Constant"&gt;&lt;span class="Constant"&gt;:&lt;/span&gt;_id&lt;/span&gt; =&amp;gt; &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;migration-list&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;)
&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;            &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;   8 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;   migrations &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Support"&gt;Rake&lt;/span&gt;::&lt;span class="Entity"&gt;Task&lt;/span&gt;.&lt;span class="Entity"&gt;tasks&lt;/span&gt;.&lt;span class="Entity"&gt;select&lt;/span&gt;{ |&lt;span class="Variable"&gt;t&lt;/span&gt;| t.&lt;span class="Entity"&gt;name&lt;/span&gt; &lt;span class="Keyword"&gt;=~&lt;/span&gt; &lt;span class="String"&gt;&lt;span class="String"&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;^migrations:&lt;/span&gt;&lt;span class="String"&gt;&lt;span class="String"&gt;/&lt;/span&gt;&lt;/span&gt; }
&lt;span class="line-numbers"&gt;  10 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;   &lt;span class="Keyword"&gt;begin&lt;/span&gt;
&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;     migrations.&lt;span class="Entity"&gt;sort&lt;/span&gt;{|&lt;span class="Variable"&gt;a&lt;/span&gt;, &lt;span class="Variable"&gt;b&lt;/span&gt;| a.&lt;span class="Entity"&gt;name&lt;/span&gt; &lt;span class="Keyword"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; b.&lt;span class="Entity"&gt;name&lt;/span&gt;}.&lt;span class="Entity"&gt;each&lt;/span&gt; &lt;span class="Keyword"&gt;do &lt;/span&gt;|&lt;span class="Variable"&gt;migration&lt;/span&gt;|
&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;       &lt;span class="Keyword"&gt;unless&lt;/span&gt; schema[migration.&lt;span class="Entity"&gt;to_s&lt;/span&gt;]
&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;         puts
&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;         puts &lt;span class="String"&gt;&lt;span class="String"&gt;"&lt;/span&gt;== running &lt;span class="String"&gt;&lt;span class="String"&gt;#{&lt;/span&gt;migration&lt;span class="String"&gt;&lt;span class="String"&gt;.&lt;/span&gt;&lt;span class="Entity"&gt;to_s&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="String"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;  16 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  17 &lt;/span&gt;         migration.&lt;span class="Entity"&gt;invoke&lt;/span&gt;
&lt;span class="line-numbers"&gt;  18 &lt;/span&gt; 
&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;         schema[migration.&lt;span class="Entity"&gt;to_s&lt;/span&gt;] &lt;span class="Keyword"&gt;=&lt;/span&gt; &lt;span class="Constant"&gt;true&lt;/span&gt;
&lt;span class="line-numbers"&gt;  20 &lt;/span&gt;       &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;     &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  22 &lt;/span&gt;   &lt;span class="Keyword"&gt;ensure&lt;/span&gt;
&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;     schema.&lt;span class="Entity"&gt;save&lt;/span&gt;
&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;   &lt;span class="Keyword"&gt;end&lt;/span&gt;
&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; &lt;span class="Keyword"&gt;end&lt;/span&gt;&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;&lt;a href="http://assets.vermicel.li/code/migrator.rake"&gt;Download the code&lt;/a&gt; and stick it in &lt;code&gt;lib/tasks/migrator.rake&lt;/code&gt;. Then, create a new file, &lt;code&gt;lib/tasks/migrations.rake&lt;/code&gt;, and create some tasks in the &lt;code&gt;:migrations&lt;/code&gt; namespace. Here’s an &lt;a href="http://assets.vermicel.li/code/migrations.rake"&gt;example file&lt;/a&gt; you can modify to suit your needs. If you end up with a lot of migrations, you can of course split them among several files.&lt;/p&gt;
        &lt;p&gt;Any un-run migrations will be run in lexical order, so you can prefix your tasks with consecutive integers as shown in the example, with timestamps, or with whatever else makes you happy, as long as you are consistent within any one project.&lt;/p&gt;
        &lt;p&gt;Then, doing &lt;code&gt;rake migrate&lt;/code&gt; will run any migrations which have not yet been run against your database.&lt;/p&gt;
        &lt;p&gt;This system has its limitations of course: among them is the need to stick everything in rake, possibly causing slowness if you end up with too many migrations. But as a lightweight, judiciously used replacement for &lt;code&gt;ActiveRecord&lt;/code&gt; migrations, I find that it is sufficient for our needs.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17704454324</link><guid>http://tumble.mlcastle.net/post/17704454324</guid><pubDate>Tue, 10 Nov 2009 20:10:00 -0500</pubDate><category>couchdb</category><category>rails</category><category>migrations</category></item><item><title>Dipping a Toe Into the Cloud, and Other Mixed Metaphors</title><description>&lt;p&gt;Cloud computing is supposedly all the rage these days: the idea is that instead of running your own infrastructure, you just outsource &lt;em&gt;everything&lt;/em&gt;.&lt;/p&gt;
        &lt;p&gt;There are some cases where this is almost self-evidently a good idea: renting a &lt;a href="http://en.wikipedia.org/wiki/Virtual_private_server"&gt;&lt;acronym title="Virtual Private Server"&gt;VPS&lt;/acronym&gt;&lt;/a&gt; is much cheaper and more reliable than sticking a server in your home and then dealing with the noise, electricity, and bandwidth that such a thing would consume. This blog, for instance, is hosted on a VPS at &lt;a href="http://www.panix.com/corp/v-colo/"&gt;Panix&lt;/a&gt;. On the other hand, an Amazon &lt;acronym title="Elastic Compute Cloud"&gt;EC2&lt;/acronym&gt; instance at the seemingly low price of $0.10 per hour will cost $72 if you leave it on for a whole month—more expensive than many low-end dedicated physical machines.&lt;/p&gt;
        &lt;p&gt;But the cloud, we are promised, can do more for us than just save us money: it can run our services for us. Programmers no longer need know how to do anything but program the things specific to their application: everything else will be taken care of by “the cloud.”&lt;/p&gt;
        &lt;p&gt;I’ve traditionally run all my services on machines (usually VPSes) that I administer. But it would be nice to get out of the system administration business.&lt;/p&gt;
        &lt;h3&gt;The Experiment&lt;/h3&gt;
        &lt;p&gt;So, I tried moving one of my small websites, the &lt;a href="http://pbcorevalidator.org/"&gt;PBCore Validator&lt;/a&gt;, completely away from servers I control and into “the cloud.” This is such a tiny site that I was able to make the move completely for free, which makes for a nice experiment.&lt;/p&gt;
        &lt;p&gt;The most important component of the setup is, of course, the hosting of the application itself. I selected &lt;a href="http://heroku.com/"&gt;Heroku&lt;/a&gt; for three reasons: they have a limited plan available for free; they support the development of &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;, the framework I’m using; and their service is incredibly slick.&lt;/p&gt;
        &lt;p&gt;Setting up the hosting was quite easy:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;$ sudo gem install heroku
$ heroku create validator
$ git push heroku&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;This is certainly easier than creating a new nginx configuration, setting up &lt;a href="http://code.macournoyer.com/thin/"&gt;Thin&lt;/a&gt;, and all the rest. However, there was an extra step here: I had to &lt;a href="http://github.com/mlc/pbcorevalidator/commit/22201d6b9ab4e5affa917c5cd34738b4790dd7b4"&gt;modify my application&lt;/a&gt; to work with Heroku’s restrictions. Previously, my application statically precompiled its homepage and stored it in the filesystem. However, Heroku provides you with a read-only filesystem, so I had to modify the application to dynamically generate the file and then to set &lt;a href="http://docs.heroku.com/http-caching"&gt;some caching headers&lt;/a&gt; so it won’t actually be dynamically generated on every request.&lt;/p&gt;
        &lt;p&gt;The changes here were trivial, but a larger application might have required more changes and thus more work. This highlights a potential pitfall of cloudlike services: you have to adapt your application to your providers’ conventions; if you need to do something which can not be done under the your providers’ constraints, you need new providers.&lt;/p&gt;
        &lt;p&gt;Another pitfall was highlighted when I made a small change to the application, then tried to push a new version to Heroku, and got this:&lt;br/&gt;&lt;img src="http://assets.vermicel.li/heroku-error.png" title="Heroku error message" alt="Heroku error message"/&gt;&lt;/p&gt;
        &lt;p&gt;Faced with such a vague, unhelpful error message, I could do nothing but try again after a few minutes, and when that failed, contact Heroku support. They fixed the problem and got back to me within a couple of hours, which is certainly not bad for a response to a nonpaying customer outside of business hours, but if this had happened to a more critical application under more critical circumstances, I might have been rather upset.&lt;/p&gt;
        &lt;h3&gt;Other Services&lt;/h3&gt;
        &lt;p&gt;Having successfully migrated my application away from my servers, what about the other services on which it depends?&lt;/p&gt;
        &lt;p&gt;&lt;acronym title="Domain Name System"&gt;DNS&lt;/acronym&gt; was easy. Heroku comes with an integration with &lt;a href="http://www.zerigo.com/managed-dns"&gt;Zerigo&lt;/a&gt;, so I could set that up with a few commands:&lt;/p&gt;
        &lt;div class="UltraViolet"&gt;
        &lt;pre class="lazy"&gt;$ heroku addons:add custom_domains:basic
$ heroku addons:add zerigo_dns:basic
$ heroku domains:add pbcorevalidator.org
$ heroku domains:add &lt;a href="http://www.pbcorevalidator.org"&gt;www.pbcorevalidator.org&lt;/a&gt;&lt;/pre&gt;
        &lt;/div&gt;
&lt;p&gt;After a quick trip to &lt;a href="https://joker.com/"&gt;my registrar&lt;/a&gt; to switch the domain to use Zerigo’s servers, I was all set. I expect that my application will never get so much traffic that their free level of service would be insufficient.&lt;/p&gt;
        &lt;p&gt;The final component of the move was of the source code hosting. I use git, so there is not really any &lt;em&gt;need&lt;/em&gt; for a centralized server, but since the Validator is a &lt;a href="http://www.gnu.org/philosophy/free-sw.html"&gt;Free Software&lt;/a&gt; project, I want to provide access to the browse or download the code and to have an option for others to be able to commit to the project if they desire. It’s &lt;a href="http://eagain.net/gitweb/?p=gitosis.git;a=blob;f=README.rst;hb=master"&gt;easy enough&lt;/a&gt; for me to host &lt;a href="http://git.mlcastle.net/"&gt;my own repositories&lt;/a&gt;, but if I want to move into the “cloud,” then the obvious choice is &lt;a href="http://github.com/"&gt;GitHub&lt;/a&gt;, so now the PBCore Validator code &lt;a href="http://github.com/mlc/pbcorevalidator"&gt;is on GitHub&lt;/a&gt;. As the Validator is a small Free Software project, the GitHub free plan is more than sufficient here as well.&lt;/p&gt;
        &lt;p&gt;And of course, the morning after I made these changes, &lt;a href="http://github.com/blog/537-post-mortem-of-this-morning-s-outage"&gt;GitHub went down&lt;/a&gt; for about an hour and a half. As &lt;a href="http://github.s3.amazonaws.com/misc/down.html"&gt;they point out&lt;/a&gt;, this isn’t the end of the world with a distributed system like git, but it’s still downtime that I had given up any ability to do anything about.&lt;/p&gt;
        &lt;h3&gt;Conclusions&lt;/h3&gt;
        &lt;p&gt;I was able to relatively quickly and easily move &lt;a href="http://pbcorevalidator.org/"&gt;a small service&lt;/a&gt; completely off of servers that I control for a total cost of $0. Of course, I now rely on the largess of three companies—if any of them goes out of business or decides that providing free limited service is unprofitable, I will need a new option. If lots and lots of people start to want to validate lots and lots PBCore documents, it’s possible I will have to pay to upgrade my service. For comparison, my Panix VPS isn’t free, but I can easily host &lt;a href="http://pbcore.vermicel.li/"&gt;a few&lt;/a&gt; &lt;a href="http://twitterrevolution.us/"&gt;smallish&lt;/a&gt; &lt;a href="http://git.mlcastle.net/"&gt;websites&lt;/a&gt; &lt;a href="http://club-mate.us/"&gt;on it&lt;/a&gt; for one fairly low monthly cost.&lt;/p&gt;
        &lt;p&gt;Reliability is definitely a concern: I hit two snafus less than twenty-four hours after my move. On the other hand, the uptime of machines that I control seems to be somewhere under 100% as well.&lt;/p&gt;
        &lt;p&gt;I can’t say for sure if I would take this approach with larger projects, but I will definitely consider it.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17704286962</link><guid>http://tumble.mlcastle.net/post/17704286962</guid><pubDate>Fri, 30 Oct 2009 16:50:00 -0400</pubDate><category>cloud computing</category><category>sysadmin</category><category>heroku</category><category>zerigo</category><category>github</category></item><item><title>An Adventure in Old Internet Memes</title><description>&lt;p&gt;Presented without (much) comment.&lt;/p&gt;
        &lt;h3&gt;An Old Internet Meme&lt;/h3&gt;
        &lt;div&gt;&lt;object width="425" height="344" type="application/x-shockwave-flash" data="http://www.youtube-nocookie.com/v/jH8gtrD4_C4&amp;amp;hl=en&amp;amp;fs=1&amp;amp;rel=0&amp;amp;color1=0xcd8a39&amp;amp;color2=0x9e6a21"&gt;&lt;param name="movie" value="http://www.youtube-nocookie.com/v/jH8gtrD4_C4&amp;amp;hl=en&amp;amp;fs=1&amp;amp;rel=0&amp;amp;color1=0xe1600f&amp;amp;color2=0xfebd01"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/object&gt;&lt;/div&gt;
        &lt;h3&gt;An Analysis&lt;/h3&gt;
        &lt;p&gt;&lt;strong&gt;Bolded&lt;/strong&gt; text indicates &lt;a href="http://en.wikipedia.org/wiki/Cognate_%28etymology%29"&gt;cognates&lt;/a&gt; in all three columns; &lt;em&gt;italicized&lt;/em&gt; text indicates cognates which were missed in the false translation. Not all such cognates are marked; notably, Moskau / Moscow, hey, ha, and ho are unmarked because they appear so often.&lt;/p&gt;</description><link>http://tumble.mlcastle.net/post/17704286858</link><guid>http://tumble.mlcastle.net/post/17704286858</guid><pubDate>Tue, 27 Oct 2009 01:35:00 -0400</pubDate><category>blog</category></item><item><title>A Letter to the NYC Department of Transportation</title><description>&lt;p&gt;I wrote a letter to the Commissioner of the &lt;acronym title="New York City"&gt;NYC&lt;/acronym&gt; Department of Transportation, urging the marking of a safe route between the &lt;a href="http://www.streetsblog.org/2009/08/31/eyes-on-the-street-a-safer-more-sociable-boulevard-takes-shape/"&gt;new bike lanes&lt;/a&gt; on Allen and Pike Streets and the &lt;a href="http://en.wikipedia.org/wiki/Manhattan_Waterfront_Greenway"&gt;East River Greenway&lt;/a&gt;. Here’s a copy.&lt;/p&gt;
        &lt;h2&gt;The Letter&lt;/h2&gt;
        &lt;blockquote&gt;
        &lt;p&gt;Dear Commissioner Sadik-Khan and Borough Commissioner Forgione:&lt;/p&gt;
        &lt;p&gt;It has been a nice surprise over the past couple of months to see the new bike lanes and other improvements along the center of Allen and Pike Streets in lower Manhattan. These cover a route that I use frequently, and I feel much safer in these new, clearly-striped lanes.&lt;/p&gt;
        &lt;p&gt;However, there is one part of the project which I feel has been overlooked: at the southern end of Pike Street, I often wish to turn onto the East River Greenway, and when returning, I often wish to turn from the Greenway north onto Pike Street. However, there are no clear markings or signals for how to make this turn. There is not even a curb cut to get on and off of the Greenway. In addition, crossing the busy car traffic on South Street always feels unsafe, especially when coming off of the Greenway onto Pike Street, when there are no visible traffic signals and cars do not generally expect my presence.&lt;/p&gt;
        &lt;p&gt;I hope that the project can be extended to encompass a safer, easier connection between two of Manhattan’s best bike routes, and I await hearing your response on this issue.&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;And if you’d like to see it exactly as sent, you can &lt;a href="http://assets.vermicel.li/pike.pdf"&gt;view the letter as a PDF&lt;/a&gt;.&lt;/p&gt;
        &lt;h2&gt;Responses&lt;/h2&gt;
        &lt;p&gt;&lt;strong&gt;Update November 5, 2009:&lt;/strong&gt; I received &lt;a href="http://assets.vermicel.li/pike-reply.pdf"&gt;a response&lt;/a&gt; from the &lt;acronym title="Department of Transportation"&gt;DOT&lt;/acronym&gt;.&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Update March 15, 2010:&lt;/strong&gt; I received another response, this time more detailed and by email:&lt;/p&gt;
        &lt;blockquote&gt;
        &lt;p&gt;Dear Mr. Castleman,&lt;/p&gt;
        &lt;p&gt;This is in response to your October 23, 2009 email regarding the bicycle lane on Pike Street.  We apologize for the delay in response.&lt;/p&gt;
        &lt;p&gt;As you may be aware, DOT recently concluded an unprecedented three-year plan to expand New York City’s on-street bicycle network by 200 miles and improve safety for cyclists on the streets; 204.5 miles was the final total.   These bicycle lanes have encouraged a low-cost, healthful mode of transportation and provided measurable safety improvements to all street users.&lt;/p&gt;
        &lt;p&gt;Thank you for reporting your observations of a missing ramp and unclear markings at the intersection of Pike Street and South Street.  Currently, cyclists should use the pedestrian ramps.  In the near future the malls and the East River Esplanade will be reconstructed; when those two projects are implemented the crossing will occur in the middle along with curb ramps.&lt;/p&gt;
        &lt;p&gt;Thank you for your interest in bicycling in New York City.&lt;/p&gt;
        &lt;p&gt;Sincerely,&lt;/p&gt;
        &lt;p&gt;Bicycle Program&lt;br/&gt;
        NYC Dept. of Transportation&lt;/p&gt;
        &lt;/blockquote&gt;</description><link>http://tumble.mlcastle.net/post/17704286736</link><guid>http://tumble.mlcastle.net/post/17704286736</guid><pubDate>Fri, 23 Oct 2009 18:36:00 -0400</pubDate><category>bike lanes</category><category>transportation</category><category>letters</category></item></channel></rss>

