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

<channel>
	<title>Brain Lint</title>
	<atom:link href="http://www.monkeyatlarge.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.monkeyatlarge.com</link>
	<description>Random musings on life, technology and other miscellany.</description>
	<lastBuildDate>Tue, 19 Jan 2010 15:27:25 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Multiple Phrase Search in PostgreSQL</title>
		<link>http://www.monkeyatlarge.com/archives/2010/01/17/multiple-phrase-search-in-postgresql/</link>
		<comments>http://www.monkeyatlarge.com/archives/2010/01/17/multiple-phrase-search-in-postgresql/#comments</comments>
		<pubDate>Mon, 18 Jan 2010 01:34:27 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=341</guid>
		<description><![CDATA[Tsearch, the full text search engine in PostgreSql, is great at rapidly searching for keywords (and combinations of keywords) in large bodies of text. It does not, however, excel at matching multi-word phrases. There are some techniques to work around that to let your application leverage tsearch to find phrases. 
Before I go on, I&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p>Tsearch, the full text search engine in PostgreSql, is great at rapidly searching for keywords (and combinations of keywords) in large bodies of text. It does not, however, excel at matching multi-word phrases. There are some techniques to work around that to let your application leverage tsearch to find phrases. </p>
<p>Before I go on, I&#8217;ll credit Paul Sephton&#8217;s <a href="http://linuxgazette.net/164/sephton.html">Understanding Full Text Search</a> for opening my eyes to some of the possibilities to enable phrase search on top of tsearch&#8217;s existing capabilities.</p>
<p>Tsearch operates on tsvectors and tsqueries. Tsvectors are a bag of words like structure &#8211; a list of the unique words appearing in a piece of text, along with their positions in the text. Searches are performed constructing a tsquery, which is boolean expression combining words with AND(&#038;), OR(|), and NOT(!) operators, then comparing the tsquery against candidate tsvectors with the @@ operator.</p>
<pre class="brush: sql; light: true;">
select * from articles where to_tsvector('english',articles.body) @@ 'meatball &amp; sub';
</pre>
<p>will match articles where the the body contains the word meatball and the word sub. If there&#8217;s an index on to_tsvector(&#8216;english&#8217;,articles.body), this query is a very efficient index lookup.</p>
<h3>
Single Phrase Search</h3>
<p>Now how do we match articles with the phrase &#8220;meatball sub&#8221;, anywhere in the article&#8217;s body? Doing the naive query</p>
<pre class="brush: sql; light: true;">
select * from articles where body like '%meatball sub%'
</pre>
<p>will work, but it will be slow because the leading wildcard kills any chance of using an index on that column. What we can do to make this go fast is the following:</p>
<pre class="brush: sql; light: true;">
select * from articles where to_tsvector('english',articles.body) @@ 'meatball &amp; sub' AND body like '%meatball sub%'
</pre>
<p>This will use the full text index to find the set of articles where the body has both words, then that (presumably) smaller set of articles can be scanned for the words together.</p>
<h3>Multi Phrase Search</h3>
<p>It&#8217;s simple to extend the above query to match two phrases:</p>
<pre class="brush: sql; light: true;">
select * from articles where to_tsvector('english',articles.body) @@ 'meatball &amp; sub &amp; ham &amp; sandwich' AND body like '%meatball sub%' AND body like '%ham sandwich%';
</pre>
<p>That query can be tightened up using postgres&#8217;s support for arrays:</p>
<pre class="brush: sql; light: true;">
select * from articles where to_tsvector('english',articles.body) @@ 'meatball &amp; sub &amp; ham &amp; sandwich' AND body like ALL('{&quot;%meatball sub%&quot;,&quot;%ham sandwich%&quot;}')
</pre>
<p>Stepping back a bit, let&#8217;s define create a table called &#8220;concepts&#8221; to allow users of an application to store searches on lists of phrases, and let&#8217;s also allow the user to specify that all phrases must match, or just one of them.</p>
<pre class="brush: sql; light: true;">
CREATE TABLE concepts
(
   id serial,
   match_all boolean,
   phrases character varying[],
   query tsquery
)
</pre>
<p>Now we can specify and execute that previous search this way:</p>
<pre class="brush: sql; light: true;">
insert into concepts(match_all,phrases,query) VALUES(TRUE,'{&quot;%meatball sub%&quot;,&quot;%ham sandwich%&quot;}','meatball &amp; sub &amp; ham &amp; sandwich');
select articles.*, join concepts on (concepts.query @@ to_tsvector(body)) AND ((match_all AND body like ALL(phrases)) OR (not match_all AND body like ANY(phrases)));
</pre>
<p>Where this approach really shines compared with an external text search tools is aggregate queries like counting up matching articles by date. </p>
<pre class="brush: sql; light: true;">
select count(distinct articles.id), articles.date from articles join concepts on (concepts.query @@ to_tsvector(body)) AND ((match_all AND body like ALL(phrases)) OR (not match_all AND body like ANY(phrases)))
group by articles.date
</pre>
<p>The logic to combine lists of phrases into the appropriate query based on the desire to match any or all of the phrases is easy to write at the application layer.  It&#8217;s desirable not to have to include the wildcards into the phrase array, and it&#8217;s easy to write a function to do that at runtime.</p>
<pre class="brush: sql; light: true;">
CREATE OR REPLACE FUNCTION wildcard_wrapper(list varchar[]) RETURNS varchar[] AS $$
      DECLARE
       return_val varchar[];
      BEGIN
        for idx in 1 .. array_upper(list, 1)
        loop
          return_val[idx] := '%' || list[idx] || '%';
        end loop;
        return return_val;
      END;
      $$ LANGUAGE plpgsql;
</pre>
<p>With that function good to go we can make that long query just a little longer:</p>
<pre class="brush: sql; light: true;">
select count(distinct articles.id), articles.date from articles join concepts on (concepts.query @@ to_tsvector(body)) AND ((match_all AND body like ALL(wildcard_wrapper(phrases))) OR (not match_all AND body like ANY(wildcard_wrapper(phrases))))
group by articles.date
</pre>
<p>It&#8217;s straightforward to collapse most, if not all of the sql on clause into a plpgsql function call without adversely affecting the query plan &#8211; it&#8217;s important that the tsvector index be involved in the query for adequate performance.</p>
<h3>Further Work</h3>
<p>This approach works well for lists of phrases. To support boolean logic on phrases, one approach might be to compile the request down to a tsquery as above, along with a regular expression to winnow down the matches to those containing the phrases.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2010/01/17/multiple-phrase-search-in-postgresql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Hierarchical Stacked Time Series</title>
		<link>http://www.monkeyatlarge.com/archives/2010/01/17/hierarchical-stacked-time-series/</link>
		<comments>http://www.monkeyatlarge.com/archives/2010/01/17/hierarchical-stacked-time-series/#comments</comments>
		<pubDate>Sun, 17 Jan 2010 23:56:07 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=338</guid>
		<description><![CDATA[I built a Flash (Flex) tool to visualize stacked time series &#8211; based on the Prefuse Flare Job Viewer application, but extended to afford exploration of a shallow, two level hierarchy of time series.

More information and links to the source code on the project page 
]]></description>
			<content:encoded><![CDATA[<p>I built a Flash (Flex) tool to visualize stacked time series &#8211; based on the Prefuse Flare Job Viewer application, but extended to afford exploration of a shallow, two level hierarchy of time series.</p>
<p><a href="http://www.monkeyatlarge.com/projects/drillable-stacked-time-series/"><img src="http://www.monkeyatlarge.com/blog/wp-content/uploads/2010/01/stackedtimeseries-300x194.png" alt="" title="stackedtimeseries" width="300" height="194" class="aligncenter size-medium wp-image-339" /></a></p>
<p>More information and links to the source code on the <a href="http://www.monkeyatlarge.com/projects/drillable-stacked-time-series/">project page</a> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2010/01/17/hierarchical-stacked-time-series/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Red line transit times on a google map</title>
		<link>http://www.monkeyatlarge.com/archives/2010/01/11/red-line-transit-times-on-a-google-map/</link>
		<comments>http://www.monkeyatlarge.com/archives/2010/01/11/red-line-transit-times-on-a-google-map/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 02:11:16 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=332</guid>
		<description><![CDATA[I visualized the transit time via the MBTA&#8217;s Red Line to park street from points within 1.5 miles of Red line stops on a google map.



More information, and the interactive google map on the project page.
]]></description>
			<content:encoded><![CDATA[<p>I <a href="http://www.monkeyatlarge.com/projects/redline-travel-time/">visualized</a> the transit time via the MBTA&#8217;s Red Line to park street from points within 1.5 miles of Red line stops on a google map.<br />
<br />
<a href="http://www.monkeyatlarge.com/projects/redline-travel-time/"><img src="http://www.monkeyatlarge.com/blog/wp-content/uploads/2010/01/redline-costs-screenshot-212x300.png" alt="" title="redline-costs-screenshot" width="212" height="300" class="alignnone size-medium wp-image-333" /></a><br />
<br />
More information, and the interactive google map on the<a href="http://www.monkeyatlarge.com/projects/redline-travel-time/"> project page</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2010/01/11/red-line-transit-times-on-a-google-map/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Another MBTA visualization, this time with the commuter rail</title>
		<link>http://www.monkeyatlarge.com/archives/2009/09/02/another-mbta-visualization-this-time-with-the-commuter-rail/</link>
		<comments>http://www.monkeyatlarge.com/archives/2009/09/02/another-mbta-visualization-this-time-with-the-commuter-rail/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 11:03:24 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=328</guid>
		<description><![CDATA[
Following up the surprising success of my first MBTA visualization, I made a new version that adds the commuter rail lines. This does have the unfortunate effect of squishing the system&#8217;s rapid transit lines because I&#8217;m not distorting the distances in any way. I also reduced the size of the markers, perhaps too small for [...]]]></description>
			<content:encoded><![CDATA[<p><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/-6Ye1AvMGtY&#038;hl=en&#038;fs=1&#038;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/-6Ye1AvMGtY&#038;hl=en&#038;fs=1&#038;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object></p>
<p>Following up the <a href="http://boston.com/news/local/breaking_news/2009/08/an_ant_farm_no.html">surprising success</a> of my first <a href="http://www.monkeyatlarge.com/archives/2009/08/30/day-in-the-life-of-the-mbta-system/">MBTA visualization</a>, I made a new version that adds the commuter rail lines. This does have the unfortunate effect of squishing the system&#8217;s rapid transit lines because I&#8217;m not distorting the distances in any way. I also reduced the size of the markers, perhaps too small for viewing at youtube resolution unless viewed at full screen.</p>
<p>Also, for Frank and Elias, I made the length of the video shorter, enlarged the numbers, and added a little visualization showing active trips sampled at 5-minute intervals with a you are here indicator to give viewers perspective on how busy the system is at that moment relative to the whole day.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2009/09/02/another-mbta-visualization-this-time-with-the-commuter-rail/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Day in the life of the MBTA system</title>
		<link>http://www.monkeyatlarge.com/archives/2009/08/30/day-in-the-life-of-the-mbta-system/</link>
		<comments>http://www.monkeyatlarge.com/archives/2009/08/30/day-in-the-life-of-the-mbta-system/#comments</comments>
		<pubDate>Mon, 31 Aug 2009 01:54:01 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=323</guid>
		<description><![CDATA[
I put together an animation of all the rail traffic in the course of a day on the MBTA&#8217;s red, blue, green and orange lines, including the Mattapan line. Its a great way to see just how complicated the system is that takes me to work every day, and perhaps be a little more patient [...]]]></description>
			<content:encoded><![CDATA[<p><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/0tuzjxEBto4&#038;hl=en&#038;fs=1&#038;rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/0tuzjxEBto4&#038;hl=en&#038;fs=1&#038;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object></p>
<p>I put together an animation of all the rail traffic in the course of a day on the MBTA&#8217;s red, blue, green and orange lines, including the Mattapan line. Its a great way to see just how complicated the system is that takes me to work every day, and perhaps be a little more patient next time things go less than perfect!</p>
<p>The current version of the animation assumes stop take no time (as does the scheduling data).</p>
<p>I&#8217;d thought about doing this before, but it would have taken screen scraping schedule information off the site. I learned recently through a developer outreach that the Massachusetts Department of Transportation is running that the MBTA had released their <a href="http://www.eot.state.ma.us/developers/">schedule information</a> in the Google transit feed specification (GTFS). With the data in hand, I went to work using the <a href="http://wiki.github.com/jashkenas/ruby-processing">ruby-processing</a> wrapper of the excellent <a href="http://processing.org">Processing graphics toolkit</a>.</p>
<p><a href="http://www.youtube.com/watch?v=0tuzjxEBto4"><br />
See the video on youtube</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2009/08/30/day-in-the-life-of-the-mbta-system/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Deferring Index costs for table to table copies in PostgreSQL</title>
		<link>http://www.monkeyatlarge.com/archives/2009/04/14/deferring-index-costs-for-table-to-table-copies-in-postgresql/</link>
		<comments>http://www.monkeyatlarge.com/archives/2009/04/14/deferring-index-costs-for-table-to-table-copies-in-postgresql/#comments</comments>
		<pubDate>Tue, 14 Apr 2009 14:27:55 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=318</guid>
		<description><![CDATA[When bulk copying data to a table, it is much faster if the destination table is index and constraint free, because it is cheaper to build an index once than maintain it over many inserts. For postgres, the pg_restore and SQL COPY commands can do this, but they both require that data be copied from [...]]]></description>
			<content:encoded><![CDATA[<p>When bulk copying data to a table, it is much faster if the destination table is index and constraint free, because it is cheaper to build an index once than maintain it over many inserts. For postgres, the pg_restore and SQL COPY commands can do this, but they both require that data be copied from the filesystem rather than directly from another table.</p>
<p>For table to table copying (and transformations) the situation isn&#8217;t as straight-forward. Recently I was working on a problem where we needed to perform some poor-man&#8217;s <a href="http://en.wikipedia.org/wiki/Extract,_transform,_load">ETL</a>, copying and transforming data between tables in different schemas. Since some of the destination tables were heavily indexed(including a full text index) the task took quite a while. In talking with a colleague about the problem, we came up with the idea of dropping the indexes and constraints prior to the data load, and restoring them afterwards. </p>
<p>First stop: how to get the DDL for indices on a table in postgres? Poking around the postgres catalogs, I managed to find a function pg_get_indexdef that would return the DDL for an index. Combining that with a query I found in a forum somewhere and altered, I came up with this query to get the names and DDL of all the indices on a table. (this one excludes the primary key index)</p>
<p><script src="http://gist.github.com/94854.js"></script></p>
<p>With that and the query to do the same for constraints its straightforward to build a helper function that will get the DDL for all indices and constraints, drop them, yield to evaluate a block and then restore the indices and constraints. The method is below: </p>
<p><script src="http://gist.github.com/95196.js"></script></p>
<p>Use of the function would look like the snippet below. This solution would also allow for arbitrarily complex transformations in Ruby as well as pure SQL.</p>
<p><script src="http://gist.github.com/94867.js"></script></p>
<p>For my task loading and transforming data into about 20 tables, doing this reduced the execution time by two-thirds. Of course, your mileage may vary depending how heavily indexed your destination tables are.</p>
<p>Here&#8217;s the whole module:</p>
<p><script src="http://gist.github.com/94853.js"></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2009/04/14/deferring-index-costs-for-table-to-table-copies-in-postgresql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My experience setting up a MOCA network at home</title>
		<link>http://www.monkeyatlarge.com/archives/2008/12/03/my-experience-setting-up-a-moca-network-at-home/</link>
		<comments>http://www.monkeyatlarge.com/archives/2008/12/03/my-experience-setting-up-a-moca-network-at-home/#comments</comments>
		<pubDate>Thu, 04 Dec 2008 03:00:31 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=309</guid>
		<description><![CDATA[I bought a couple of coax-ethernet bridges in the hopes of speeding media transfers to and from my Tivo HD. The devices work great, but it turns out my Tivo itself is the bottleneck &#8211; it just doesn&#8217;t serve media very fast even over ethernet. I recommend a &#8220;Moca&#8221;:http://www.mocalliance.org based ethernet over coax network if [...]]]></description>
			<content:encoded><![CDATA[<p>I bought a couple of coax-ethernet bridges in the hopes of speeding media transfers to and from my Tivo HD. The devices work great, but it turns out my Tivo itself is the bottleneck &#8211; it just doesn&#8217;t serve media very fast even over ethernet. I recommend a &#8220;Moca&#8221;:http://www.mocalliance.org based ethernet over coax network if you&#8217;re in need of more speed than wireless will give you, but don&#8217;t expect miracles on the Tivo front.</p>
<h3>Why go back to wires?</h3>
<p>Sure wireless is nice and easy and fast enough for many applications, but you can&#8217;t beat the bandwidth of a wire for guaranteed bandwidth. I live in a densely populated area in which I can see about 40 wireless networks, and about a third of those overlap my wireless band to one degree or another. I get just a fraction of the theoretical 54mbps of a g-based wifi network. Compare that to 100 mbps point to point for coax (actually around 240mbps total band width if you&#8217;ve got a mesh network set up). </p>
<h3>Taking the plunge</h3>
<p>First you&#8217;ve got to get yourself a couple of coax bridges. The problem here is that no one sells them at retail right now. Fortunately Verizon&#8217;s FIOS service made heavy use of the Motorola NIM-100 bridge but is now phasing them out, so you can get them cheap on ebay. I got a pair for $75, shipped.</p>
<p>Each bridge has an ethernet port, and two coax ports, one labeled &#8220;in&#8221;, the other labeled &#8220;out&#8221;. If you have cable internet you&#8217;ll likely put one of these next to your cable modem. In that case, connect a wire from the wall to the coax in port, and another from the out port to the cable modem. An ethernet wire to your router, and now you&#8217;ve got an ethernet network running over your coaxial cable wires. Plug another one in somewhere else in your house, wall to the in port, and ethernet to some device and you&#8217;re in business. I got north of 80mbps between two laptops over the coax bridge.</p>
<p>This should work out of the box if your bridges came reset to their factory configuration. Unfortunately that means you can&#8217;t administer them and they&#8217;re using a default encryption key (traffic over the coax is encrypted because it probably leaks a bit out of your house)</p>
<h3>Taking control</h3>
<p>I&#8217;d recommend spending a bit of time to make your new bridges configurable- they have web interfaces, its just a matter of getting to them that&#8217;s tricky. I pieced together this information from several sources on the web.<br />
The first problem is getting into the web interface. The default settings are for the bridge to auto assign itself an IP address in the range 169.254.1.x , and it won&#8217;t accept admin connections from devices that aren&#8217;t on the same ip range so here&#8217;s what you do:</p>
<ol>
<li>Take a computer and set your ethernet interface to have a static IP address of 169.254.1.100</li>
<li>Connect the computer directly to the bridge over ethernet</li>
<li>Goto http://169.254.1.1 . If that doesn&#8217;t work, increment the last digit until it does</li>
<li>When you see the web interface, the default password is &#8220;entropic&#8221; &#8211; they&#8217;re apparently the only people who make the chips for these devices</li>
</ol>
<p>Once you&#8217;re in the configuration works much like any other network device. You should definitely set a new password under &#8220;coax security&#8221; &#8211; you&#8217;ll have to repeat this for all your devices. Also, I&#8217;d recommend setting the device to use DHCP or a fixed IP in your usual IP range if you&#8217;d like to change anything in the future.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2008/12/03/my-experience-setting-up-a-moca-network-at-home/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>We the savers</title>
		<link>http://www.monkeyatlarge.com/archives/2008/10/07/we-the-savers/</link>
		<comments>http://www.monkeyatlarge.com/archives/2008/10/07/we-the-savers/#comments</comments>
		<pubDate>Wed, 08 Oct 2008 02:27:36 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=307</guid>
		<description><![CDATA[ING Direct put up a short manifesto titled &#8220;We, The Savers&#8220;. Its a good read, and we could all do better by it.
Number 3 struck me especially:
We will take care of our money. It’s not enough to have money in a bank. We will put it where it will grow. We’ll keep track of it. [...]]]></description>
			<content:encoded><![CDATA[<p>ING Direct put up a short manifesto titled &#8220;<a href="http://www.ingdirect.com/wethesavers/">We, The Savers</a>&#8220;. Its a good read, and we could all do better by it.</p>
<p>Number 3 struck me especially:</p>
<blockquote><p><strong>We will take care of our money</strong>. It’s not enough to have money in a bank. We will put it where it will grow. We’ll keep track of it. And we’ll check every account we have every year to protect ourselves against fraud or escheatment.</p></blockquote>
<p>&#8220;We will put it where it will grow&#8221; &#8211; well where will it grow. It seems the first tool brought to bear on any stock market bump is to lower interest rates, which in effect punishes those of us who do actually have money in a savings account. We lament the low savings rate in America, but then we go make it more appealing to borrow and less appealing to save by dropping rates again and again.</p>
<p>Another item is this &#8211; not everyone has the internet access or savvy to move their money to a place like ING Direct. Those people have their money stuck in a savings account that probably pays well under one-percent interest. I think its high time this country had a better program to get more people online so people can get away from their no-interest paying banks.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2008/10/07/we-the-savers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PNG Thumbnails for PDF files. Take two</title>
		<link>http://www.monkeyatlarge.com/archives/2008/10/07/png-thumbnails-for-pdf-files-take-two/</link>
		<comments>http://www.monkeyatlarge.com/archives/2008/10/07/png-thumbnails-for-pdf-files-take-two/#comments</comments>
		<pubDate>Wed, 08 Oct 2008 02:16:20 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=305</guid>
		<description><![CDATA[Updating my previous post, I finished up the work of extending attachment_fu to optionally create PNG thumbnails of updated PDF files. Check out the fork on github
]]></description>
			<content:encoded><![CDATA[<p>Updating my <a href="http://www.monkeyatlarge.com/archives/2008/09/16/creating-thumbnails-of-pdfs-with-attachment_fu/">previous post</a>, I finished up the work of extending attachment_fu to optionally create PNG thumbnails of updated PDF files. Check out the <a href="http://github.com/jkebinger/attachment_fu/tree/master">fork on github</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2008/10/07/png-thumbnails-for-pdf-files-take-two/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating thumbnails of PDFs with attachment_fu</title>
		<link>http://www.monkeyatlarge.com/archives/2008/09/16/creating-thumbnails-of-pdfs-with-attachment_fu/</link>
		<comments>http://www.monkeyatlarge.com/archives/2008/09/16/creating-thumbnails-of-pdfs-with-attachment_fu/#comments</comments>
		<pubDate>Tue, 16 Sep 2008 20:56:33 +0000</pubDate>
		<dc:creator>James Kebinger</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[attachment_fu]]></category>
		<category><![CDATA[pdf]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[thumbnails]]></category>

		<guid isPermaLink="false">http://www.monkeyatlarge.com/?p=301</guid>
		<description><![CDATA[
We needed to create some thumbnails from uploading PDF files for a new site feature &#8211; We&#8217;re using attachment_fu which doesn&#8217;t support that (yet?), but we&#8217;re using RMagick as our processor and it understands PDF files.

 I came up with the hack below (warning, first draft, only briefly tested) which works without having to modify [...]]]></description>
			<content:encoded><![CDATA[<p>
We needed to create some thumbnails from uploading PDF files for a new site feature &#8211; We&#8217;re using <a href="http://github.com/technoweenie/attachment_fu/tree/master">attachment_fu </a>which doesn&#8217;t support that (yet?), but we&#8217;re using <a href="http://rmagick.rubyforge.org/">RMagick</a> as our processor and it understands PDF files.
</p>
<p> I came up with the hack below (warning, first draft, only briefly tested) which works without having to modify the attachment_fu plugin itself. One day I&#8217;ll loop back and figure out a cleaner way to do this and see which of attachment_fu&#8217;s other image processors can even support pdfs.
</p>
<p>
There are three methods to override to make a go of this:</p>
<ol>
<li>self.image? :  consider pdf files as an image so thumbnail process will happen</li>
<li>thumbnail_name_for : change the extension of the saved thumbnail filename to png</li>
<li>resize_image: override to change format via block passed to to_blob</li>
</ol>
<p>Apologies for the crappy source formatting, I have to install a plugin to do that well one of these days</p>
<p><code><br />
###Hacks to allow creation of png thumbnails for pdf uploads - depends on RMagic being the configured processor<br />
## likely very fragile</p>
<p>def self.image?(content_type)<br />
(content_types +  ['application/pdf']).include?(content_type)<br />
end</p>
<p>alias_method <img src='http://www.monkeyatlarge.com/blog/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> riginal_thumbnail_name_for, :thumbnail_name_for<br />
def thumbnail_name_for(thumbnail=nil)<br />
return original_thumbnail_name_for(thumbnail) unless (content_type == 'application/pdf' &amp;&amp; !thumbnail.blank?)<br />
basename = filename.gsub /\.\w+$/ do |s|<br />
ext = s; ''<br />
end<br />
"#{basename}_#{thumbnail}.png"<br />
end<br />
#copied from rmagick_processor with change in last few lines<br />
def resize_image(img, size)<br />
size = size.first if size.is_a?(Array) &amp;&amp; size.length == 1 &amp;&amp; !size.first.is_a?(Fixnum)<br />
if size.is_a?(Fixnum) || (size.is_a?(Array) &amp;&amp; size.first.is_a?(Fixnum))<br />
size = [size, size] if size.is_a?(Fixnum)<br />
img.thumbnail!(*size)<br />
else<br />
img.change_geometry(size.to_s) { |cols, rows, image| image.resize!(cols&lt;1 ? 1 : cols, rows&lt;1 ? 1 : rows) }<br />
end<br />
img.strip! unless attachment_options[:keep_profile]<br />
if content_type == 'application/pdf' # here force the output format to PNG if its a pdf<br />
self.temp_path = write_to_temp_file(img.to_blob {self.format = 'PNG'})<br />
else<br />
self.temp_path = write_to_temp_file(img.to_blob)<br />
end<br />
end</p>
<p></code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.monkeyatlarge.com/archives/2008/09/16/creating-thumbnails-of-pdfs-with-attachment_fu/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
