<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.heh.fi/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
  <title>Johan Kiviniemi’s series of tubes</title>
  
  <link href="http://johan.kiviniemi.name/" type="text/html" />
  <updated>2010-02-10T15:27:45+02:00</updated>
  <id>http://johan.kiviniemi.name/</id>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.heh.fi/JohanKiviniemi" /><feedburner:info uri="johankiviniemi" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title>World of Resources</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/Ls9Q9sVjJdE/" />
    <id>http://johan.kiviniemi.name/blag/world-of-resources/</id>
    <updated>2010-02-10T03:00:07+02:00</updated>
    <content type="html">&lt;p&gt;Back in 2006, David Heinemeier Hansson’s RailsConf talk titled &lt;em&gt;“Discovering a World of Resources on Rails”&lt;/em&gt; was quite an eye-opener for me.&lt;/p&gt;

&lt;p&gt;He demonstrates how embracing a &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer" title="Representational State Transfer"&gt;RESTful&lt;/a&gt; architecture in web development forces you to think in ways that result in a considerably better design.&lt;/p&gt;

&lt;p&gt;Even though his examples are based on Ruby on Rails, the concepts apply to all web development, no matter which platform/framework you’re using.&lt;/p&gt;

&lt;p&gt;Seriously, if you do any web programming at all, go and watch the talk. :-)&lt;/p&gt;

&lt;p&gt;I took the trouble to sync and merge the PDF slides with the video. &lt;strong&gt;&lt;a href="world_of_resources.torrent"&gt;Get the merged video here&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;(&lt;a href="http://www.scribemedia.org/2006/07/09/dhh/"&gt;Source without the slides and the separate PDF are here&lt;/a&gt;. Thanks to ScribeMedia for providing them. © ScribeLabs under a &lt;a href="http://creativecommons.org/licenses/by-nc/3.0/us/"&gt;Creative Commons Attribution-Noncommercial 3.0 United States License&lt;/a&gt;.)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/Ls9Q9sVjJdE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/world-of-resources/</feedburner:origLink></entry>
    <entry>
    <title>Metaterrorism</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/_rPDELqYk90/" />
    <id>http://johan.kiviniemi.name/blag/metaterrorism/</id>
    <updated>2009-12-31T04:10:45+02:00</updated>
    <content type="html">&lt;p&gt;met·a-&lt;strong&gt;ter&lt;/strong&gt;·ror·ism /ˌmɛtəˈtɛrəˌrɪzəm/&lt;/p&gt;

&lt;p&gt;n. The instillation of terror about terrorism for political purposes, &lt;em&gt;e.g. to take away the people’s freedoms on the basis of protection from terrorists who hate them for their freedom&lt;/em&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/_rPDELqYk90" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/metaterrorism/</feedburner:origLink></entry>
    <entry>
    <title>extservice: A jQuery plugin</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/cVU4mM1xZ9A/" />
    <id>http://johan.kiviniemi.name/blag/extservice-jquery-plugin/</id>
    <updated>2009-02-22T12:35:37+02:00</updated>
    <content type="html">&lt;p&gt;I wrote a new jQuery plugin: &lt;a href="http://plugins.jquery.com/project/extservice"&gt;extservice&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It allows you to add embedded items for external services to a webpage easily and with a consistent syntax.&lt;/p&gt;

&lt;p&gt;Currently supported services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Digg (the “digg it” button)&lt;/li&gt;
&lt;li&gt;Disqus (the forum thread for your webpage)&lt;/li&gt;
&lt;li&gt;Google Analytics (the tracking code)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;It looks pretty much like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Find the element with id=digg and replace its contents (if any) with the
// “digg it” button.
$('#digg').loadDigg ();

// Load Google Analytics.
$.loadGoogleAnalytics ('UA-0123456-1');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://heh.fi/jquery/"&gt;Download the plugin&lt;/a&gt;, or see it in use by viewing the source of this webpage or &lt;a href="http://heh.fi/heroes/"&gt;my Heroes page&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/cVU4mM1xZ9A" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/extservice-jquery-plugin/</feedburner:origLink></entry>
    <entry>
    <title>FFUU</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/rKhtz0kOMHA/" />
    <id>http://johan.kiviniemi.name/blag/ffuu/</id>
    <updated>2009-02-08T04:34:44+02:00</updated>
    <content type="html">&lt;p&gt;You all have seen &lt;a href="http://www.epicrage.org/wp-content/uploads/2009/01/no_step.jpg"&gt;the&lt;/a&gt; &lt;a href="http://apina.biz/12464.png"&gt;plethora&lt;/a&gt; &lt;a href="http://www.epicrage.org/wp-content/uploads/2008/09/mario.jpg"&gt;of&lt;/a&gt; &lt;a href="http://www.epicrage.org/wp-content/uploads/2008/09/shhhh.jpg"&gt;comic&lt;/a&gt; &lt;a href="http://heh.fi/tmp/presidential_snowball"&gt;strips&lt;/a&gt; featuring the hand-drawn angry face beginning to shout “FUN!”&lt;/p&gt;

&lt;p style="text-align: center;"&gt;&lt;img src="ffuu.png" alt="FFUU"&gt;&lt;/p&gt;


&lt;p&gt;The train of thought to the following should be obvious.&lt;/p&gt;

&lt;p&gt;I did something useful for once: a graph of the number of Google results for words with various amounts of the letter F and various amounts of the letter U.&lt;/p&gt;

&lt;p&gt;Enjoy:&lt;/p&gt;

&lt;div id="ffuu-container"&gt;&lt;p&gt;Loading... (JavaScript support required, sorry.)&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;What have we learned? Nothing really.&lt;/p&gt;

&lt;p&gt;The diagonal lines are interesting, though.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/rKhtz0kOMHA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/ffuu/</feedburner:origLink></entry>
    <entry>
    <title>Weight tracker</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/1m7OSlTlL4A/" />
    <id>http://johan.kiviniemi.name/blag/weight-tracker/</id>
    <updated>2008-12-27T23:25:29+02:00</updated>
    <content type="html">&lt;p&gt;A side effect of a drug caused me to gain weight. Now that I’m off the drug, I’ll try to lose some of it. :-)&lt;/p&gt;

&lt;p&gt;I wrote a small program to track my weight. &lt;a href="http://github.com/ion1/weight-tracker"&gt;Get it here&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/1m7OSlTlL4A" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/weight-tracker/</feedburner:origLink></entry>
    <entry>
    <title>XMoto: private room guide</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/S6Djbcoa_aE/" />
    <id>http://johan.kiviniemi.name/blag/xmoto-private-room-guide/</id>
    <updated>2008-12-24T22:37:38+02:00</updated>
    <content type="html">&lt;p&gt;This is a short guide I give to people I give access to an XMoto private room.&lt;/p&gt;

&lt;p&gt;To view the high scores or change your password, hit “&lt;a href="http://xmoto.tuxfamily.org/index.php?page=all_rooms"&gt;Private rooms&lt;/a&gt;” on the &lt;a href="http://xmoto.tuxfamily.org/"&gt;XMoto website&lt;/a&gt;, choose the room and log in.&lt;/p&gt;

&lt;p&gt;In the game, choose Options → WWW → Room 2 (I recommend leaving WR as the Reference room), activate the checkbox, select the room and input your credentials. Hit “Upload all highscores” to do a bulk upload.&lt;/p&gt;

&lt;p&gt;Immediately after playing a level, you can hit “Upload” if you beat the current high score.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/S6Djbcoa_aE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/xmoto-private-room-guide/</feedburner:origLink></entry>
    <entry>
    <title>New “creating a weblog in 15 minutes with Rails” video</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/IzVpArdWFuk/" />
    <id>http://johan.kiviniemi.name/blag/new-rails-video/</id>
    <updated>2008-12-24T05:04:33+02:00</updated>
    <content type="html">&lt;p&gt;There’s a &lt;a href="http://media.rubyonrails.org/video/rails_blog_2.mov"&gt;new version of the “creating a weblog in 15 minutes with Rails” video&lt;/a&gt; on the &lt;a href="http://rubyonrails.org/screencasts"&gt;Ruby on Rails website&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;“In 15 minutes, we go from scratch to complete weblog engine with comments, ajax, an ATOM feed, an XML and JSON API, tests, an administrative interface, and much more!”&lt;/p&gt;&lt;/blockquote&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/IzVpArdWFuk" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/new-rails-video/</feedburner:origLink></entry>
    <entry>
    <title>My Ubuntu installation checklist</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/QDH_cY8NJu8/" />
    <id>http://johan.kiviniemi.name/blag/ubuntu-checklist/</id>
    <updated>2008-12-14T00:13:52+02:00</updated>
    <content type="html">&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Language packs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure the language packs are fully installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fonts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Switch to &lt;a href="/blag/ubuntu-fonts/"&gt;sharp font rendering&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable universe etc.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable &lt;a href="http://medibuntu.org/"&gt;Medibuntu&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% sudo wget -O /etc/apt/sources.list.d/medibuntu.list \
    http://www.medibuntu.org/sources.list.d/jaunty.list
% sudo apt-get update; \
    sudo apt-get install medibuntu-keyring; \
    sudo apt-get update
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optionally install restricted stuff&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;% sudo apt-get install non-free-codecs libdvdcss2&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install smart-notifier&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;% sudo apt-get install --no-install-recommends smart-notifier&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Enable smartd in &lt;code&gt;/etc/default/smartmontools&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install ntp support&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;% sudo apt-get install ntp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;/etc/ntp.conf: &lt;code&gt;server ntp.tdc.fi iburst&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;/etc/dhcp3/dhclient.conf: &lt;code&gt;request ntp-servers&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Make sure the HDD isn’t doing constant load cycles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://launchpad.net/bugs/59695"&gt;Bug #59695&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the load cycle rate, run &lt;code&gt;sudo smartctl -A /dev/sda | grep Load_Cycle_Count&lt;/code&gt; twice with a delay of, say, an hour.&lt;/p&gt;

&lt;p&gt;On various Thinkpads the fix seems to be to enable laptop-mode in /etc/default/acpi-support, and to set the following in /etc/laptop-mode/laptop-mode.conf:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CONTROL_HD_IDLE_TIMEOUT=1

LM_BATT_HD_IDLE_TIMEOUT_SECONDS=60
LM_AC_HD_IDLE_TIMEOUT_SECONDS=7200
NOLM_HD_IDLE_TIMEOUT_SECONDS=7200

CONTROL_HD_POWERMGMT=1

BATT_HD_POWERMGMT=128
LM_AC_HD_POWERMGMT=254
NOLM_AC_HD_POWERMGMT=254
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Make the on-startup partition checks less frequent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;% sudo tune2fs -c 0 -i 180d /dev/...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When the HDD breaks down, I’ll just consult the backups.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Make the on-startup partition check repair errors without asking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s not as if I’d choose “no” when it asks about it.&lt;/p&gt;

&lt;p&gt;/etc/default/rcS: &lt;code&gt;FSCKFIX=yes&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add Tampere, Finland to the Gnome clock/calendar/weather applet.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Switch Totem to the xine backend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;% sudo apt-get install totem-xine totem-gstreamer-&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;...as long as DVD menus are &lt;a href="http://launchpad.net/bugs/41335"&gt;broken&lt;/a&gt; with the gstreamer backend&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/QDH_cY8NJu8" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/ubuntu-checklist/</feedburner:origLink></entry>
    <entry>
    <title>Viewing order of the Babylon 5 episodes</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/vr3_EtblTLw/" />
    <id>http://johan.kiviniemi.name/blag/b5/</id>
    <updated>2008-03-24T22:15:11+02:00</updated>
    <content type="html">&lt;p&gt;Based on &lt;a href="Http://www.ntua.gr/lurk/lurker.html"&gt;The Lurker’s Guide to Babylon 5&lt;/a&gt;, but with some errors corrected. For instance, In The Beginning should be watched after 4x22, not before everything else.&lt;/p&gt;

&lt;h3&gt;Season 1&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;1x00: The Gathering&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;1x01: Midnight on the Firing Line&lt;/li&gt;
&lt;li&gt;1x02: Soul Hunter&lt;/li&gt;
&lt;li&gt;1x03: Born to the Purple&lt;/li&gt;
&lt;li&gt;1x04: Infection&lt;/li&gt;
&lt;li&gt;1x05: The Parliament of Dreams&lt;/li&gt;
&lt;li&gt;1x06: Mind War&lt;/li&gt;
&lt;li&gt;1x07: The War Prayer&lt;/li&gt;
&lt;li&gt;1x08: And the Sky Full of Stars&lt;/li&gt;
&lt;li&gt;1x09: Deathwalker&lt;/li&gt;
&lt;li&gt;1x10: Believers&lt;/li&gt;
&lt;li&gt;1x11: Survivors&lt;/li&gt;
&lt;li&gt;1x12: By Any Means Necessary&lt;/li&gt;
&lt;li&gt;1x13: Signs and Portents&lt;/li&gt;
&lt;li&gt;1x15: Grail&lt;/li&gt;
&lt;li&gt;1x16: Eyes&lt;/li&gt;
&lt;li&gt;1x18: A Voice in the Wilderness (1)&lt;/li&gt;
&lt;li&gt;1x19: A Voice in the Wilderness (2)&lt;/li&gt;
&lt;li&gt;1x20: Babylon Squared&lt;/li&gt;
&lt;li&gt;1x21: The Quality of Mercy&lt;/li&gt;
&lt;li&gt;1x14: TKO&lt;/li&gt;
&lt;li&gt;1x17: Legacies&lt;/li&gt;
&lt;li&gt;1x22: Chrysalis&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Season 2&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2x01: Points of Departure&lt;/li&gt;
&lt;li&gt;2x02: Revelations&lt;/li&gt;
&lt;li&gt;2x03: The Geometry of Shadows&lt;/li&gt;
&lt;li&gt;2x04: A Distant Star&lt;/li&gt;
&lt;li&gt;2x05: The Long Dark&lt;/li&gt;
&lt;li&gt;2x06: Spider in the Web&lt;/li&gt;
&lt;li&gt;2x08: A Race Through Dark Places&lt;/li&gt;
&lt;li&gt;2x07: Soul Mates&lt;/li&gt;
&lt;li&gt;2x09: The Coming of Shadows&lt;/li&gt;
&lt;li&gt;2x10: GROPOS&lt;/li&gt;
&lt;li&gt;2x11: All Alone in the Night&lt;/li&gt;
&lt;li&gt;2x12: Acts of Sacrifice&lt;/li&gt;
&lt;li&gt;2x13: Hunter, Prey&lt;/li&gt;
&lt;li&gt;2x14: There All the Honor Lies&lt;/li&gt;
&lt;li&gt;2x15: And Now for a Word&lt;/li&gt;
&lt;li&gt;2x17: Knives&lt;/li&gt;
&lt;li&gt;2x16: In the Shadow of Z’ha’dum&lt;/li&gt;
&lt;li&gt;2x18: Confessions and Lamentations&lt;/li&gt;
&lt;li&gt;2x19: Divided Loyalties&lt;/li&gt;
&lt;li&gt;2x20: The Long Twilight Struggle&lt;/li&gt;
&lt;li&gt;2x21: Comes the Inquisitor&lt;/li&gt;
&lt;li&gt;2x22: The Fall of Night&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Season 3&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;3x01: Matters of Honor&lt;/li&gt;
&lt;li&gt;3x02: Convictions&lt;/li&gt;
&lt;li&gt;3x03: A Day in the Strife&lt;/li&gt;
&lt;li&gt;3x04: Passing Through Gethsemane&lt;/li&gt;
&lt;li&gt;3x05: Voices of Authority&lt;/li&gt;
&lt;li&gt;3x06: Dust to Dust&lt;/li&gt;
&lt;li&gt;3x07: Exogenesis&lt;/li&gt;
&lt;li&gt;3x08: Messages from Earth (1)&lt;/li&gt;
&lt;li&gt;3x09: Point of No Return (2)&lt;/li&gt;
&lt;li&gt;3x10: Severed Dreams (3)&lt;/li&gt;
&lt;li&gt;3x11: Ceremonies of Light and Dark&lt;/li&gt;
&lt;li&gt;3x13: A Late Delivery from Avalon&lt;/li&gt;
&lt;li&gt;3x12: Sic Transit Vir&lt;/li&gt;
&lt;li&gt;3x14: Ship of Tears&lt;/li&gt;
&lt;li&gt;3x15: Interludes and Examinations&lt;/li&gt;
&lt;li&gt;3x18: Walkabout&lt;/li&gt;
&lt;li&gt;3x16: War Without End (1)&lt;/li&gt;
&lt;li&gt;3x17: War Without End (2)&lt;/li&gt;
&lt;li&gt;3x19: Grey 17 Is Missing&lt;/li&gt;
&lt;li&gt;3x20: And the Rock Cried Out, No Hiding Place&lt;/li&gt;
&lt;li&gt;3x21: Shadow Dancing&lt;/li&gt;
&lt;li&gt;3x22: Z’ha’dum&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Season 4&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;4x01: The Hour of the Wolf&lt;/li&gt;
&lt;li&gt;4x02: What Ever Happened to Mr. Garibaldi?&lt;/li&gt;
&lt;li&gt;4x03: The Summoning&lt;/li&gt;
&lt;li&gt;4x04: Falling Towards Apotheosis&lt;/li&gt;
&lt;li&gt;4x05: The Long Night&lt;/li&gt;
&lt;li&gt;4x06: Into the Fire&lt;/li&gt;
&lt;li&gt;4x07: Epiphanies&lt;/li&gt;
&lt;li&gt;4x08: The Illusion of Truth&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Movie: Thirdspace&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;4x09: Atonement&lt;/li&gt;
&lt;li&gt;4x10: Racing Mars&lt;/li&gt;
&lt;li&gt;4x11: Lines of Communication&lt;/li&gt;
&lt;li&gt;4x12: Conflicts of Interest&lt;/li&gt;
&lt;li&gt;4x13: Rumors, Bargains and Lies&lt;/li&gt;
&lt;li&gt;4x14: Moments of Transition&lt;/li&gt;
&lt;li&gt;4x15: No Surrender, No Retreat&lt;/li&gt;
&lt;li&gt;4x16: Exercise of Vital Powers&lt;/li&gt;
&lt;li&gt;4x17: The Face of the Enemy&lt;/li&gt;
&lt;li&gt;4x18: Intersections in Real Time&lt;/li&gt;
&lt;li&gt;4x19: Between the Darkness and the Light&lt;/li&gt;
&lt;li&gt;4x20: Endgame&lt;/li&gt;
&lt;li&gt;4x21: Rising Star&lt;/li&gt;
&lt;li&gt;4x22: The Deconstruction of Falling Stars&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Movie: In the Beginning&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Season 5&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;5x01: No Compromises&lt;/li&gt;
&lt;li&gt;5x02: The Very Long Night of Londo Mollari&lt;/li&gt;
&lt;li&gt;5x03: The Paragon of Animals&lt;/li&gt;
&lt;li&gt;5x04: A View from the Gallery&lt;/li&gt;
&lt;li&gt;5x05: Learning Curve&lt;/li&gt;
&lt;li&gt;5x06: Strange Relations&lt;/li&gt;
&lt;li&gt;5x07: Secrets of the Soul&lt;/li&gt;
&lt;li&gt;5x09: In the Kingdom of the Blind&lt;/li&gt;
&lt;li&gt;5x10: A Tragedy of Telepaths&lt;/li&gt;
&lt;li&gt;5x11: Phoenix Rising&lt;/li&gt;
&lt;li&gt;5x12: The Ragged Edge&lt;/li&gt;
&lt;li&gt;5x08: Day of the Dead&lt;/li&gt;
&lt;li&gt;5x13: The Corps Is Mother, the Corps Is Father&lt;/li&gt;
&lt;li&gt;5x14: Meditations on the Abyss&lt;/li&gt;
&lt;li&gt;5x15: Darkness Ascending&lt;/li&gt;
&lt;li&gt;5x16: And All My Dreams Torn Asunder&lt;/li&gt;
&lt;li&gt;5x17: Movements of Fire and Shadow (1)&lt;/li&gt;
&lt;li&gt;5x18: The Fall of Centauri Prime (2)&lt;/li&gt;
&lt;li&gt;5x19: The Wheel of Fire&lt;/li&gt;
&lt;li&gt;5x20: Objects in Motion&lt;/li&gt;
&lt;li&gt;5x21: Objects at Rest&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Movie: River of Souls&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Movie: The Legend of the Rangers&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Movie: A Call to Arms&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Crusade&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Crusade 1x01: War Zone&lt;/li&gt;
&lt;li&gt;Crusade 1x02: The Long Road&lt;/li&gt;
&lt;li&gt;Crusade 1x03: The Well of Forever&lt;/li&gt;
&lt;li&gt;Crusade 1x04: The Path of Sorrows&lt;/li&gt;
&lt;li&gt;Crusade 1x05: Patterns of the Soul&lt;/li&gt;
&lt;li&gt;Crusade 1x06: Ruling from the Tomb&lt;/li&gt;
&lt;li&gt;Crusade 1x07: The Rules of the Game&lt;/li&gt;
&lt;li&gt;Crusade 1x08: Appearances and Other Deceits&lt;/li&gt;
&lt;li&gt;Crusade 1x09: Racing the Night&lt;/li&gt;
&lt;li&gt;Crusade 1x10: The Memory of War&lt;/li&gt;
&lt;li&gt;Crusade 1x11: The Needs of Earth&lt;/li&gt;
&lt;li&gt;Crusade 1x12: Visitors from Down the Street&lt;/li&gt;
&lt;li&gt;Crusade 1x13: Each Night I Dream of Home&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;The rest&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;5x22: Sleeping in Light&lt;/li&gt;
&lt;li&gt;6x01: The Lost Tales; Voices in the Dark&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/vr3_EtblTLw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/b5/</feedburner:origLink></entry>
    <entry>
    <title>Fixing the font rendering in Ubuntu</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/JPhAQ2EMtA0/" />
    <id>http://johan.kiviniemi.name/blag/2008/01/12/ubuntu-hardy-fonts/</id>
    <updated>2008-01-11T22:59:33+02:00</updated>
    <content type="html">&lt;p&gt;&lt;strong&gt;News&lt;/strong&gt;: Martin Ankerl posted &lt;a href="http://martin.ankerl.com/2009/01/22/beautiful-font-hinting-in-ubuntu-810/"&gt;screenshots comparing the font renderers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re like me and hate the blurry font rendering used by default in Ubuntu since 8.04 (hardy), you might want to install the following config files:&lt;/p&gt;

&lt;h3&gt;/etc/fonts/conf.d/99-sharp-fonts.conf&lt;/h3&gt;

&lt;p&gt;Since the filename begins with 99, these settings override pretty much everything else.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;!DOCTYPE fontconfig SYSTEM "fonts.dtd"&amp;gt;
&amp;lt;fontconfig&amp;gt;
  &amp;lt;match target="font"&amp;gt;
    &amp;lt;edit name="antialias" mode="assign"&amp;gt;&amp;lt;bool&amp;gt;true&amp;lt;/bool&amp;gt;&amp;lt;/edit&amp;gt;
    &amp;lt;edit name="hinting" mode="assign"&amp;gt;&amp;lt;bool&amp;gt;true&amp;lt;/bool&amp;gt;&amp;lt;/edit&amp;gt;
    &amp;lt;edit name="hintstyle" mode="assign"&amp;gt;&amp;lt;const&amp;gt;hintfull&amp;lt;/const&amp;gt;&amp;lt;/edit&amp;gt;
    &amp;lt;edit name="lcdfilter" mode="assign"&amp;gt;&amp;lt;const&amp;gt;lcdlegacy&amp;lt;/const&amp;gt;&amp;lt;/edit&amp;gt;
    &amp;lt;edit name="rgba" mode="assign"&amp;gt;&amp;lt;const&amp;gt;rgb&amp;lt;/const&amp;gt;&amp;lt;/edit&amp;gt;
  &amp;lt;/match&amp;gt;
&amp;lt;/fontconfig&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Change &lt;code&gt;lcdlegacy&lt;/code&gt; to &lt;code&gt;lcdfilterlegacy&lt;/code&gt; if you’re running 8.04.&lt;/p&gt;

&lt;h3&gt;/etc/X11/Xresources/sharp-fonts&lt;/h3&gt;

&lt;p&gt;As of this writing, cairo is unable to read the settings from fontconfig, so let’s add the equivalent X resources for cairo applications to obey.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Xft.antialias:  true
Xft.hinting:    true
Xft.hintstyle:  hintfull
Xft.lcdfilter:  lcdlegacy
Xft.rgba:       rgb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, change &lt;code&gt;lcdlegacy&lt;/code&gt; to &lt;code&gt;lcdfilterlegacy&lt;/code&gt; if you’re running 8.04.&lt;/p&gt;

&lt;h3&gt;Explanation of my issue with the default font rendering&lt;/h3&gt;

&lt;p&gt;The default font renderer draws &lt;strong&gt;translucent&lt;/strong&gt; subpixels – a.k.a. blur – around vertical lines. That makes text less readable.&lt;/p&gt;

&lt;p&gt;The legacy renderer draws all horizontal and vertical lines without any translucent pixels/subpixels. Only oblique lines and curves are rendered with antialiasing. That means the text is as sharp as possible with the display technology.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/JPhAQ2EMtA0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/ubuntu-fonts/</feedburner:origLink></entry>
    <entry>
    <title>The speed difference between svn diff and git diff</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/MElKWwP31Oo/" />
    <id>http://johan.kiviniemi.name/blag/2008/01/10/svn-diff-git-diff-speed/</id>
    <updated>2008-01-10T04:27:47+02:00</updated>
    <content type="html">&lt;p&gt;&lt;code&gt;svn diff&lt;/code&gt; in a SVN checkout of &lt;code&gt;https://panotools.svn.sourceforge.net/svnroot/panotools/trunk/libpano&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% time svn diff -r 706 &amp;gt;/dev/null
svn diff -r 706 &amp;gt; /dev/null  0.40s user 0.14s system 1% cpu 42.378 total
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The equivalent &lt;code&gt;git diff&lt;/code&gt; in a Git branch imported from the same SVN branch:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% time git diff $(git svn find-rev r706) &amp;gt;/dev/null
git diff $(git svn find-rev r706) &amp;gt; /dev/null  0.19s user 0.04s system 85% cpu 0.276 total
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(Both are the fastest of four subsequent tries, but the differences were negligible.)&lt;/p&gt;

&lt;p&gt;A 15000% difference. Really. Just because SVN requires network access for this functionality (and pretty much everything else). The entire Git suite is just insanely fast compared to the competition, not to mention that it handles things like branching and merging in a refreshingly pleasant way.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;git diff&lt;/code&gt; from the project’s very first SVN revision:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% time git diff $(git svn find-rev r29) &amp;gt;/dev/null
git diff $(git svn find-rev r29) &amp;gt; /dev/null  0.38s user 0.04s system 95% cpu 0.434 total
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Still less than half a second.&lt;/p&gt;

&lt;p&gt;I recommend watching &lt;a href="http://youtube.com/watch?v=4XpnKHJAok8"&gt;Linus Torvalds’ entertaining tech talk about Git&lt;/a&gt; (&lt;a href="http://ash-v131.ash.youtube.com/get_video?video_id=4XpnKHJAok8"&gt;download flv&lt;/a&gt;), no matter which VCS you’re using.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/MElKWwP31Oo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/svn-diff-git-diff-speed/</feedburner:origLink></entry>
    <entry>
    <title>Shameful confession</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/96fLr9n6YNU/" />
    <id>http://johan.kiviniemi.name/blag/2007/11/08/shameful-confession/</id>
    <updated>2007-11-08T16:09:58+02:00</updated>
    <content type="html">&lt;p&gt;I gave in to peer pressure and joined &lt;a href="http://www.facebook.com/"&gt;Facepoop&lt;/a&gt;. ;-)&lt;/p&gt;

&lt;p&gt;Here’s &lt;a href="http://www.facebook.com/people/Johan_Kiviniemi/635104511"&gt;my profile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.facebook.com/people/Johan_Kiviniemi/635104511" title="My Facebook profile"&gt;&lt;img src="http://badge.facebook.com/badge/635104511.130.444689305.png" alt="My Facebook profile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/96fLr9n6YNU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/shameful-confession/</feedburner:origLink></entry>
    <entry>
    <title>The order of Heroes episodes and graphic novels</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/dkix3C8_4Gc/" />
    <id>http://johan.kiviniemi.name/blag/2007/10/15/the-order-of-heroes-episodes-and-graphic-novels/</id>
    <updated>2007-10-15T16:13:35+03:00</updated>
    <content type="html">&lt;p&gt;The episodes often assume you’ve read the corresponding comics. Occasionally the comics even originally introduce characters used in following episodes.&lt;/p&gt;

&lt;p&gt;This page is meant to help watch and read them in the correct order: &lt;a href="http://heh.fi/heroes/"&gt;The order of Heroes episodes and graphic novels&lt;/a&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/dkix3C8_4Gc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/the-order-of-heroes-episodes-and-graphic-novels/</feedburner:origLink></entry>
    <entry>
    <title>Upstart and interaction with user</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/jkClK5jAsO4/" />
    <id>http://johan.kiviniemi.name/blag/2007/03/21/upstart-and-interaction-with-user/</id>
    <updated>2007-03-20T23:19:01+02:00</updated>
    <content type="html">&lt;p&gt;This page describes an idea about a library and a set of programs that make it possible for normally non-interactive programs such as daemons and system startup jobs to interact with the user when necessary. For example, the &lt;code&gt;fsck&lt;/code&gt; job might show a progress bar and ask questions about repairing a filesystem.&lt;/p&gt;

&lt;p&gt;It is important that multiple programs may present questions simultaneously and the user may answer to them in any order.&lt;/p&gt;

&lt;p&gt;It is essential that the interaction works&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when only a Linux virtual console is in use,&lt;/li&gt;
&lt;li&gt;when a boot splash screen is visible and&lt;/li&gt;
&lt;li&gt;when there’s an active X session.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;General design&lt;/h3&gt;

&lt;p&gt;I’ll use the term &lt;em&gt;application&lt;/em&gt; when talking about system programs that need to interact with the user, and &lt;em&gt;frontend&lt;/em&gt; when talking about programs that present an interface to the user.&lt;/p&gt;

&lt;p&gt;Applications as well as frontends use &lt;code&gt;libwhat&lt;/code&gt; – for lack of a better name – which communicates with &lt;code&gt;whatd&lt;/code&gt; over a UNIX socket. &lt;code&gt;whatd&lt;/code&gt; handles the message passing between applications and frontends.&lt;/p&gt;

&lt;p&gt;When an application creates a new &lt;code&gt;What&lt;/code&gt; instance, it must define a title, under which all its entries are shown. E.g. &lt;code&gt;fsck&lt;/code&gt; with &lt;code&gt;libwhat&lt;/code&gt; support might use&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;what = what_new (sprintf ("Checking filesystem %s", devname));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By default, &lt;code&gt;what_new&lt;/code&gt; blocks until it has successfully connected to &lt;code&gt;whatd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;libwhat&lt;/code&gt; API allows applications to open questions in&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;blocking mode:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;answer = what_choice (what, "Will you marry me?",
                      WHAT_YES, WHAT_NO, "Never!");
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;non-blocking mode:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;question_handle = what_input_nonblock (what, my_callback, my_data,
                                       "What's bothering you?", "");
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;For non-blocking mode, adapters for e.g. glib and Qt main loops will be implemented.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;libwhat&lt;/code&gt; can be used easily from shell scripts using the command line tool:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/sbin/what choice "jackbauerd" "The mainframe matrix has been backtraced
    in order to enhance the subnet node. How should I proceed?" \\
    "Access the protocol stream" "Engage the socket firewall"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It waits for the answer and prints it to the standard output.&lt;/p&gt;

&lt;h3&gt;Frontends&lt;/h3&gt;

&lt;p&gt;When a frontend connects to &lt;code&gt;whatd&lt;/code&gt;, it immediately receives the full list of any currently shown UI elements. After that, it receives messages when applications create new elements, close or change existing ones, or when a question has been answered in another simultaneously running frontend.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;whatd&lt;/code&gt; receives an answer to the same question from multiple frontends, the first one is processed and the rest are ignored. When the user answers a question in a frontend, or when a frontend learns a question has been answered in another frontend, the question in question is removed from the UI. Any questions?&lt;/p&gt;

&lt;p&gt;The user can move between questions freely, and answer to them in any order.&lt;/p&gt;

&lt;h4&gt;&lt;code&gt;what-terminal&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;A plain terminal-based UI. Normally it’s opened in its own virtual console as soon as possible during the system startup. It starts working immediately after &lt;code&gt;whatd&lt;/code&gt; is running.&lt;/p&gt;

&lt;p&gt;A mockup:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────────────────┐
│ Distro branding, blahblah                                                  │
├────────────────────────────────────────────────────────────────────────────┤
│ Checking filesystem /dev/sda1                                              │
│ [#############################                                     ] 43.9% │
│ Do you want set inode 12765 on fire?  [ OK ] [ Cancel ]                    │
├────────────────────────────────────────────────────────────────────────────┤
│ Checking filesystem /dev/sdb1                                              │
│ [###########                                                       ] 16.7% │
├────────────────────────────────────────────────────────────────────────────┤
│ Test entry with a pulsating (moving back and forth) progress bar           │
│ [                                            ########                    ] │
│ What's your social security number? ______________________________________ │
│                                                                            │
│                                                                            │
│                                                                            │
│                                                                            │
│                                                                            │
│                                                                            │
│                                                                            │
│                                                                            │
│                                                                            │
└────────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;&lt;code&gt;what-gtk&lt;/code&gt; and &lt;code&gt;what-qt&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;When a privileged user (e.g. member of the ‘admin’ group, depends on the configuration) starts an X session, the appropriate X frontend is started. It’s invisible, until a progress bar or a question exists. When that is the case, it creates a system tray icon and shows a notification bubble. When the icon is clicked, it opens something like the following mockup:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://johan.kiviniemi.name/tmp/what-gtk" alt="A mockup of the what-gtk UI" /&gt;&lt;/p&gt;

&lt;h4&gt;&lt;code&gt;usplash&lt;/code&gt; and other boot splash programs&lt;/h4&gt;

&lt;p&gt;Boot splash programs should implement &lt;code&gt;libwhat&lt;/code&gt; support as well, so that progress bars and questions are shown in the splash screen.&lt;/p&gt;

&lt;p&gt;A mockup:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://johan.kiviniemi.name/tmp/what-usplash" alt="A mockup of the what-usplash UI" /&gt;&lt;/p&gt;

&lt;h3&gt;Considerations&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;There must be a way for an administrator to prevent any questions from being asked, for example on a public or headless machine. A good solution is defining a default answer for each question, which will be used when the question will not or can not be shown.&lt;/p&gt;

&lt;p&gt;A default answer for each question would also provide a nice UI enhancement: say the movement between questions happens with arrow up/down and the movement between different answers to a multiple choice question happens with arrow left/right. The default answer would always be the one initially selected when moving to a question.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Should there be a mechanism for letting applications affect the order in which their respective UI elements are shown? For example, it might be desired that the progress bar presented by a program that keeps track of the progress of the system startup is always the topmost element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Should unprivileged users be able to see progress bars, even though they aren’t able to answer questions? Should it be possible for applications to present questions that &lt;strong&gt;any&lt;/strong&gt; user may answer? (The answer to both is probably yes.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How should localization be implemented?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;D-Bus instead of whatd? That would be a possible solution, but it would require D-Bus to be one of the first things started during system startup. The D-Bus daemon and libraries would need to reside in the root partition.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/jkClK5jAsO4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/upstart-and-interaction-with-user/</feedburner:origLink></entry>
    <entry>
    <title>Upstart and mounting partitions, part 2</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/sdano5D2kS4/" />
    <id>http://johan.kiviniemi.name/blag/2007/03/19/upstart-and-mounting-partitions-part-2/</id>
    <updated>2007-03-19T13:07:46+02:00</updated>
    <content type="html">&lt;p&gt;There has been a nice discussion about my &lt;a href="http://johan.kiviniemi.name/blag/2007/03/15/upstart-and-mounting-partitions/"&gt;previous post&lt;/a&gt; on the &lt;a href="https://lists.ubuntu.com/mailman/listinfo/upstart-devel"&gt;Upstart mailing list&lt;/a&gt;. Here’s an updated version with the ideas from the list applied.&lt;/p&gt;

&lt;p&gt;The examples still do not check whether the filesystems should be checked automatically, and lack error checking. That will be trivial to add, but for now, I’ll focus on simplicity.&lt;/p&gt;

&lt;h3&gt;/etc/event.d/fsck&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;start on block-device-added

instance

script
    locks="$(sysblock --short --physdev "$DEVNAME")"
    # DEVNAME=/dev/mapper/vg-root → locks="sda sdb" (RAID-1)
    # DEVNAME=/dev/sda1           → locks="sda"

    locking $locks -- fsck -y "$DEVNAME"

    initctl variable append checked-filesystems "$DEVNAME"

    initctl emit filesystem-checked "$DEVNAME" -eDEVNAME
end script
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;/etc/event.d/mount&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;start on filesystem-checked
start on filesystem-mounted

instance

script
    initctl variable list checked-filesystems | while read devname; do
        mountpoint="$(getmntent -fD "$devname")"

        if getmntent --prereqs-satisfied -d "$mountpoint"; then
            mount "$mountpoint"

            initctl variable remove checked-filesystems "$devname"

            initctl emit filesystem-mounted "$mountpoint" \\
                -eDEVNAME="$devname" -eMOUNTPOINT="$mountpoint"
        fi
    done
end script
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;The commands&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;initctl variable&lt;/code&gt; has the following subcommands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;clear NAME&lt;/code&gt;: Removes all entries from NAME. The underlying implementation frees NAME from memory if it becomes empty, and any operations transparently allocate a new list on demand.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set NAME VALUE [...]&lt;/code&gt;: Sets NAME to &lt;code&gt;[VALUE]&lt;/code&gt;, or &lt;code&gt;[VALUE0, VALUE1, ...]&lt;/code&gt; in case of multiple VALUE parameters, overwriting any existing values. Any duplicate entries are ignored.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;append NAME VALUE [...]&lt;/code&gt;: Appends each VALUE to the list NAME. If any of them already exist in NAME, they are ignored.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;remove NAME VALUE [...]&lt;/code&gt;: Removes each VALUE from the list NAME. If any of them do not exist in NAME, they are ignored.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;list NAME&lt;/code&gt;: Prints each entry to stdout on separate lines.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;getmntent&lt;/code&gt; command will grow the following functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--prereqs&lt;/code&gt;: Implies &lt;code&gt;-f&lt;/code&gt; (parse &lt;code&gt;/etc/fstab&lt;/code&gt;). Prints each fstab entry that is a prerequisite of the mountpoint in question, e.g. &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;/var&lt;/code&gt; need to be mounted before &lt;code&gt;/var/tmp&lt;/code&gt; is mounted, if all of them exist in fstab.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--prereqs-satisfied&lt;/code&gt;: Computes the list of prerequisite mounts from &lt;code&gt;/etc/fstab&lt;/code&gt; and exits with the return value of &lt;code&gt;0&lt;/code&gt; if all of them exist in &lt;code&gt;/etc/mtab&lt;/code&gt; (that is, all of them are already mounted), or &lt;code&gt;1&lt;/code&gt; if some of them are still not mounted.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;locking [LOCKNAME ...] -- CMDLINE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;locking(1)&lt;/code&gt; opens and exclusively locks &lt;code&gt;sprintf ("/var/run/locking/%s.%s", sanitize (lockname), sha1sum (lockname))&lt;/code&gt; for each &lt;code&gt;LOCKNAME&lt;/code&gt; and runs the command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;sysblock [--short] [--dev] [--parent|--slaves|--holders|--physdev] [DEVICE ...]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sysblock(1)&lt;/code&gt; maps &lt;code&gt;/dev&lt;/code&gt; nodes to &lt;code&gt;/sys/block&lt;/code&gt; nodes and vice versa. All &lt;code&gt;/dev&lt;/code&gt; paths listed on the command line are converted to their &lt;code&gt;/sys/block&lt;/code&gt; equivalents before processing. When called with the &lt;code&gt;--dev&lt;/code&gt; parameter, the resulting output list of &lt;code&gt;/sys/block&lt;/code&gt; paths is converted to the &lt;code&gt;/dev&lt;/code&gt; equivalents.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;--parent&lt;/code&gt; parameter, it converts &lt;code&gt;/sys/block/foo/bar&lt;/code&gt; to &lt;code&gt;/sys/block/foo&lt;/code&gt;, and &lt;code&gt;/sys/block/baz&lt;/code&gt; to nothing.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;--slaves&lt;/code&gt; or the &lt;code&gt;--holders&lt;/code&gt; parameter, it lists the devices linked from &lt;code&gt;/sys/block/device/slaves&lt;/code&gt; or &lt;code&gt;/sys/block/device/holders&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;--physdev&lt;/code&gt; parameter, it follows each device’s parents or slaves, until it reaches the end of the tree. The final devices it ended up at are listed.&lt;/p&gt;

&lt;p&gt;Usage examples:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sysblock /dev/sda1                             → /sys/block/sda/sda1
sysblock --dev /sys/block/sda/sda1             → /dev/sda1
sysblock --dev sda/sda1                        → /dev/sda1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(&lt;code&gt;/sys/block&lt;/code&gt; prefix is assumed)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sysblock --short /dev/sda1                     → sda/sda1
sysblock --short --dev /sys/block/sda/sda1     → sda1
sysblock --short --dev sda/sda1                → sda1

sysblock --short /dev/mapper/vg-root           → dm-0
sysblock --short --slaves /dev/mapper/vg-root  → md0
sysblock --short --slaves md0                  → sda/sda1 sdb/sdb1
sysblock --short --parent sda/sda1 sdb/sdb1    → sda sdb

sysblock --short --physdev /dev/mapper/vg-root → sda sdb

sysblock --dev --physdev /dev/mapper/vg-root   → /dev/sda /dev/sdb
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/sdano5D2kS4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/upstart-and-mounting-partitions-part-2/</feedburner:origLink></entry>
    <entry>
    <title>Upstart and mounting partitions</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/uqVKQNVzPUM/" />
    <id>http://johan.kiviniemi.name/blag/2007/03/15/upstart-and-mounting-partitions/</id>
    <updated>2007-03-15T13:06:40+02:00</updated>
    <content type="html">&lt;p&gt;(Also see the &lt;a href="http://johan.kiviniemi.name/blag/2007/03/19/upstart-and-mounting-partitions-part-2/"&gt;updated version of this post&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;This piece of text is a braindump about how one might begin to implement the mounting of partitions with &lt;a href="http://upstart.ubuntu.com/"&gt;Upstart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the jobs below, there are some things that aren’t implemented very nicely, especially the computation of prerequisite mounts for mountpoints (e.g. &lt;code&gt;/var&lt;/code&gt; needs to be mounted before &lt;code&gt;/var/run&lt;/code&gt; is mounted). I think there is a consensus that such functionality that allows a cleaner implementation of the job needs to be implemented to Upstart itself.&lt;/p&gt;

&lt;p&gt;Exactly what does that mean? Well, feel free to share &lt;em&gt;your&lt;/em&gt; ideas at the &lt;a href="https://lists.ubuntu.com/mailman/listinfo/upstart-devel"&gt;Upstart mailing list&lt;/a&gt;. One way to do it might be a &lt;em&gt;metajob&lt;/em&gt; that parses &lt;code&gt;fstab&lt;/code&gt; and programmatically builds a job for each mountpoint that waits for the appropriate prerequisites.&lt;/p&gt;

&lt;p&gt;Whether partitions should actually be automounted or not isn’t checked yet. The &lt;a href="http://codebrowse.launchpad.net/~keybuk/upstart/replacement-initscripts/files/scott%40netsplit.com-20070213145300-ulgf4s7og5yocm6e?file_id=src-20070213144831-o2stwz7zt81kbh8a-1"&gt;getmntent&lt;/a&gt; command probably should be modified so that it’s possible to reverse the query – e.g. search for entries that do &lt;em&gt;not&lt;/em&gt; have ‘&lt;code&gt;noauto&lt;/code&gt;’ in the options.&lt;/p&gt;

&lt;p&gt;The yet-to-be-written commands &lt;code&gt;locking&lt;/code&gt;, &lt;code&gt;lread&lt;/code&gt;, &lt;code&gt;lwrite&lt;/code&gt;, &lt;code&gt;lfilter&lt;/code&gt; and &lt;code&gt;sysblock&lt;/code&gt; are explained in the end.&lt;/p&gt;

&lt;h3&gt;/etc/event.d/fsck&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;start on block-device-added

instance

script
    locks="$(sysblock --short --physdev "$BLOCKDEV")"
    # BLOCKDEV=/dev/mapper/vg-root → locks="sda sdb" (RAID-1)
    # BLOCKDEV=/dev/sda1           → locks="sda"

    locking $locks -- fsck -y "$BLOCKDEV"

    echo "$BLOCKDEV" | lwrite --append /var/run/checked-filesystems

    initctl emit filesystem-checked -eBLOCKDEV
end script
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;/etc/event.d/mount&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;start on filesystem-checked
start on filesystem-mounted

instance

script
    prereqs_satisfied() {
        local mountpoint="$1"
        getmntent -fD | while read dir; do
            case "$mountpoint" in
            "$dir"/*)
                # It is a prerequisite. Is it mounted?
                getmntent -mqd "$dir" || return 1
            esac
        done
        return 0
    }

    blockdevs=$(lread /var/run/checked-filesystems)

    for blockdev in $blockdevs; do
        mountpoint="$(getmntent -fD "$blockdev")"

        if prereqs_satisfied "$mountpoint"; then
            mount "$mountpoint"

            lfilter /var/run/checked-filesystems grep -Fxv "$blockdev"

            initctl emit filesystem-mounted -eBLOCKDEV="$blockdev" \\
                -eMOUNTPOINT="$mountpoint"
        fi
    done
end script
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;The commands used in the examples&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;locking [LOCKNAME ...] -- CMDLINE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;locking(1)&lt;/code&gt; opens and exclusively locks &lt;code&gt;sprintf ("/var/run/locking/%s.%s", sanitize (lockname), sha1sum (lockname))&lt;/code&gt; for each &lt;code&gt;LOCKNAME&lt;/code&gt; and runs the command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;lread [FILENAME ...]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For each &lt;code&gt;FILENAME&lt;/code&gt;, &lt;code&gt;lread(1)&lt;/code&gt; opens it, places a shared lock on it and cats it to stdout.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;lwrite [--append] FILENAME&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lwrite(1)&lt;/code&gt; opens the file, places an exclusive lock on it and cats stdin to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;lfilter FILENAME CMDLINE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lfilter(1)&lt;/code&gt; places an exclusive lock on the file, pipes the contents to the command’s stdin and replaces the contents with the command’s output.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;sysblock [--short] [--dev] [--parent|--slaves|--holders|--physdev] [DEVICE ...]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sysblock(1)&lt;/code&gt; maps &lt;code&gt;/dev&lt;/code&gt; nodes to &lt;code&gt;/sys/block&lt;/code&gt; nodes and vice versa. All &lt;code&gt;/dev&lt;/code&gt; paths listed on the command line are converted to their &lt;code&gt;/sys/block&lt;/code&gt; equivalents before processing. When called with the &lt;code&gt;--dev&lt;/code&gt; parameter, the resulting output list of &lt;code&gt;/sys/block&lt;/code&gt; paths is converted to the &lt;code&gt;/dev&lt;/code&gt; equivalents.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;--parent&lt;/code&gt; parameter, it converts &lt;code&gt;/sys/block/foo/bar&lt;/code&gt; to &lt;code&gt;/sys/block/foo&lt;/code&gt;, and &lt;code&gt;/sys/block/baz&lt;/code&gt; to nothing.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;--slaves&lt;/code&gt; or the &lt;code&gt;--holders&lt;/code&gt; parameter, it lists the devices linked from &lt;code&gt;/sys/block/device/slaves&lt;/code&gt; or &lt;code&gt;/sys/block/device/holders&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;--physdev&lt;/code&gt; parameter, it follows each device’s parents or slaves, until it reaches the end of the tree. The final devices it ended up at are listed.&lt;/p&gt;

&lt;p&gt;Usage examples:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sysblock /dev/sda1                             → /sys/block/sda/sda1
sysblock --dev /sys/block/sda/sda1             → /dev/sda1
sysblock --dev sda/sda1                        → /dev/sda1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(&lt;code&gt;/sys/block&lt;/code&gt; prefix is assumed)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sysblock --short /dev/sda1                     → sda/sda1
sysblock --short --dev /sys/block/sda/sda1     → sda1
sysblock --short --dev sda/sda1                → sda1

sysblock --short /dev/mapper/vg-root           → dm-0
sysblock --short --slaves /dev/mapper/vg-root  → md0
sysblock --short --slaves md0                  → sda/sda1 sdb/sdb1
sysblock --short --parent sda/sda1 sdb/sdb1    → sda sdb

sysblock --short --physdev /dev/mapper/vg-root → sda sdb

sysblock --dev --physdev /dev/mapper/vg-root   → /dev/sda /dev/sdb
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/uqVKQNVzPUM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/upstart-and-mounting-partitions/</feedburner:origLink></entry>
    <entry>
    <title>Making X report the mouse position with subpixel precision</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/utZwfCBax0Q/" />
    <id>http://johan.kiviniemi.name/blag/2007/02/23/making-x-report-the-mouse-position-with-subpixel-precision/</id>
    <updated>2007-02-23T17:20:23+02:00</updated>
    <content type="html">&lt;p&gt;The precision of mice is generally much better than the precision of screens. I’m not very familiar with the internals of X, but AFAIK it only reports the mouse position to clients at the screen’s precision.&lt;/p&gt;

&lt;p&gt;My modest proposal is to make it possible for X clients to know the mouse position with subpixel precision via an extension.&lt;/p&gt;

&lt;p&gt;As I’m surely not the only one who has had this idea, perhaps it’s already being designed or even implemented. If that is the case, please excuse my ignorance :-). Otherwise, I hope this idea reaches the &lt;a href="http://x.org/"&gt;Xorg&lt;/a&gt; folks.&lt;/p&gt;

&lt;p&gt;(I started a &lt;a href="http://lists.freedesktop.org/archives/xorg/2007-February/022092.html"&gt;discussion&lt;/a&gt; about this at the Xorg mailing list.)&lt;/p&gt;

&lt;p&gt;The feature would have a couple of benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Scrollbars&lt;/p&gt;

&lt;p&gt;On a long page, a single pixel of scrollbar movement may cause the content to scroll a huge amount. The entire design of scrollbars could be better, but even with the current design, subpixel accuracy would make it easier to control the scrolling.&lt;/p&gt;

&lt;p&gt;GUI toolkits such as Gtk and Qt would have to be modified to support this feature.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transformed windows&lt;/p&gt;

&lt;p&gt;With some recent &lt;a href="http://lists.freedesktop.org/archives/xorg/2007-February/021515.html"&gt;changes&lt;/a&gt;, it’s possible for a window to have an arbitrary coordinate map, to which input events are transformed. This allows windows to be e.g. scaled or rotated in three dimensions and still receive input correctly.&lt;/p&gt;

&lt;p&gt;Is the coordinate transformation going to happen from the screen’s pixel grid? If a window is scaled down, that would mean that all its pixels might not be reachable.&lt;/p&gt;

&lt;p&gt;If the transformed mouse position is calculated from the subpixel screen coordinates, this problem would vanish.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Image processing&lt;/p&gt;

&lt;p&gt;In programs such as &lt;a href="http://gimp.org/" title="The GNU Image Manipulation Program"&gt;GIMP&lt;/a&gt;, there are obvious benefits from being able to draw and move things with subpixel precision, especially if the image has been zoomed out.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Others&lt;/p&gt;

&lt;p&gt;There are many other programs that can benefit from this feature: 3D modeling software, games, screen magnification, you name it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Implementation&lt;/h3&gt;

&lt;p&gt;First of all, the X server would need to handle mouse coordinates internally as floating-point numbers, not integers. The screen can be thought as if it consisted of X·Y rectangles, all of which are the size of the corresponding pixel.&lt;/p&gt;

&lt;p&gt;On a 1600×1200 screen, the internal coordinates would satisfy&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0.0 ≤ x &amp;lt; 1600.0&lt;/li&gt;
&lt;li&gt;0.0 ≤ y &amp;lt; 1200.0&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Temporarily ignoring acceleration, it would be very simple to assume or query the DPI value(s) for the mouse/mice and assume or query the DPI value(s) for the display(s). As I mentioned earlier, the former is generally much higher than the latter.&lt;/p&gt;

&lt;p&gt;Floating-point movement of the mouse can then be converted to the floating-point screen coordinates very easily using the DPI ratio. The implementation of acceleration on top of that shouldn’t be any trouble either.&lt;/p&gt;

&lt;p&gt;Of course, normal X clients (that don’t need to care about the floating-point coordinates) would receive the coordinates rounded to the “current” pixel’s integer coordinates (simple number truncation).&lt;/p&gt;

&lt;p&gt;Things like only sending motion notifications when the cursor crosses a pixel boundary have to be thought about, but they shouldn’t be a problem.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/utZwfCBax0Q" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/making-x-report-the-mouse-position-with-subpixel-precision/</feedburner:origLink></entry>
    <entry>
    <title>Structure and Interpretation of Computer Programs</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/SOsUEoNGB9E/" />
    <id>http://johan.kiviniemi.name/blag/2007/02/13/structure-and-interpretation-of-computer-programs/</id>
    <updated>2007-02-13T02:47:37+02:00</updated>
    <content type="html">&lt;p&gt;The &lt;a href="http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/"&gt;Structure and Interpretation of Computer Programs&lt;/a&gt; video lectures by Hal Abelson and Gerald Jay Sussman, recorded in the 1980s, are absolutely fascinating.&lt;/p&gt;

&lt;p&gt;If you’re a programmer, you may learn new ideas and ways to think from those lectures, and become better at what you do.&lt;/p&gt;

&lt;p&gt;If you learned to program in the schools of today, you &lt;strong&gt;will&lt;/strong&gt; learn new ideas and ways to think, and become better at what you do. ;-)&lt;/p&gt;

&lt;p&gt;Alas, some of the files contain a &lt;em&gt;very&lt;/em&gt; muffled audio track.&lt;/p&gt;

&lt;p&gt;Using the command&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;mplayer -af equalizer=-12:-12:-12:0:0:0:12:12:-6:-12 Lecture-xx.avi
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;to play them seems to help a lot. (See the &lt;a href="http://www.mplayerhq.hu/"&gt;mplayer&lt;/a&gt; website.)&lt;/p&gt;

&lt;p&gt;For other players with a graphic equalizer, this means&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;canceling frequencies &amp;lt; 200 Hz (the overly emphasized bass frequencies)&lt;/li&gt;
&lt;li&gt;amplifying frequencies about 2 kHz – 4 kHz (including the consonants in speech)&lt;/li&gt;
&lt;li&gt;attenuating frequencies ≥ 8 kHz (the noise)&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/SOsUEoNGB9E" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/structure-and-implementation-of-computer-programs/</feedburner:origLink></entry>
    <entry>
    <title>Program of the fortnight: atool</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/ensd2ak1LFQ/" />
    <id>http://johan.kiviniemi.name/blag/2007/02/01/atool/</id>
    <updated>2007-02-01T03:37:00+02:00</updated>
    <content type="html">&lt;p&gt;The &lt;code&gt;aunpack&lt;/code&gt; utility from the &lt;a href="http://www.nongnu.org/atool/"&gt;atool&lt;/a&gt; package extracts an archive, automatically choosing an appropriate unpacker depending on the archive’s type.&lt;/p&gt;

&lt;p&gt;If there are multiple files in the archive’s root directory, &lt;code&gt;aunpack&lt;/code&gt; extracts them to a new directory. Very handy.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/ensd2ak1LFQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/atool/</feedburner:origLink></entry>
    <entry>
    <title>A room illusion with POV-Ray</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/hG7HfC-gt1U/" />
    <id>http://johan.kiviniemi.name/blag/2007/01/15/room-illusion/</id>
    <updated>2007-01-14T23:19:55+02:00</updated>
    <content type="html">&lt;p&gt;I’ve been planning to model a 3D scene and paint it to my bedroom walls, ceiling and floor so that the three-dimensional surroundings are apparent if you look at the room from the correct position.&lt;/p&gt;

&lt;p&gt;Finally I got around to testing what it would take to render the images that should be painted to the walls.&lt;/p&gt;

&lt;p&gt;It was quite simple, actually.&lt;/p&gt;

&lt;h3&gt;The original scene&lt;/h3&gt;

&lt;p&gt;First create a scene normally. Choose the camera position, that’s the position from where the illusion will work.&lt;/p&gt;

&lt;p&gt;Then imagine an accurate model of the room around the camera. I chose the camera position to be at the door. I can ignore the wall adjacent to the door, so I’ll need to generate five textures: the wall opposite to the door, the walls to the left and to the right, the ceiling and the floor.&lt;/p&gt;

&lt;p&gt;To generate the textures, here’s a &lt;a href="http://johan.kiviniemi.name/stuff/povray/render_plane.inc"&gt;POV-Ray macro I came up with&lt;/a&gt;. It sets the camera so that the &lt;a href="http://povray.org/documentation/images/reference/perspcam.gif"&gt;image plane&lt;/a&gt; is equal to an imaginary plane (one of the walls).&lt;/p&gt;

&lt;p&gt;For example, if the camera position is &lt;code&gt;&amp;lt;0,1.8,-2.5&amp;gt;&lt;/code&gt; and the floor is imagined to be &lt;code&gt;box { &amp;lt;-4,0,-2.5&amp;gt; &amp;lt;4,0,2.5&amp;gt; }&lt;/code&gt;, you’d render the corresponding texture with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Render_Plane_From(&amp;lt;0,1.8,-2.5&amp;gt;, &amp;lt;0,0,0&amp;gt;, &amp;lt;8,0,0&amp;gt;, &amp;lt;0,0,5&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;where &lt;code&gt;&amp;lt;0,0,0&amp;gt;&lt;/code&gt; is the center of the plane, &lt;code&gt;&amp;lt;8,0,0&amp;gt;&lt;/code&gt; is the “right” vector and &lt;code&gt;&amp;lt;0,0,5&amp;gt;&lt;/code&gt; is the “up” vector.&lt;/p&gt;

&lt;h3&gt;The test scene&lt;/h3&gt;

&lt;p&gt;Next, create an actual model of the room to test the textures that were created. Just create the planes and apply the textures to them.&lt;/p&gt;

&lt;p&gt;Remember: the camera position must be exactly the same as it was in the original scene for the illusion to work. The direction and the angle do not matter.&lt;/p&gt;

&lt;p&gt;The following images demonstrate the effect. One is from the correct camera position and the other is from a “wrong” camera position. Ignore the ugly original scene, it was just hastily thrown together to test this technique.&lt;/p&gt;

&lt;div class="clearfix"&gt;
  &lt;div class="thumbnail"&gt;
    &lt;a class="lightbox" href="http://johan.kiviniemi.name/pictures/misc/room-illusion/room-right_position/original" title="Room (right camera position)"&gt;&lt;img src="http://johan.kiviniemi.name/pictures/misc/room-illusion/room-right_position/thumbnail" alt="Room (right camera position)" /&gt;&lt;/a&gt;          
  &lt;/div&gt;
  &lt;div class="thumbnail"&gt;
    &lt;a class="lightbox" href="http://johan.kiviniemi.name/pictures/misc/room-illusion/room-wrong_position/original" title="Room (wrong camera position)"&gt;&lt;img src="http://johan.kiviniemi.name/pictures/misc/room-illusion/room-wrong_position/thumbnail" alt="Room (wrong camera position)" /&gt;&lt;/a&gt;          
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Check out the &lt;a href="http://soijabanaani.net/tmp/room-test1.avi"&gt;video&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m probably not going to manage to model a good-looking scene and paint it to the room any time soon, but at least now I know how to do it. :-)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/hG7HfC-gt1U" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/room-illusion/</feedburner:origLink></entry>
    <entry>
    <title>”Tiedät varmaan, miksi soitan”</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/s9RIohL1FmI/" />
    <id>http://johan.kiviniemi.name/blag/2007/01/02/tiedat-varmaan-miksi-soitan/</id>
    <updated>2007-01-02T00:13:07+02:00</updated>
    <content type="html">&lt;p&gt;Tapahtui muutama vuosi sitten:&lt;/p&gt;

&lt;p&gt;Puhelimeni soi. Vastaan. Mieshenkilö esittelee itsensä: ”Tässä Etunimi Sukunimi siitä ja siitä puhelinyhtiöstä. Tiedät varmaan, miksi soitan.”&lt;/p&gt;

&lt;p&gt;Olin ollut kuukausia aiemmin kyseisessä firmassa kesätöissä. Yritän miettiä, että mitä soittaja mahtaa ajaa takaa, ja miksei hän vain kerro asiaansa.&lt;/p&gt;

&lt;p&gt;Lyhyen tauon jälkeen vastaan, etten kyllä keksi.&lt;/p&gt;

&lt;p&gt;Soittaja väittää, että olen murtautunut heidän palvelimeensa ja asentanut &lt;a href="http://en.wikipedia.org/wiki/Rootkit"&gt;rootkitin&lt;/a&gt;. En tiennytkään tehneeni moista. Hänellä on kuulemma varmat todisteet teostani. Pyydän päästä juttelemaan kasvokkain jonkun kanssa paikan päällä, ja sovimme niin.&lt;/p&gt;

&lt;p&gt;Tapaan sovittuna ajankohtana puhelinyhtiöllä henkilön, jonka alaisuudessa olin toiminut kesätyöjaksona. Kerron hänelle, etten ole tehnyt väitettyä tietomurtoa, eikä minulla ole mitään motivaatiota tehdä sellaista. Hän näyttää uskovan minua. Saan kuulla heidän havainneen, että koneelle oli murtauduttu – muistaakseni sitä oli käytetty roskapostin lähettämiseen. Minähän vihaan spämmiä sydämeni pohjasta.&lt;/p&gt;

&lt;p&gt;Saan myös kuulla, että heillä on sopimus erään toisen firman kanssa: kyseinen firma ylläpitää heidän palvelimiaan hoitaen tietoturvapäivitykset ymv. He olivat vieneet saastuneen koneen ko. firmalle tutkittavaksi ja sieltä oli ilmoitettu, että tunnus &lt;code&gt;johkiv&lt;/code&gt; oli tietomurron takana.&lt;/p&gt;

&lt;p&gt;Muistan, että jossain vaiheessa kesätyöjaksoa minulle oli tehty tunnus heidän nimipalvelimelleen työtehtävää varten. En ollut muistanut koko tunnuksen olemassaoloa. (Ihmettelen myös, miksi tunnusta ei oltu poistettu palvelimelta työn tehtyäni.)&lt;/p&gt;

&lt;p&gt;Miettiessäni, miksi koneen tutkineesta firmasta väitettäisiin, että &lt;em&gt;minä&lt;/em&gt; olisin murtautumisen takana, seuraavat mahdollisuudet tulevat mieleen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tunkeutuja &lt;a href="http://en.wikipedia.org/wiki/Brute_force_attack"&gt;bruteforcetti&lt;/a&gt; salasanani. Pidän tätä hyvin epätodennäköisenä, koska pyrin käyttämään hankalasti arvattavia salasanoja. Mikäli koneelle kuitenkin päästiin alunperin juuri minun käyttäjätunnuksellani, tunkeutujan piti vielä saada jotenkin pääkäyttäjän oikeudet rootkitin asentaakseen. Siinä tapauksessa koneella piti olla vanha, tietoturvareikäinen versio jostakin ohjelmasta, josta päästäänkin seuraavaan kohtaan:&lt;/li&gt;
&lt;li&gt;Koneella oli vanha, tietoturvareikäinen versio jostakin palvelinohjelmasta (esim. ssh, bind), jota tunkeutuja hyödynsi. Tässä tapauksessa &lt;strong&gt;firma, jonka he olivat palkanneet hoitamaan palvelimen tietoturvan, ei ollut hoitanut tehtäväänsä&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;Tätä kautta tunkeutuja sai suoraan pääkäyttäjän oikeudet. Miten sitten minun tunnukseeni päädyttiin?

&lt;ul&gt;
&lt;li&gt;On lievä mahdollisuus, että tunkeutuja purki palvelimelle tar-paketin, joka sisälsi sillä käyttäjätunnuksen ID-numerolla olevia tiedostoja, joka sattui vastaamaan tunnusta &lt;code&gt;johkiv&lt;/code&gt; kyseisellä koneella.&lt;/li&gt;
&lt;li&gt;On jonkinasteinen mahdollisuus, että koneen tutkineessa firmassa valittiin tunnukseni sijaiskärsijäksi sen sijaan, että he olisivat myöntäneet hoitaneensa ylläpitotehtävänsä heikosti.&lt;/li&gt;
&lt;li&gt;Ehkäpä firmasta kerrottiin, että ”joku” koneen käyttäjätunnuksista oli tietomurron takana sen sijaan, että he olisivat myöntäneet laiminlyöntinsä, ja jollakin puhelinyhtiön väestä välähti, että ”se oli varmaan tuo kesätyöläinen”.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Joka tapauksessa minua ei enää syytetty tuon jälkeen. Loppu hyvin, kaikki hyvin.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/s9RIohL1FmI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/tiedat-varmaan-miksi-soitan.fi/</feedburner:origLink></entry>
    <entry>
    <title>Fun with dc</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/UV1MpFDiCmA/" />
    <id>http://johan.kiviniemi.name/blag/2006/12/28/fun-with-dc/</id>
    <updated>2006-12-28T20:43:31+02:00</updated>
    <content type="html">&lt;p&gt;Something I wrote a couple of years ago:&lt;/p&gt;

&lt;p&gt;ASCII &lt;a href="http://en.wikipedia.org/wiki/Mandelbrot_set"&gt;Mandelbrot&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[lolssdsl0lqx]sx[1+lddd*lld*-ls+dsdrll2**lo+dsld*rd*+4&amp;lt;kd15&amp;gt;q]sq[q
]9ksk[d77/3*2-ss47lxx-P1+d78&amp;gt;0]s00[d23/.5-3*so0l0xr10P1+d24&amp;gt;u]dsux
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Quine_(computing)"&gt;Quine&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[91PP[dx]93PP]dx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(&lt;a href="http://en.wikipedia.org/wiki/Dc_(Unix)"&gt;dc&lt;/a&gt; is an &lt;a href="http://en.wikipedia.org/wiki/Reverse_Polish_notation" title="Reverse Polish notatiton"&gt;RPN&lt;/a&gt; calculator. It’s one of the oldest Unix utilities.)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/UV1MpFDiCmA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/fun-with-dc/</feedburner:origLink></entry>
    <entry>
    <title>Emacs key bindings in Gtk apps</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/KveOyz3J7FY/" />
    <id>http://johan.kiviniemi.name/blag/2006/12/26/emacs-key-bindings-in-gtk-apps/</id>
    <updated>2006-12-26T17:04:14+02:00</updated>
    <content type="html">&lt;pre&gt;&lt;code&gt;echo 'gtk-key-theme-name = "Emacs"' &amp;gt;&amp;gt;~/.gtkrc-2.0
gconftool -t string --set /desktop/gnome/interface/gtk_key_theme Emacs
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now ^W does what it’s supposed to do, instead of e.g. accidentally closing the tab in Firefox. Yay.&lt;/p&gt;

&lt;p&gt;What I really want are Vim key bindings in all text boxes, though.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/KveOyz3J7FY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/emacs-key-bindings-in-gtk-apps/</feedburner:origLink></entry>
    <entry>
    <title>Ananasmurska – Excellence™</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/vIQXxCrHGcU/" />
    <id>http://johan.kiviniemi.name/blag/2006/12/26/excellence/</id>
    <updated>2006-12-25T22:48:27+02:00</updated>
    <content type="html">&lt;p&gt;&lt;a href="http://dailymodel.blogspot.com/"&gt;Felor&lt;/a&gt; kindly captured a video of the realtime Excel™ demo Excellence™ by &lt;a href="http://ananasmurska.org/"&gt;Ananasmurska&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The demo placed 1st in the BASIC demo competition at &lt;a href="http://altparty.org/"&gt;Alternative Party&lt;/a&gt; 2004.&lt;/p&gt;

&lt;p&gt;Stream the video at &lt;a href="http://www.youtube.com/watch?v=_whSnPErl7c"&gt;YouTube&lt;/a&gt; or &lt;a href="http://soijabanaani.net/tmp/ananasmurska/ananasmurska-excellence.avi"&gt;download&lt;/a&gt; it.&lt;/p&gt;

&lt;div id="video"&gt;
  &lt;p&gt;Your browser doesn't seem to support the embedded video player. Click the link above to download the video.&lt;/p&gt;
&lt;/div&gt;

&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/vIQXxCrHGcU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/excellence/</feedburner:origLink></entry>
    <entry>
    <title>Ruby vs. Python (suomeksi)</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/5WzRMu0-PuE/" />
    <id>http://johan.kiviniemi.name/blag/2006/12/10/ruby-vs-python.fi/</id>
    <updated>2006-12-10T00:26:45+02:00</updated>
    <content type="html">&lt;p&gt;(&lt;a href="/blag/ruby-vs-python/"&gt;In English&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Pidin aikoinaan Pythonia miellyttävimpänä niistä tulkattavista kielistä, joihin olin tutustunut. Sitten tutustuin Rubyyn – ja sitä miellyttävämpää kieltä en ole vielä löytänyt. Tällä sivulla on perusteluita mielipiteelleni.&lt;/p&gt;

&lt;p&gt;Ennen kuin siirryn asiaan, on paras mainita, että Ruby &lt;em&gt;on&lt;/em&gt; tällä hetkellä hitaampi kuin Python. Toisaalta Python on hitaampi kuin C. Ehkä Ruby soveltuu parhaiten projektillesi, ehkä ei.&lt;/p&gt;

&lt;p&gt;Itse en ole vielä törmännyt tilanteeseen, jossa Ruby ei olisi riittävän nopea. YMMV. Huomaa myös, että on helppo kirjoittaa pullonkaulat uudelleen C:llä profiloituaan koodin.&lt;/p&gt;

&lt;h3&gt;Yksityiset metodit ja muuttujat&lt;/h3&gt;

&lt;p&gt;Saadakseen Pythonissa metodit ja instanssimuuttujat yksityisiksi, joutuu &lt;strong&gt;aina&lt;/strong&gt; kirjoittamaan &lt;code&gt;__&lt;/code&gt; nimen eteen. Rubyssä instanssimuuttujat ovat vakiona yksityisiä. &lt;code&gt;private&lt;/code&gt;-metodikutsun jälkeen määritellyt metodit ovat yksityisiä.&lt;/p&gt;

&lt;h3&gt;Funktiot ja metodit&lt;/h3&gt;

&lt;p&gt;Rubyssä ei ole erikseen funktioita ja metodeja; kaikki ovat metodeja. Esimerkkinä Pythonissa &lt;code&gt;len()&lt;/code&gt; on funktio, mutta &lt;code&gt;items()&lt;/code&gt; on metodi. Tällaiset epäkonsistentit ratkaisut tuovat mieleen &lt;a href="http://tnx.nl/php.jpg" title="PHP – Training wheels without the bike"&gt;PHP:n&lt;/a&gt;, &lt;em&gt;*hrrr*&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Python:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string = 'Hello world'
print string.count('o'), len(string)  # prints 2, 11 – why not string.len()?
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ruby:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string = 'Hello world'
puts string.count('o'), string.length  # prints 2, 11
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;”&lt;code&gt;self&lt;/code&gt;”&lt;/h3&gt;

&lt;p&gt;Pythonissa metodin määrittelyn ensimmäiseksi parametriksi joutuu kirjoittamaan &lt;code&gt;self&lt;/code&gt; Perl-tyyliin. Kaiken lisäksi Python ei edes vaadi kyseisen muuttujan nimen olevan &lt;code&gt;self&lt;/code&gt;, se voi olla Pythonin puolesta vaikka &lt;code&gt;kakka_rules&lt;/code&gt;. Rubyssä &lt;code&gt;self&lt;/code&gt; on samalla lailla automaattisesti saatavilla kuin C++:ssa &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lisäksi metodikutsun &lt;code&gt;self.method&lt;/code&gt; voi lyhentää muotoon &lt;code&gt;method&lt;/code&gt;, koska &lt;code&gt;self&lt;/code&gt; on vakio-”receiver”.&lt;/p&gt;

&lt;h3&gt;Instanssimuuttujien merkintä&lt;/h3&gt;

&lt;p&gt;Pythonissa yksityiset instanssimuuttujat joutuu kirjoittamaan turhauttavan pitkässä muodossa &lt;code&gt;self.__foobar&lt;/code&gt;. Rubyssä muoto on &lt;code&gt;@foobar&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Monet python-ohjelmoijat, joiden kanssa olen keskustellut, sortuvat vain päästäkseen vähemmällä kirjoittamisella tekemään sellaisista muuttujista (ja metodeista) julkisia, joilla ei olisi mitään syytä olla julkisia. Sitten he sanovat, että se on hyvä asia, ”the Python way”. No, heillä on oikeus mielipiteeseensä.&lt;/p&gt;

&lt;h3&gt;Rubyssä ”everything is an object”&lt;/h3&gt;

&lt;p&gt;Jopa numerot. Se mahdollistaa seuraavankaltaisten asioiden tekemisen:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;4.megabytes &amp;gt; 2345.kilobytes          # ⇒ true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Tai:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;3.weeks.ago                           # ⇒ Sun Nov 19 09:39:05 EET 2006
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Edellisen esimerkin &lt;code&gt;Numeric#weeks&lt;/code&gt; on määritelty seuraavasti: &lt;code&gt;self * 7.days&lt;/code&gt;. Voinet arvata, miten &lt;code&gt;Numeric#days&lt;/code&gt;, &lt;code&gt;#hours&lt;/code&gt; jne. ovat määritelty. :-)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Numeric#ago&lt;/code&gt; on &lt;code&gt;Time.now - self&lt;/code&gt;, ts. se palauttaa &lt;code&gt;Time&lt;/code&gt;-olion.&lt;/p&gt;

&lt;h3&gt;Koodiblokit&lt;/h3&gt;

&lt;p&gt;Rubyssä on &lt;em&gt;todella&lt;/em&gt; nätti syntaksi koodiblokin antamiseen parametriksi metodikutsulle (ks. myös &lt;a href="http://martinfowler.com/bliki/Closure.html"&gt;closure&lt;/a&gt;; funktioviittaus &lt;a href="http://mail.python.org/pipermail/python-list/2004-July/270951.html"&gt;ei ole yhtä kuin closure&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Kuvitteellinen esimerkki:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;time_tomorrow = TimeTravel.travel(1.day.from_now) { Time.now }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Meillä on metodi &lt;code&gt;TimeTravel.travel&lt;/code&gt;, jolle annetaan parametreiksi &lt;strong&gt;ajankohta&lt;/strong&gt; sekä &lt;strong&gt;koodiblokki&lt;/strong&gt;. Metodi voi tehdä mitä tahansa haluaa saamallaan blokilla: suorittaa jotakin ja ajaa sitten blokin; suorittaa blokin moneen kertaan; laittaa blokin talteen myöhempää suoritusta varten jne.&lt;/p&gt;

&lt;p&gt;Tässä tapauksessa metodi matkustaa aikakoneella määriteltyyn aikaan, suorittaa
sitten blokin ja lopuksi palauttaa blokin palauttaman arvon.&lt;/p&gt;

&lt;p&gt;Tässä esimerkissä blokki palauttaa &lt;code&gt;Time.now&lt;/code&gt;, joka päätyy &lt;code&gt;time_tomorrow&lt;/code&gt;-muuttujaan.&lt;/p&gt;

&lt;p&gt;Mahdollisuus antaa blokki metodikutsulle tarjoaa tehokkaan tavan tehdä monia asioita. Kätevä syntaksi tekee siitä kivaa.&lt;/p&gt;

&lt;h4&gt;Ensimmäinen tosielämän esimerkki: &lt;code&gt;each&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Useat luokat, kuten sisäänrakennetut &lt;a href="http://ruby-doc.org/core/classes/Array.html"&gt;Array&lt;/a&gt;, &lt;a href="http://ruby-doc.org/core/classes/Hash.html"&gt;Hash&lt;/a&gt;, &lt;a href="http://ruby-doc.org/core/classes/IO.html"&gt;IO&lt;/a&gt; ja &lt;a href="http://ruby-doc.org/core/classes/Dir.html"&gt;Dir&lt;/a&gt;, toteuttavat &lt;code&gt;each&lt;/code&gt;-metodin. Se yksinkertaisesti iteroi sen yli, mitä olio sattuu ”sisältämään”, suorittaen annetun blokin kerran jokaista elementtiä kohti.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Array#each&lt;/code&gt; iteroi taulukon elementtien yli. &lt;code&gt;Hash#each&lt;/code&gt; iteroi avain/arvo-parien yli. &lt;code&gt;IO#each&lt;/code&gt; iteroi &lt;code&gt;IO&lt;/code&gt;-olion vastaanottamien rivien yli (avoimesta tiedostosta, HTTP-yhteydestä jne). &lt;code&gt;Dir#each&lt;/code&gt; iteroi hakemistolistauksen yli sitä mukaa, kun sitä vastaanotetaan.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Iterate over the elements of an array.
[42, 'blah', Time.now].each do |element|
  puts element
end

# Iterate over the file line by line.
open('filename').each do |line|
  puts line
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Saatat miettiä, mitä hyötyä on &lt;code&gt;each&lt;/code&gt;ista, kun voisi vain käyttää &lt;code&gt;for&lt;/code&gt;-rakennetta useiden muiden kielien tapaan.&lt;/p&gt;

&lt;p&gt;Rubyssä on sisäänrakennettu &lt;a href="http://en.wikipedia.org/wiki/Mixin"&gt;mixin&lt;/a&gt; nimeltään &lt;a href="http://ruby-doc.org/core/classes/Enumerable.html"&gt;Enumerable&lt;/a&gt;. Voit sisällyttää sen mihin tahansa luokkaan &lt;code&gt;include&lt;/code&gt;-metodilla, ja jos luokkasi määrittelee &lt;code&gt;each&lt;/code&gt;-metodin, saat nipun erittäin käytännöllisiä metodeja ”ilmaiseksi”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;array.all? {|item| item &amp;lt; 42 }&lt;/code&gt; palauttaa arvon &lt;code&gt;true&lt;/code&gt;, jos jokainen taulukon elementti on pienempi kuin 42.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dir.any? {|filename| filename =~ /foo/ }&lt;/code&gt; palauttaa arvon &lt;code&gt;true&lt;/code&gt;, jos mikä tahansa tiedostonimi hakemistossa sisältää tekstin ”foo”.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;object.each_with_index {|item, i| puts "Item number #{i}: #{item}" }&lt;/code&gt; – each\&lt;em&gt;with\&lt;/em&gt;index on kuin each, mutta se antaa blokille lisäksi kokonaislukuparametrin, alkaen nollasta.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;books.sort_by {|book| book.author }&lt;/code&gt; palauttaa listan kirjoista aakkostettuna kirjoittajan nimen mukaan.&lt;/li&gt;
&lt;li&gt;jne. jne.&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;Muita esimerkkejä&lt;/h4&gt;

&lt;h5&gt;Tiedostot&lt;/h5&gt;

&lt;p&gt;Voit antaa blokin &lt;code&gt;open&lt;/code&gt;-kutsulle:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;open 'filename', 'w' do |io|
  io.puts "Hello world!"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Kun &lt;code&gt;open&lt;/code&gt;-metodia kutsutaan blokin kanssa, se&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avaa tiedoston&lt;/li&gt;
&lt;li&gt;suorittaa blokin antaen IO-olion parametriksi&lt;/li&gt;
&lt;li&gt;sulkee tiedoston – &lt;strong&gt;tästä ei tarvi huolehtia käsin&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;palauttaa blokin palauttaman arvon&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Sanotaan, että haluamme variantin open-metodista, joka&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;annetun tiedostonimen sijaan &lt;strong&gt;avaa väliaikaisen tiedoston kirjoitusta varten&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;suorittaa blokin antaen IO-olion parametriksi&lt;/li&gt;
&lt;li&gt;sulkee tiedoston&lt;/li&gt;
&lt;li&gt;jos blokki suoritettiin onnistuneesti,

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;siirtää väliaikaistiedoston kohdetiedoston tilalle&lt;/strong&gt; ja palauttaa blokin paluuarvon,&lt;/li&gt;
&lt;li&gt;muussa tapauksessa poistaa väliaikaistiedoston ja heittää exceptionin eteenpäin kutsujalle&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Arvaatko, kuinka paljon erilainen kyseinen metodikutsu olisi, kun hyödynnämme blokkeja?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Tempfile.open_auto_rename 'filename' do |io|
  io.puts "Hello world!"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Siinä se.&lt;/p&gt;

&lt;h5&gt;Säikeet&lt;/h5&gt;

&lt;p&gt;Jos &lt;a href="http://ruby-doc.org/core/classes/Thread.html"&gt;säikeet&lt;/a&gt; ovat sinulle tuttuja, tulet huomaamaan, että blokit ovat luontainen tapa luoda niitä.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;my_thread = Thread.new do
  puts  "This code is running in a thread."
  sleep 10
  puts  "Slept for a while."
end

# Now we can control the thread using the my_thread object.
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Yksikkötestaus&lt;/h5&gt;

&lt;p&gt;&lt;a href="http://rspec.rubyforge.org/"&gt;RSpec&lt;/a&gt;-yksikkötestausjärjestelmää käytettäessä määritellään testit seuraavalla syntaksilla. Huomaa, että jokainen &lt;code&gt;do&lt;/code&gt;...&lt;code&gt;end&lt;/code&gt;-pätkä on blokki.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;context "Book" do
  setup do
    @book = Book.new :author =&amp;gt; "Douglas Adams",
                     :title  =&amp;gt; "The Hitchhiker's Guide to the Galaxy",
                     :year   =&amp;gt; 1979
  end

  specify "should have the correct author" do
    @book.author.should == "Douglas Adams"
  end

  specify "should have the correct title" do
    @book.title.should == "The Hitchhiker's Guide to the Galaxy"
  end

  specify "should have the correct year" do
    @book.year.should == 1979
  end

  specify "should not have an ISBN" do
    @book.isbn.should == nil
  end

  specify "should be able to add the ISBN" do
    isbn = '0-330-25864-8' 

    @book.isbn        =  isbn
    @book.isbn.should == isbn
  end

  specify "should not be able to burn the book" do
    lambda { @book.burn! }.should_raise Book::DoesNotCatchFireError
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Tuloste näyttää tältä:&lt;/p&gt;

&lt;blockquote class="rspec"&gt;
&lt;ul&gt;
&lt;li&gt;Book
  &lt;ul&gt;
  &lt;li class="success"&gt;should have the correct author&lt;/li&gt;
  &lt;li class="success"&gt;should have the correct title&lt;/li&gt;
  &lt;li class="success"&gt;should have the correct year&lt;/li&gt;
  &lt;li class="success"&gt;should not have an ISBN&lt;/li&gt;
  &lt;li class="success"&gt;should be able to add the ISBN&lt;/li&gt;
  &lt;li class="success"&gt;should not be able to burn the book&lt;/li&gt;
  &lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finished in 0.025893 seconds&lt;/p&gt;
&lt;p class="success"&gt;6 specifications, 0 failures&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h5&gt;Mechanize&lt;/h5&gt;

&lt;p&gt;&lt;a href="http://rubyforge.org/projects/mechanize"&gt;WWW::Mechanize&lt;/a&gt; on &lt;strong&gt;todella&lt;/strong&gt; kiva luokka tiedon keräämiseen web-palveluiden sisällöstä (&lt;a href="http://en.wikipedia.org/wiki/Screen_scraping"&gt;screen scraping&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Taannoin kaipasin tapaa käskeä Mechanizea palaamaan tietylle sivulle sivuhistoriassa sekalaisten linkkien klikkaamisen ja mahdollisesti exceptionin heittävän koodin suorittamisen jälkeen.&lt;/p&gt;

&lt;p&gt;Kirjoitin &lt;code&gt;transact&lt;/code&gt;-nimisen metodin, joka – yllätys – hyödyntää blokkeja.&lt;/p&gt;

&lt;p&gt;Esimerkki sen käytöstä:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get "http://example.net/video_index.html"

# Find all links starting with "Video:"
page.links.text(/^Video:/).each do |link|
  begin
    transact do
      click link  # Example: http://example.net/videos/ruby_on_rails.html

      # From the resulting page, find the actual download link.
      video_link = page.links.text('Download this video').first
      filename   = File.basename video_link.href

      download video_link, filename
    end

  rescue =&amp;gt; e
    log.error "Failed to download from #{link.href}: #{e.class}: #{e}"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Blokin jälkeen olemme automaattisesti palanneet videolistan pääsivulle siitä riippumatta, suoritettiinko transactille annettu koodi onnistuneesti vai heittikö se mahdollisesti exceptionin.&lt;/p&gt;

&lt;p&gt;Toisin sanoen olemme valmiit klikkaamaan taas seuraavaa videolinkkiä listasta.&lt;/p&gt;

&lt;h3&gt;Loppusanat&lt;/h3&gt;

&lt;p&gt;Rubyssä on paljon asioita, jotka tekevät ohjelmoinnista mukavaa. Suosittelen vilpittömästi siihen tutustumista.&lt;/p&gt;

&lt;p&gt;Muuten, tässä on &lt;a href="http://johan.kiviniemi.name/stuff/ruby/acme/timetravel.html"&gt;TimeTravel-luokan toteutus&lt;/a&gt;. ;-)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/5WzRMu0-PuE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/ruby-vs-python.fi/</feedburner:origLink></entry>
    <entry>
    <title>Ruby vs. Python</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/aV3CNJCYzj0/" />
    <id>http://johan.kiviniemi.name/blag/2006/05/28/ruby-vs-python/</id>
    <updated>2006-05-28T23:50:56+03:00</updated>
    <content type="html">&lt;p&gt;(&lt;a href="/blag/ruby-vs-python.fi/"&gt;Suomeksi / in Finnish&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I used to regard Python as the most enjoyable interpreted language I knew. Then I got to know Ruby – and I have yet to find a nicer one. I have written some arguments for my opinion to this page.&lt;/p&gt;

&lt;p&gt;Before discussing the subject, I should mention that Ruby &lt;em&gt;is&lt;/em&gt; currently slower than Python. OTOH, Python is slower than C. Perhaps Ruby fits best for your project, perhaps not.&lt;/p&gt;

&lt;p&gt;Personally I have yet to bump into a situation where Ruby isn’t fast enough. YMMV. Also note that you can easily rewrite the bottlenecks in C after profiling your code.&lt;/p&gt;

&lt;h3&gt;Private methods and variables&lt;/h3&gt;

&lt;p&gt;To make methods and instance variables private in Python, one &lt;strong&gt;always&lt;/strong&gt; needs to write &lt;code&gt;__&lt;/code&gt; in front of the name. In Ruby, instance variables are private by default. Methods defined after the method call &lt;code&gt;private&lt;/code&gt; are private.&lt;/p&gt;

&lt;h3&gt;Functions and methods&lt;/h3&gt;

&lt;p&gt;There are no functions and methods separately in Ruby; all of them are methods. In Python, e.g. &lt;code&gt;len()&lt;/code&gt; is a function, but &lt;code&gt;items()&lt;/code&gt; is a method. Such inconsistencies remind me of &lt;a href="http://tnx.nl/php.jpg" title="PHP – Training wheels without the bike"&gt;PHP&lt;/a&gt;, &lt;em&gt;*shiver*&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Python:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string = 'Hello world'
print string.count('o'), len(string)  # prints 2, 11 – why not string.len()?
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ruby:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string = 'Hello world'
puts string.count('o'), string.length  # prints 2, 11
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;“&lt;code&gt;self&lt;/code&gt;”&lt;/h3&gt;

&lt;p&gt;In Python, one needs to write &lt;code&gt;self&lt;/code&gt; as the first parameter of a method definition (alike Perl). Furthermore, Python doesn’t even require the variable name to be &lt;code&gt;self&lt;/code&gt;, it can be &lt;code&gt;poop_rules&lt;/code&gt; for all Python cares. In Ruby, &lt;code&gt;self&lt;/code&gt; is automatically available in a similar fashion as &lt;code&gt;this&lt;/code&gt; in C++.&lt;/p&gt;

&lt;p&gt;Additionally, the method call &lt;code&gt;self.method&lt;/code&gt; can be shortened to &lt;code&gt;method&lt;/code&gt;, as &lt;code&gt;self&lt;/code&gt; is the default receiver.&lt;/p&gt;

&lt;h3&gt;Notation of instance variables&lt;/h3&gt;

&lt;p&gt;In Python, one has to write private instance variables using the frustratingly long form &lt;code&gt;self.__foobar&lt;/code&gt;. In Ruby, the form is &lt;code&gt;@foobar&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get away with less typing, many Python programmers I’ve discussed with resort to making variables (and methods) public even when there’s no reason for them to be public. Then they say it’s a good thing and the “Python way”. Oh well, they are entitled to their opinion.&lt;/p&gt;

&lt;h3&gt;In Ruby, everything is an object&lt;/h3&gt;

&lt;p&gt;Even numbers. That lets you do stuff like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;4.megabytes &amp;gt; 2345.kilobytes          # ⇒ true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;3.weeks.ago                           # ⇒ Sun Nov 19 09:39:05 EET 2006
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the example above, &lt;code&gt;Numeric#weeks&lt;/code&gt; is defined as &lt;code&gt;self * 7.days&lt;/code&gt;. You can probably guess how &lt;code&gt;Numeric#days&lt;/code&gt;, &lt;code&gt;#hours&lt;/code&gt; etc. are defined. :-)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Numeric#ago&lt;/code&gt; is &lt;code&gt;Time.now - self&lt;/code&gt;, i.e. it returns a &lt;code&gt;Time&lt;/code&gt; object.&lt;/p&gt;

&lt;h3&gt;Blocks&lt;/h3&gt;

&lt;p&gt;Ruby has a &lt;em&gt;very&lt;/em&gt; neat syntax for passing a code block as a parameter to a method call (see also &lt;a href="http://martinfowler.com/bliki/Closure.html"&gt;closures&lt;/a&gt;; function reference &lt;a href="http://mail.python.org/pipermail/python-list/2004-July/270951.html"&gt;does not equal a closure&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A hypothetical example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;time_tomorrow = TimeTravel.travel(1.day.from_now) { Time.now }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we have the method &lt;code&gt;TimeTravel.travel&lt;/code&gt; which accepts a &lt;strong&gt;time value&lt;/strong&gt; and &lt;strong&gt;a block of code&lt;/strong&gt; as parameters. The method can do anything it wants with the block of code: run something and then execute the block; execute the block multiple times; store the block for later execution etc.&lt;/p&gt;

&lt;p&gt;In this case, the method travels to the given time using a time machine, then invokes the block and finally returns the block’s return value.&lt;/p&gt;

&lt;p&gt;In this example, the block returns &lt;code&gt;Time.now&lt;/code&gt;, which is passed to the &lt;code&gt;time_tomorrow&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;The ability to pass a block to a method gives you a powerful way for doing many things. Having a nice syntax for doing it makes it fun.&lt;/p&gt;

&lt;h4&gt;The first real-world example: &lt;code&gt;each&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Many classes, such as the built-in &lt;a href="http://ruby-doc.org/core/classes/Array.html"&gt;Array&lt;/a&gt;, &lt;a href="http://ruby-doc.org/core/classes/Hash.html"&gt;Hash&lt;/a&gt;, &lt;a href="http://ruby-doc.org/core/classes/IO.html"&gt;IO&lt;/a&gt; and &lt;a href="http://ruby-doc.org/core/classes/Dir.html"&gt;Dir&lt;/a&gt;, implement the &lt;code&gt;each&lt;/code&gt; method. It simply iterates over whatever the object “contains”, invoking the given block once for each item.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Array#each&lt;/code&gt; iterates over the array’s elements. &lt;code&gt;Hash#each&lt;/code&gt; iterates over the key/value pairs. &lt;code&gt;IO#each&lt;/code&gt; iterates over lines received by an &lt;code&gt;IO&lt;/code&gt; object (from an open file, a HTTP connection etc). &lt;code&gt;Dir#each&lt;/code&gt; iterates over a directory listing while it’s being received.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Iterate over the elements of an array.
[42, 'blah', Time.now].each do |element|
  puts element
end

# Iterate over the file line by line.
open('filename').each do |line|
  puts line
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You may wonder what’s the value of &lt;code&gt;each&lt;/code&gt;, when you could just have a &lt;code&gt;for&lt;/code&gt; structure, like in many other programming languages.&lt;/p&gt;

&lt;p&gt;Ruby has a built-in &lt;a href="http://en.wikipedia.org/wiki/Mixin"&gt;mixin&lt;/a&gt; called &lt;a href="http://ruby-doc.org/core/classes/Enumerable.html"&gt;Enumerable&lt;/a&gt;. You can &lt;code&gt;include&lt;/code&gt; it in any class, and if your class defines the &lt;code&gt;each&lt;/code&gt; method, you get a bunch of really useful methods “for free”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;array.all? {|item| item &amp;lt; 42 }&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; if all items in an array are less than 42.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dir.any? {|filename| filename =~ /foo/ }&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; if any filename in the directory contains the text “foo”.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;object.each_with_index {|item, i| puts "Item number #{i}: #{item}" }&lt;/code&gt; – each\&lt;em&gt;with\&lt;/em&gt;index is like each, but it passes an additional integer to the block, starting from zero.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;books.sort_by {|book| book.author }&lt;/code&gt; returns a list of books sorted by the author’s name.&lt;/li&gt;
&lt;li&gt;etc. etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;Other examples&lt;/h4&gt;

&lt;h5&gt;Files&lt;/h5&gt;

&lt;p&gt;You can pass a block to an &lt;code&gt;open&lt;/code&gt; call:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;open 'filename', 'w' do |io|
  io.puts "Hello world!"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When &lt;code&gt;open&lt;/code&gt; is called with a block, it&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;opens the file&lt;/li&gt;
&lt;li&gt;invokes the block with the IO object as a parameter&lt;/li&gt;
&lt;li&gt;closes the file – &lt;strong&gt;no need to take care of that manually&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;returns the block’s return value&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Let’s say we want a variant of the &lt;code&gt;open&lt;/code&gt; method that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instead of the given filename, &lt;strong&gt;opens a temporary file for writing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;invokes the block with the IO object as a parameter&lt;/li&gt;
&lt;li&gt;closes the file&lt;/li&gt;
&lt;li&gt;if the block returned successfully,

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;moves the temporary file over the requested filename&lt;/strong&gt; and returns the block’s return value,&lt;/li&gt;
&lt;li&gt;otherwise deletes the temporary file and passes the exception to the caller&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Can you guess how much different the method call would be, when we can utilize blocks?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Tempfile.open_auto_rename 'filename' do |io|
  io.puts "Hello world!"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h5&gt;Threads&lt;/h5&gt;

&lt;p&gt;If you're familiar with &lt;a href="http://ruby-doc.org/core/classes/Thread.html"&gt;threading&lt;/a&gt;, you’ll realize blocks are a natural way to spawn threads:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;my_thread = Thread.new do
  puts  "This code is running in a thread."
  sleep 10
  puts  "Slept for a while."
end

# Now we can control the thread using the my_thread object.
&lt;/code&gt;&lt;/pre&gt;

&lt;h5&gt;Unit testing&lt;/h5&gt;

&lt;p&gt;With the &lt;a href="http://rspec.rubyforge.org/"&gt;RSpec&lt;/a&gt; unit testing framework, you define the tests using the following syntax. Note that each &lt;code&gt;do&lt;/code&gt;...&lt;code&gt;end&lt;/code&gt; segment is a block.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;context "Book" do
  setup do
    @book = Book.new :author =&amp;gt; "Douglas Adams",
                     :title  =&amp;gt; "The Hitchhiker's Guide to the Galaxy",
                     :year   =&amp;gt; 1979
  end

  specify "should have the correct author" do
    @book.author.should == "Douglas Adams"
  end

  specify "should have the correct title" do
    @book.title.should == "The Hitchhiker's Guide to the Galaxy"
  end

  specify "should have the correct year" do
    @book.year.should == 1979
  end

  specify "should not have an ISBN" do
    @book.isbn.should == nil
  end

  specify "should be able to add the ISBN" do
    isbn = '0-330-25864-8' 

    @book.isbn        =  isbn
    @book.isbn.should == isbn
  end

  specify "should not be able to burn the book" do
    lambda { @book.burn! }.should_raise Book::DoesNotCatchFireError
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output looks like:&lt;/p&gt;

&lt;blockquote class="rspec"&gt;
&lt;ul&gt;
&lt;li&gt;Book
  &lt;ul&gt;
  &lt;li class="success"&gt;should have the correct author&lt;/li&gt;
  &lt;li class="success"&gt;should have the correct title&lt;/li&gt;
  &lt;li class="success"&gt;should have the correct year&lt;/li&gt;
  &lt;li class="success"&gt;should not have an ISBN&lt;/li&gt;
  &lt;li class="success"&gt;should be able to add the ISBN&lt;/li&gt;
  &lt;li class="success"&gt;should not be able to burn the book&lt;/li&gt;
  &lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finished in 0.025893 seconds&lt;/p&gt;
&lt;p class="success"&gt;6 specifications, 0 failures&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h5&gt;Mechanize&lt;/h5&gt;

&lt;p&gt;&lt;a href="http://rubyforge.org/projects/mechanize"&gt;WWW::Mechanize&lt;/a&gt; is a &lt;strong&gt;really&lt;/strong&gt; nice class for &lt;a href="http://en.wikipedia.org/wiki/Screen_scraping"&gt;screen scraping&lt;/a&gt; the web.&lt;/p&gt;

&lt;p&gt;Some time ago I found myself wishing for a way to tell Mechanize to go back to a certain page in the page history after clicking various links and running code that might raise an exception.&lt;/p&gt;

&lt;p&gt;I wrote a method called &lt;code&gt;transact&lt;/code&gt;, which – surprise – utilizes blocks.&lt;/p&gt;

&lt;p&gt;An example of the its usage:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;get "http://example.net/video_index.html"

# Find all links starting with "Video:"
page.links.text(/^Video:/).each do |link|
  begin
    transact do
      click link  # Example: http://example.net/videos/ruby_on_rails.html

      # From the resulting page, find the actual download link.
      video_link = page.links.text('Download this video').first
      filename   = File.basename video_link.href

      download video_link, filename
    end

  rescue =&amp;gt; e
    log.error "Failed to download from #{link.href}: #{e.class}: #{e}"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After the block, we have automatically returned to the video index page, no matter whether the code passed to &lt;code&gt;transact&lt;/code&gt; succeeded or raised an exception.&lt;/p&gt;

&lt;p&gt;That means we’re ready to click the next video page link from the index.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;There are many things in Ruby that makes coding fun. I sincerely recommend learning it.&lt;/p&gt;

&lt;p&gt;By the way, here’s the &lt;a href="http://johan.kiviniemi.name/stuff/ruby/acme/timetravel.html"&gt;TimeTravel implementation&lt;/a&gt;. ;-)&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/aV3CNJCYzj0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/ruby-vs-python/</feedburner:origLink></entry>
    <entry>
    <title>Syyt olla k{ytt{m{tt{ 8-bittisi{ merkist|j{</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/ALbMB67e1Tg/" />
    <id>http://johan.kiviniemi.name/blag/2006/01/03/anti-8bit/</id>
    <updated>2006-01-03T00:57:55+02:00</updated>
    <content type="html">&lt;p&gt;Er{{t tahot ovat esitt{neet ISO-Latin-1:n (toiselta nimelt{{n ISO-8859-1) k{ytt||n siirtymist{ IRC:ss{ alkaen 1.1.1990, ja t{m{ ehdotus on esitetty "yhteisesti tehtyn{ sopimuksena". Todellisuudessa kyseess{ on kuitenkin muutaman yksitt{isen henkil{n mielipide, ja syit{ olla siirtym{tt{ viel{ Latin-1:n k{ytt||n l|ytyy lukuisia. K{yd{{np{ perusteluiden kera l{pi muutamia syit{ olla siirtym{tt{ viel{ Latin-1:een.&lt;/p&gt;

&lt;h3&gt;"Latin-1 -tuki on saatavilla useimpiin IRC-clientteihin" -v{ite&lt;/h3&gt;

&lt;p&gt;Aloitetaan yleisimm{st{, eli irc:st{. irc:i{ k{ytt{nee suurin osa irkkaajista, ja se lienee yleisin juuri niiden irkkaajien keskuudessa jotka eiv{t ole kovin teknisesti orientoituneita. Kaveri on vain opastanut miten asennetaan ohjelma "jolla p{{st{{n sinne irkkiin". irc ei tue vakiona Latin-1 -merkist|{, eik{ tule sit{ viel{ v{h{{n aikaan tukemaankaan. Jonkinlainen Latin-1 -tuki on saatavilla skriptien muodossa, mutta moniko n{ist{ perusk{ytt{jist{ osaa hakea/asentaa/k{ytt{{ moisia? Ohjeiden julkaisu ei juuri asiaa auta, moni n{ist{ k{ytt{jist{ ei niit{ osaa hakea eik{ osaa/jaksa soveltaa. Lis{ksi irc:n Latin-1 -skripteist{ on raportoitu erilaisia toimintaongelmia.&lt;/p&gt;

&lt;p&gt;Sitten toinen yleinen, ircII. Kuten Latin-1:n kannattajat v{itt{v{tkin, uusin versio ircII:st{ todellakin tukee kunnolla Latin-1:t{. Valitettavasti sit{ ei kuitenkaan viel{ saa asennettua suoraan yleisimpien k{ytt|j{rjestelmien pakettienhallinnan avulla. Esimerkiksi HP-UX:st{ l|ytyy kyll{ experimental -paketti uusimmasta ircII:st{, mutta vakaana ainoastaan vanhempi versio. Sama p{tee 4.3BSD:hen, stablesta ei viel{ uusinta ircII:t{ saa. Experimental -pakettien asennus tuotantopalvelimiin (esimerkiksi shell-palveluntarjoajat) taas ei todellakaan ole vaihtoehto, niill{ kun on mahdollista saada aikaan mit{ moninaisimpia ongelmia. My|sk{{n ircII:n k{{nt| koneisiin l{hdekoodeista ohi pakettienhallinnan ei ole vaihtoehto. Pakettienhallinnan ohittamisella aiheutetaan vain lis{{ ongelmia itselle tulevaisuudessa.&lt;/p&gt;

&lt;p&gt;Mainittakoon lis{ksi viel{ ett{ ircII:nkin Latin-1 -tuen k{ytt||notto ja uuden ircII:n asentaminen vaativat jonkin verran osaamista. Kaikilla perusk{ytt{jill{ ei sit{ ole, vaan ircII on esimerkiksi asentunut distribuution mukana ilman ett{ k{ytt{j{ olisi juuri vaivaa joutunut sen eteen n{kem{{n. On varmasti monia k{ytt{ji{ joilla ei ole aikaa, halua tai osaamista alkaa n{hd{ ylim{{r{ist{ vaivaa Latin-1:n k{ytt||noton eteen. Lis{ksi Latin-1:n k{ytt||notto saattaa vaatia asetusten muutoksia my|s paikallisella koneella, joka vaatii taas hieman enemm{n osaamista k{ytt{j{lt{.&lt;/p&gt;

&lt;h3&gt;Latin-1 -merkist|n k{yt|n "hy|dyt"&lt;/h3&gt;

&lt;p&gt;Mit{ todellista hy|ty{ Latin-1:st{ sitten normaalik{ytt{j{lle olisi? Er{{t harvoin k{ytetyt merkit, kuten tietyt skandinaaviset "{{kk|set" n{kyisiv{t oikein vanhoihin 7-bittisiin merkist|ihin verrattuna. Kuinka moni k{ytt{j{ todella voi sanoa tarvitsevansa niit{? Jokaiselle n{ist{ merkeist{ on kuitenkin olemassa t{ysin toimiva korvike. Mik{{n ei esimerkiksi est{ kirjoittamasta skandeja korvaavilla merkeill{ (kuten { ja | tai d ja v) ja t{m{ saattaa olla joissakin tilanteissa jopa selke{mp{{.&lt;/p&gt;

&lt;p&gt;Toinen Latin-1:n etu on mm. monien matemaattisten symbolien ja valuuttamerkkien k{ytt|mahdollisuus. Moniko irkkaaja kuitenkaan tarvitsee esimerkiksi astemerkki{ tai 1/2 -merkki{? Ehk{ joku, mutta harvassa lienev{t suomalaisten irkkaajien keskuudessa.&lt;/p&gt;

&lt;h3&gt;Latin-1 -tuki fonteissa&lt;/h3&gt;

&lt;p&gt;Miljoonat ja taas miljoonat tietokoneissa olevat olevat fontit eiv{t toimi 8-bittisten merkist|jen kanssa sit{k{{n v{h{{ mit{ ne toimivat 7-bittisten merkist|jen kanssa.&lt;/p&gt;

&lt;h3&gt;Vanhempien ohjelmistojen ja laitteiden tuki Latin-1:lle&lt;/h3&gt;

&lt;p&gt;Lukuisat vanhat ohjelmat eiv{t 8-bittisten merkist|jen p{{lle ymm{rr{. Jos koneelta v{{nt{{ merkist|n Latin-1:ksi, kyseiset softat eiv{t toimi tai n{ytt{v{t ep{m{{r{ist{ merkkisotkua. Koska useimmiten kyseess{ ovat vanhat ohjelmat, joita ei en{{ aktiivisesti kehitet{, eik{ niist{ v{ltt{m{tt{ ole saatavilla l{hdekoodiakaan, ongelman korjaamiseen ei ole mahdollisuutta. Joissakin tilanteissa kyseisten ohjelmistojen k{ytt||n kuitenkin on pakottavia syit{.&lt;/p&gt;

&lt;p&gt;My|sk{{n monet vanhat k{ytt|j{rjestelm{t, joita yh{ saattaa olla tuotantok{yt|ss{, eiv{t Latin-1:n p{{lle ymm{rr{ mit{{n. N{in ollen k{ytt{j{t joutuisivat koneensa asetukset Latin-1 -aikaan v{{nnetty{{n n{kem{{n turhaa lis{vaivaa n{iden j{rjestelmien et{k{yt|n yhteydess{.&lt;/p&gt;

&lt;p&gt;N{m{ syyt kuitenkin koskettavat harvempaa k{ytt{j{{, joten niiden painoarvo ei niin suuri ole.&lt;/p&gt;

&lt;h3&gt;Yhteenvetoa&lt;/h3&gt;

&lt;p&gt;Kyll{, 8-bittiset merkist|t ovat hieno asia ja tulevat varmasti helpottamaan tulevaisuudessa jokaisen meist{ el{m{{ v{hent{m{ll{ yhteensopivuusongelmia eri j{rjestelmiss{ k{ytett{vien merkist|jen v{lill{. T{m{n hetken tilanne on kuitenkin viel{ se, ett{ Latin-1:n k{ytt{minen vaatii ylim{{r{ist{ virittely{ tai riskien ottoa testausvaiheessa olevien ohjelmistojen kanssa, ja kaikkien ohjelmien tai k{ytt|j{rjestelmien kanssa sen k{ytt| ei ole edes mahdollista.&lt;/p&gt;

&lt;p&gt;Palataan asiaan, kun Latin-1:n k{ytt||notto on oikeasti suurimmalle osalle k{ytt{jist{ mahdollista vain muutaman, helposti opastettavan komennon kautta, ellei jopa oletusarvona k{ytt|j{rjestelmiss{ ja ohjelmissa, my|s IRC-clienteiss{. N{in tulee varmasti tapahtumaan muutamien vuosien kuluessa nykyhetkest{.&lt;/p&gt;

&lt;p&gt;Siihen asti, 8-bittisi{ merkkej{ irkkiin suoltavat saavat ainakin allekirjoittaneen toimesta kehotuksen korjata merkist|ns{, ja tarvittaessa keng{nkuvan takalistoonsa. Samaa suosittelen muillekin.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/ALbMB67e1Tg" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/anti-8bit.fi/</feedburner:origLink></entry>
    <entry>
    <title>Wannabe Hacker Emblem</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/5Fct8qnaypo/" />
    <id>http://johan.kiviniemi.name/blag/2005/12/31/wannabe-hacker-emblem/</id>
    <updated>2005-12-31T05:44:14+02:00</updated>
    <content type="html">&lt;p&gt;&lt;img src="http://johan.kiviniemi.name/pictures/humor/wannabe_hacker_emblem/original" title="Wannabe Hacker Emblem" alt="Wannabe Hacker Emblem" /&gt;&lt;/p&gt;

&lt;p&gt;Starfleet has the &lt;a href="http://en.wikipedia.org/wiki/Image:Starfleet_command_emblem.png"&gt;Starfleet Command emblem&lt;/a&gt;, Batman has &lt;a href="http://images.google.com/images?q=batman+logo"&gt;his logo&lt;/a&gt;, Superman has &lt;a href="http://images.google.com/images?q=superman+logo"&gt;his logo&lt;/a&gt; and hackers have &lt;a href="http://catb.org/hacker-emblem/"&gt;the glider&lt;/a&gt;. What we haven’t had, historically, is an emblem that represents the &lt;em&gt;entire&lt;/em&gt; wannabe hacker community – until now.&lt;/p&gt;

&lt;p&gt;All the wannabe hackers this idea was alpha-tested on instantaneously said “Wow! Cool!” without needing any further explanation.&lt;/p&gt;

&lt;p&gt;The emblem has became so popular that it has been added to the &lt;a href="http://unicode.org/"&gt;Unicode&lt;/a&gt; standard: &lt;span style="font-size: 200%; vertical-align: middle;"&gt;⠵&lt;/span&gt; (U+2835).&lt;/p&gt;

&lt;h3&gt;What will I be saying if I display it?&lt;/h3&gt;

&lt;p&gt;When you put the wannabe hacker emblem on your web page, or wear it on clothing, or display it in some other way, you are visibly associating yourself with the wannabe hacker culture. By using this emblem, you express sympathy with wannabe hackers’ goals, wannabe hackers’ values, and the wannabe hacker way of living.&lt;/p&gt;

&lt;h3&gt;How can I use it?&lt;/h3&gt;

&lt;p&gt;The emblem is not copyrighted or trademarked. The recommended way to use it is on a web page, with an image or the Unicode character and a link back to this page.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/5Fct8qnaypo" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/wannabe-hacker-emblem/</feedburner:origLink></entry>
    <entry>
    <title>Yksikön gb määritelmä</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/pwPkEuWMB4I/" />
    <id>http://johan.kiviniemi.name/blag/2005/12/31/gb.fi/</id>
    <updated>2005-12-31T04:54:07+02:00</updated>
    <content type="html">&lt;p&gt;(&lt;a href="/blag/gb/"&gt;In English&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://fi.wikipedia.org/wiki/Bitti"&gt;Bitti&lt;/a&gt; on informaation perusyksikkö tietotekniikassa. Yksittäinen bitti voi merkitä arvoa, joka on joko tosi tai epätosi – yksi tai nolla.&lt;/p&gt;

&lt;p&gt;Tietokonemuistin, kuten &lt;a href="http://fi.wikipedia.org/wiki/Keskusmuisti"&gt;keskusmuistin&lt;/a&gt; ja &lt;a href="http://fi.wikipedia.org/wiki/Kiintolevy"&gt;kiintolevytilan&lt;/a&gt;, määrä mitataan yleensä bitteinä tai bitistä johdettuina yksikköinä, kuten &lt;a href="http://fi.wikipedia.org/wiki/Tavu_(tietotekniikka)"&gt;tavuina&lt;/a&gt; (tavu on yhtä kuin 8 bittiä).&lt;/p&gt;

&lt;p&gt;Pitkään tämä riitti, mutta viime aikoina tulleiden uusien teknologioiden myötä tilanne on muuttunut.&lt;/p&gt;

&lt;p&gt;Kuten nörteimmät meistä jo tietävätkin, uusin tietokonelaitteiston valmistajien innovaatio on bitti, jonka paino voi vaihdella. Tämän teknologian nimi on &lt;em&gt;VWBS&lt;/em&gt;, Variable-Weight Bit Storage. Aiemmin kaikki bitit yksittäisessä kiintolevyssä painoivat saman verran. Sitä mukaa, kun valmistajat kykenivät tekemään kevyempiä bittejä, kiintolevyjen fyysinen koko pieneni ja yksittäiselle kiintolevylle mahtuvan tiedon määrä kasvoi.&lt;/p&gt;

&lt;p&gt;VWBS:n myötä kiintolevylle mahtuva bittien määrä ei ole enää vakio. Sen sijaan kunkin bitin paino vaikuttaa kiintolevylle mahtuvien bittien kokonaismäärään. Tämän vuoksi otettiin käyttöön uusi yksikkö muistikapasiteetin mittaamiseen: &lt;strong&gt;grammabitti, &lt;code&gt;gb&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yksikköä &lt;code&gt;gb&lt;/code&gt; ei pidä sekoittaa yksikköön &lt;code&gt;GB&lt;/code&gt; (gigatavu), jolla usein ilmaistaan sellaisten kiintolevyjen kapasiteettia, jotka eivät tue VWBS-teknologiaa.&lt;/p&gt;

&lt;h3&gt;Yksinkertaistettu esimerkki&lt;/h3&gt;

&lt;p&gt;Kuvitellaan, että meillä on &lt;code&gt;100 gb&lt;/code&gt;:n kiintolevy. Jos käytämme &lt;code&gt;0.10 ng&lt;/code&gt; (nanogramman) bittejä, voimme tallentaa &lt;code&gt;100 gb/(0.10 ng) = 1 000 Gb&lt;/code&gt; (gigabitin) verran informaatiota sille. Mutta jos käytämme kevyempiä &lt;code&gt;0.08 ng&lt;/code&gt;:n bittejä, voimme tallentaa &lt;code&gt;100 gb/(0.08 ng) = 1 250 Gb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1 000 Gb = 125 GB&lt;/code&gt; ja &lt;code&gt;1 250 Gb ≈ 156 GB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Jos olemme tallentaneet &lt;code&gt;500 Gb&lt;/code&gt;:n verran &lt;code&gt;0.08 ng&lt;/code&gt;:n painoisia bittejä &lt;code&gt;100 gb&lt;/code&gt;:n kokoiselle kiintolevylle, jäljellä oleva kapasiteetti on &lt;code&gt;100 gb − (500 Gb · 0.08 ng) = 100 gb − 40 gb = 60 gb&lt;/code&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/pwPkEuWMB4I" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/gb.fi/</feedburner:origLink></entry>
    <entry>
    <title>Definition of gb</title>
    <link href="http://feeds.heh.fi/~r/JohanKiviniemi/~3/OUE9Gn-dCyM/" />
    <id>http://johan.kiviniemi.name/blag/2005/12/31/gb/</id>
    <updated>2005-12-31T04:53:52+02:00</updated>
    <content type="html">&lt;p&gt;(&lt;a href="/blag/gb.fi/"&gt;Suomeksi / in Finnish&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Bit"&gt;Bit&lt;/a&gt; is the basic unit of information used in computing. A single bit can represent a value that is either true or false – one or zero.&lt;/p&gt;

&lt;p&gt;The amount of computer memory, such as &lt;a href="http://en.wikipedia.org/wiki/Random_Access_Memory" title="Random Access Memory"&gt;RAM&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Hard_disk"&gt;harddisk&lt;/a&gt; space, is usually measured in bits or units derived from bit, such as &lt;a href="http://en.wikipedia.org/wiki/Byte"&gt;bytes&lt;/a&gt; (a byte is equal to 8 bits).&lt;/p&gt;

&lt;p&gt;For a long time that was all that was needed, but with recent new technologies the situation has changed.&lt;/p&gt;

&lt;p&gt;As the geekier ones of us already know, the latest innovation from the computer equipment manufacturers is a bit that can vary in weight. The technology is called &lt;em&gt;VWBS&lt;/em&gt;, i.e. Variable-Weight Bit Storage. Previously all the bits in a single hard disk had the same weight. As the manufacturers were able to make lighter bits, the hard disks got physically smaller, and the amount of data that a single hard disk was able to store got larger.&lt;/p&gt;

&lt;p&gt;With VWBS, the exact amount of bits that a hard disk is able to store isn’t a constant anymore. Instead the weight of each bit stored affects the total amount of bits that the hard disk can hold. That’s why a new unit of memory capacity was adopted: the &lt;strong&gt;grambit, &lt;code&gt;gb&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gb&lt;/code&gt; is not to be mixed up with &lt;code&gt;GB&lt;/code&gt; (gigabyte), which is often used to measure the capacity of a hard disk without VWBS technology.&lt;/p&gt;

&lt;h3&gt;A simplified example&lt;/h3&gt;

&lt;p&gt;Let’s say we have a &lt;code&gt;100 gb&lt;/code&gt; hard disk. If we use &lt;code&gt;0.10 ng&lt;/code&gt; (nanogram) bits, we’re able to store &lt;code&gt;100 gb/(0.10 ng) = 1 000 Gb&lt;/code&gt; (gigabit) of information to it. But if we use the lighter &lt;code&gt;0.08 ng&lt;/code&gt; bits, we’re able to store &lt;code&gt;100 gb/(0.08 ng) = 1 250 Gb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1 000 Gb = 125 GB&lt;/code&gt; and &lt;code&gt;1 250 Gb ≈ 156 GB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we have stored &lt;code&gt;500 Gb&lt;/code&gt; of &lt;code&gt;0.08 ng&lt;/code&gt; bits to a &lt;code&gt;100 gb&lt;/code&gt; hard disk, the remaining capacity is &lt;code&gt;100 gb − (500 Gb · 0.08 ng) = 100 gb − 40 gb = 60 gb&lt;/code&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/JohanKiviniemi/~4/OUE9Gn-dCyM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://johan.kiviniemi.name/blag/gb/</feedburner:origLink></entry>
  </feed>
