<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>flame.org - Home</title>
  <id>tag:blog.flame.org,2008:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.8.0">Mephisto Drax</generator>
  <link href="http://blog.flame.org/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://blog.flame.org/" rel="alternate" type="text/html"/>
  <updated>2008-05-20T03:15:00Z</updated>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2008-05-20:395</id>
    <published>2008-05-20T02:30:00Z</published>
    <updated>2008-05-20T03:15:00Z</updated>
    <category term="Technology"/>
    <link href="http://blog.flame.org/2008/5/20/vmware-server-and-xen" rel="alternate" type="text/html"/>
    <title>Vmware Server and Xen</title>
<content type="html">
            &lt;p&gt;In the past, I&#8217;ve played with VMware (as a &#8220;workstation&#8221; &#8220;server&#8221;, not the bare-metal one we have access to now) but was never quite happy with it.  Some of the problems I had might have been that I ran it on windows Vista, not Linux.  However, from a VM point of view, the VM itself should be more or less identical.&lt;/p&gt;

&lt;p&gt;Recently I tried out Xen on the same hardware, but using NetBSD/amd64 as the &#8220;host&#8221; OS.&lt;/p&gt;

&lt;h3&gt;Hardware&lt;/h3&gt;

&lt;p&gt;The machine is Gateway GM5446E with a dual core Intel Core 2 Duo with 3 GB of ram.  The machine has three SATA hard drives, connected via an Intel AHCI controller running in native AHCI mode.&lt;/p&gt;

&lt;h3&gt;Bare Hardware Baseline&lt;/h3&gt;

&lt;p&gt;I booted a standard NetBSD-current/amd64 kernel and ran some speed tests, which gives a baseline for dom0 and guest OS disk I/O speed tests.  See below.&lt;/p&gt;

&lt;h3&gt;VMware&lt;/h3&gt;

&lt;p&gt;In my VMware install, I used the machine running &#8220;Windows Vista Media Center&#8221; &#8211; it was what came pre-installed on the machine.&lt;/p&gt;

&lt;p&gt;Host OS:  Windows Vista Media Center&lt;/p&gt;

&lt;p&gt;Guest OSs tried:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NetBSD-4.0/i386&lt;/li&gt;
&lt;li&gt;NetBSD-4.0/amd64&lt;/li&gt;
&lt;li&gt;NetBSD-current/i386&lt;/li&gt;
&lt;li&gt;NetBSD-current/amd64&lt;/li&gt;
&lt;li&gt;Linux Ubuntu (server, then-current version)&lt;/li&gt;
&lt;li&gt;Linux Debian (then-current version)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Linux booted in 64-bit mode, but would crap out at some later point with similar issues that NetBSD had.&lt;/p&gt;

&lt;p&gt;One machine, named &#8220;nfsd&#8221;, was dedicated to serving out home directories and source trees of NetBSD.  The other host OSs mounted /home or /netbsd-src from nfsd.&lt;/p&gt;

&lt;p&gt;Each machine had a &#8220;local&#8221; disk to store object files from pkgsrc, src, and other OS-related builds.&lt;/p&gt;

&lt;h4&gt;General VMware Problems&lt;/h4&gt;

&lt;p&gt;I could not get netbsd/amd64 (current or 4.0 release) to &#8220;self host&#8221; &#8211; build /usr/src and kernels &#8211; reliably.  They would either silently lock up without reason, or they would crash with an odd CPU exception.&lt;/p&gt;

&lt;p&gt;Timekeeping was whacky.  Without running the VMware-supplied (closed source) tools on a client, time was off, and apparently in uncorrectable ways.  Running ntp made things seriously whacky as the time would drift wildly as ntp tried to correct a guest.&lt;/p&gt;

&lt;p&gt;The VMware closed-source tools are only available on a small, limited number of OS types, and then specific versions of many of them.  They do supply a .so that is, in theory, linkable on many versions of Linux, but the install procedure warns loudly of warnings pertaining to compatibility.&lt;/p&gt;

&lt;p&gt;VMware running on anything but Intel chips with synchronized cycle
counters (which most OSs use for high-res timekeeping these days) was a
disaster.&lt;/p&gt;

&lt;h4&gt;VMware Strengths&lt;/h4&gt;

&lt;p&gt;If the problems above are solved, VMware is a true virtual machine architecture that will run any OS without modification.  VMware could run windows guests, right along with unmodified NetBSD, FreeBSD, Linux, and Solaris/x86 guests.&lt;/p&gt;

&lt;h3&gt;Xen&lt;/h3&gt;

&lt;p&gt;I tried Xen 3.1.3.&lt;/p&gt;

&lt;p&gt;Xen is a different architecture than vmware in that it prefers to use &#8220;paravirtualization&#8221; rather than a full virtual machine.  It has a host machine (called &#8220;domain 0&#8221; or &#8220;dom0&#8221;) which attaches to the physical hardware and acts as a conduit between the xen hypervisor and hardware.&lt;/p&gt;

&lt;p&gt;The boot process is that the xen kernel is booted first, which then boots the dom0 host.  Multiple domains can be created, serving different hardware, but in practice this is rarely done.&lt;/p&gt;

&lt;p&gt;Each host OS has a config file, and is stated with &#8220;xm create /path/to/file.conf&#8221;.  This boots the guest OS and connects a serial console, which can be used with &#8220;xm console &amp;lt;name&gt;&#8221;.&lt;/p&gt;

&lt;p&gt;Since the &#8220;dom0&#8221; is a fully functional OS in its own rights, I have it serve NFS to the guest OSs.&lt;/p&gt;

&lt;p&gt;I created the following guest OSs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NetBSD-current/i386&lt;/li&gt;
&lt;li&gt;NetBSD-current/amd64&lt;/li&gt;
&lt;li&gt;Windows XP Pro (32-bit)&lt;/li&gt;
&lt;li&gt;Windows Server 2003 (32-bit)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, I managed to install Windows XP Pro and Server 2003.  They run in a &#8220;vnc&#8221; console, and for all practical purposes looks like windows.  This is using Xen&#8217;s &#8220;hvm&#8221; &#8211; which is a full hardware emulated virtual machine, and allows running unmodified guest OSs.  People have Vista running in a virual machine under Xen, but I do not have &#8220;real&#8221; Vista install media or licenses, just the ones that came with and is tied to my hardware.&lt;/p&gt;

&lt;p&gt;Xen also supports both realtime and offline &#8220;migration.&#8221;  As I have only one machine of the same type, I have not yet read up on how this works.  The basics:  A realtime copy is made of the guest&#8217;s ram, device state, and other data.  It is transmitted to the new destination, and synced up until a very small switchover time can be used to swap where that guest is running.  Xen claims 100 ms switchover time is possible, but there are restrictions:  The disks are NOT migrated, so must reside on a shared volume.  The physical network each dom0 is on is also shared, in order to avoid disruption of TCP connections.  I also believe fairly identical CPU and dom0 operating systems should be used.&lt;/p&gt;

&lt;p&gt;Offline migration involves shutting the guest down, copying the disks over, and restarting it on a new dom0.  This will, of course, interrupt service.&lt;/p&gt;

&lt;h4&gt;Xen Problems&lt;/h4&gt;

&lt;p&gt;It is difficult to configure for the fist time.  The documentation is&#8230; lacking.  It is also only as solid as the host OS is, but vmware has the same issue in &#8220;server&#8221; or &#8220;workstation&#8221; incarnations.&lt;/p&gt;

&lt;p&gt;Xen is also very, very &#8220;linux&#8221; specific in documentation and examples. Most of these can be translated &#8211; I certainly did so easily enough &#8211; but this is being corrected in their documentation as more OSs are able to boot as dom0.&lt;/p&gt;

&lt;h4&gt;Xen Strengths&lt;/h4&gt;

&lt;p&gt;Timekeeping in Xen, since it is paravirtualized, is almost perfect.  Small drifts will occur without running ntp, but all guests (and the host) can run ntp and obtain sanity.&lt;/p&gt;

&lt;p&gt;It also appears that all guests and the dom0 &#8220;drift&#8221; identically, so this is probably related to hardware timekeeping issues.  The measured drift of an uncorrected NetBSD guest was 4 seconds in two weeks.  ntp correction kept the others in perfect real-world sync.&lt;/p&gt;

&lt;p&gt;It is as free as you want it to be.  Support and commercial versions exist, but the free stuff works amazingly well.&lt;/p&gt;

&lt;h3&gt;Performance&lt;/h3&gt;

&lt;p&gt;On Xen, all tests were performed with the domu&#8217;s running but idle, and no hvm guests running (windows is just too unpredictable.)  On VMware, only one VM was active at once, and the Vista host was as idle as it could be made.&lt;/p&gt;

&lt;p&gt;I measured three main things here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Boot speed:  How fast a kernel gets from loading to the first /etc/rc message.&lt;/li&gt;
&lt;li&gt;Disk speed:  read/write speed.&lt;/li&gt;
&lt;li&gt;CPU Performance:&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;Boot Speed&lt;/h4&gt;

&lt;p&gt;The dom0 boots as fast as any other kernel boots; it must probe the hardware, wait for hardware to change state, etc.  No measured difference between a standard NetBSD-current/amd64 kernel and the dom0 kernel.&lt;/p&gt;

&lt;p&gt;The domUs (guests) boot so fast it is nearly impossible to measure. This is because the devices they have access to are known &#8211; all are on a virtual bus, and are directly enumerable, so there is no need to probe for devices, wait for them to change state, or time out when not present.  As best I can measure, just under 2 seconds is a fair estimate.&lt;/p&gt;

&lt;p&gt;The hvm (windows) guest seems to be about as fast as windows is.  I did not analyze this one much.&lt;/p&gt;

&lt;p&gt;On VMware, the host OSs boot at about the same speed as a &#8220;real&#8221; machine boots unless a custom kernel is built with just &#8220;known present&#8221; devices.  Even then, boot times are 15-20 seconds.&lt;/p&gt;

&lt;h4&gt;Disk Speed&lt;/h4&gt;

&lt;p&gt;In all host/dom0 tests, &#8220;iozone&#8221; version 3.263 was used, with a 1 GB file on the same disk.  Each test was performed only once; for real comparison data we&#8217;d want to run it more than once, but this is just a first-pass test.&lt;/p&gt;

&lt;p&gt;For native NetBSD/amd64, I had to increase the file size to 4 GB to avoid the cache, as the machine has 3 GB of ram.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wd0 is a 500 GB SATA 3.0Gb/sec disk.&lt;/li&gt;
&lt;li&gt;wd1 is present but unused.&lt;/li&gt;
&lt;li&gt;wd2 is a 320 GB SATA 1.5Gb/sec disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All are on different channels of an Intel AHCI controller running in native SATA mode.&lt;/p&gt;

&lt;p&gt;For the Xen tests, all disks were mounted as files on the dom0 host. From dom0&#8217;s point of view, the file is mounted on a &#8220;vnd&#8221; virtual disk, and that virtual disk is exported to the host.&lt;/p&gt;

&lt;p&gt;For the VMware test, all disks were mounted as files in the Windows filesystem.&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;OS&lt;/th&gt;
    &lt;th&gt;Disk&lt;/th&gt;
    &lt;th&gt;Block Size&lt;/th&gt;
    &lt;th&gt;Read&lt;/th&gt;
    &lt;th&gt;Write&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/amd64 native&lt;/td&gt;
    &lt;td&gt;wd0&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;60762&lt;/td&gt;
    &lt;td&gt;59949&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;60545&lt;/td&gt;
    &lt;td&gt;60141&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;wd2&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;78342&lt;/td&gt;
    &lt;td&gt;76342&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;78311&lt;/td&gt;
    &lt;td&gt;75252&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/amd64 dom0&lt;/td&gt;
    &lt;td&gt;wd0&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;60641&lt;/td&gt;
    &lt;td&gt;60109&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;60459&lt;/td&gt;
    &lt;td&gt;61919&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;wd2&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;80258&lt;/td&gt;
    &lt;td&gt;79102&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;80187&lt;/td&gt;
    &lt;td&gt;80295&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/amd64 domu&lt;/td&gt;
    &lt;td&gt;wd0&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;51205&lt;/td&gt;
    &lt;td&gt;24004&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;51714&lt;/td&gt;
    &lt;td&gt;27971&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;wd2&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;77990&lt;/td&gt;
    &lt;td&gt;23997&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;77282&lt;/td&gt;
    &lt;td&gt;22496&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/i386 domu&lt;/td&gt;
    &lt;td&gt;wd0&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;41730&lt;/td&gt;
    &lt;td&gt;25012&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;42008&lt;/td&gt;
    &lt;td&gt;24543&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;wd2&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;66401&lt;/td&gt;
    &lt;td&gt;26048&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;66201&lt;/td&gt;
    &lt;td&gt;28910&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/i386 vmware&lt;/td&gt;
    &lt;td&gt;wd0&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;25014&lt;/td&gt;
    &lt;td&gt;13912&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;25417&lt;/td&gt;
    &lt;td&gt;13771&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;wd2&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;38831&lt;/td&gt;
    &lt;td&gt;16100&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;38994&lt;/td&gt;
    &lt;td&gt;16332&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;I also repeated one test with a raw, physical partition mounted in the netbsd-current/amd64 domU, which bypasses the &#8220;double filesystem&#8221; issue:&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/amd64 domu&lt;/td&gt;
    &lt;td&gt;wd2&lt;/td&gt;
    &lt;td&gt;8192&lt;/td&gt;
    &lt;td&gt;79915&lt;/td&gt;
    &lt;td&gt;76992&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Physical mount&lt;/td&gt;
    &lt;td&gt;wd2&lt;/td&gt;
    &lt;td&gt;16384&lt;/td&gt;
    &lt;td&gt;79744&lt;/td&gt;
     &lt;td&gt;77102&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h4&gt;CPU Performance&lt;/h4&gt;

&lt;p&gt;Each CPU speed test was run with:  Dhrystone Benchmark, Version 2.1 (Language: C)  Program compiled without &#8216;register&#8217; attribute.&lt;/p&gt;

&lt;p&gt;I used an iteration count of 1,000,000,000 for each test.&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Operating System&lt;/th&gt;
    &lt;th&gt;Dhrystones per second&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/amd64 native&lt;/td&gt;
    &lt;td&gt;11,013,216&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/amd64 dom0&lt;/td&gt;
    &lt;td&gt;10,365,917&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/amd64 domu&lt;/td&gt;
    &lt;td&gt;11,130,899&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/i386 domu&lt;/td&gt;
    &lt;td&gt;4,935,347&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;netbsd-current/i386 vmware&lt;/td&gt;
    &lt;td&gt;5,012,123&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Just for grins, I ran the following tests, one dhrystone on one guest and another on a different one.  Since each guest is uniprocessor in my configuration, I did not run two benchmarks on the same host.&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Operating Systems&lt;/th&gt;
    &lt;th&gt;Speed 1&lt;/th&gt;
    &lt;th&gt;Speed 2&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Running both domu/i386 and domu/amd64&lt;/td&gt;
    &lt;td&gt;4916421.0&lt;/td&gt;
    &lt;td&gt;11135857.0&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Running both dom0/amd64 and domu/amd64&lt;/td&gt;
    &lt;td&gt;10298661.0&lt;/td&gt;
    &lt;td&gt;11135857.0&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Running both dom0/amd64 and domu/i386&lt;/td&gt;
    &lt;td&gt;10373444.0&lt;/td&gt;
    &lt;td&gt;4921260.0&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;

&lt;p&gt;Xen is production ready.&lt;/p&gt;

&lt;p&gt;When the host OS can be modified, much higher performance numbers are obtained vs. the low-end VMware server I ran.&lt;/p&gt;

&lt;p&gt;While it might be extremely tempting to build one guest that does one very specific function, this probably does not scale:  memory is pre-allocated and dedicated to a guest, and while some swapping is allowed, it will slow the guest at seemingly random times; disk can be overcommitted, but the OS sees failure to allocate a block as a hardware failure; the more hosts, the more maintenance costs are present: maintaining packages on each guest, upgrading, etc.&lt;/p&gt;

&lt;p&gt;VMware &#8220;hmx&#8221; or whatever the name of the run-on-bare-metal product should be tested.&lt;/p&gt;

&lt;p&gt;I&#8217;d love to install Xen on a huge machine with lots of ram and many, many CPUs as a test.  Would someone like to ship me a 4 CPU quad core with 64 GB?&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2008-04-23:194</id>
    <published>2008-04-23T04:55:00Z</published>
    <updated>2008-04-23T05:03:07Z</updated>
    <category term="isp"/>
    <category term="rant"/>
    <link href="http://blog.flame.org/2008/4/23/your-cable-company-owns-you" rel="alternate" type="text/html"/>
    <title>Your cable company owns you</title>
<content type="html">
            &lt;p&gt;Well, ok, perhaps not entirely&#8230; yet.&lt;/p&gt;

&lt;p&gt;This is actually a rant on something cable modems allow your cable internet provider to do to you.&lt;/p&gt;

&lt;p&gt;They restrict access to your own hardware.&lt;/p&gt;

&lt;p&gt;Why would they do this?  Paranoia.  A while back, there was a security hole in a network monitoring tool called Simple Network Management Protocol, or SNMP.  This security issue allowed people to crash other people&#8217;s modems, break into their own and change upload/download speeds, and other nasty things.&lt;/p&gt;

&lt;p&gt;All of these have been fixed.  However, people are still breaking into their modems to &#8220;uncap&#8221; them &#8211; change speed settings.  They just don&#8217;t use SNMP to do it anymore.  They&#8217;ve become more advanced and use things like internal serial ports or JTAG ports.&lt;/p&gt;

&lt;p&gt;So, why do cable companies still restrict access to SNMP, and worse, to some of your modem&#8217;s diagnostic features?  I suspect it is because they don&#8217;t want to have to answer questions about why they suck.  They hide the real details of what your modem is doing from you.&lt;/p&gt;

&lt;p&gt;Why is this a big deal?&lt;/p&gt;

&lt;p&gt;For one, I own the hardware, but my cable company configures it against my wishes.  I can understand rate limiting &#8211; I pay for the fastest service already &#8211; but I cannot understand restricting diagnostic tools.&lt;/p&gt;

&lt;p&gt;For two, I have spent, in the last 6 months, perhaps 40 hours debugging a cable internet issue with techs from Cox Communications.  After many, many rounds of techs who report &#8220;all signal levels are good&#8221; I finally got a real live network engineer on the line, who, in 5 minutes, could look at all the statistics on my modem.  And solve problems.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2008-03-24:94</id>
    <published>2008-03-24T19:20:00Z</published>
    <updated>2008-03-24T20:45:21Z</updated>
    <category term="Programming"/>
    <category term="commerce"/>
    <category term="ruby"/>
    <link href="http://blog.flame.org/2008/3/24/checking-credit-card-numbers-in-ruby" rel="alternate" type="text/html"/>
    <title>Checking Credit Card Numbers in Ruby</title>
<content type="html">
            &lt;p&gt;This is not meant to be an exhaustive list of all possible numbers, nor the only or best method to verify that they pass the &#8220;checksum&#8221; test, but here&#8217;s what I came up with.&lt;/p&gt;

&lt;p&gt;I wrote this mostly to link a Ruby version of the code to Wikipedia&#8217;s article on Luhn checksum validation, since nearly every other language in use was listed, but Ruby was sadly missing.&lt;/p&gt;

&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;17&lt;tt&gt;
&lt;/tt&gt;18&lt;tt&gt;
&lt;/tt&gt;19&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;21&lt;tt&gt;
&lt;/tt&gt;22&lt;tt&gt;
&lt;/tt&gt;23&lt;tt&gt;
&lt;/tt&gt;24&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;26&lt;tt&gt;
&lt;/tt&gt;27&lt;tt&gt;
&lt;/tt&gt;28&lt;tt&gt;
&lt;/tt&gt;29&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;31&lt;tt&gt;
&lt;/tt&gt;32&lt;tt&gt;
&lt;/tt&gt;33&lt;tt&gt;
&lt;/tt&gt;34&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;35&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;36&lt;tt&gt;
&lt;/tt&gt;37&lt;tt&gt;
&lt;/tt&gt;38&lt;tt&gt;
&lt;/tt&gt;39&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;40&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;41&lt;tt&gt;
&lt;/tt&gt;42&lt;tt&gt;
&lt;/tt&gt;43&lt;tt&gt;
&lt;/tt&gt;44&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;45&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;46&lt;tt&gt;
&lt;/tt&gt;47&lt;tt&gt;
&lt;/tt&gt;48&lt;tt&gt;
&lt;/tt&gt;49&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;50&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;51&lt;tt&gt;
&lt;/tt&gt;52&lt;tt&gt;
&lt;/tt&gt;53&lt;tt&gt;
&lt;/tt&gt;54&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;55&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;56&lt;tt&gt;
&lt;/tt&gt;57&lt;tt&gt;
&lt;/tt&gt;58&lt;tt&gt;
&lt;/tt&gt;59&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;60&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;61&lt;tt&gt;
&lt;/tt&gt;62&lt;tt&gt;
&lt;/tt&gt;63&lt;tt&gt;
&lt;/tt&gt;64&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;65&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;66&lt;tt&gt;
&lt;/tt&gt;67&lt;tt&gt;
&lt;/tt&gt;68&lt;tt&gt;
&lt;/tt&gt;69&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;70&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;71&lt;tt&gt;
&lt;/tt&gt;72&lt;tt&gt;
&lt;/tt&gt;73&lt;tt&gt;
&lt;/tt&gt;74&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;75&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;76&lt;tt&gt;
&lt;/tt&gt;77&lt;tt&gt;
&lt;/tt&gt;78&lt;tt&gt;
&lt;/tt&gt;79&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;80&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;81&lt;tt&gt;
&lt;/tt&gt;82&lt;tt&gt;
&lt;/tt&gt;83&lt;tt&gt;
&lt;/tt&gt;84&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;85&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;86&lt;tt&gt;
&lt;/tt&gt;87&lt;tt&gt;
&lt;/tt&gt;88&lt;tt&gt;
&lt;/tt&gt;89&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;90&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;91&lt;tt&gt;
&lt;/tt&gt;92&lt;tt&gt;
&lt;/tt&gt;93&lt;tt&gt;
&lt;/tt&gt;94&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;95&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;96&lt;tt&gt;
&lt;/tt&gt;97&lt;tt&gt;
&lt;/tt&gt;98&lt;tt&gt;
&lt;/tt&gt;99&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;100&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;101&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# Copyright (c) 2008 Michael Graff.  All rights reserved.&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# Redistribution and use in source and binary forms, with or&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# without modification, are permitted provided that the following&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# conditions are met:&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# 1. Redistributions of source code must retain the above copyright&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#    notice, this list of conditions and the following disclaimer.&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# 2. Redistributions in binary form must reproduce the above&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#    copyright notice, this list of conditions and the following&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#    disclaimer in the documentation and/or other materials provided&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#    with the distribution.&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# 3. The name of Michael Graff may not be used to endorse or promote&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#    products derived from this software without specific prior&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#    written permission.&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# THIS SOFTWARE IS PROVIDED BY Michael Graff ``AS IS'' AND ANY&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL Micahel Graff&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;# OF SUCH DAMAGE.&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;cl&quot;&gt;Luhn&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  public&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;pc&quot;&gt;self&lt;/span&gt;.check_luhn(s)&lt;tt&gt;
&lt;/tt&gt;    s.gsub!(&lt;span class=&quot;rx&quot;&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;[^0-9]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;    ss = s.reverse.split(&lt;span class=&quot;rx&quot;&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;/&lt;/span&gt;&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;    alternate = &lt;span class=&quot;pc&quot;&gt;false&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    total = &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    ss.each &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |c|&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; alternate&lt;tt&gt;
&lt;/tt&gt;        total += double_it(c.to_i)&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;else&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;        total += c.to_i&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      alternate = !alternate&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    (total % &lt;span class=&quot;i&quot;&gt;10&lt;/span&gt;) == &lt;span class=&quot;i&quot;&gt;0&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  private&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;pc&quot;&gt;self&lt;/span&gt;.double_it(i)&lt;tt&gt;
&lt;/tt&gt;    i = i * &lt;span class=&quot;i&quot;&gt;2&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; i &amp;gt; &lt;span class=&quot;i&quot;&gt;9&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      i = i % &lt;span class=&quot;i&quot;&gt;10&lt;/span&gt; + &lt;span class=&quot;i&quot;&gt;1&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    i&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;gv&quot;&gt;$0&lt;/span&gt; == &lt;span class=&quot;pc&quot;&gt;__FILE__&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;test_valid&lt;/span&gt;(s)&lt;tt&gt;
&lt;/tt&gt;    result = &lt;span class=&quot;co&quot;&gt;Luhn&lt;/span&gt;::check_luhn(s)&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; result&lt;tt&gt;
&lt;/tt&gt;      puts &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;VALID: &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;s&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;else&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      puts &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;INVALID: &lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;dl&quot;&gt;#{&lt;/span&gt;s&lt;span class=&quot;dl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt; (should be valid)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;5105 1051 0510 5100&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;c&quot;&gt;# Mastercard&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;5555 5555 5555 4444&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;c&quot;&gt;# Mastercard&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;4222 2222 2222 2&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)    &lt;span class=&quot;c&quot;&gt;# Visa&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;4111 1111 1111 1111&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;c&quot;&gt;# Visa&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;4012 8888 8888 1881&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;c&quot;&gt;# Visa&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3782 8224 6310 005&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)  &lt;span class=&quot;c&quot;&gt;# American Express&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3714 4963 5398 431&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)  &lt;span class=&quot;c&quot;&gt;# American Express&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3787 3449 3671 000&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)  &lt;span class=&quot;c&quot;&gt;# American Express Corporate&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3782 8224 6310 005&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)  &lt;span class=&quot;c&quot;&gt;# Amex&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3400 0000 0000 009&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)  &lt;span class=&quot;c&quot;&gt;# Amex&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3700 0000 0000 002&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)  &lt;span class=&quot;c&quot;&gt;# Amex&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;38520000023237&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)      &lt;span class=&quot;c&quot;&gt;# Diners Club (14 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;30569309025904&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)      &lt;span class=&quot;c&quot;&gt;# Diners Club (14 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;6011111111111117&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)    &lt;span class=&quot;c&quot;&gt;# Discover (16 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;6011 0000 0000 0004&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;c&quot;&gt;# Discover&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;6011 0000 0000 0012&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;c&quot;&gt;# Discover&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;6011000990139424&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)    &lt;span class=&quot;c&quot;&gt;# Discover (16 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;6011601160116611&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)    &lt;span class=&quot;c&quot;&gt;# Discover (16 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3530111333300000&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)    &lt;span class=&quot;c&quot;&gt;# JCB (16 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3566002020360505&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)    &lt;span class=&quot;c&quot;&gt;# JCB (16 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;  test_valid(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;5431111111111111&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)    &lt;span class=&quot;c&quot;&gt;# Mastercard (16 digits)&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2008-03-19:82</id>
    <published>2008-03-19T18:25:00Z</published>
    <updated>2008-03-20T04:46:18Z</updated>
    <category term="Programming"/>
    <category term="security"/>
    <category term="web-development"/>
    <link href="http://blog.flame.org/2008/3/19/javascript-application-framework-extjs-and-privacy" rel="alternate" type="text/html"/>
    <title>Javascript application framework 'extjs' and privacy</title>
<content type="html">
            &lt;p&gt;Out of the box, &lt;a href=&quot;http://extjs.com/&quot;&gt;extjs&lt;/a&gt; version 2.0.2 leaks privacy information.&lt;/p&gt;

&lt;p&gt;If you fail to change the value of &lt;code&gt;Ext.BLANK_IMAGE_URL&lt;/code&gt; to something local, it will default to http://extjs.com/s.gif.  At first this might not seem bad, but remember that every time this image is fetched the referring URL is sent to the extjs.com web server.&lt;/p&gt;

&lt;p&gt;At worse, this is a minor information link.  Depending on what you might place in your URL line, this could be a major issue.&lt;/p&gt;

&lt;p&gt;I have posted a comment on the extjs forums, but so far the developers don&#8217;t see the problem.  They say it is well documented in their FAQ, and that it is documented in the API docs.&lt;/p&gt;

&lt;p&gt;I would prefer they opt for a warning message saying &#8220;You did not set &#8230;&#8221; rather than leaking information by default.  I&#8217;ll probably have to post a CERT on this one.  :/&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2007-10-31:12</id>
    <published>2007-10-31T05:34:00Z</published>
    <updated>2008-04-01T04:15:42Z</updated>
    <category term="Technology"/>
    <category term="apache"/>
    <category term="ipv6"/>
    <link href="http://blog.flame.org/2007/10/31/fun-with-apache-and-virtual-hosts" rel="alternate" type="text/html"/>
    <title>Fun With Apache and Virtual Hosts</title>
<content type="html">
            &lt;p&gt;Specifically, name based virtual hosts.&lt;/p&gt;

&lt;p&gt;I recently tried to add IPv6 support to my web server.  I used to have it, I remember having it, so this should not be all that hard.&lt;/p&gt;

&lt;p&gt;After an hour of hacking, I ended up finding two gotchas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make certain, I mean certain, that all virtual hosts for name-based servers have a unique &lt;code&gt;ServerName&lt;/code&gt; line.&lt;/li&gt;
&lt;li&gt;Make certain, and I mean certain, to save your original configuration files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yea, I know, I should have known better.  But this is a simple thing to change, right?&lt;/p&gt;

&lt;p&gt;A very useful tool is &lt;code&gt;apachectl -S&lt;/code&gt;, which lists all virtual hosts.  Even better is to run that output through &lt;code&gt;sort&lt;/code&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2007-10-28:7</id>
    <published>2007-10-28T07:49:00Z</published>
    <updated>2008-03-20T04:47:32Z</updated>
    <category term="Programming"/>
    <category term="apache"/>
    <category term="rails"/>
    <link href="http://blog.flame.org/2007/10/28/simple-rails-caching" rel="alternate" type="text/html"/>
    <title>Simple Rails Caching</title>
<content type="html">
            &lt;p&gt;In &lt;a href=&quot;/2007/10/28/mongrel-apache-and-rails&quot;&gt;my apache configuration&lt;/a&gt; I have Apache serving static pages from the public directory.  After playing around with Mephisto a bit, I&#8217;ve learned a few tricks on caching.&lt;/p&gt;

&lt;p&gt;The common belief is to use memcache for caching on sites that are large enough to need it.  However, I think with a few tricks, I can do better.  I think in some cases, letting Apache serve the content is a far wiser move.&lt;/p&gt;

&lt;p&gt;For example, I will begin writing out the images for avatars on &lt;a href=&quot;http://guildcorner.org/&quot;&gt;guildcorner.org&lt;/a&gt; to files, and let Apache serve them.  I will do this when they are created, updated, or rendered to a browser.  This will let Apache serve the next request for it.  I also plan on deleting the cache file if the avatar is deleted.&lt;/p&gt;

&lt;p&gt;Since I use Capistrano to push out releases now, and the cached files are not in subversion, upgrading to a new application version won&#8217;t require anything special.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2007-10-28:6</id>
    <published>2007-10-28T07:48:00Z</published>
    <updated>2008-06-03T04:24:22Z</updated>
    <category term="Technology"/>
    <category term="apache"/>
    <category term="capistrano"/>
    <category term="mongrel"/>
    <category term="rails"/>
    <link href="http://blog.flame.org/2007/10/28/mongrel-apache-and-rails" rel="alternate" type="text/html"/>
    <title>Mongrel, Apache, and Rails</title>
<content type="html">
            &lt;p&gt;When I first started running Rails applications on my web server, I chose to use FastCGI.  Specifically, the mod_fcgid module, which had some features I wanted.  It also has the unfortunate by-product of corrupting Apache&#8217;s memory.  Bad news.&lt;/p&gt;

&lt;p&gt;I&#8217;ve since removed FastCGI entirely and moved to a proxy to mongrel_cluster setup.  And I&#8217;ve started deploying with Capistrano.&lt;/p&gt;

&lt;h1&gt;Capistrano&lt;/h1&gt;

&lt;p&gt;I have a certain amount of concern with moving to a deployment system I knew very little about.  Just like a new backup system, I feel like I&#8217;m handing the keys to my data over to something not written by me.  And, while it is fairly simple to set up, Capistrano is somewhat complicated internally.&lt;/p&gt;

&lt;p&gt;I already push out my operating system upgrades in an automated way.  I compile NetBSD on one machine here at home, and push the binaries out to all the machines I have which run NetBSD.  This means about 7 machines rsync from the build box with one command.  This can be scary, but I&#8217;ve been doing it for 5 years now, and it just works.  How can a web site be scary compared to kernels and system binaries?&lt;/p&gt;

&lt;p&gt;The answer is, it&#8217;s not.  If something breaks it is fairly easy to manually reconfigure if I need to.  So, I&#8217;ve relaxed a bit.  My concerns are still there, and I&#8217;m keeping a careful watch on how Capistrano runs each time I deploy.  I have yet to do a &lt;em&gt;real&lt;/em&gt; deployment after all!  So far, I&#8217;ve not done a single migration, and have not had to roll back.  And I&#8217;m pushing to a single machine, which runs the database as well as the site.&lt;/p&gt;

&lt;p&gt;I suspect that, as I become comfortable with this new method to update my web sites, I&#8217;ll start thinking of it as rsync++.  It really is that simple.&lt;/p&gt;

&lt;h1&gt;mongrel_cluster&lt;/h1&gt;

&lt;p&gt;Mongrel is a vary amazing little widget.  Sure, it&#8217;s slower than Apache, but that&#8217;s ok.  Mongrel is still far, far faster than restarting Rails for each web hit, and far more reliable than mod_fcgid.&lt;/p&gt;

&lt;p&gt;In my configuration, I run each site on ports 10000, 10010, 10020, etc. with up to 3 servers per.  This means application #1 is on 10000 through 10002, with room to grow should I need to run more.   If I find myself running more than 10 servers for a site it needs a new machine anyway, or more machines.  And if that happens, I hope I&#8217;ll have a budget.&lt;/p&gt;

&lt;h1&gt;Apache load balancing&lt;/h1&gt;

&lt;p&gt;This is a new feature in Apache 2.1, and apparently is very reliable with Apache 2.2.  This is currently my favorite way to run a web site.&lt;/p&gt;

&lt;p&gt;My configuration, which happens to be for this site:&lt;/p&gt;

&lt;ol class=&quot;CodeRay&quot;&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;&amp;lt;proxy balancer://blog&amp;gt;&lt;/li&gt;&lt;li&gt;  BalancerMember http://localhost:10010&lt;/li&gt;&lt;li&gt;  BalancerMember http://localhost:10011&lt;/li&gt;&lt;li&gt;  BalancerMember http://localhost:10012&lt;/li&gt;&lt;li&gt;&amp;lt;/proxy&amp;gt;&lt;/li&gt;&lt;li&gt;&amp;lt;VirtualHost blog.flame.org:80&amp;gt;&lt;/li&gt;&lt;li&gt;  DocumentRoot /www/blog/flame-blog/current/public&lt;/li&gt;&lt;li&gt;  &amp;lt;directory &amp;quot;/www/blog/flame-blog/current/public&amp;quot;&amp;gt;&lt;/li&gt;&lt;li&gt;    Options FollowSymLinks&lt;/li&gt;&lt;li&gt;    AllowOverride None&lt;/li&gt;&lt;li&gt;    Order allow,deny&lt;/li&gt;&lt;li&gt;    Allow from all&lt;/li&gt;&lt;li&gt;  &amp;lt;/directory&amp;gt;&lt;/li&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;  ProxyRequests off&lt;/li&gt;&lt;li&gt;  &amp;lt;proxy *&amp;gt;&lt;/li&gt;&lt;li&gt;    order deny,allow&lt;/li&gt;&lt;li&gt;    allow from all&lt;/li&gt;&lt;li&gt;  &amp;lt;/proxy&amp;gt;&lt;/li&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;  RewriteEngine on&lt;/li&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;  # Check for maintenance file. Let apache load it if it exists&lt;/li&gt;&lt;li&gt;  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f&lt;/li&gt;&lt;li&gt;  RewriteRule . /system/maintenance.html [L]&lt;/li&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;  # Rewrite index to check for static&lt;/li&gt;&lt;li&gt;  RewriteRule ^/$ balancer://blog%{REQUEST_URI} [L,P,QSA]&lt;/li&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;  # Let apache serve static files (send everything via mod_proxy that&lt;/li&gt;&lt;li&gt;  # is *no* static file (!-f)&lt;/li&gt;&lt;li&gt;  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f&lt;/li&gt;&lt;li&gt;  RewriteRule .* balancer://blog%{REQUEST_URI} [L,P,QSA]&lt;/li&gt;&lt;li&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;It is important, at least on my host, to use localhost in the balancer destinations.  This is due to mongrel suddenly running on IPv6 loopback (&lt;code&gt;::1&lt;/code&gt;) rather than the usual IPv4 loopback (&lt;code&gt;127.0.0.1&lt;/code&gt;).  I don&#8217;t know why this happened, but the localhost trick makes Apache try both addresses, and whichever works it will use.&lt;/p&gt;

&lt;p&gt;This configuration makes Apache serve static content, and sends all other requests off to one of the Mongrel processes.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2007-10-27:4</id>
    <published>2007-10-27T15:43:00Z</published>
    <updated>2007-10-27T15:53:20Z</updated>
    <category term="ansteorra"/>
    <category term="bjornsborg"/>
    <category term="sca"/>
    <link href="http://blog.flame.org/2007/10/27/ursae-lyons-2007" rel="alternate" type="text/html"/>
    <title>Ursae-Lyons</title>
<content type="html">
            &lt;p&gt;Last weekend my wife and I attended a lovely little event in the &lt;a href=&quot;http://www.bjornsborg.org/&quot;&gt;Barony of Bjornsborg&lt;/a&gt; in the &lt;a href=&quot;http://www.ansteorra.org/&quot;&gt;Kingdom of Ansteorra&lt;/a&gt;.  We had a wonderful time.  There was music, a bardic circle, and lots of singing (most of it good!) coming from Cynric&#8217;s tavern.&lt;/p&gt;

&lt;p&gt;The best things to happen there, in my opinion, was that Baron Cynric of Bedwyn was awarded with the Kingdom&#8217;s highest persona award, &lt;a href=&quot;http://heraldry.ansteorra.org/honors/lions.php&quot;&gt;Lions of Ansteorra, Devenders of the Dream&lt;/a&gt;, and his lady wife Baroness Seraphina Maslowska was awarded with a &lt;a href=&quot;http://pelican.ansteorra.org/&quot;&gt;Pelican&lt;/a&gt;!  Congratulations to both of you!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2007-10-27:2</id>
    <published>2007-10-27T02:44:00Z</published>
    <updated>2008-03-20T04:48:17Z</updated>
    <category term="Programming"/>
    <category term="rails"/>
    <category term="ruby"/>
    <link href="http://blog.flame.org/2007/10/27/railsmode-not-env-production" rel="alternate" type="text/html"/>
    <title>RailsMode (not ENV['production'])</title>
<content type="html">
            &lt;h1&gt;RailsMode&lt;/h1&gt;

&lt;p&gt;I&#8217;ve been spreading things like this all around my code, where I wanted to do something differently in production vs. development mode.  Previously, I&#8217;d write something like this:&lt;/p&gt;

&lt;ol class=&quot;CodeRay&quot;&gt;&lt;li&gt;&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;co&quot;&gt;ENV&lt;/span&gt;[&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&#8217;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#8217;&lt;/span&gt;&lt;/span&gt;] == &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&#8216;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#8217;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;  &#8230; perform magic &#8230;&lt;/li&gt;&lt;li&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;While this is pretty simple, it just didn&#8217;t feel very DRY.  So, I decided to use this as a reason to learn about modules and mixins.&lt;/p&gt;

&lt;p&gt;I have a generic plug-in in &lt;code&gt;vendor/plugins&lt;/code&gt; that I put small bits of code like this.  You might as well, but if you don&#8217;t, you can drop this in a helper.&lt;/p&gt;

&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;6&lt;tt&gt;
&lt;/tt&gt;7&lt;tt&gt;
&lt;/tt&gt;8&lt;tt&gt;
&lt;/tt&gt;9&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;11&lt;tt&gt;
&lt;/tt&gt;12&lt;tt&gt;
&lt;/tt&gt;13&lt;tt&gt;
&lt;/tt&gt;14&lt;tt&gt;
&lt;/tt&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;tt&gt;
&lt;/tt&gt;16&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;cl&quot;&gt;RailsMode&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;railsmode&lt;/span&gt;(*list)&lt;tt&gt;
&lt;/tt&gt;    list.map! &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |item|&lt;tt&gt;
&lt;/tt&gt;      item.to_s&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; block_given?&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; list.include?(&lt;span class=&quot;co&quot;&gt;ENV&lt;/span&gt;[&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;        &lt;span class=&quot;r&quot;&gt;yield&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;else&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;      &lt;span class=&quot;r&quot;&gt;return&lt;/span&gt; list.include?(&lt;span class=&quot;co&quot;&gt;ENV&lt;/span&gt;[&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;RAILS_ENV&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;])&lt;tt&gt;
&lt;/tt&gt;    &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;I also put this line in my environment.rb file:&lt;/p&gt;

&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;include &lt;span class=&quot;co&quot;&gt;RailsMode&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;This mixes the module into the current class.  Doing this in &lt;code&gt;environment.rb&lt;/code&gt; makes it available everywhere in rails.  Putting that line in a specific file would also work, such as a controller, or a helper.&lt;/p&gt;

&lt;p&gt;With this, I can now write:&lt;/p&gt;

&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; railsmode(&lt;span class=&quot;sy&quot;&gt;:production&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  ... perform magic ...&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;I can also check for multiple modes at once:&lt;/p&gt;

&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;if&lt;/span&gt; railsmode(&lt;span class=&quot;sy&quot;&gt;:production&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:development&lt;/span&gt;)&lt;tt&gt;
&lt;/tt&gt;  ... perform magic ...&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;And of course, who needs an &lt;code&gt;if&lt;/code&gt; when I can pass in a block:&lt;/p&gt;

&lt;table class=&quot;CodeRay&quot;&gt;&lt;tr&gt;
  &lt;td title=&quot;click to toggle&quot; class=&quot;line_numbers&quot;&gt;&lt;pre&gt;1&lt;tt&gt;
&lt;/tt&gt;2&lt;tt&gt;
&lt;/tt&gt;3&lt;tt&gt;
&lt;/tt&gt;4&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
  &lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;tt&gt;
&lt;/tt&gt;railsmode(&lt;span class=&quot;sy&quot;&gt;:production&lt;/span&gt;) &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;  ... perform magic ...&lt;tt&gt;
&lt;/tt&gt;&lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;&lt;tt&gt;
&lt;/tt&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
          </content>  </entry>
  <entry xml:base="http://blog.flame.org/">
    <author>
      <name>michael</name>
    </author>
    <id>tag:blog.flame.org,2007-10-26:3</id>
    <published>2007-10-26T00:47:00Z</published>
    <updated>2008-02-11T17:25:10Z</updated>
    <link href="http://blog.flame.org/2007/10/26/well-i-ve-done-it" rel="alternate" type="text/html"/>
    <title>Well, I've done it...</title>
<content type="html">
            &lt;h1&gt;Michael, meet blog.  Blog, meet Michael&lt;/h1&gt;

&lt;p&gt;I&#8217;ve gone and joined the blogging world.  Sure, I could have done this &lt;em&gt;years&lt;/em&gt; ago, back when 5 year olds didn&#8217;t post to blogs every day&#8230;  but then I&#8217;d be a trend-setter.&lt;/p&gt;
          </content>  </entry>
</feed>
