<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Mad Marmot</title>
    <link>http://www.lukeludwig.com/blog</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>A blog about programming, ruby, rails, and my crazy outdoor pursuits</description>
    <item>
      <title>Switching from mongrel to mod_rails.</title>
      <description>&lt;p&gt;A &lt;a href="http://www.modrails.com/"&gt;mod_rails for Apache, also called Phusion Passenger&lt;/a&gt;, has been released.&amp;nbsp; I switched this blogs deployment from using mongrel to using mod_rails in less than 20 minutes without any problems. The installation procedure is dead simple. I had to look up one thing in the user&amp;#39;s guide, how to serve the rails app off a sub path of the domain (lukeludwig.com/blog), and I found it quickly. Overall I am extremely impressed and am curious how Passenger will do with higher traffic production sites.&lt;/p&gt;&lt;p&gt;Previously I was running this blog on a single mongrel instance, which is all that is needed due to the low traffic on this site. But lets just say that I wrote an article with great content and it got digged, causing traffic to spike like crazy for a few days. The single mongrel would quickly be overwhelmed and my site would be for the most part, unusuable, until I noticed and was able to start up a whole mongrel cluster of say a dozen mongrels. My server has 1 GB of RAM, most of which goes unused. Passenger is able to handle the number of rails instances in a much nicer manner. From a configuration perspective, I set the RailsMaxPoolSize to 12 and I&amp;#39;m done. Passenger will run as many rails instances as it needs depending on load, up to the max of 12. When traffic slows down, it kills the idle rails instances to conserve memory. Great stuff. &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;

</description>
      <pubDate>Sun, 13 Apr 2008 09:40:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:5d959884-da4c-4bf2-ba13-58e7bd0b12d3</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2008/04/13/switching-from-mongrel-to-mod_rails</link>
      <category>Programming</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/9</trackback:ping>
    </item>
    <item>
      <title>Use pagination to save memory when iterating over a lot of ActiveRecord objects.</title>
      <description>It is very easy to consume a ton of memory when using ActiveRecord&amp;#39;s find(:all) method. When transitioning the &lt;a href="http://www.teamsporttech.com"&gt;Team Sport Tech&lt;/a&gt; rails app from using file column to attachment_fu I wrote a migration to convert all of the photos we had to the new database format and the new location on disk. Without thinking I wrote code like this:&lt;br /&gt;&lt;br /&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Photo.find(:all).each do |photo|&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # conversion commands&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&amp;nbsp;&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&lt;br /&gt;I didn&amp;#39;t notice any issues executing this on my laptop which has 2 GB of RAM, but when I went to run this migration on our staging server over at &lt;a href="http://engineyard.com/"&gt;Engine Yard&lt;/a&gt; I had problems. Our staging server only has 640 MB of RAM. Our database has over 100,000 photos. That is an array with 100,000 ActiveRecord objects in memory all at once. As the migration executed on the staging server I could see that the rake task was using all available RAM and it was paging like crazy, utilizing 600 MB of virtual memory from disk. The cpu fluctuated between 0 and 1 percent of utilization due to the paging. If adequate memory was available this migration would take around 10 minutes and the cpu would be working like crazy. I did a few other things for awhile and came back to this migration 2 hours later. Still working and it probably had a long ways yet to go. 

&lt;p&gt;While the memory limitation on the staging server isn&amp;#39;t that important, the real question was how the production server with 1024 MB of RAM would handle the migration since the live app has to be down for maintenance when we roll this out. Instead of wondering I decided to fix the real problem, which was the stupid code I wrote to load all of the photos in memory for iteration. I quickly realized that the problem was already solved for me in the form of pagination. Pagination is usually thought of as a way to display N number of items to the user on a web page and providing links to move to the &amp;quot;Next&amp;quot; or &amp;quot;Previous&amp;quot; pages. When doing this the pagination code smartly loads only the ActiveRecord objects that are needed for the current page in memory, which is exactly the behavior I needed. &lt;br /&gt; &lt;br /&gt; We recently switched from using the classic rails pagination to the newer &lt;a href="http://errtheblog.com/posts/47-i-will-paginate"&gt;will_paginate&lt;/a&gt; plugin. Since I knew I would need to use will_paginate in this manner in other migrations, and possibly in actual application code, I wrote this iterator for reuse: &lt;br /&gt; &lt;br /&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def apply_to_all(klass, per_page)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; objects = klass.paginate :page =&amp;gt; 1, :per_page =&amp;gt; per_page&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; num_pages = objects.page_count&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; total_entries = objects.total_entries&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for page in 1 .. num_pages&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; objects = klass.paginate(:page =&amp;gt; page, :per_page =&amp;gt; per_page, :total_entries =&amp;gt; total_entries)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; objects.each do |object|&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; yield object&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; So I changed my migration code to look like this:&lt;br /&gt; &lt;br /&gt;&lt;font color="#ff0000"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; apply_to_all(Photo, 500) do |photo|&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # conversion commands&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; end &lt;/font&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/p&gt;&lt;p&gt;A key thing to note is the use of total_entries. Each time the paginate method is called, 2 sql queries are performed.&amp;nbsp; One to get the records, and one to count how many records there are in total. This counting query can be very expensive when there are a lot of records, and if you provide the total_entries option to the paginate method the counting query will be skipped. While I chose to reuse the will_paginate plugin here, it wouldn&amp;#39;t be very difficult to rewrite the above code without it by using LIMIT with an offset, which is how the will_paginate plugin works. Additionally it may be useful to add a third parameter to the apply_to_all method called options, which is simply passed onto the paginate method allowing such things as conditions or includes to be execute in the paginate sql query. &lt;/p&gt;&lt;p&gt; So now the migration code loads 500 Photo objects in memory at once. I ran this migration on the staging server and it barely used any RAM and executed in the expected 10 minutes. This short piece of code is a prime example of why I enjoy programming in ruby.&lt;/p&gt;</description>
      <pubDate>Sat, 16 Feb 2008 22:57:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:294b91e6-351e-4cef-933a-163ac71010c7</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2008/02/16/use-pagination-to-save-memory-when-iterating-over-a-lot-of-activerecord-objects</link>
      <category>Programming</category>
      <category>will_paginate</category>
      <category>attachment_fu</category>
      <category>ActiveRecord</category>
      <category>ruby</category>
      <category>rails</category>
      <category>apply_to_all</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/8</trackback:ping>
    </item>
    <item>
      <title>RMagick has memory problems. MiniMagick with Attachment_Fu is slowwww. Go ImageScience.</title>
      <description>&lt;p&gt;Most Rails applications have to deal with resizing uploaded images for the creation of thumbnails.&amp;nbsp; The main choices include &lt;a href="http://rmagick.rubyforge.org/"&gt;RMagick&lt;/a&gt;, &lt;a href="https://rubyforge.org/projects/mini-magick/"&gt;MiniMagick&lt;/a&gt;, or &lt;a href="http://seattlerb.rubyforge.org/ImageScience.html"&gt;ImageScience&lt;/a&gt;, all of which come packaged as gems. Alternatively you can write your own which really isn&amp;#39;t that difficult.&amp;nbsp; So which one should you use? First I would recommend not writing your own, because it is really nice to take advantage of one of the very fine attachment plugins that are available. &lt;a href="http://wiki.rubyonrails.org/rails/pages/File+Column+Plugin"&gt;File column&lt;/a&gt; is the old standby rails attachment plugin, but it uses RMagick. &lt;a href="http://svn.techno-weenie.net/projects/plugins/attachment_fu/"&gt;Attachment_Fu&lt;/a&gt; is more flexible since you have the choice of using RMagick, MiniMagick, or ImageScience and can switch between them easily. At &lt;a href="http://www.teamsporttech.com"&gt;TeamSportTech&lt;/a&gt; where I work I recently transitioned our relatively large rails application from using file column and RMagick to using attachment_fu and ImageScience. Originally I was planning on using MiniMagick instead of ImageScience, but it turns out that MiniMagick is quite slow. The following timing results are on my Mac Book when running in development mode, and include uploading a single 3.4 MB jpeg which is resized down to 4 different sizes. MiniMagick consistently took 18 seconds to accomplish this, RMagick 7 seconds, and ImageScience 6 seconds. This doesn&amp;#39;t accurately represent a production environment, but I do believe it is a fair comparison. Note that the time to upload is not a factor since this was done entirely on my local computer.&lt;br /&gt;&lt;br /&gt;So why is MiniMagick with attachment_fu so slow? And why not use RMagick?&amp;nbsp; RMagick and MiniMagick use the well known &lt;a href="http://imagemagick.org/"&gt;ImageMagick&lt;/a&gt; C libraries. RMagick works by providing API ruby bindings to the ImageMagick libraries, which means that RMagick operates within the ruby process. It is well documented that RMagick consumes a lot of memory and has memory leaks as well. See &lt;a href="http://blog.craigambrose.com/past/2007/11/27/image_management_that_will_scale/"&gt;Craig Ambrose&amp;#39;s article&lt;/a&gt; and a &lt;a href="http://mephistoblog.com/2007/1/9/new-asset-thumbnailing-options-available"&gt;Mephisto article.&lt;/a&gt; It appears like &lt;a href="http://rmagick.rubyforge.org/rmagick2.html"&gt;RMagick2&lt;/a&gt; provides better memory management. &lt;/p&gt;

Even if you have memory to spare, the more memory your Mongrels utilize the more garbage collection that will have to be done, which eventually will bog down the Mongrels. MiniMagick solves this memory problem by utilizing the shell commands provided by ImageMagick, which means all image manipulations are done in separate processes outside the ruby process&amp;#39; memory space. ImageScience is a different beast altogether since it uses the &lt;a href="http://sourceforge.net/projects/freeimage"&gt;FreeImage&lt;/a&gt; library instead of ImageMagick, and it is more similar to RMagick in that it runs inside the ruby process. MiniMagick has extra overhead to start up a separate process for each ImageMagick command it executes. It also has extra disk overhead since it must operate on images on the filesystem instead of images loaded in memory.&lt;br /&gt;&lt;br /&gt;I put a print out in the MiniMagick source to track all the ImageMagick commands that MiniMagick runs to accomplish this single upload with 4 resizing operations. Here is the output with timing in the order that they were executed:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;1.421884 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.594993 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.592467 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;0.602928 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.594078 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.584066 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;1.444892 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;240x240&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.024799 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.019388 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;0.593593 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.580201 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.572714 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.590181 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.600532 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;1.396459 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -shave &amp;quot;648x0&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.973819 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;75x75!&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.016706 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.016919 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;0.594606 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.690731 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.592638 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;1.513243 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;550x550&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.028199 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.029103 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;0.610143 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.593422 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.638836 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;1.777569 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;1024x1024&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.089828 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.057866 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&lt;br /&gt;So as you can see, MiniMagick executed 30 separate shell commands. You can also see the temporary files on disk that it had to create to do these operations. To be fair, it is more correct to say that Attachment Fu executed 30 MiniMagick commands. I was curious why so many identify commands were needed. Each of the identify -format commands returns either the width (%w) or height (%h) of the image, which Attachment Fu stores in the database. This is very wasteful, because the initial identify command done for each resizing operation does include the width and height in the output, which should be parsed to save time. And for some reason notice that there are 10 identify commands to retrieve the width and 10 for the height, even though there are only 5 images involved (the original and 4 resized images). I don&amp;#39;t know why this would be necessary and haven&amp;#39;t checked the source to find out. Instead of pursuing these weird findings of how Attachment Fu uses MiniMagick, I decided to switch to Image Science. It is fast and is not a hog on memory.&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; </description>
      <pubDate>Sat, 16 Feb 2008 18:35:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:4508a339-f655-467d-aef3-f8f46bb58571</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2008/02/16/rmagick-has-memory-problems-minimagick-with-attachment_fu-is-slowwww-go-imagescience</link>
      <category>Programming</category>
      <category>MiniMagick</category>
      <category>attachment_fu</category>
      <category>file column</category>
      <category>RMagick</category>
      <category>ImageScience</category>
      <category>ImageMagick</category>
      <category>FreeImage</category>
      <category>ruby</category>
      <category>rails</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/7</trackback:ping>
    </item>
    <item>
      <title>Running ar_sendmail with monit</title>
      <description>&lt;p&gt;Sending email from a web application, especially blast emails to a lot of people, can take a lot of time. Generally you don&amp;#39;t want the user to wait until all the emails have been handed off to the smtp server. You also probably don&amp;#39;t want to tie up an entire mongrel with sending mail. The &lt;a href="http://blog.segment7.net/articles/2006/08/15/ar_mailer"&gt;ar_mailer gem&lt;/a&gt; solves this problem in excellent fashion, by saving pending emails to the database and having a separate ruby daemon process periodically check the database and send emails. I recently set up one of our rails apps at &lt;a href="http://teamsporttech.com"&gt;work&lt;/a&gt; to use ar_mailer. Configuring it to use ar_mailer was incredibly easy, but it was tricky to get the ar_sendmail ruby daemon process to run under &lt;a href="http://www.tildeslash.com/monit/"&gt;monit&lt;/a&gt;. On our production servers which we have hosted at &lt;a href="http://engineyard.com/"&gt;Engine Yard&lt;/a&gt;, we want every process that our application depends on to be monitored by monit.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The primary feature that ar_sendmail lacks to play nice with monit is the ability to leave a pid file after it starts up and to remove it when the process exits. This has already been pointed out on &lt;a href="http://rubyforge.org/tracker/index.php?func=detail&amp;amp;aid=14839&amp;amp;group_id=1513&amp;amp;atid=5924"&gt;rubyforge as a feature request&lt;/a&gt;. Here is what I did to get ar_sendmail working under monit: (ar_mailer 1.3.1) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0) Find the file ar_sendmail.rb on your system and open it for editing.&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;For me this was at /usr/local/lib/ruby/gems/1.8/gems/ar_mailer-1.3.1/lib/action_mailer/.&amp;nbsp; Another common location is /usr/lib/ruby/gems/1.8/gems/ar_mailer-1.3.1/lib/action_mailer/ &lt;/p&gt;&lt;p&gt;&lt;strong&gt;1) Create a class variable to store the pid file path in and a method to remove the pid file.&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;I put this just below &amp;quot;attr_accessor :failed_auth_count&amp;quot; &lt;/p&gt;&lt;pre&gt;  @@pid_file = nil&lt;/pre&gt;&lt;pre&gt;  def self.remove_pid_file&lt;br /&gt;    FileUtils.rm(@@pid_file) if @@pid_file&lt;br /&gt;  end  &amp;nbsp;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;2) Modify the self.run method in ar_sendmail.rb to create a pid file, and to not start up if an ar_sendmail process is already running&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if options[:Daemon] then &lt;br /&gt;      require &amp;#39;webrick/server&amp;#39;       &lt;br /&gt;      @@pid_file = &amp;quot;#{options[:Chdir]}/log/ar_mailer.pid&amp;quot;            &lt;br /&gt;      if File.exists? @@pid_file             &lt;br /&gt;        # check to see if process is actually running&lt;br /&gt;        pid = &amp;#39;&amp;#39;&lt;br /&gt;        File.open(@@pid_file, &amp;#39;r&amp;#39;) {|f| pid = f.read.chomp }             &lt;br /&gt;        if &lt;span style="padding: 0pt; background-color: yellow; color: black; display: inline; font-size: inherit"&gt;system&lt;/span&gt;(&amp;quot;ps -p #{pid} | grep #{pid}&amp;quot;) # returns true if process is running, o.w. false&lt;br /&gt;          $stderr.puts &amp;quot;Warning: The pid file #{@@pid_file} exists and ar_sendmail is running. Shutting down.&amp;quot;         &lt;br /&gt;          exit&lt;br /&gt;        else&lt;br /&gt;          # not running, so remove existing pid file and continue&lt;br /&gt;          self.remove_pid_file&lt;br /&gt;          log &amp;quot;ar_sendmail is not running. Removing existing pid file and starting up...&amp;quot;          &lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;      WEBrick::Daemon.start  &lt;br /&gt;      File.open(@@pid_file, &amp;#39;w&amp;#39;) {|f| f.write(&amp;quot;#{Process.pid}\n&amp;quot;)}    &lt;br /&gt;    end                &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;If the pid file already exists, this code will check to see if the process is actually running.&amp;nbsp; If it is then it will exit, otherwise it will remove the file and continue. This is useful for when the process dies ungracefully somehow (server crashes, killed with -9, .etc), in which case it will leave a pid file behind. Note that this differs from the code suggested on the &lt;a href="http://rubyforge.org/tracker/index.php?func=detail&amp;amp;aid=14839&amp;amp;group_id=1513&amp;amp;atid=5924"&gt;rubyforge feature request&lt;/a&gt;, not only in that it checks the existence of the pid file, but it references options[:Chdir] instead of Dir.pwd in order to be compatible with the -c and --chdir ar_sendmail option.&amp;nbsp;  &lt;/p&gt;&lt;p&gt;&lt;strong&gt;3) Modify the do_exit method in ar_sendmail.rb to remove the pid file&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;  def do_exit&lt;br /&gt;    log &amp;quot;caught signal, shutting down and removing pid file&amp;quot;&lt;br /&gt;    self.remove_pid_file&lt;br /&gt;    exit&lt;br /&gt;  end &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;4) Create the monit file /etc/monit.d/ar_sendmail.teamsport.monitrc&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;check process ar_sendmail_teamsport&lt;br /&gt;  with pidfile /data/teamsport/current/log/ar_mailer.pid&lt;br /&gt;  start program = &amp;quot;/usr/bin/ar_sendmail -d -e production -c /data/teamsport/current/&amp;quot; as uid teamsport and gid teamsport&lt;br /&gt;  stop program = &amp;quot;/usr/local/bin/stop_ar_sendmail&amp;quot; as uid teamsport and gid teamsport&lt;br /&gt;  if totalmem is greater than 65.0 MB for 2 cycles then restart      # eating up memory?&lt;br /&gt;  if loadavg(5min) greater than 10 for 8 cycles then restart          # bad, bad, bad&lt;br /&gt;  if 20 restarts within 20 cycles then timeout                        # something is wrong, call the sys-admin&lt;br /&gt;  group ar_sendmail &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Note that I am using a simple shell script script called stop_ar_sendmail to stop the ar_sendmail process.&amp;nbsp; ar_sendmail has signal handlers for SIGINT and SIGTERM so we should use these signals to kill it, which will cause the do_exit method to be triggered. The stop_ar_sendmail script looks like this:&lt;/p&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;kill -2 `cat /data/teamsport/current/log/ar_mailer.pid` &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Originally I tried putting the contents of this shell script in the .montrc file like this:&lt;/p&gt;&lt;pre&gt;stop program = &amp;quot;/bin/kill -2 `cat /data/teamsport/current/log/ar_mailer.pid`&amp;quot; as uid teamsport and gid teamsport&amp;nbsp;&lt;/pre&gt;&lt;p&gt;This however does not work, since apparently monit doesn&amp;#39;t know what to do with the backticks. Alternatively you could use some sort of grep and kill script, such as pkill, to stop the ar_sendmail process. Ideally in the future, ar_sendmail will support some sort of stop command in the same manner as mongrel so that you could run &amp;quot;ar_sendmail stop&amp;quot; to stop it.&lt;/p&gt;&lt;p&gt;Once you have this all setup you can control ar_sendmail via monit. When creating the ar_sendmail.teamsport.monitrc file, make sure you change &amp;quot;teamsport&amp;quot; to the user that you want to run the process as.&amp;nbsp; Then do a &amp;quot;&lt;font color="#ff0000"&gt;sudo monit reload&lt;/font&gt;&amp;quot; and monit should see that ar_sendmail is not running and will start it for you. To make sure everything is working correctly try &amp;quot;&lt;font color="#ff0000"&gt;sudo monit stop ar_sendmail_teamsport&lt;/font&gt;&amp;quot; and &amp;quot;&lt;font color="#ff0000"&gt;sudo monit start ar_sendmail_teamsport&lt;/font&gt;&amp;quot; (replacing &amp;quot;teamsport&amp;quot; with the appropriate user name). &lt;/p&gt;&lt;p&gt;Other than not working well with monit out of the box, the only other issue I have with ar_sendmail is the memory footprint, which is dependent to some degree on your rails app. The ar_sendmail process for my app runs at around 50 MB, and just to send mail! I assume this is due to the fact that ar_sendmail loads the rails app&amp;#39;s environment.rb file. The environment.rb file runs the boot.rb file, which bootstraps and initializes the entire rails app. Additionally, our environment.rb has several other plugins required inside of it. I think the environment.rb file is loaded primarily just to get at the ActiveMailer smtp_settings, which is a slick way to allow for easy integration of ar_mailer with minimal changes to your existing rails app. Many people wouldn&amp;#39;t think twice about 50 MB, but rails hosts charge quite a bit for RAM. I can definitely envision a slimmed down ar_sendmail that doesn&amp;#39;t load the rails app&amp;#39;s environment.rb file, but it seems almost impossible to do this without making integration with existing rails apps more difficult.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Wed, 05 Dec 2007 19:35:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:672193d6-be4f-4545-acdd-42e3f4f669d6</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/12/05/running-ar_sendmail-with-monit</link>
      <category>rails</category>
      <category>ar_mailer</category>
      <category>ar_sendmail</category>
      <category>monit</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/6</trackback:ping>
    </item>
    <item>
      <title>How to add a new column to Rails' sessions</title>
      <description>&lt;p&gt;For a rails app at work we store user access privileges in the session.&amp;nbsp; This is done as an optimization to avoid an extra sql query that would need to be done for every page view to determine if the user has edit privileges.&amp;nbsp; Security-minded people may see this as a security hole.&amp;nbsp; For this application I don&amp;#39;t see this as a big deal.&amp;nbsp; Its not like our rails app is controlling the launch of nuclear missiles.&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;p&gt;The problem is that when an admin goes to modify the user access privileges for someone, the changes won&amp;#39;t take affect until the user next logs in since the user access privileges are stored in the session. So if the user is already logged in this is a problem. They will have to log out and log back in for the changes to take affect. The solution to this problem is to modify the session of the user who&amp;#39;s access privileges were modified.&amp;nbsp; &lt;/p&gt;&lt;p&gt;To do this we need to add a user_id column to the sessions table. This can be done like any other migration. The tricky part is accessing the user_id column. We will want to set the user_id of the session when someone logs in. This of course will not work:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; session.user_id = @user.id&lt;/font&gt; &lt;/p&gt;

&lt;p&gt;The session we are so familiar with is not like other ActiveRecord objects. If you are using ActiveRecordStore for your session storage (the rails built-in database session storage mechanism), this is easy. For ActiveRecordStore, the session has a model attribute allowing you to access the ActiveRecord session object. First you need to make a session model class which extends ActiveRecord. Mine looked lke this:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; class Session &amp;lt; ActiveRecord::Base &lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/font&gt; &lt;/p&gt;&lt;p&gt;Then inside your &amp;quot;login&amp;quot; action you can do:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; session.model.user_id = @user.id&lt;/font&gt;&lt;/p&gt;&lt;p&gt;Then just rely on the automatic session saving built into rails to save the user_id to the database.&amp;nbsp;&lt;/p&gt;&lt;p&gt;If you are using SqlSessionStore, you don&amp;#39;t have access to the model attribute. Instead you can do this to set the user_id:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; my_session = Session.find_by_session_id(session.session_id)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; my_session.update_attribute(:user_id, current_user.id)&lt;/font&gt;&lt;/p&gt;&lt;p&gt;Now presumably at some point in your application you will need to access and make use of the user_id column. For me this is when the user access privileges are modified. Since we created a session.rb model class, we can use the built in rails find method to find all sessions for the user. &lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; sessions = Session.find(:all, :conditions =&amp;gt; &amp;quot;user_id = #{user.id}&amp;quot;, :select =&amp;gt; &amp;quot;session_id&amp;quot;)&amp;nbsp;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;To take advantage of the built-in session data marshaling in rails, we need to access the session like this:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session = &lt;span class="caps"&gt;CGI&lt;/span&gt;::Session::ActiveRecordStore::Session.find_by_session_id(sess.session_id)&lt;/font&gt;&lt;/p&gt;&lt;p&gt;Then we can set a session value using the data attribute and save the session off.&amp;nbsp; Here is the full code for updating the sessions:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; sessions.each do |sess|&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session = &lt;span class="caps"&gt;CGI&lt;/span&gt;::Session::ActiveRecordStore::Session.find_by_session_id(sess.session_id)&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session.data[:access_privileges] = new_access_privileges&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session.save&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; end &lt;br /&gt;&lt;/font&gt;&lt;/p&gt;</description>
      <pubDate>Mon, 26 Nov 2007 17:59:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:bb8cf08e-09a7-4b7b-a2ee-302495c3108e</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/11/26/how-to-add-a-new-column-to-rails-sessions</link>
      <category>Programming</category>
      <category>rails</category>
      <category>sessions</category>
      <category>ActiveRecordStore</category>
      <category>SqlSessionStore</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/5</trackback:ping>
    </item>
    <item>
      <title>A Virus the World Needs : How to Topple the Reign of Internet Explorer</title>
      <description>&lt;p&gt;For my first project at my new job, a Ruby on Rails position with &lt;a href="http://www.teamsporttech.com/" title="Team Sport Technologies"&gt;Team Sport Technologies&lt;/a&gt;, I implemented a table builder, which allows a user to create simple tables using their browser without writing HTML or any other special markup. My JavaScript was a bit rusty, so it took a few days before I had the table builder complete. I was working with a brand new Mac Pro without Parallels installed, and so I had yet to try the table builder in Internet Explorer, but it worked perfectly in Firefox, Safari, and Opera. I soon found out that it failed miserably in Internet Explorer. I was assuming it would be a quick fix, but it turned out that Microsoft&amp;#39;s implementation of the DOM Event model is so messed up that I had to redo the entire implementation using a different approach.&amp;nbsp; &lt;/p&gt;&lt;p&gt;Internet Explorer is a source of constant frustration and wasted hours of web developers across the world. All other web browsers follow standards set by the &lt;a href="http://www.w3.org/" title="W3C Web Consortium"&gt;W3C World Wide Web Consortium &lt;/a&gt;describing how web browsers should interpret Cascading Style Sheets, JavaScript, and the Document Object Model. Books on these subjects dedicate dozens of pages to detailing the crazy hacks that programmers need to use to get their web sites to operate in Internet Explorer. Microsoft breaks many of these standards intentionally, and can get away with it simply due to having a majority of the browser market.  &lt;/p&gt;

&lt;p&gt;Wikipedia has an &lt;a href="http://en.wikipedia.org/wiki/Usage_share_of_web_browsers"&gt;interesting chart on web browser&lt;/a&gt; usage which shows that in 2004 Internet Explorer had 95% of the market. According to &lt;a href="http://www.onestat.com/html/aboutus_pressbox53-firefox-mozilla-browser-market-share.html" title="OneStat.com"&gt;OneStat.com&lt;/a&gt;, in June of 2007 approximately 85% of people browsing the web worldwide use Internet Explorer. In recent years Firefox has been gaining users from Internet Explorer, but I don&amp;#39;t think we can expect this trend to continue. The typical non-technical person wants a simple and familiar web browser. Isn&amp;#39;t Firefox simple?&amp;nbsp; Of course, but it is not familiar. The refresh button looks different. Everything looks different. Most people dislike learning how to use new software. It doesn&amp;#39;t matter if the unfamiliar product is clearly superior. &lt;/p&gt;&lt;p&gt;So this leaves us web developers in a disgruntled state, forced to ensure our code works with a crappy piece of software that everyone uses. We can expect this as long as Microsoft and Windows dominates the world of computers. Windows only ships with Internet Explorer and people just don&amp;#39;t want to download and install a new browser, no matter how easy it is. So what can we do to topple the reign of Internet Explorer? I can imagine a program which automatically installs Firefox, pre-loaded with an &lt;a href="http://johnhaller.com/jh/mozilla/firefox_internet_explorer/"&gt;Internet Explorer theme&lt;/a&gt; so it feels familiar to people. This program could even relink the Internet Explorer shortcuts to point to Firefox, and then &lt;a href="http://www.techsupportalert.com/how_to_disable_internet_explorer.htm"&gt;disable Internet Explorer&lt;/a&gt; by assigning an invalid proxy server. And while unethical, wouldn&amp;#39;t it be nice if this program was released as a virus to spread across the world? Sure would be interesting. The word &lt;em&gt;virus&lt;/em&gt; has such a negative rap. It would probably go over better if we called it a security patch and actually asked the user if they would like to install it. After all, Internet Explorer is known to have quite a few security holes.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Previously I thought JavaScript was a crappy language and I was annoyed that it is essentially the only choice for a client-side browser programming language. Over the last two weeks my opinion has changed. JavaScript is a decent language, especially with the addition of excellent libraries such as &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt; and &lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt;. JavaScript even has &lt;a href="http://en.wikipedia.org/wiki/Closure_(computer_science)"&gt;closures&lt;/a&gt;, an advanced programming language feature made popular by Lisp and not found in many other languages such as Java. But writing JavaScript code is still frustrating and will be as long as we have to play by the rules of Internet Explorer. It would make little difference if a Ruby interpreter came built-in to all web browsers. Internet Explorer would still fail to implement the Document Object Model correctly and web development would remain what it is today: An exciting fast paced industry pursued by a little devil with an eery cackle and a sharp pitchfork --&amp;gt; Microsoft. &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Sat, 20 Oct 2007 17:39:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:19999c1b-1989-4c3a-8549-c0406539d8af</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/10/20/a-virus-the-world-needs-how-to-topple-the-reign-of-internet-explorer</link>
      <category>Programming</category>
      <category>JavaScript</category>
      <category>Internet Explorer</category>
      <category>Prototype</category>
      <category>Scriptaculous</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/4</trackback:ping>
    </item>
    <item>
      <title>Erlang - The Next Great Programming Language?</title>
      <description>&lt;p&gt;&lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; - The Next Great Programming Language? I don&amp;#39;t think so. I probably shouldn&amp;#39;t be doing this. At least not yet. I just don&amp;#39;t know enough about Erlang to make any predictions. I haven&amp;#39;t written any Erlang code myself. This will change shortly, as the &lt;a href="http://pragmaticprogrammer.com/titles/jaerlang/index.html"&gt;book&lt;/a&gt; is on its way. I guess I am impatient. And curious to see if my opinion will change.&lt;/p&gt;&lt;p&gt;All signs seem to point to Erlang being the next great programming language. It has momentum, it has hype, it even has the &lt;a href="http://pragmaticprogrammer.com/titles/jaerlang/index.html"&gt;Pragmatic Programmers&lt;/a&gt; on its side. And like Ruby, it has a killer app -- parallel processing. Ruby&amp;#39;s killer app is Rails of course, which greatly eases the burden in developing web applications. Ruby is the latest great programming language. For the past 3-4 years Ruby (and Rails) is the language that everyone has been talking about, the language that all the talented programmers want to learn in their spare time. Before Ruby, Python may have stood for a short time on the latest great programming language podium. Python never quite had the killer app that Ruby does with Rails, but it still shocked the world (well, the world of nerds anyway) with its pseudocode look and its use of whitespace. Before Python there was Java, and before Java C++, and before C++ there was C. I wasn&amp;#39;t programming at the time, but yes people I believe if you could travel back in time there was a point where C was the latest cool programming language that all the cool kids (aka major nerds) had to try out.&lt;/p&gt;&lt;p&gt;And now Erlang? Certainly it seems like all the cool kids are sneaking off to become Erlang experts during their lunch break. Which is the beginning of how the next great programming language comes to be. Due to its killer app, parallel processing, Erlang is impossible to ignore. Multi-core computers are now a reality, with more cores coming out every year. Intel could have &lt;a href="http://www.engadget.com/2006/07/10/intel-goes-multi-core-crazy-for-keifer-server-line/"&gt;32 cores by 2010&lt;/a&gt;. The tides have turned and the software world now lags behind the hardware world. In the past we always had software programs that would consume the available processor time or eat up all the RAM. But now, with 32 cores just around the corner, very few software programs will be able to take advantage of this. Unless Erlang becomes the next great programming language, and quickly. &lt;/p&gt;

&lt;p&gt;Parallel-processing comes built-in to Erlang. Whereas conventional programming languages use a concurrency-model with shared state, Erlang uses a message-passing concurrency model where the only way for &amp;quot;threads&amp;quot; to exchange state is via asynchronous messages. The shared state concurrency model causes a host of problems, most of which fall under two categories: race conditions and deadlocks. These problems are extremely frustrating to fix and it can be very difficult to prove their absence. Edward A. Lee has written an excellent paper on &lt;em&gt;&lt;a href="http://www.google.com/url?sa=t&amp;amp;ct=res&amp;amp;cd=1&amp;amp;url=http%3A%2F%2Fwww.eecs.berkeley.edu%2FPubs%2FTechRpts%2F2006%2FEECS-2006-1.pdf&amp;amp;ei=afAER-nWG5aSiwHYlMWeBQ&amp;amp;usg=AFQjCNFKbD8gsVjP71jOd1-cFbUiGK96Cw&amp;amp;sig2=yAxX3buosNNTyr_ZFBuUOg"&gt;The Problem With Threads&lt;/a&gt;,&lt;/em&gt; where he basically says that the dominant shared state concurrency model in use today is not the way to go. He argues that &lt;em&gt;&amp;quot;Nondeterminism should be explicitly and judiciously introduced where needed, rather than removed where not needed&amp;quot;&lt;/em&gt;. I don&amp;#39;t know if Erlang is quite what Edward A. Lee was envisioning, but right now it is the best we have. Along with Erlang&amp;#39;s asynchronous message-passing concurrency model, it automatically distributes the multiple &amp;quot;threads&amp;quot; (which are really processes) across cores, which essentially means an Erlang program running on a 32 core machine can run 32 times faster than on a single core, assuming there are no other bottlenecks such as disk to slow it down. &lt;/p&gt;&lt;p&gt;Why don&amp;#39;t I think Erlang will be the next great programming language? It is too radically different. It is too functional.&amp;nbsp;No real variables? No mutable state? My simple-minded brain understands the importance of no-shared state between &amp;quot;threads&amp;quot;, but with Erlang I can&amp;#39;t even change the value of &lt;em&gt;foo&lt;/em&gt;? And has a functional programming language ever become popular with the masses? Everyone knows that Lisp is more powerful than other languages, but no one cares. &lt;/p&gt;&lt;p&gt;Erlang is simply too different. In &lt;em&gt;&lt;a href="http://ejohnson.blogs.com/software/2004/11/i_find_c_intere.html"&gt;C++ - The Forgotten Trojan Horse&lt;/a&gt;&lt;/em&gt;, Eric Johnson reminds us how C++ took over the world. C++ extended C in a manner that allowed developers to gradually transist into the world of C++. Eric Johnson writes that &lt;em&gt;&amp;quot;Too often we developers think of great change as requiring discrete, atomic, massive events that stand out as a fork in the road&amp;quot;&lt;/em&gt;. In reality, great change in the programming world is gradual, not all at once. Scott Berkun in his &lt;em&gt;&lt;a href="http://www.amazon.com/Myths-Innovation-Scott-Berkun/dp/0596527055"&gt;The Myths of Innovation&lt;/a&gt;&lt;/em&gt;, points out that Compatibility is one of the main factors in how quickly innovations spread. Scott writes &lt;em&gt;&amp;quot;How much effort is required to transition from the current thing to the innovation? If this cost is greater than the relative advantage, most people won&amp;#39;t try the innovation&amp;quot;&lt;/em&gt;.&amp;nbsp;&lt;/p&gt;&lt;p&gt;But Erlang has the momentum and the hype and in the next few years I am sure most of the cool kids will bring it around the block for a spin to see what it can do. The best hackers have probably done this already and most likely they realized that Erlang is too different. What the world needs now is a language like Erlang with its asynchronous message-passing concurrency model and its automatic parallelization across cores, but without its functional nature and without its lack of real variables. People can only take so much change at a time, and switching from a shared-state to a message-passing concurrency model is a rather large change by itself. I expect that before long a new language will appear to steal the glory. Or maybe an existing language (Ruby anyone?) will be extended to include the more important concepts underlying Erlang. As Scott Berkun points out in &lt;em&gt;&lt;a href="http://www.amazon.com/Myths-Innovation-Scott-Berkun/dp/0596527055"&gt;The Myths of Innovation&lt;/a&gt;&lt;/em&gt;, the best ideas do not always win. &lt;/p&gt;</description>
      <pubDate>Thu, 04 Oct 2007 08:13:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:57c108bc-4866-4411-a3b2-ce14425ad3e0</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/10/04/erlang-the-next-great-programming-language</link>
      <category>Programming</category>
      <category>Erlang</category>
    </item>
    <item>
      <title>There is more to life than code.</title>
      <description>&lt;p&gt;Many skilled programmers are good, in part, because it is all they do. At work they code and they can&amp;#39;t wait to get home so they can code on their own project. The world of programming is a large turbulent pool of knowledge, ideas, and skillsets too vast and changing for any one human mind to conquer. For many people this world of code is their primary passion in life. Programmers get sucked into an hour-gobbling vortex that leaves little time for other activities. Tired of sitting at your desk all day? Followed by sitting in your car as you commute home, and further by sitting in front of the tv or computer at home? &lt;strong&gt;&lt;font color="#ff0000"&gt;Sit, sit sit, its what Americans do, especially lazy but passionate-about-code programmers.&lt;/font&gt;&lt;/strong&gt; I think it is important for the health of the human psyche to have at least one hobby or activity, unrelated to work, that gets you excited, and why not make it a physical activity since the majority of your life is spent sitting on your bottom?&lt;/p&gt;&lt;p&gt;Physical activity? Most people are turned off just by the thought of it. Physical activity is usually set aside as one of those healthy things you should be doing, but never have time for. Like flossing your teeth. Occasionally inspiration hits and you&amp;#39;ll go for a run every other day or hit the weight room. But if you are like most people, this usually lasts for only a week or two before you are back on the couch. You wouldn&amp;#39;t want to miss an episode of Heroes. The problem is that very few people actually enjoy running or lifting weights. &lt;strong&gt;&lt;font color="#ff0000"&gt;What you need is a physical hobby which excites you as much as the latest cool programming language. &lt;/font&gt;&lt;/strong&gt;If you are passionate about excelling at a physical hobby, then your own self-motivation will drive you to stay in great shape, just as it drives you to stay on top of the latest programming trends. &lt;/p&gt;

&lt;p&gt;There are many physical hobbies to choose from, such as road biking, mountain biking, whitewater kayaking, sea kayaking, geo caching, backpacking, and various forms of martial arts. Find one which you are excited about and get started! If you need help choosing, then let me convince you to take up lightweight backpacking. Especially if you sort of like nature, or at least you don&amp;#39;t hate it and could maybe grow to enjoy it. The lightweight backpacking movement shares several similarities with the agile programming movement. Both are about constantly improving and excelling at their respective topics. Both embrace simplicity. Agile practices are often described as lightweight since they attempt to eliminate complexity and unnecessary waste in the software development process. One of the well-known agile methodologies is actually known as Lean Software Development. A lightweight backpacker similarly eliminates waste by only bringing essential items.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Just as the best programmers are agile, the best backpackers go lightweight. The amount of weight one carries while backpacking plays a huge role in your comfort and enjoyment while on the trail. A traditional backpacker typically carries between 35 and 60 pounds, travels between 4 and 8 miles a day, and honestly dreads time on the trail. A lightweight backpacker typically carries between 15 and 25 pounds, travels between 8 and 30 miles a day, and thoroughly enjoys their time on the trail. Whereas the goal of agile programming, produce better software on time and within budget to maximize value, is simply about becoming better at software development, the goal of the lightweight backpacker varies from person to person. Many lightweight backpackers enjoy perfecting the art of wilderness travel and strive to cover as many miles per day as they can. For others, backpacking lightweight may allow them to carry other gear such as photography equipment, enabling them to get shots that normal outdoor photographers dream about. And some lightweight backpackers simply want to enjoy their time in nature, and instead of pushing themselves to cover miles they travel in a more relaxed manner without the burden of a heavy pack.&amp;nbsp; &lt;/p&gt;&lt;p&gt;Backpacking can be a challenging hobby. Their are many skills to refine, including orienteering, regulating body temperature, staying dry in bad weather, wilderness first aid, physical fitness, hanging your food pack, and caring for your feet. Many of these skills relate to using your gear, and gear research and selection prior to trips is a large part of the lightweight backpacking hobby. Lightweight backpackers keep gear lists, which have every piece of gear they bring on trips along with their weight. Example Excel templates can be found at BackpackingLight, one of the best resources for lightweight backpacking. You&amp;#39;ll find a healthy online community of lightweight backpackers at this site to answer your questions and to sell you their used gear for cheap. Backpacking lightweight does not have to be expensive, and the best way to learn is to jump right in and try it.&lt;/p&gt;&lt;p&gt;So stop sitting on your butt all day! Find something to do outside which you are excited about and start doing it, because there is more to life than code.&amp;nbsp; &lt;/p&gt;</description>
      <pubDate>Tue, 02 Oct 2007 22:34:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:a2e6092f-fd95-494c-a716-10535296192f</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/10/02/there-is-more-to-life-than-code</link>
      <category>Lightweight Backpacking</category>
      <category>Sea Kayaking</category>
      <category>Programming</category>
      <category>agile_programming</category>
    </item>
    <item>
      <title>Inspiration While Kayaking</title>
      <description>&lt;p&gt;One Thursday afternoon in early May, I decided to leave work early at 3:30 and go kayaking. Adios manager Chris. After a boring hour commute home to lovely Woodbury, Minnesota, I tossed my kayak on top of my truck, drove to nearby St. Croix Bluffs Regional Park, launched, and was on the water by 5:00. My kayak is a yellow 17 foot long Current Designs Caribou and is designed for &amp;quot;seas&amp;quot; as it is a sea kayak. I paddled across the wide St. Croix river to the Wisconsin side and headed downriver along the shore. Out for exercise, I was planning to paddle about 8 miles in a moderately strenuous manner. The planned 4-mile turn around point is where the St. Croix joins with the Mississippi.  &lt;/p&gt;&lt;p&gt;I soon got in a rhythm and began thinking about, what else -&amp;gt; backpacking. &lt;font color="#ff0000"&gt;&lt;font color="#00ff00"&gt;&lt;strong&gt;If you were to just meet me and ask what I do I would say&lt;/strong&gt;&lt;strong&gt; &amp;quot;I backpack, paddle, and write code&amp;quot;&lt;/strong&gt;&lt;/font&gt; &lt;/font&gt;[1]. Certainly you were expecting an answer more like, &amp;quot;I work as a Simulation Engineer at BAE Systems in Fridley. We make the biggest guns in the world, aka howitzers, for the army.&amp;quot; Backpacking, paddling kayaks and canoes, and writing code are my three main... passions or hobbies? If I just call them passions then I have to make the standard disclaimer about my wife Becca being my first passion. How about passion-hobbies. &lt;/p&gt;

&lt;p&gt;So I was thinking about backpacking. Specifically the 2 week, solo, 240 mile trip I was planning for late summer in the Cascades. &lt;font color="#000000"&gt;I don&amp;#39;t just backpack, I backpack light. &lt;/font&gt;Similar to not being a traditional backpacker, I am not a conventional programmer. I am an agile programmer. Both lightweight backpacking and agile programming are movements in their respective worlds that have been expanding in popularity in the last 5 years or so. While expanding, agile programmers and lightweight backpackers are certainly still minorities. My inspiration, while out kayaking, is that the guts behind each of the movements are incredibly similar. What was I to do about it? Write of course. There are lots of websites about backpacking and even more about programming, but &lt;strong&gt;&lt;font color="#00ff00"&gt;hopefully I will have the first ever website on both backpacking and programming.&lt;/font&gt;&lt;/strong&gt; Does this make sense? I don&amp;#39;t know. But I will be sharing similarities between the two and how ideas from both of them can be brought into everyday life. Maybe I&amp;#39;ll be able to convince a few programmers to take up lightweight backpacking. Somehow I doubt I&amp;#39;ll be able to convince any backpackers to take up programming!  &lt;/p&gt;&lt;p&gt;I turned around shortly before reaching the junction with the Mississippi and headed back up river and into the wind. I concentrated on using my torso instead of just my arms to propel my body and kayak through the water. The river being straight here, I spotted on the distant horizon what I thought was the take out. As I neared it I realized I was mistaken and was only half-way back. Since I was paddling hard this was a bit disheartening. No matter. A short time later I was observing a bald eagle being harried by a smaller bird a few hundred feet overhead. &lt;strong&gt;&lt;font color="#00ff00"&gt;While under attack, the bald eagle suddently dropped its feet, opened its large talons and dove down to the water.&lt;/font&gt;&lt;/strong&gt; I was expecting a big splash in the water, but wow, this baldy barely got its toe-nails wet. Its prize was a small sunfish, wiggling in the eagles death grip.&lt;/p&gt;&lt;p&gt;As I neared the real take out I noticed a big fishing boat towing an even larger fishing boat back to the public launch. As a kayaker this makes me smile. Simplicity is one of the primary properties underlying lightweight backpacking, agile programming, and kayaking. &lt;font color="#000000"&gt;Kayaking is one of the simplest ways to enjoy being on the water.&lt;/font&gt; There is no outboard motor to fail and leave you stranded. You travel via your own power. I need to remember to always bring my tow rope with me. If I had, and the other fishing boat wasn&amp;#39;t around, I literally would have towed this boat, family and all, back to the launch[2]. This would have brought an even bigger smile to my face.   &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;[1] This concept of answering &amp;quot;What do you do?&amp;quot; by describing what I&amp;#39;m passionate about instead of what I do for a job deserves a reference, since I saw it on a short film last week about a guy who skis (he works as a waiter in case you are wondering) while watching the 2007 Banff Film Festival at Midwest Mountaineering&amp;#39;s Spring Outdoor Adventure Expo. Not much of a reference since I can&amp;#39;t remember the guys name.&lt;/p&gt;&lt;p&gt;[2] Skeptical about a kayaker towing a boat? Don&amp;#39;t be. It has certainly happened before. I&amp;#39;m not saying it wouldn&amp;#39;t be a lot of work, but it is more doable than it sounds. Once you get momentum going I imagine it would only be strenuous work (as opposed to completely exhausting).&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Thu, 03 May 2007 19:23:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:db2782f9-9332-4493-8e1d-d00f1d32c09c</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/05/03/inspiration-while-kayaking</link>
      <category>Lightweight Backpacking</category>
      <category>Sea Kayaking</category>
      <category>Programming</category>
      <category>agile_programming</category>
    </item>
  </channel>
</rss>
