<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Chip's Tips for Developers</title>
	
	<link>http://www.chipstips.com</link>
	<description>Contains coding, but not narcotic.</description>
	<lastBuildDate>Sun, 05 Sep 2010 22:11:44 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ChipsTips" /><feedburner:info uri="chipstips" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>47.65526</geo:lat><geo:long>-122.535083</geo:long><feedburner:emailServiceId>ChipsTips</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item>
		<title>Solving old problems: ASCII conversion</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/7d6uP-OME30/</link>
		<comments>http://www.chipstips.com/?p=552#comments</comments>
		<pubDate>Sun, 05 Sep 2010 22:11:44 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[ascii]]></category>
		<category><![CDATA[binary]]></category>
		<category><![CDATA[hexadecimal]]></category>
		<category><![CDATA[octal]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=552</guid>
		<description><![CDATA[In a perfect world, all characters would be in Unicode, and all software would know how to deal with them. Although Unicode adoption has grown, especially on the web, there&#8217;s still a lot of software out there that uses good old ASCII. As a developer, I still regularly need to convert between ASCII and some [...]]]></description>
			<content:encoded><![CDATA[<p>In a perfect world, all characters would be in Unicode, and all software would know how to deal with them.  Although Unicode adoption has grown, especially on the web, there&#8217;s still a lot of software out there that uses good old ASCII.  As a developer, I still regularly need to convert between ASCII and some numeric format &#8212; usually decimal or hexadecimal, but sometimes even octal or binary.  I used to refer to <a href="http://www.asciitable.com/">this online ASCII table</a>, but waiting for the page to load and then visually finding the desired entry all take time &#8212; a few seconds at least.</p>
<p>Then I realized that I could perform most conversions at a shell prompt.  For instance, to get the numeric value of the character &#8216;A&#8217;, you can use Ruby:</p>
<p><code>$ ruby -e "print ?A"</code></p>
<p>But I wanted something with a little less typing that would be easier to remember.  So I created a Ruby script named &#8216;ascii&#8217;, which you can download below or grab from the <a href="http://bitbucket.org/sterlingcamden/ascii">BitBucket repository</a>.</p>
<p>If you invoke it without arguments, you get the full ASCII table.  If you supply arguments, they&#8217;re evaluated.  If they result in numeric values, then you get the equivalent ASCII values.  Otherwise, you get the values of each character in the argument.  You can also override the numeric evaluation and specify that you want all arguments treated as string using the -s option.  For each value the script outputs, you get the decimal, octal, hexadecimal, and character representations.</p>
<p>ASCII only goes up to 255 in its 8-bit form, but this script actually allows higher values.  In that case, the character representation will be modulo 256 &#8212; but you can use this &#8216;feature&#8217; to perform decimal/octal/hex/binary conversions.  Since ascii accepts any numeric argument, you can prefix it with 0b for binary, 0o (or just 0) for octal, or 0x for hex.  You can even do arithmetic evaluations like &#8216;?A+32&#8242; (the value of &#8216;A&#8217; plus 32) or &#8217;65|0&#215;20&#8242; (another way of getting the same value).  Note the quotes to prevent the shell from evaluation the ? or the |.</p>
<p>See the included man page for details.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbascii.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/KwDgdANisn-g2l-REYI9sMPZNdo/0/da"><img src="http://feedads.g.doubleclick.net/~a/KwDgdANisn-g2l-REYI9sMPZNdo/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/KwDgdANisn-g2l-REYI9sMPZNdo/1/da"><img src="http://feedads.g.doubleclick.net/~a/KwDgdANisn-g2l-REYI9sMPZNdo/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=7d6uP-OME30:TxSccaEh_3k:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=7d6uP-OME30:TxSccaEh_3k:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=7d6uP-OME30:TxSccaEh_3k:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=7d6uP-OME30:TxSccaEh_3k:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=7d6uP-OME30:TxSccaEh_3k:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=7d6uP-OME30:TxSccaEh_3k:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=7d6uP-OME30:TxSccaEh_3k:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=7d6uP-OME30:TxSccaEh_3k:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=7d6uP-OME30:TxSccaEh_3k:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=7d6uP-OME30:TxSccaEh_3k:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=7d6uP-OME30:TxSccaEh_3k:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/7d6uP-OME30" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=552</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=552</feedburner:origLink></item>
		<item>
		<title>Aqua not, and functional musings</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/Blrz3SIHdbk/</link>
		<comments>http://www.chipstips.com/?p=551#comments</comments>
		<pubDate>Sun, 05 Sep 2010 19:51:33 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[abook]]></category>
		<category><![CDATA[contacts]]></category>
		<category><![CDATA[fp]]></category>
		<category><![CDATA[lambda]]></category>
		<category><![CDATA[mutt]]></category>
		<category><![CDATA[query]]></category>
		<category><![CDATA[rbaqua]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=551</guid>
		<description><![CDATA[&#8220;What&#8217;s missing from aqua?&#8221; &#8220;Not&#8221; &#8220;Naught&#8217;s missing? That&#8217;s great!&#8221; &#8220;No, you need to add NOT.&#8221; &#8220;&#8216;Add not&#8217; means leave it alone &#8212; it&#8217;s perfect.&#8221; &#8220;No, it&#8217;s not perfect. It needs NOT.&#8221; &#8220;It has to be needy to be perfect?&#8221; So, I aded a logical not operator to aqua. If the first character of a token [...]]]></description>
			<content:encoded><![CDATA[<p>&#8220;What&#8217;s missing from <a href="http://chipstips.com/?p=550">aqua</a>?&#8221;</p>
<p>&#8220;Not&#8221;</p>
<p>&#8220;Naught&#8217;s missing?  That&#8217;s great!&#8221;</p>
<p>&#8220;No, you need to add NOT.&#8221;</p>
<p>&#8220;&#8216;Add not&#8217; means leave it alone &#8212; it&#8217;s perfect.&#8221;</p>
<p>&#8220;No, it&#8217;s not perfect.  It needs NOT.&#8221;</p>
<p>&#8220;It has to be needy to be perfect?&#8221;</p>
<p>So, I aded a logical not operator to aqua.  If the first character of a token is &#8216;!&#8217;, that&#8217;s a not.</p>
<p>I also tightened up the code a bit, at which point I realized that this utility&#8217;s parser doesn&#8217;t need any data structures other than the incoming stream of characters and the single, resulting lambda that performs the requested operation &#8212; composed of lambdas for each test, bound in closures that obviate the need for any describing data structure.  It&#8217;s the first time I&#8217;ve written a parser in such a functional style.  Maybe I should rewrite it in Haskell just for kicks.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbaqua.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>
<div><a href="http://bitbucket.org/sterlingcamden/aqua">BitBucket Repository</a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/H9acf31Jse8GCgaI7Dl8UiayKxQ/0/da"><img src="http://feedads.g.doubleclick.net/~a/H9acf31Jse8GCgaI7Dl8UiayKxQ/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/H9acf31Jse8GCgaI7Dl8UiayKxQ/1/da"><img src="http://feedads.g.doubleclick.net/~a/H9acf31Jse8GCgaI7Dl8UiayKxQ/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Blrz3SIHdbk:HWof1CsBS08:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Blrz3SIHdbk:HWof1CsBS08:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Blrz3SIHdbk:HWof1CsBS08:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Blrz3SIHdbk:HWof1CsBS08:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Blrz3SIHdbk:HWof1CsBS08:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Blrz3SIHdbk:HWof1CsBS08:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Blrz3SIHdbk:HWof1CsBS08:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Blrz3SIHdbk:HWof1CsBS08:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Blrz3SIHdbk:HWof1CsBS08:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Blrz3SIHdbk:HWof1CsBS08:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Blrz3SIHdbk:HWof1CsBS08:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/Blrz3SIHdbk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=551</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=551</feedburner:origLink></item>
		<item>
		<title>Aqua: Advanced Query Using Abook</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/We2WkYu_Uuk/</link>
		<comments>http://www.chipstips.com/?p=550#comments</comments>
		<pubDate>Sun, 05 Sep 2010 02:02:34 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[abook]]></category>
		<category><![CDATA[contacts]]></category>
		<category><![CDATA[mutt]]></category>
		<category><![CDATA[query]]></category>
		<category><![CDATA[rbaqua]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=550</guid>
		<description><![CDATA[I like using mutt for my email, and abook for my contacts. They&#8217;re simple but powerful programs designed to work together without locking you into one way of doing things. If something doesn&#8217;t work the way you like, you can plug in your own replacement for that piece without giving up on the whole product. [...]]]></description>
			<content:encoded><![CDATA[<p>I like using <a href="http://mutt.org">mutt</a> for my email, and <a href="http://abook.sourceforge.net">abook</a> for my contacts.  They&#8217;re simple but powerful programs designed to work together without locking you into one way of doing things.  If something doesn&#8217;t work the way you like, you can plug in your own replacement for that piece without giving up on the whole product.</p>
<p>Abook provides a mutt-query feature that&#8217;s intended as the query_command for mutt &#8212; that is, how to find an email address for one of your contacts.  The feature works well, but it has some limitations:</p>
<ul>
<li>It only searches the name and email fields</li>
<li>You can&#8217;t combine search criteria</li>
<li>You can only search for literal strings</li>
</ul>
<p>Since mutt&#8217;s query_command provides a well-defined interface, I decided to create a replacement that still uses the abook contacts, but allows you to:</p>
<ul>
<li>Search all contact fields</li>
<li>Search only specific fields</li>
<li>Combine searches with logical and/or</li>
<li>Specify precedence of grouping with parentheses</li>
<li>Use regular expressions</li>
</ul>
<p>The result, which you can download below or snatch from its <a href="http://butbucket.org/sterlingcamden/aqua">BitBucket repository</a>, is aqua &#8212; Advanced Query Using Abook.  Plug it into .muttrc:</p>
<p><code>set query_command="aqua %s"</code></p>
<p>See the included man page for details, and then you can do things like:</p>
<p>Query: notes=geek</p>
<p>So you can tell them all about it!</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbaqua.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/rYke6RenHd725_jizm-kjWGnr1Y/0/da"><img src="http://feedads.g.doubleclick.net/~a/rYke6RenHd725_jizm-kjWGnr1Y/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/rYke6RenHd725_jizm-kjWGnr1Y/1/da"><img src="http://feedads.g.doubleclick.net/~a/rYke6RenHd725_jizm-kjWGnr1Y/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=We2WkYu_Uuk:Xt7Tla_jlvI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=We2WkYu_Uuk:Xt7Tla_jlvI:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=We2WkYu_Uuk:Xt7Tla_jlvI:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=We2WkYu_Uuk:Xt7Tla_jlvI:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=We2WkYu_Uuk:Xt7Tla_jlvI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=We2WkYu_Uuk:Xt7Tla_jlvI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=We2WkYu_Uuk:Xt7Tla_jlvI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=We2WkYu_Uuk:Xt7Tla_jlvI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=We2WkYu_Uuk:Xt7Tla_jlvI:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=We2WkYu_Uuk:Xt7Tla_jlvI:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=We2WkYu_Uuk:Xt7Tla_jlvI:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/We2WkYu_Uuk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=550</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=550</feedburner:origLink></item>
		<item>
		<title>More fun with SQLite3/Ruby</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/ZuVQFPeYoU4/</link>
		<comments>http://www.chipstips.com/?p=548#comments</comments>
		<pubDate>Wed, 25 Aug 2010 05:02:47 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[rbisspam]]></category>
		<category><![CDATA[sqlite3]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=548</guid>
		<description><![CDATA[See Chip sweat Not knowing where to look for SQLite3 maximums at the time (here they are), I became a little nervous as my Bayesian spam filter database approached 2 gigabytes. When it passed that milestone and continued working, I breathed a sigh of relief. Then one day, I noticed that the spam totals were [...]]]></description>
			<content:encoded><![CDATA[<h3>See Chip sweat</h3>
<p>Not knowing where to look for SQLite3 maximums at the time (<a href="http://sqlite.org/limits.html">here they are</a>), I became a little nervous as my <a href="http://www.chipstips.com/?p=533">Bayesian spam filter</a> database approached 2 gigabytes.  When it passed that milestone and continued working, I breathed a sigh of relief.  Then one day, I noticed that the spam totals were wrong &#8212; my total number of spam messages had been replaced with a nil!</p>
<p>Fortunately, I discovered the problem on the day it first occurred.  I restored the database from the previous day&#8217;s backup &#8212; then started to try to figure out what was wrong.  I was just about certain that 2GB was the culprit.  Probably a pointer being cast to int somewhere in the implementation of SQLite3 or the gem.  Using irb I verified that I could reproduce the problem with a simple insert.  So I logged an issue on the <a href="https://groups.google.com/group/sqlite3-ruby">sqlite3-ruby Google Group</a>.</p>
<p>Luis Lavena responded almost immediately.  In trying to help him reproduce the problem without uploading 2GB of data, I tried out each step myself as well.  Suddenly I found that I couldn&#8217;t get an insert to work, even on an empty database!  That let the 2GB boundary off the hook.  What could I have possibly screwed up?</p>
<p>It turns out that all I had done was to upgrade the gem, which came through on a portupgrade.  Even though I still can&#8217;t find it anywhere in the docs, Luis let me know that the parameters to a query need to be passed as an array now, instead of as individual arguments (UPDATE: <a href="http://github.com/luislavena/sqlite3-ruby/commit/43dd1e0024aa33efbc293303859a61e6ccb69a23">it&#8217;s been added now</a>).  Be warned, and please upgrade your copy of isspam before you get version 1.3.0 or greater of the sqlite3 gem.  As usual, the tarball link is below, or you can pull it from <a href="http://bitbucket.org/sterlingcamden/isspam">Bitbucket</a>.</p>
<p>In case you&#8217;re curious, SQLite3 supports a maximum of 1073741823 pages out of the box, with a default page size of 1024.  That means a maximum file size of about 1 terabyte.  But if you tweak the page size, you can approach 32 terabytes.</p>
<h3>See Chip smack his forehead</h3>
<p>I had a V-8 moment when I discovered that the <a href="http://github.com/luislavena/sqlite3-ruby#readme">sqlite3 rubygem</a> allows you to pass a code block to its database queries.  That block gets called once per row as they are being extracted from the database.  That made my <a href="http://www.chipstips.com/?p=546">paged query</a> obsolete, so I ripped it out.</p>
<h3>See Chip code another feature just for the fun of it</h3>
<p>Ever wonder how an individual word affects spam score?  Of course, you could always do <code>echo <em>word</em> | isspam -p</code>, but that only gives you that word&#8217;s spam score.  It doesn&#8217;t tell you how many times that word has been seen in spam or non-spam.  So I added a -w option, which takes all the phrases from the input and gives you the detailed statistics on those phrases.  You can control the length of phrase to analyze with -m, so if you only want individual words, use -m 1.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbisspam.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/uQP6Aah1XVcb51aOeu1OxkTxDew/0/da"><img src="http://feedads.g.doubleclick.net/~a/uQP6Aah1XVcb51aOeu1OxkTxDew/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/uQP6Aah1XVcb51aOeu1OxkTxDew/1/da"><img src="http://feedads.g.doubleclick.net/~a/uQP6Aah1XVcb51aOeu1OxkTxDew/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=ZuVQFPeYoU4:VOqGpHo2bGA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=ZuVQFPeYoU4:VOqGpHo2bGA:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=ZuVQFPeYoU4:VOqGpHo2bGA:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=ZuVQFPeYoU4:VOqGpHo2bGA:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=ZuVQFPeYoU4:VOqGpHo2bGA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=ZuVQFPeYoU4:VOqGpHo2bGA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=ZuVQFPeYoU4:VOqGpHo2bGA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=ZuVQFPeYoU4:VOqGpHo2bGA:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=ZuVQFPeYoU4:VOqGpHo2bGA:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=ZuVQFPeYoU4:VOqGpHo2bGA:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=ZuVQFPeYoU4:VOqGpHo2bGA:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/ZuVQFPeYoU4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=548</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=548</feedburner:origLink></item>
		<item>
		<title>Handling mammoth queries in SQLite/Ruby</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/smuNogqFTqc/</link>
		<comments>http://www.chipstips.com/?p=546#comments</comments>
		<pubDate>Thu, 05 Aug 2010 01:23:24 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[rbisspam]]></category>
		<category><![CDATA[sqlite3]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=546</guid>
		<description><![CDATA[As my isspam Bayesian spam filter learns about the spamminess of new words and phrases, its SQLite database grows correspondingly. It&#8217;s now up to 1.8 gigabytes, containing more than 6.5 million unique phrases. Because it&#8217;s an SQL database, the size doesn&#8217;t impact performance much, except when gathering database-wide statistics. The &#8216;isspam -d&#8217; command, which dumps [...]]]></description>
			<content:encoded><![CDATA[<p>As my <a href="http://www.chipstips.com/?p=533">isspam Bayesian spam filter</a> learns about the spamminess of new words and phrases, its SQLite database grows correspondingly.  It&#8217;s now up to 1.8 gigabytes, containing more than 6.5 million unique phrases.  Because it&#8217;s an SQL database, the size doesn&#8217;t impact performance much, except when gathering database-wide statistics.  The &#8216;isspam -d&#8217; command, which dumps all of the data to stdout, would run for hours and exhaust available memory (in the previous version).  That&#8217;s because <a href="http://sqlite-ruby.rubyforge.org/">SQLite/Ruby</a> wants to return the result of a SELECT operation as an array of rows.  6.5 <strong>million</strong> rows.</p>
<p>That just won&#8217;t do, especially as the database continues to grow.  So I reworked the &#8216;dump&#8217; option to read the database in pages of 10,000 rows at a time on one thread, while the main thread formats the output to the screen.  I generalized the concept slightly by monkeypatching SQLite3::Database to add a &#8216;paged_execute&#8217; method to which you can pass the desired page size, and which returns a PagedRows object that has an  &#8216;each&#8217; method and a &#8216;count&#8217; attribute.  Thus, you can use it in place of a Database#execute for most operations that will potentially return an enormous number of rows.</p>
<p>The main thread self-balances its sleep time between checking for input from the database query thread, in order to minimize overall runtime.</p>
<p>I&#8217;ve included the code and docs in the tarball below, or you can grab them from the <a href="http://bitbucket.org/sterlingcamden/isspam/">BitBucket repository</a>.  But in case you&#8217;re not interested in all that spam filter code, here&#8217;s the bit that adds paged queries to SQLite3::Database:</p>
<pre><code>
# class to create an array-like set of paged database query results
class PagedRows
  # count of items retrieved so far
  attr :count

  # Create a paged query
  #  db = SQLite3::Database
  #  pagesize = int, number of rows per query
  #  query = select statement without limit or offset
  #  param = parameter for query
  def initialize(db, pagesize, query, param)
    @db = db
    @query = query
    @param = param
    @pagesize = pagesize
    @count = 0
  end

  # Iterate over all rows from multiple queries
  def each(&#038;block)
    queue = []			# pages yet to be processed
    done = false
    mutex = Mutex.new

    # worker thread for database queries
    t = Thread.new {
      offset = 0
      begin
	# grab a page
        enq = @db.execute(@query + " LIMIT #{@pagesize} OFFSET #{offset}", @param)
	mutex.synchronize do
	  queue << enq		# add to queue
	end
        offset += @pagesize
        @count += enq.size
      end until enq.size < @pagesize
      enq = nil			# so last page gets released after processing
      done = true
    }

    nsleep = 0.5		# how long to wait for worker thread
    deq = nil			# establish scope of deq
    while (!done)
      sleep nsleep
      while queue.size > 0	# any rows already fetched?
	mutex.synchronize do
       	  deq = queue.shift
        end
	deq.each &#038;block		# process the page of rows
	deq = nil		# release for GC
	Thread.pass		# give the worker thread a chance
	nsleep = [0.001, (nsleep*0.6666666667)].max	# Ok, we waited long enough (will be bumped below)
      end
      nsleep *= 1.5		# wait a little longer next time
    end

    t.join			# this should be a formality
  end
end

class SQLite3::Database
  # Create a paged query
  #  pagesize = number of rows per query
  #  query = database select statement
  #  param = parameter for query
  def paged_execute(pagesize, query, param)
    PagedRows.new self, pagesize, query, param
  end
end
</pre>
<p></code></p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbisspam.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/OQjhJkewaD5GPWvyBEXWvF3OGuY/0/da"><img src="http://feedads.g.doubleclick.net/~a/OQjhJkewaD5GPWvyBEXWvF3OGuY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/OQjhJkewaD5GPWvyBEXWvF3OGuY/1/da"><img src="http://feedads.g.doubleclick.net/~a/OQjhJkewaD5GPWvyBEXWvF3OGuY/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=smuNogqFTqc:D7zknWvVIA0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=smuNogqFTqc:D7zknWvVIA0:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=smuNogqFTqc:D7zknWvVIA0:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=smuNogqFTqc:D7zknWvVIA0:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=smuNogqFTqc:D7zknWvVIA0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=smuNogqFTqc:D7zknWvVIA0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=smuNogqFTqc:D7zknWvVIA0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=smuNogqFTqc:D7zknWvVIA0:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=smuNogqFTqc:D7zknWvVIA0:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=smuNogqFTqc:D7zknWvVIA0:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=smuNogqFTqc:D7zknWvVIA0:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/smuNogqFTqc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=546</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=546</feedburner:origLink></item>
		<item>
		<title>Tiling Windows windows, my way</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/LL7wScwDkLY/</link>
		<comments>http://www.chipstips.com/?p=541#comments</comments>
		<pubDate>Sun, 01 Aug 2010 00:50:54 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[C and C++]]></category>
		<category><![CDATA[Wildly popular]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[cpptile]]></category>
		<category><![CDATA[C_language]]></category>
		<category><![CDATA[desktop]]></category>
		<category><![CDATA[tiling]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=541</guid>
		<description><![CDATA[Now that I&#8217;ve been using XMonad as my window manager on FreeBSD for a while, I find that when I have to work on Windows one of the more painful aspects thereof is, ironically, window management. I spend way too much time hunting for the right window or arranging windows so I can clearly see [...]]]></description>
			<content:encoded><![CDATA[<p>Now that I&#8217;ve been using <a href="http://xmonad.org">XMonad</a> as my window manager on FreeBSD for a while, I find that when I have to work on Windows one of the more painful aspects thereof is, ironically, window management.  I spend way too much time hunting for the right window or arranging windows so I can clearly see all the ones I need at once.  Windows Vista and 7 have the ability to view windows side-by-side or stacked, but that&#8217;s not adequate to my needs and it virtually requires using the mouse.  I desperately want an automatic tiling window manager like XMonad.</p>
<p>So, I created the next best thing:  a command-line utility that will arrange all the windows in a tiled fashion, according to a pattern specified per monitor.  In the download below you will find all the sources, plus in the Release subdirectory I&#8217;ve included an executable built with Visual Studio 2010.</p>
<p>For usage, type &#8217;tile&#8217; at a command prompt without arguments.  But, to give you some examples:</p>
<p><code>tile 1 2</code></p>
<p>This tiles both the primary and second monitors using a column-based scheme, with no maximum of columns.  Each display will be divided into the number of columns needed to hold all of the windows on that monitor.</p>
<p><code>tile 1:2r 2:3c</code></p>
<p>This tiles the primary monitor into 2 rows, with each row split as evenly as possible to accomodate all of the windows.  Thus, if the primary window contains three windows, the first window will occupy the entire top row, and the second row will be split between the other two windows.  On monitor #2, we&#8217;ll have three columns &#8212; so if we have five windows there, we&#8217;ll get one window in the first column and two windows stacked in each of the two remaining columns.</p>
<p>If you specify more rows or columns than there are windows on that monitor, it&#8217;s automatically reduced to the number of windows.  Tile silently ignores most errors, such as specifying a monitor that doesn&#8217;t exist.  Tile does not affect windows that are maximized or minimized.  In order to be considered within a monitor, at least 10 pixels of the window in both directions must intersect the monitor&#8217;s rectangle.</p>
<p>As noted above, all parts of an argument except for the monitor number are optional.  If omitted, the max defaults to 99, and row/column defaults to column.</p>
<p>So, now I can choose my optimal layout, put it in a little batch file named &#8216;t.bat&#8217;, and whenever I need my windows to snap into place I can just type &#8216;t&#8217; at a command prompt.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/cpptile.zip"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/c4YGn6ft-QlDPY0Q7ZMFj0Tw4IY/0/da"><img src="http://feedads.g.doubleclick.net/~a/c4YGn6ft-QlDPY0Q7ZMFj0Tw4IY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/c4YGn6ft-QlDPY0Q7ZMFj0Tw4IY/1/da"><img src="http://feedads.g.doubleclick.net/~a/c4YGn6ft-QlDPY0Q7ZMFj0Tw4IY/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LL7wScwDkLY:CGCUQ4X6pwc:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LL7wScwDkLY:CGCUQ4X6pwc:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LL7wScwDkLY:CGCUQ4X6pwc:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LL7wScwDkLY:CGCUQ4X6pwc:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LL7wScwDkLY:CGCUQ4X6pwc:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LL7wScwDkLY:CGCUQ4X6pwc:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LL7wScwDkLY:CGCUQ4X6pwc:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LL7wScwDkLY:CGCUQ4X6pwc:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LL7wScwDkLY:CGCUQ4X6pwc:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LL7wScwDkLY:CGCUQ4X6pwc:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LL7wScwDkLY:CGCUQ4X6pwc:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/LL7wScwDkLY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=541</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=541</feedburner:origLink></item>
		<item>
		<title>OPML-based feed subscriptions made easy</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/oJGw1cxHpsg/</link>
		<comments>http://www.chipstips.com/?p=539#comments</comments>
		<pubDate>Fri, 30 Jul 2010 19:50:12 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[ATOM]]></category>
		<category><![CDATA[OPML]]></category>
		<category><![CDATA[RSS]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[atom]]></category>
		<category><![CDATA[mutt]]></category>
		<category><![CDATA[newspipe]]></category>
		<category><![CDATA[rbopmlsub]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=539</guid>
		<description><![CDATA[I&#8217;ve been a much happier feed consumer since I started using Newspipe to pump my feeds to mutt. Subscribing to new feeds, however, got a bit more complicated. I&#8217;d have to edit my OPML file and make sure I got the new element inserted with all the right syntax. Even before that, I&#8217;d have to [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been a much happier feed consumer since I started <a href="http://www.geeksaresexy.net/2010/06/16/how-to-teach-your-old-mutt-some-new-tricks/">using Newspipe to pump my feeds to mutt</a>.  Subscribing to new feeds, however, got a bit more complicated.  I&#8217;d have to edit my OPML file and make sure I got the new element inserted with all the right syntax.  Even before that, I&#8217;d have to find the link to the feed I wanted.  You&#8217;d be suprised how many sites don&#8217;t provide a direct link to their feed, so I&#8217;d often have to view the page source and search for an RSS autodiscovery link.</p>
<p>Then a few days ago, a reader asked me if I knew of a utility that would list all the autodiscovered feeds on a site.  Since I could obviosuly use the same thing myself, I wrote it.  It&#8217;s called feeds.rb.  You just type a command like this at the command prompt:<br />
<code>feeds.rb http://example.com</code></p>
<p>&#8230; and it lists out all the feeds that have autodiscovery links on that page.</p>
<p>The next step, of course, is to automate including one of these links in my OPML file.  Yes, I&#8217;m lazy &#8212; it&#8217;s one of the traits of an efficient programmer.  I created a second script, called opmlsub.rb:</p>
<p><code>opmlsub.rb myopmlfile.opml -s http://example.com > myopmlfile.new</code></p>
<p>This one takes the given URL and adds an outline element for it to the incoming OPML file, spitting the result to stdout.  If the URL is a feed, it will just use it directly.  If it&#8217;s an HTML page, it will look for autodiscovery links and use the first RSS 2.0 link, if available, or the first ATOM link if an RSS 2.0 link can&#8217;t be found (I have to prefer RSS 2.0, being a member of <a href="http://www.rssboard.org">The Board</a>).</p>
<p>You can also instruct opmlsub.rb where to place the new link.  The option <code>-i TEXT</code> specifies the value of the &#8220;text&#8221; attribute of an existing &#8220;outline&#8221; entry inside which the new link will be placed, as the new last child element.  If not specified or not found, then the new element will be the last child of the &#8220;body&#8221; element.</p>
<p>I didn&#8217;t include options for editing and deleting elements.  That&#8217;s easy enough to do with vim on the rare occasions when it&#8217;s needed.</p>
<p>Finally, I wanted to be able to do all this while looking at a page in Firefox (or Chromium, when it&#8217;s released on FreeBSD).  So I created a third script named &#8216;clipsub&#8217;, which takes a URL from the clipboard (using xclip) and adds it to my OPML file without any intervention.  I then mapped that to a key shortcut (mod4+shift+S) in my window manager, xmonad.  Because opmlsub.rb validates a subset of the feed, I don&#8217;t have to worry about accidentally invoking this when a non-URL is on the clipboard.  If any error occurs, clipsub pops up the error message in an xmessage window.</p>
<p>So now, when I see a site to which I&#8217;d like to subscribe, I just press ctrl+L (highlight the URL in the address bar), ctrl+C (copy it to the clipboard), and mod4+shift+S (subscribe) &#8212; and the posts start magically showing up in my feeds folder in mutt.</p>
<p><a href="http://bitbucket.org/sterlingcamden/opmlsub/">Mercurial repository on BitBucket</a></p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbopmlsub.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/EOajoFevRtII9EHdHKXONsrzJUY/0/da"><img src="http://feedads.g.doubleclick.net/~a/EOajoFevRtII9EHdHKXONsrzJUY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/EOajoFevRtII9EHdHKXONsrzJUY/1/da"><img src="http://feedads.g.doubleclick.net/~a/EOajoFevRtII9EHdHKXONsrzJUY/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=oJGw1cxHpsg:zyyfYDOTiwI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=oJGw1cxHpsg:zyyfYDOTiwI:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=oJGw1cxHpsg:zyyfYDOTiwI:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=oJGw1cxHpsg:zyyfYDOTiwI:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=oJGw1cxHpsg:zyyfYDOTiwI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=oJGw1cxHpsg:zyyfYDOTiwI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=oJGw1cxHpsg:zyyfYDOTiwI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=oJGw1cxHpsg:zyyfYDOTiwI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=oJGw1cxHpsg:zyyfYDOTiwI:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=oJGw1cxHpsg:zyyfYDOTiwI:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=oJGw1cxHpsg:zyyfYDOTiwI:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/oJGw1cxHpsg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=539</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=539</feedburner:origLink></item>
		<item>
		<title>Handling iCalendar attachments with mutt and when</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/h92YZUnwO3U/</link>
		<comments>http://www.chipstips.com/?p=538#comments</comments>
		<pubDate>Tue, 27 Jul 2010 04:55:01 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[icalendar]]></category>
		<category><![CDATA[mailcap]]></category>
		<category><![CDATA[mutt]]></category>
		<category><![CDATA[rbremindwhen]]></category>
		<category><![CDATA[when]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=538</guid>
		<description><![CDATA[The other day I received a meeting request sent from Microsoft Outlook. This is the first such request that I&#8217;ve received since converting to mutt as my Mail User Agent (MUA). Thus, the request came as a file attachment of type &#8220;text/calendar&#8221;, which I could view as text. If you&#8217;ve ever looked at an iCalendar [...]]]></description>
			<content:encoded><![CDATA[<p>The other day I received a meeting request sent from Microsoft Outlook.  This is the first such request that I&#8217;ve received since converting to <a href="http://mutt.org">mutt</a> as my Mail User Agent (MUA).  Thus, the request came as a file attachment of type &#8220;text/calendar&#8221;, which I could view as text.  If you&#8217;ve ever looked at an iCalendar file, you know that they aren&#8217;t organized for easy reading.  Finally, after I gathered the details, I added the appointment to my <a href="http://www.lightandmatter.com/when/when.html">when</a> calendar.</p>
<p>&#8220;I should automate this,&#8221; thought I.  So I did.  I&#8217;ve added some scripts to the remindwhen project to handle iCalendar attachments, including one &#8220;glue&#8221; script that&#8217;s specifically designed for use with mutt and when.  See the README for details.  As usual, tarball link is on the button below, or you can clone the Mercurial repository from <a href="http://bitbucket.org/sterlingcamden/remindwhen">BitBucket</a>.</p>
<p>With the icalmutt.rb script in .mailcap for &#8216;text/calendar&#8217;, I can now view an iCalendar attachment without all the meaningless extra garbage that Outlook throws into it.  At the bottom, it asks me whether I want to respond with accept, decline, tentative, or not now.  If one of the first three, then the script creates an iCalendar response to the meeting and emails it to the meeting organizer using mutt (so it goes in my sent folder, too).  If the response is &#8220;accept&#8221;, then the script also adds the event to my when calendar, with any specified advance alarm for remindwhen.</p>
<p>At some point, I&#8217;d like to add a script for creating a meeting as the organizer, but right now I&#8217;m tired of looking at the iCalendar specs and the sparse documentation of the icalendar rubygem (I ended  up reading the source instead a lot of the time).</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbremindwhen.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/DCKpEt0h_ACBRI5TqFPNz8AHreE/0/da"><img src="http://feedads.g.doubleclick.net/~a/DCKpEt0h_ACBRI5TqFPNz8AHreE/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/DCKpEt0h_ACBRI5TqFPNz8AHreE/1/da"><img src="http://feedads.g.doubleclick.net/~a/DCKpEt0h_ACBRI5TqFPNz8AHreE/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=h92YZUnwO3U:KEaNJG82Xfs:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=h92YZUnwO3U:KEaNJG82Xfs:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=h92YZUnwO3U:KEaNJG82Xfs:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=h92YZUnwO3U:KEaNJG82Xfs:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=h92YZUnwO3U:KEaNJG82Xfs:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=h92YZUnwO3U:KEaNJG82Xfs:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=h92YZUnwO3U:KEaNJG82Xfs:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=h92YZUnwO3U:KEaNJG82Xfs:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=h92YZUnwO3U:KEaNJG82Xfs:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=h92YZUnwO3U:KEaNJG82Xfs:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=h92YZUnwO3U:KEaNJG82Xfs:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/h92YZUnwO3U" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=538</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=538</feedburner:origLink></item>
		<item>
		<title>Vim-friendly proofreading tools</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/1GWnUHM_o50/</link>
		<comments>http://www.chipstips.com/?p=537#comments</comments>
		<pubDate>Sat, 24 Jul 2010 19:33:13 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[mattmight]]></category>
		<category><![CDATA[proofreading]]></category>
		<category><![CDATA[rbproofread]]></category>
		<category><![CDATA[sh]]></category>
		<category><![CDATA[utilities]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=537</guid>
		<description><![CDATA[Matt Might published three proof-reading scripts to detect so-called &#8220;weasel words&#8221; (words you really shouldn&#8217;t use in good writing), passive voice, and &#8220;lexical illusions&#8221; (word duplication across a line boundary). I like these scripts, but I needed to adapt them to my evil purposes. First and foremost, all of Matt&#8217;s scripts require file arguments. For [...]]]></description>
			<content:encoded><![CDATA[<p>Matt Might published <a href="http://matt.might.net/articles/shell-scripts-for-passive-voice-weasel-words-duplicates/"> three proof-reading scripts</a> to detect so-called &#8220;weasel words&#8221; (words you really shouldn&#8217;t use in good writing), passive voice, and &#8220;lexical illusions&#8221; (word duplication across a line boundary).  I like these scripts, but I needed to adapt them to my evil purposes.</p>
<p>First and foremost, all of Matt&#8217;s scripts require file arguments.  For use from within vim, I find piping more useful.  So I&#8217;ve modified them all to allow either.</p>
<p>Second, I found some of the scripts to be too complex.  I think one optional weasel-word file should be enough.  I also reduced the size and complexity of the duplicate word detector by rewriting it (in Ruby).</p>
<p>Finally, I built spell-checking into the suite by invoking aspell(1) in pipe mode and filtering the results.</p>
<p>Of course, I advise against relying on these tools to replace the human eye and brain &#8212; they aren&#8217;t foolproof.  But they can fill in some of the gaps that said human apparatus might miss.</p>
<p>I didn&#8217;t see any license mentioned on Matt&#8217;s site, but I&#8217;m releasing my versions under the <a href="http://owl.apotheon.org">Open Works License (OWL)</a> as usual.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbproofread.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/Uqa7FNJ3lsv8JE6Y7owMA6WjMLY/0/da"><img src="http://feedads.g.doubleclick.net/~a/Uqa7FNJ3lsv8JE6Y7owMA6WjMLY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/Uqa7FNJ3lsv8JE6Y7owMA6WjMLY/1/da"><img src="http://feedads.g.doubleclick.net/~a/Uqa7FNJ3lsv8JE6Y7owMA6WjMLY/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=1GWnUHM_o50:FPHkMMqWImQ:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=1GWnUHM_o50:FPHkMMqWImQ:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=1GWnUHM_o50:FPHkMMqWImQ:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=1GWnUHM_o50:FPHkMMqWImQ:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=1GWnUHM_o50:FPHkMMqWImQ:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=1GWnUHM_o50:FPHkMMqWImQ:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=1GWnUHM_o50:FPHkMMqWImQ:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=1GWnUHM_o50:FPHkMMqWImQ:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=1GWnUHM_o50:FPHkMMqWImQ:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=1GWnUHM_o50:FPHkMMqWImQ:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=1GWnUHM_o50:FPHkMMqWImQ:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/1GWnUHM_o50" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=537</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=537</feedburner:origLink></item>
		<item>
		<title>To spam or not to spam; I have an answer (maybe)</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/vEQ_iY3Aa5w/</link>
		<comments>http://www.chipstips.com/?p=533#comments</comments>
		<pubDate>Mon, 19 Jul 2010 23:15:48 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[bayesian]]></category>
		<category><![CDATA[filtering]]></category>
		<category><![CDATA[getmail]]></category>
		<category><![CDATA[mutt]]></category>
		<category><![CDATA[paulgraham]]></category>
		<category><![CDATA[rbisspam]]></category>
		<category><![CDATA[spam]]></category>
		<category><![CDATA[sqlite3]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=533</guid>
		<description><![CDATA[Overall I&#8217;ve been very happy with my getlessmail spam filter. Using a simple Ruby script to describe the rules for weeding out the canned content posing as real meat provides enough flexibility without complexity most of the time. However, spammers are a smart lot (if you overlook the questionable decision to get into the spamming [...]]]></description>
			<content:encoded><![CDATA[<p>Overall I&#8217;ve been very happy with my <a href="http://chipstips.com/?tag=rbgetlessmail">getlessmail</a> spam filter.  Using a simple Ruby script to describe the rules for weeding out the canned content posing as real meat provides enough flexibility without complexity most of the time.  However, spammers are a smart lot (if you overlook the questionable decision to get into the spamming game in the first place).  They take great pains to defy simple rules for identifying them as spammers.  I needed something additional.</p>
<p>A few years ago, I read Paul Graham&#8217;s essay <a href="http://www.paulgraham.com/spam.html">A Plan for Spam</a> and its sequel <a href="http://www.paulgraham.com/better.html">Better Bayesian Filtering</a>.  The simple yet convincing logic of his approach fascinated me, and I itched to try implementing it.  Now that I have a reason to do so, I wrote one in Ruby.  I implemented it as a class (IsSpam), with a command-line utility wrapper (isspam).  The former can be easily built into a getlessmail script without spawning another process (not that there&#8217;s anything wrong with that), while the latter can be used from an MUA or cron job to populate the database.</p>
<p>You can grab the tarball at the link below, or clone the <a href="http://bitbucket.org/sterlingcamden/isspam">Mercurial repository</a> from BitBucket.  See the README for an overview.  I&#8217;ve also included a man page for the command-line utility, and RDoc pages for the Ruby class.</p>
<p>I&#8217;ve followed Graham pretty closely in translating the algorithms from Lisp to Ruby.  Some exceptions include how I handle non-word characters and how I test for phrases.</p>
<p>For non-word characters, I do two things: I test the words without them, and with them.  The exception to this are the punctuation characters [.:;,], which I don&#8217;t include in a word if it is followed by whitespace (this pattern can be overridden by the &#8216;word_split&#8217; attribute).  Additionally, if a word ends in [?!] (overridable via the &#8216;trailing&#8217; attribute), then I also test the same word without that character, recursively.  Thus, &#8220;buy!!!&#8221; tests as &#8220;buy!!!&#8221;, &#8220;buy!!&#8221;, &#8220;buy!&#8221;, and &#8220;buy&#8221;.</p>
<p>In addition to testing each word individually, I test the combination of adjacent words up to the value of the &#8216;max_phrase_length&#8217; attribute (3 by default, but it can be overridden).  Certain combinations of words should have their own score, but if you test for too long a combination, then you penalize performance for little or no gain.  In any case, I don&#8217;t test any phrase longer than 256 characters.</p>
<p>So far, the results look promising.  My only problem is that I don&#8217;t yet have enough data.  For the first time in my life, I find myself looking forward to receiving more spam, so I can collect a better sample size.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbisspam.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/wYqJutvcxtghHRVORb0i-A8IZew/0/da"><img src="http://feedads.g.doubleclick.net/~a/wYqJutvcxtghHRVORb0i-A8IZew/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/wYqJutvcxtghHRVORb0i-A8IZew/1/da"><img src="http://feedads.g.doubleclick.net/~a/wYqJutvcxtghHRVORb0i-A8IZew/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vEQ_iY3Aa5w:G3pBjo7ku-E:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vEQ_iY3Aa5w:G3pBjo7ku-E:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vEQ_iY3Aa5w:G3pBjo7ku-E:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vEQ_iY3Aa5w:G3pBjo7ku-E:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vEQ_iY3Aa5w:G3pBjo7ku-E:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vEQ_iY3Aa5w:G3pBjo7ku-E:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vEQ_iY3Aa5w:G3pBjo7ku-E:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vEQ_iY3Aa5w:G3pBjo7ku-E:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vEQ_iY3Aa5w:G3pBjo7ku-E:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vEQ_iY3Aa5w:G3pBjo7ku-E:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vEQ_iY3Aa5w:G3pBjo7ku-E:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/vEQ_iY3Aa5w" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=533</wfw:commentRss>
		<slash:comments>27</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=533</feedburner:origLink></item>
		<item>
		<title>Per-CPU usage stats for FreeBSD</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/SmJE6Fx3Mnk/</link>
		<comments>http://www.chipstips.com/?p=527#comments</comments>
		<pubDate>Mon, 12 Jul 2010 19:47:31 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[C and C++]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[cpu]]></category>
		<category><![CDATA[C_language]]></category>
		<category><![CDATA[c_pcpustat]]></category>
		<category><![CDATA[freebsd]]></category>
		<category><![CDATA[stats]]></category>
		<category><![CDATA[sysctl]]></category>
		<category><![CDATA[utility]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=527</guid>
		<description><![CDATA[On my FreeBSD system, I like to keep various statistics displayed in my xmobar at all times. Most of the system statistics that are built into xmobar require the /proc filesystem procfs(5), which isn&#8217;t mounted by default in FreeBSD for security reasons. Thus, I have rolled my own pipes to grab this infomation from sysctl(8) [...]]]></description>
			<content:encoded><![CDATA[<p>On my FreeBSD system, I like to keep various statistics displayed in my <a href="http://code.haskell.org/~arossato/xmobar/">xmobar</a> at all times.  Most of the system statistics that are built into xmobar require the /proc filesystem procfs(5), which isn&#8217;t mounted by default in FreeBSD for security reasons.  Thus, I have rolled my own pipes to grab this infomation from sysctl(8) and other places.  One such statistic that proved difficult was per-CPU usage.  The top(1) utility shows this information if you specify the -P option, but you can&#8217;t get that output to a pipe in order to filter or reformat it.</p>
<p>After digging around in top&#8217;s sources (/usr/src/contrib/top and /usr/src/usr.bin/top), I was able to clone top&#8217;s algorithm with an output that mimics iostat(8).  Behold:</p>
<pre><code>
% pcpustat -c 10
               cpu 0               cpu 1               cpu 2               cpu 3
  us  ni  sy  in  id  us  ni  sy  in  id  us  ni  sy  in  id  us  ni  sy  in  id
  12   1   5   0  83   2   1   3   0  94  79   1   7   1  13   1   0   0   0  99
  46   0   5   0  50   2   0   2   0  97  50   0   2   0  47   1   1   5   0  93
  30   1   5   1  64   3   1   4   0  92  44   0   5   0  51  14   0   5   0  81
   2   2   1   0  95   0   0   6   0  94  92   0   5   0   3   1   1   0   0  98
   6   1   6   0  87   8   2   4   0  87  76   0   8   0  16   1   0   5   0  95
   0   0   6   0  94  33   2   3   0  62  49   0   8   0  44  10   0   2   0  89
   2   1   3   0  94  26   0   5   0  70  69   1   5   0  25   0   0   1   0  99
   2   1   7   0  91  12   1   2   0  85  80   0   5   0  14   2   0   0   0  98
   1   0   6   0  93   7   1   3   0  88  86   1   8   0   5   1   0   1   0  98
  26   1   6   0  68  50   0   4   0  47   8   0   1   1  90  14   0   2   0  84
</code></pre>
<p>I have a &#8216;make buildworld&#8217; in progress, otherwise it would be almost all idle time.</p>
<p>I&#8217;ve included options to filter the states and CPUs you want to monitor, as well as to flip the percentages (i.e., <em>not</em> in that state).  For example:</p>
<p><code>pcpustat --not --idle --quiet --wait 2</code></p>
<p>generates four columns every 2 seconds, representing the overall utilization of each CPU.</p>
<p>Note how I have both long and short forms of each switch.  This utility provided a good chance for me to become familiar with the getopt_long(3) function in C.  It also serves as a good example of not only how to use it, but also how to provide help for it.</p>
<p>Rather than putting everything into the README, I decided this would be a good opportunity to write my first man(1) page.  I&#8217;ve included that, and if you want to read it right where it is (after you extract the tarball below or clone the <a href="http://bitbucket.org/sterlingcamden/pcpustat">BitBucket Repository</a>), you can use:</p>
<p><code>man -M <em>project-path</em>/man pcpustat</code></p>
<p>or copy it to /usr/local/man.</p>
<p>I&#8217;m thinking this utility might be a good candidate for my first FreeBSD port.  I don&#8217;t think it would port well to non-BSD systems, because it makes use of sysctl(3).</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/pcpustat-1.0.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/6HKKA6M3laT1cZe6Yh-wgfItg3A/0/da"><img src="http://feedads.g.doubleclick.net/~a/6HKKA6M3laT1cZe6Yh-wgfItg3A/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/6HKKA6M3laT1cZe6Yh-wgfItg3A/1/da"><img src="http://feedads.g.doubleclick.net/~a/6HKKA6M3laT1cZe6Yh-wgfItg3A/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=SmJE6Fx3Mnk:gjVhquRYBqw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=SmJE6Fx3Mnk:gjVhquRYBqw:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=SmJE6Fx3Mnk:gjVhquRYBqw:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=SmJE6Fx3Mnk:gjVhquRYBqw:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=SmJE6Fx3Mnk:gjVhquRYBqw:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=SmJE6Fx3Mnk:gjVhquRYBqw:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=SmJE6Fx3Mnk:gjVhquRYBqw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=SmJE6Fx3Mnk:gjVhquRYBqw:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=SmJE6Fx3Mnk:gjVhquRYBqw:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=SmJE6Fx3Mnk:gjVhquRYBqw:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=SmJE6Fx3Mnk:gjVhquRYBqw:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/SmJE6Fx3Mnk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=527</wfw:commentRss>
		<slash:comments>23</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=527</feedburner:origLink></item>
		<item>
		<title>Rump gets more CRUDdy</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/dhkno-QP9B0/</link>
		<comments>http://www.chipstips.com/?p=525#comments</comments>
		<pubDate>Sat, 10 Jul 2010 20:22:32 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[metaweblogapi]]></category>
		<category><![CDATA[rbrump]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=525</guid>
		<description><![CDATA[Since the MetaWebLog API provides &#8220;getPost&#8221;, &#8220;editPost&#8221;, and sometimes &#8220;deletePost&#8221; methods, I&#8217;ve added corresponding operations to rump (my text-editor-oriented blogging tool) via three new command-line switches: -u ID Updates the post identified by ID. This overrides the default operation of adding a new post. It has no effect if -p (preview), -f (fetch), or -d [...]]]></description>
			<content:encoded><![CDATA[<p>Since the MetaWebLog API provides &#8220;getPost&#8221;, &#8220;editPost&#8221;, and sometimes &#8220;deletePost&#8221; methods, I&#8217;ve added corresponding operations to rump (my text-editor-oriented blogging tool) via three new command-line switches:</p>
<p><code>-u ID</code></p>
<p>Updates the post identified by ID.  This overrides the default operation of adding a new post.  It has no effect if -p (preview), -f (fetch), or -d (delete) are also specified.</p>
<p><code>-d ID</code></p>
<p>Deletes the post identified by ID.  This overrides the default operation of adding a new post, or any -u switch.  It has no effect if -p (preview) or -f (fetch) are also specified.  Since deletePost is not included in the MetaWebLog API specification, you&#8217;ll have to see whether your blogging framework provides it.  WordPress does.</p>
<p><code>-f ID</code></p>
<p>Fetches the post identified by ID.  This overrides the default operation of adding a new post, or any -u or -d switch.  It has no effect if -p (preview) is specified.</p>
<p>When fetching, replacements are performed on the resulting post, which is then sent to $stdout in rump source format.  That allows you to edit it and then resubmit it either as an update to the original or as a new post.</p>
<p>This opens up a number of new possible uses for rump.  You could use it to backup your blog to rump files.  You could retrieve and edit posts in vim (or your preferred text editor, as long as it can read/write pipes) without ever saving them to a local file.  You could construct a fetch/update pipe in a loop to perform mass edits.  Your blog is now entirely pipable.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbrump.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>
<p><a href="http://bitbucket.org/sterlingcamden/rump">BitBucket repository</a></p>

<p><a href="http://feedads.g.doubleclick.net/~a/UAoR3KB2SbA26ErPoIlCHz8dBb0/0/da"><img src="http://feedads.g.doubleclick.net/~a/UAoR3KB2SbA26ErPoIlCHz8dBb0/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/UAoR3KB2SbA26ErPoIlCHz8dBb0/1/da"><img src="http://feedads.g.doubleclick.net/~a/UAoR3KB2SbA26ErPoIlCHz8dBb0/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=dhkno-QP9B0:Wupkin6KQwQ:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=dhkno-QP9B0:Wupkin6KQwQ:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=dhkno-QP9B0:Wupkin6KQwQ:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=dhkno-QP9B0:Wupkin6KQwQ:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=dhkno-QP9B0:Wupkin6KQwQ:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=dhkno-QP9B0:Wupkin6KQwQ:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=dhkno-QP9B0:Wupkin6KQwQ:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=dhkno-QP9B0:Wupkin6KQwQ:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=dhkno-QP9B0:Wupkin6KQwQ:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=dhkno-QP9B0:Wupkin6KQwQ:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=dhkno-QP9B0:Wupkin6KQwQ:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/dhkno-QP9B0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=525</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=525</feedburner:origLink></item>
		<item>
		<title>GetLessMail gets more info</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/gC2RjrNDRfs/</link>
		<comments>http://www.chipstips.com/?p=524#comments</comments>
		<pubDate>Mon, 05 Jul 2010 21:43:35 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[getmail]]></category>
		<category><![CDATA[rbgetlessmail]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=524</guid>
		<description><![CDATA[In one of my curious moods, I began to wonder how difficult it would be to figure out the location of an email sender based on the IP address shown in the &#8220;Received&#8221; header fields. It turns out to be more difficult than you may have thought, because: An email often contains multiple &#8220;Received&#8221; headers, [...]]]></description>
			<content:encoded><![CDATA[<p>In one of my curious moods, I began to wonder how difficult it would be to figure out the location of an email sender based on the IP address shown in the &#8220;Received&#8221; header fields.  It turns out to be more difficult than you may have thought, because:</p>
<ol>
<li>An email often contains multiple &#8220;Received&#8221; headers, one for each relay point.  The innermost (last) is the original sender.</li>
<li>However, the original transmission is often within a local network, so the first one or few IPs may be in the reserved local range.</li>
<li>No free, global, authoritative database exists that contains the location of all IPs.  At least, not that I&#8217;ve found.  However, there are some free databases you can download that are updated from time to time.</li>
<li>The owner of the IP address may not be located at the same place as the connection.  In fact, it usually isn&#8217;t, but it may be close.</li>
</ol>
<p>Despite these impediments, I have implemented IP Geolocation for Ruby, and created a method specialized for GetLessMail that uses it.</p>
<p>The two scripts IPGeo.rb and IPGeoMail.rb should be placed somewhere in your Ruby require path.  The example database, which I downloaded from <a href="http://linuxbox.co.uk/ip-address-whois-database.php">http://linuxbox.co.uk/ip-address-whois-database.php</a>, should be placed in /usr/local/share/IPGeo (or you can modify the script to access it wherever you choose).  The included dot.getlessmail shows how you could use it to add an &#8220;X-IP-Location&#8221; header that provides the IP Location data, if found.</p>
<p>As I intimated, you could also use IPGeo.rb outside of the context of email.  It would be trivial to write a script that accepts an IP Address and prints out the information.  Like so:</p>
<pre><code>
require 'IPGeo'
$<.each do |line|
  puts IPGeo.locate IPGeo.get_ip(line)
end
</code></pre>
<p>Of course, this information is only as good as your database.  The one I've included hasn't been updated since August 2009.  You can probably find better databases out there, if you're willing to spend some money on them.  I'm not.</p>
<p>You can get the updated tarball using the button below, or scrape it out of the <a href="http://bitbucket.org/apotheon/getlessmail">BitBucket</a>.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbgetlessmail.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/Tkib2k5FiZle03GIN7ft3i5nh-k/0/da"><img src="http://feedads.g.doubleclick.net/~a/Tkib2k5FiZle03GIN7ft3i5nh-k/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/Tkib2k5FiZle03GIN7ft3i5nh-k/1/da"><img src="http://feedads.g.doubleclick.net/~a/Tkib2k5FiZle03GIN7ft3i5nh-k/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=gC2RjrNDRfs:D-GZPchk0MI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=gC2RjrNDRfs:D-GZPchk0MI:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=gC2RjrNDRfs:D-GZPchk0MI:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=gC2RjrNDRfs:D-GZPchk0MI:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=gC2RjrNDRfs:D-GZPchk0MI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=gC2RjrNDRfs:D-GZPchk0MI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=gC2RjrNDRfs:D-GZPchk0MI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=gC2RjrNDRfs:D-GZPchk0MI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=gC2RjrNDRfs:D-GZPchk0MI:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=gC2RjrNDRfs:D-GZPchk0MI:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=gC2RjrNDRfs:D-GZPchk0MI:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/gC2RjrNDRfs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=524</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=524</feedburner:origLink></item>
		<item>
		<title>Jab gets events and other nice things</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/vfIvQQJ8KfE/</link>
		<comments>http://www.chipstips.com/?p=523#comments</comments>
		<pubDate>Sat, 03 Jul 2010 22:35:21 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[chat]]></category>
		<category><![CDATA[rbjab]]></category>
		<category><![CDATA[xmpp]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=523</guid>
		<description><![CDATA[As I&#8217;ve been using my new chat client, Jab, I&#8217;ve been tweaking it for usability. You can download the updated tarball below, or grab it from BitBucket. The first thing that bothered me is that some chat agents send status updates frequently, even if status hasn&#8217;t changed. So I changed Jab to only notify of [...]]]></description>
			<content:encoded><![CDATA[<p>As I&#8217;ve been using my new chat client, <a href="http://www.chipstips.com/?p=522">Jab</a>, I&#8217;ve been tweaking it for usability.  You can download the updated tarball below, or grab it from <a href="http://bitbucket.org/sterlingcamden/jab">BitBucket</a>.</p>
<p>The first thing that bothered me is that some chat agents send status updates frequently, even if status hasn&#8217;t changed.  So I changed Jab to only notify of status change if the status is different than what it was the last time we received one for that user ID.  Of course, when you &#8216;tap&#8217; a user, Jab will forget any previously known status so you can see the update.</p>
<p>I wanted to set my status to :away when I close a connection.  So I added the concept of events to Jab.  You can hook as many procs as you want to any event, and you can define and signal as many events as you like.  Jab predefines and signals several.  You can see a list of these in the README.  For my status purpose, I just hook the :disconnect event in my .jabrc:</p>
<pre><code>
on :disconnect do
  status :away, "Swapped out"
end
</code></pre>
<p>Another thing I noticed is that I often didn&#8217;t notice when new jabs came in, because I had another workspace active.  Since I use xmobar to display the number of new email messages, I thought I should include the number of new jabs as well.  I decided to define &#8216;new&#8217; jabs as any jabs that came in since I entered the most recent command in Jab.  Thus, it was fairly easy to define hooks for :input and :jab, and then zero or increment the count at each hook, respectively.  I&#8217;ve included that code in a file named &#8216;notify-new&#8217;, which you can simply &#8216;source&#8217; in your .jabrc to get the same action.  This code writes the number to a file (~/.jab/new).  Then, I can create an xmobar monitor in my .xmobarrc as follows:</p>
<p><code><br />
Run Com "cat" ["~/.jab/new"] "jabs" 150<br />
</code></p>
<p>Simple, huh?</p>
<p>Let&#8217;s say you wanted to play a sound whenever a jab came in.  You could do that as follows:</p>
<pre><code>
on :jab do |body|
  system 'mplayer -quiet ~/.jab/new-jab.wav >/dev/null 2>&#038;1'
  body
end
</code></pre>
<p>or use whatever media player you prefer.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbjab.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/C64FbiOn9Y8sTYIogcOx4wr_VtQ/0/da"><img src="http://feedads.g.doubleclick.net/~a/C64FbiOn9Y8sTYIogcOx4wr_VtQ/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/C64FbiOn9Y8sTYIogcOx4wr_VtQ/1/da"><img src="http://feedads.g.doubleclick.net/~a/C64FbiOn9Y8sTYIogcOx4wr_VtQ/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vfIvQQJ8KfE:HI1cdYaf4UE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vfIvQQJ8KfE:HI1cdYaf4UE:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vfIvQQJ8KfE:HI1cdYaf4UE:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vfIvQQJ8KfE:HI1cdYaf4UE:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vfIvQQJ8KfE:HI1cdYaf4UE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vfIvQQJ8KfE:HI1cdYaf4UE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vfIvQQJ8KfE:HI1cdYaf4UE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vfIvQQJ8KfE:HI1cdYaf4UE:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vfIvQQJ8KfE:HI1cdYaf4UE:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=vfIvQQJ8KfE:HI1cdYaf4UE:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=vfIvQQJ8KfE:HI1cdYaf4UE:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/vfIvQQJ8KfE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=523</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=523</feedburner:origLink></item>
		<item>
		<title>Jab: a minimal chat client of, by, and for Ruby</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/V0nIwbgD1X4/</link>
		<comments>http://www.chipstips.com/?p=522#comments</comments>
		<pubDate>Mon, 28 Jun 2010 16:36:01 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[chat]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[edsl]]></category>
		<category><![CDATA[jabber]]></category>
		<category><![CDATA[rbjab]]></category>
		<category><![CDATA[xmpp]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=522</guid>
		<description><![CDATA[I don&#8217;t chat a lot. It&#8217;s too disruptive to use for most communications. But I do have a few friends and colleagues with whom I enjoy the occasional real-time text conversation. In order to make myself available for those people to ping, I have to keep a chat client open. Up until a few days [...]]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t chat a lot.  It&#8217;s too disruptive to use for most communications.  But I do have a few friends and colleagues with whom I enjoy the occasional real-time text conversation.  In order to make myself available for those people to ping, I have to keep a chat client open.  Up until a few days ago, that client was Pidgin.</p>
<p>Pidgin is a nice piece of software &#8212; reliable, and pretty well designed from a user interface perspective.  But there are a couple of things I don&#8217;t like about it.  Pidgin has a large memory footprint &#8212; it&#8217;s like having another browser open.  Granted, on my 4GB system, I don&#8217;t really miss 100+MB, but it&#8217;s the whole idea that you could blow 100MB on something a simple as chat that makes me uncomfortable.</p>
<p>A bigger concern for me is screen real estate.  I like to keep chat and email open on the same workspace, which I divided with xmonad into 80% mutt, 20% pidgin.  Pidgin, though, is not very happy with 20%.  Perhaps if I could figure out a way to use a smaller font it would be OK, but as it stands you can&#8217;t see much of a conversation in that space, and not all the buttons are visible.  Plus, each conversation wants to pop up a new window, which crunches all the others down.  I really don&#8217;t want to dedicate more space to chat.</p>
<p>Like most X11 programs, Pidgin favors the mouse.  I think there probably are ways to use the keyboard to achieve most tasks, but Pidgin was definitely designed for use with the mouse.  The X11 requirement also means that you can&#8217;t use it from a virtual console.  Sure there&#8217;s finch, but I didn&#8217;t find that very impressive.</p>
<p>What I needed was a lean, compact, text-based, customizable chat client that runs in a terminal window.  So I wrote one.</p>
<p>I call it jab, because it uses XMPP (Jabber), and it&#8217;s written in Ruby (the contrarian in me decided to drop the &#8216;r&#8217; in Jabber rather than adding one).  All chat actions are Ruby statements that you enter as commands within a single terminal window.  Responses are displayed in the same window, but they begin with a &#8216;>&#8217; to distinguish them.  You can also apply color filters to help visually separate conversations with different people, or different kinds of notifications.</p>
<p>Because jab runs as a read-eval loop in Ruby, it&#8217;s essentially a specialized irb.  In fact, I&#8217;ve separated the Jab class into its own file, so if you prefer you could even run your chat session from irb instead of jab.  Because you have the full power of Ruby in your hands, you can customize jab in an infinite number of ways.  Naturally, it supports rc files to set up your preferences for each session.</p>
<p>I&#8217;ve tested jab with Jabber and Gmail accounts on both ends, running on FreeBSD 8 and Windows Vista (though Vista reports errors from Ruby&#8217;s Readline module if you don&#8217;t have terminfo and stty &#8212; I use MKS&#8217;s version).  I haven&#8217;t tried any other hosts or client platforms, so if you do please let me know your results.</p>
<p>For full documentation, see the README.  For concise documentation, start jab and type &#8216;help&#8217;.</p>
<p>You can download the tarball below, or clone the sources from <a href="http://bitbucket.org/sterlingcamden/jab">BitBucket</a>.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbjab.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/MA6pxr-sOLWkfEeQWl9NVvnih0o/0/da"><img src="http://feedads.g.doubleclick.net/~a/MA6pxr-sOLWkfEeQWl9NVvnih0o/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/MA6pxr-sOLWkfEeQWl9NVvnih0o/1/da"><img src="http://feedads.g.doubleclick.net/~a/MA6pxr-sOLWkfEeQWl9NVvnih0o/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=V0nIwbgD1X4:1cQ6RKCKk50:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=V0nIwbgD1X4:1cQ6RKCKk50:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=V0nIwbgD1X4:1cQ6RKCKk50:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=V0nIwbgD1X4:1cQ6RKCKk50:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=V0nIwbgD1X4:1cQ6RKCKk50:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=V0nIwbgD1X4:1cQ6RKCKk50:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=V0nIwbgD1X4:1cQ6RKCKk50:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=V0nIwbgD1X4:1cQ6RKCKk50:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=V0nIwbgD1X4:1cQ6RKCKk50:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=V0nIwbgD1X4:1cQ6RKCKk50:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=V0nIwbgD1X4:1cQ6RKCKk50:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/V0nIwbgD1X4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=522</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=522</feedburner:origLink></item>
		<item>
		<title>Preview your Rump before posting it</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/MwEEUqUGdIc/</link>
		<comments>http://www.chipstips.com/?p=521#comments</comments>
		<pubDate>Sat, 19 Jun 2010 23:26:31 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[metaweblogapi]]></category>
		<category><![CDATA[rbrump]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=521</guid>
		<description><![CDATA[Right after posting my previous entry, I realized that rump needs a preview capability. Embedded code is hard to get right the first time, and so are any other embedded HTML tags. Plus, you want to see how images line up and check the links. I&#8217;d like to be able to make any corrections to [...]]]></description>
			<content:encoded><![CDATA[<p>Right after posting <a href="http://www.chipstips.com/?p=511">my previous entry</a>, I realized that <a href="http://chipstips.com/?tag=rbrump"> rump</a> needs a preview capability.  Embedded code is hard to get right the first time, and so are any other embedded HTML tags.  Plus, you want to see how images line up and check the links.  I&#8217;d like to be able to make any corrections to those in vim before submitting a draft to WordPress.</p>
<p>So, I&#8217;ve added the -p switch to preview the post.  You have to supply an RHTML file for formatting, and then you can generate HTML (or anything else, for that matter) from your post.  I&#8217;ve also provided some scripts to make it easy to see how to send that output to Firefox, w3m, or whatever browser you choose.  See the README file for details.</p>
<p>If you want your RHTML template to produce a page that looks just like your blog, you can do what I did:</p>
<p>1.  Fetch a single, existing post from your blog.  For example:</p>
<p><code>fetch -o preview.rhtml "http://chipstips.com/?p=511"</code></p>
<p>2.  Edit the file to:<br />
    a. add the necessary require (as per the README): <code><% require "rump_preview" %></code><br />
    b. change all occurrences of the title to <code><%=title%></code><br />
    c. replace the content of the post with <code><%=content%></code><br />
    d. replace the categories with <code><%=categories.length > 0 ? categories.join(",") : "Uncategorized" %></code><br />
    e. replace the tags with <code><%=tags.size > 0 ? tags : "None"%></code></p>
<p>3.  Voila!  As long as your stylesheets and scripts are linked by absolute URL, your previews should look just like your blog posts.</p>
<p>You can download the updated tarball below, or get it from the <a href="http://bitbucket.org/sterlingcamden/rump">BitBucket repository</a>.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbrump.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/3XJ-wQL7eoXWjKFnMYAmBl9BsuY/0/da"><img src="http://feedads.g.doubleclick.net/~a/3XJ-wQL7eoXWjKFnMYAmBl9BsuY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/3XJ-wQL7eoXWjKFnMYAmBl9BsuY/1/da"><img src="http://feedads.g.doubleclick.net/~a/3XJ-wQL7eoXWjKFnMYAmBl9BsuY/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=MwEEUqUGdIc:vdHg65_R6m4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=MwEEUqUGdIc:vdHg65_R6m4:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=MwEEUqUGdIc:vdHg65_R6m4:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=MwEEUqUGdIc:vdHg65_R6m4:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=MwEEUqUGdIc:vdHg65_R6m4:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=MwEEUqUGdIc:vdHg65_R6m4:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=MwEEUqUGdIc:vdHg65_R6m4:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=MwEEUqUGdIc:vdHg65_R6m4:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=MwEEUqUGdIc:vdHg65_R6m4:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=MwEEUqUGdIc:vdHg65_R6m4:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=MwEEUqUGdIc:vdHg65_R6m4:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/MwEEUqUGdIc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=521</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=521</feedburner:origLink></item>
		<item>
		<title>Rump – Ruby Upload Metaweblog Post</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/_tQ79sxTRBQ/</link>
		<comments>http://www.chipstips.com/?p=511#comments</comments>
		<pubDate>Wed, 16 Jun 2010 22:14:42 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[metaweblogapi]]></category>
		<category><![CDATA[rbrump]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=511</guid>
		<description><![CDATA[Ever since moving to vim as my primary editor, I&#8217;ve wished that I could also use it as my blogging platform, without having to copy/paste and reformat everything. Well, now I can &#8212; in fact, I&#8217;m posting this directly from vim. I created a Ruby script named rump &#8212; it&#8217;s an acronym for Ruby Upload [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since <a href="http://www.chipsquips.com/?p=1123">moving to vim as my primary editor</a>,  I&#8217;ve wished that I could also use it as my blogging platform, without having to copy/paste and reformat everything.  Well, now I can &#8212; in fact, I&#8217;m posting this directly from vim.</p>
<p>I created a Ruby script named rump &#8212; it&#8217;s an acronym for Ruby Upload Metaweblog Post, but it also reflects the fact that blogging is a form of showing your ass.  You can download this script below, or access it from its <a href="http://bitbucket.org/sterlingcamden/rump">Bitbucket repository</a>.</p>
<p>See the included README file for details.  Essentially, you write your blog post as Ruby code &#8212; but rump provides some methods to keep that code to a minimum.  For instance, in this post, I have a command for title, categories, tags, and content &#8212; the latter using a Here document as its parameter.  Let me illustrate:</p>
<p>
<pre><code>title "Rump - Ruby Upload Metaweblog Post"

tags "ruby, blogging, metaweblogapi, vim"
category "Ruby"
category "Web"

content &lt;&lt;EOF

(content goes here)

EOF </code></pre>
</p>
<p>The rump script takes this file, or any number of files, as arguments.  So, I use a configuration file as the first argument to set up static things like what server to access, followed by the file that contains the post.  You can also override settings in the command line with -e &#8220;commands&#8221;. The special file name &#8220;-&#8221; represents STDIN.</p>
<p>To make this even easier, I set up a separate directory on my local system for each blog, then put all the configuration commands into a file named .rump.  Then, I mapped F12 in vim to post using that file, by adding the following to my .vimrc:</p>
<p><code> :map &lt;F12&gt; :w !rump .rump -^M<br />
</code></p>
<p>That &#8220;^M&#8221; is an actual return character.</p>
<p>So, now that I&#8217;m done with this post, I&#8217;ll press F12 and it will magically appear on Chip&#8217;s Tips.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbrump.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/MVmgkVyyZidTsZa2qaXoL598iUo/0/da"><img src="http://feedads.g.doubleclick.net/~a/MVmgkVyyZidTsZa2qaXoL598iUo/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/MVmgkVyyZidTsZa2qaXoL598iUo/1/da"><img src="http://feedads.g.doubleclick.net/~a/MVmgkVyyZidTsZa2qaXoL598iUo/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=_tQ79sxTRBQ:ZOwMX0OB2yc:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=_tQ79sxTRBQ:ZOwMX0OB2yc:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=_tQ79sxTRBQ:ZOwMX0OB2yc:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=_tQ79sxTRBQ:ZOwMX0OB2yc:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=_tQ79sxTRBQ:ZOwMX0OB2yc:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=_tQ79sxTRBQ:ZOwMX0OB2yc:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=_tQ79sxTRBQ:ZOwMX0OB2yc:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=_tQ79sxTRBQ:ZOwMX0OB2yc:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=_tQ79sxTRBQ:ZOwMX0OB2yc:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=_tQ79sxTRBQ:ZOwMX0OB2yc:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=_tQ79sxTRBQ:ZOwMX0OB2yc:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/_tQ79sxTRBQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=511</wfw:commentRss>
		<slash:comments>10</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=511</feedburner:origLink></item>
		<item>
		<title>When — I just can’t leave it alone</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/YN57YTW-2J8/</link>
		<comments>http://www.chipstips.com/?p=507#comments</comments>
		<pubDate>Fri, 28 May 2010 22:11:29 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[when rbremindwhen pcal w3m calendar xmonad xmobar]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=507</guid>
		<description><![CDATA[I’ve added some new features to my add-ons for the when calendar program.  You can download the tarball below, or pull it from BitBucket. Audible reminders I found that I wasn’t always noticing my reminders in xmobar, so I decided to add sound effects.  I changed remindwhen.rb to look for a file named ~/.when/reminder.wav.  If [...]]]></description>
			<content:encoded><![CDATA[<p>I’ve added some new features to my add-ons for the <a href="http://www.lightandmatter.com/when/when.html">when</a> calendar program.  You can download the tarball below, or pull it from <a href="http://bitbucket.org/sterlingcamden/remindwhen">BitBucket</a>.</p>
<p><strong>Audible reminders</strong></p>
<p>I found that I wasn’t always noticing my reminders in xmobar, so I decided to add sound effects.  I changed remindwhen.rb to look for a file named ~/.when/reminder.wav.  If it exists, that file is played (using mplayer) whenever a reminder is output.</p>
<p>Since my xmobar checks reminders every minute, and I sometimes want advance warning on a reminder, I found that the beeping once a minute can get pretty aggravating.  So I wanted a way to mute the alarms temporarily.  Thus, I created a shell script to turn the alarm on or off (by renaming reminder.wav) and tied it to a key shortcut in xmonad.  I then added a visual indicator to xmobar when alarms are muted.  How that works is all described in the README.</p>
<p>I also found that for some reminders I don’t want an audible alarm ever.  So I added a ‘quiet’ option within the text of the reminder, and I changed remindwhen.rb to strip that out (as well as any advance) before outputting the event.</p>
<p><strong>Calendar format</strong></p>
<p>I was so proud of my enhancements to when that I showed my wife my new scheduling tools.  “Yuck,” she said, “how can I see it as a calendar?”</p>
<p>Hmm, that’s true.  When looking for open days, you need to see the whole grid with weekdays – not just the already scheduled events.</p>
<p>So I wrote a couple of more scripts to pipe when’s output to <a href="http://pcal.sourceforge.net/">pcal</a> to create an HTML calendar that includes the event text on the right days.  Then I created another script that, with one command, invokes when piping the result through the first script and into <a href="http://w3m.sourceforge.net/">w3m</a> for viewing on a terminal.</p>
<p><strong>By-product</strong></p>
<p>As part of the implementation of the alarm support, I wrote a very handy little script to do an in-line, ternary conditional echo.  It’s called, oddly enough, “echoif”:</p>
<p><code>echoif “likeit” “applause” “`criticism`”</code></p>
<p>For details on how to use these new scripts, see the README.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbremindwhen.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/b5ZxFACgg_SHvUVLH-WHfHZnNWw/0/da"><img src="http://feedads.g.doubleclick.net/~a/b5ZxFACgg_SHvUVLH-WHfHZnNWw/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/b5ZxFACgg_SHvUVLH-WHfHZnNWw/1/da"><img src="http://feedads.g.doubleclick.net/~a/b5ZxFACgg_SHvUVLH-WHfHZnNWw/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=YN57YTW-2J8:nIgYf19GgVc:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=YN57YTW-2J8:nIgYf19GgVc:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=YN57YTW-2J8:nIgYf19GgVc:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=YN57YTW-2J8:nIgYf19GgVc:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=YN57YTW-2J8:nIgYf19GgVc:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=YN57YTW-2J8:nIgYf19GgVc:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=YN57YTW-2J8:nIgYf19GgVc:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=YN57YTW-2J8:nIgYf19GgVc:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=YN57YTW-2J8:nIgYf19GgVc:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=YN57YTW-2J8:nIgYf19GgVc:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=YN57YTW-2J8:nIgYf19GgVc:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/YN57YTW-2J8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=507</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=507</feedburner:origLink></item>
		<item>
		<title>Synthesis 2.1.4:  split the difference</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/Ukr-tLca0zU/</link>
		<comments>http://www.chipstips.com/?p=505#comments</comments>
		<pubDate>Wed, 26 May 2010 17:12:32 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Regexen]]></category>
		<category><![CDATA[SynergyDE]]></category>
		<category><![CDATA[regexen]]></category>
		<category><![CDATA[richardbarndt]]></category>
		<category><![CDATA[split]]></category>
		<category><![CDATA[synthesis]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=505</guid>
		<description><![CDATA[Richard Barndt sent me a first pass implementation of a split method for Regex – that is, the ability to split a string at the location of an expression.  He was also nice enough to include several test cases using assertions.  I’ve adapted his syntax to my own preferences and included Regex.split in version 2.1.4 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.spoke.com/info/p6PYOlj/RichardBarndt">Richard Barndt</a> sent me a first pass implementation of a split method for Regex – that is, the ability to split a string at the location of an expression.  He was also nice enough to include several test cases using assertions.  I’ve adapted his syntax to my own preferences and included <a href="http://chipstips.com/doc/synthesis/regex.html#ref-split">Regex.split</a> in version 2.1.4 of Synthesis, which you can download below.</p>
<p>Whereas Richard’s version returns an ArrayList of strings, I elected to return an <a href="http://chipstips.com/doc/synthesis/ls.html">ls</a> of <a href="http://chipstips.com/doc/synthesis/var.html">Vars</a>.  An ls can be used as an Arraylist, but it adds many useful methods.  Var is the normal way to store strings in an ls, for various reasons.  I also elected not to include the static Regex.split method – it seems needlessly repetitive, since you have to supply an expression anyway.  I did, however, add a split method to <a href="http://chipstips.com/doc/synthesis/var.html#ref-split">Var</a> for convenience.</p>
<p>This implementation of split uses the <a href="http://chipstips.com/doc/synthesis/regex.html#ref-globalsearch">GlobalSearch</a> option (g) to determine whether to split at every occurrence of the expression or only the first one.  Captured groups within the expression are returned in the array as well at their respective positions – otherwise the match to the expression is not returned.  There’s also an option to include empty strings that result from the split, which is true by default.</p>
<p>Thanks, Richard, for the idea, the code, and the test cases!</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbpmask.zip"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/eAdTUQxgWOcFjh2t2_Ikg5oA94Q/0/da"><img src="http://feedads.g.doubleclick.net/~a/eAdTUQxgWOcFjh2t2_Ikg5oA94Q/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/eAdTUQxgWOcFjh2t2_Ikg5oA94Q/1/da"><img src="http://feedads.g.doubleclick.net/~a/eAdTUQxgWOcFjh2t2_Ikg5oA94Q/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Ukr-tLca0zU:pFTcYU6dUUU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Ukr-tLca0zU:pFTcYU6dUUU:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Ukr-tLca0zU:pFTcYU6dUUU:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Ukr-tLca0zU:pFTcYU6dUUU:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Ukr-tLca0zU:pFTcYU6dUUU:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Ukr-tLca0zU:pFTcYU6dUUU:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Ukr-tLca0zU:pFTcYU6dUUU:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Ukr-tLca0zU:pFTcYU6dUUU:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Ukr-tLca0zU:pFTcYU6dUUU:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=Ukr-tLca0zU:pFTcYU6dUUU:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=Ukr-tLca0zU:pFTcYU6dUUU:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/Ukr-tLca0zU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=505</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=505</feedburner:origLink></item>
		<item>
		<title>When you forget to ask when, use remindwhen:  reminders for the when calendar</title>
		<link>http://feedproxy.google.com/~r/ChipsTips/~3/LgmhF08ifkw/</link>
		<comments>http://www.chipstips.com/?p=501#comments</comments>
		<pubDate>Thu, 06 May 2010 17:41:24 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[calendar]]></category>
		<category><![CDATA[freebsd]]></category>
		<category><![CDATA[rbremindwhen]]></category>
		<category><![CDATA[reminders]]></category>
		<category><![CDATA[when]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=501</guid>
		<description><![CDATA[In my quest to find more lightweight, componentized, customizable tools for my work, I decided to use when for my calendar.  When uses a simple text file for calendar events and sports a command-line interface, yet its handling of dates and recurrence is quite sophisticated. When doesn’t have any mechanism for reminders, which is something [...]]]></description>
			<content:encoded><![CDATA[<p>In my quest to find more <a href="http://www.chipstips.com/?p=495">lightweight</a>, <a href="http://www.geeksaresexy.net/2010/04/28/how-to-copy-outlook-contacts-to-abook/">componentized</a>, <a href="http://www.chipstips.com/?p=488">customizable</a> <a href="http://www.chipsquips.com/?p=2335">tools</a> for my work, I decided to use <a href="http://www.lightandmatter.com/when/when.html">when</a> for my calendar.  When uses a simple text file for calendar events and sports a command-line interface, yet its handling of dates and recurrence is quite sophisticated.</p>
<p>When doesn’t have any mechanism for reminders, which is something my quinquagenarian brain finds necessary.  But that turns out to be good Unix design, because you can easily layer your own reminder mechanism over when, as I have in the scripts you can download as a tarball below, or pull the files from the <a href="http://bitbucket.org/sterlingcamden/remindwhen">BitBucket repository</a>.</p>
<p>When’s event format is simple:  each line begins with a specification of the date(s) on which the event occurs, followed by a comma, followed by a free-text description.  There is no requirement or convention for specifying a time for the event.   It therefore just ends up as part of the event’s text.</p>
<p>Some users invoke when as part of their shell profile, so they’re reminded of upcoming events whenever they log on.  But I stay logged on all day, so I need more frequent opportunities for reminders.  Thus, I created remindwhen.rb, which I run once a minute from <a href="http://code.haskell.org/~arossato/xmobar/">xmobar</a> so I get a bright yellow reminder at the top of my screen as events become due:</p>
<p><img style="display: inline; border-width: 0px;" title="image" src="http://chipstips.com/wp-content/images/Whenyouforgettoaskwhenuseremindwhenremin_9834/image.png" border="0" alt="image" width="517" height="66" /></p>
<p>In keeping with the Unix philosophy, though, you can send the output of the script to whatever notification mechanism you prefer.  The script merely parses the when output looking for events that are due within a range of the current time and sends them to stdout.  For an event to be considered, it has to contain a time in the format <em>HH</em>:<em>MM</em>[pm][+<em>n</em>], where <em>HH </em>is the hour, <em>MM</em> is the minute, “pm” is optional, and <em>n </em>optionally specifies an advance warning, in minutes.</p>
<p>To avoid cluttering the regular calendar with daily reminders, these scripts assume that you have at least two calendar files: the default (~/.when/calendar), and one just for reminders (~/.when/reminders).  The remindwhen.rb script searches the latter first, then the former.  So if you just want a reminder, add it to reminders.  If you also want to see it on your daily calendar, add it to calendar instead.</p>
<p>I’ve also created some supporting scripts to make reminders even easier to manage:</p>
<p><code>remind <em>time description</em></code></p>
<p>adds a reminder to the reminders calendar for the specified <em>time</em> on today’s date.</p>
<p><code>reminders</code></p>
<p>shows today’s reminders form the reminders calendar</p>
<p><code>reminders e</code></p>
<p>edits the reminders calendar.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbremindwhen.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>

<p><a href="http://feedads.g.doubleclick.net/~a/aavKyyNdblkC62WC_wb0Or6Ng4o/0/da"><img src="http://feedads.g.doubleclick.net/~a/aavKyyNdblkC62WC_wb0Or6Ng4o/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/aavKyyNdblkC62WC_wb0Or6Ng4o/1/da"><img src="http://feedads.g.doubleclick.net/~a/aavKyyNdblkC62WC_wb0Or6Ng4o/1/di" border="0" ismap="true"></img></a></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LgmhF08ifkw:7arJM_jvwAM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LgmhF08ifkw:7arJM_jvwAM:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LgmhF08ifkw:7arJM_jvwAM:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LgmhF08ifkw:7arJM_jvwAM:k4C0Fa6AxEo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=k4C0Fa6AxEo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LgmhF08ifkw:7arJM_jvwAM:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LgmhF08ifkw:7arJM_jvwAM:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LgmhF08ifkw:7arJM_jvwAM:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LgmhF08ifkw:7arJM_jvwAM:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LgmhF08ifkw:7arJM_jvwAM:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ChipsTips?i=LgmhF08ifkw:7arJM_jvwAM:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ChipsTips?a=LgmhF08ifkw:7arJM_jvwAM:AqSrPXiwvXI"><img src="http://feeds.feedburner.com/~ff/ChipsTips?d=AqSrPXiwvXI" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ChipsTips/~4/LgmhF08ifkw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=501</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=501</feedburner:origLink></item>
	</channel>
</rss>
