<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>7fff</title>
	
	<link>http://7fff.com</link>
	<description />
	<lastBuildDate>Fri, 30 Dec 2011 21:03:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/7fff-ThinkMaxValue" /><feedburner:info uri="7fff-thinkmaxvalue" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Technology resolutions for the New Year</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/Tb8JBDH1CGU/</link>
		<comments>http://7fff.com/2011/12/30/technology-resolutions-for-the-new-year/#comments</comments>
		<pubDate>Fri, 30 Dec 2011 21:03:10 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=692</guid>
		<description><![CDATA[I know most people make resolutions to lose weight, to have a sunnier disposition, to not kill kittens, etc., but I think this year some technology resolutions are appropriate.
To wit . . . in no particular order. My goal is to accomplish 5 after the first one (which is mandatory!).

Do my part to ensure the [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>I know most people make resolutions to lose weight, to have a sunnier disposition, to not kill kittens, etc., but I think this year some technology resolutions are appropriate.</p>
<p>To wit . . . in no particular order. My goal is to accomplish 5 after the first one (which is mandatory!).</p>
<ul>
<li>Do my part to ensure the ongoing awesomeness of <a href="http://www.iorahealth.com/">Iora Health</a>&#8217;s technology!</li>
</ul>
<ol>
<li>Attend at least one European technology conference, and/or a medical technology conference.</li>
<li>Don&#8217;t do any non-Iora contracting or proprietary development for friends or former colleagues, no matter how interesting the project is; only open source, which should emerge from Iora. I&#8217;ll allow a couple of projects to be grandfathered-in to a small extent, but that&#8217;s it.</li>
<li>Freshen at least one legacy project with guidance from <a href="http://www.amazon.com/gp/product/0131177052/ref=as_li_ss_tl?ie=UTF8&#038;tag=ce1-20">Working Effectively with Legacy Code</a>.</li>
<li>Present at a Ruby conference.</li>
<li>Don&#8217;t let myself or others build in excess of the story.</li>
<li>Be more persuasive in opposition when I observe myself or others using APIs and/or techniques that are costly (in time or money) or inappropriate. I went down the rabbit hole a bit this year with Google Calendar integration, against my better judgement.</li>
<li>Tighten up my Linux/OS/X and vim dotfiles. I&#8217;ve never used a pre-fab dotfiles and have my own, but it&#8217;s time to look over what&#8217;s out there and integrate some new features. Bonus: Stop using the arrow keys in vim. Use standard vim movement bindings instead.</li>
<li>Sharpen up my Scheme. Be able to write the major combinators without consulting an authority.</li>
<li>Cover more &#8220;hard cases&#8221; for BDD. I allowed too many specs to be more shallow than I know is proper.</li>
<li>Think about how I can productively contribute to hacktivism and/or &#8220;digital humanities&#8221; &#8212; I have a lot of latent knowledge and experience in this area, and it&#8217;s time to revive it. Why should the kids have all the fun?</li>
</ol>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/Tb8JBDH1CGU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/12/30/technology-resolutions-for-the-new-year/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/12/30/technology-resolutions-for-the-new-year/</feedburner:origLink></item>
		<item>
		<title>ZOMG! Windows!</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/qCpBr_aaagg/</link>
		<comments>http://7fff.com/2011/12/17/zomg-windows/#comments</comments>
		<pubDate>Sun, 18 Dec 2011 02:30:56 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=690</guid>
		<description><![CDATA[I haven&#8217;t touched a Windows computer in many years. OK, I have a virtual machine with XP installed, but it&#8217;s a clean install with no cruft and I basically only use it to verify browser stuff.
My dad, though, has two Windows computers, a laptop and a desktop, both running XP from, oh, six years ago. [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>I haven&#8217;t touched a Windows computer in many years. OK, I have a virtual machine with XP installed, but it&#8217;s a clean install with no cruft and I basically only use it to verify browser stuff.</p>
<p>My dad, though, has two Windows computers, a laptop and a desktop, both running XP from, oh, six years ago. It is like the dark ages. In our house he needs to use wifi. That means that for his desktop, he needs to use a wireless USB adapter.</p>
<p>Among other things, you can&#8217;t just plug it in and expect it to work: You have to install with a CDROM. That&#8217;s WORNG #1.</p>
<p>Then you get this funky configuration screen just for that adapter, and it supplants your normal Windows settings. WORNG #2.</p>
<p>Then it belabors everything. The scan for SSID&#8217;s is amazingly slow. Then when you pick your network, it wants to show you every damn setting, and exposes the fact that it&#8217;s WPA, etc. A nightmare. WORNG #3.</p>
<p>Then it is so to get itself established after a reboot. WORNG #4.</p>
<p>So it&#8217;s just garbage. I guess I should count my blessings that I could but a device &#8220;Certified for Windows 7&#8243; and see that it works on XP.</p>
<p>Meanwhile . . . email. Outlook Express, ZOMG!!</p>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/qCpBr_aaagg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/12/17/zomg-windows/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/12/17/zomg-windows/</feedburner:origLink></item>
		<item>
		<title>ASUS AiGuru SV1T Skype VideoPhone – Worst consumer electronics product of 2011 (and 2010 and maybe 2009)</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/-lCUxCvpD_E/</link>
		<comments>http://7fff.com/2011/12/14/asus-aiguru-sv1t-skype-videophone-worst-consumer-electronics-product-of-2011-and-2010-and-maybe-2009/#comments</comments>
		<pubDate>Wed, 14 Dec 2011 14:04:50 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Reviews]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=675</guid>
		<description><![CDATA[

But it seemed like such a good idea at the time,
such a very very good idea at the time.

&#8211; The Darkness





I&#8217;ve used a lot of awful consumer electronics products, but the ASUS AiGuru SV1T wins the &#8220;Worst Consumer Electronics Product of 2011&#8243; Award. Oh, wait, you could buy one in 2010; maybe even in 2009. [...]]]></description>
			<content:encoded><![CDATA[<p></p><div style="float: right;">
<div width="200" style="float: left;">
But it seemed like such a good idea at the time,<br />
such a very very good idea at the time.</p>
<div style="float: right">
&#8211; The Darkness
</div>
</div>
</div>
<div style="clear: both;"></div>
<p><br/></p>
<p>I&#8217;ve used a lot of awful consumer electronics products, but the ASUS AiGuru SV1T wins the &#8220;Worst Consumer Electronics Product of 2011&#8243; Award. Oh, wait, you could buy one in 2010; maybe even in 2009. So it sweeps the award for three years running.</p>
<p>This is supposed to be an &#8220;appliance&#8221; Skype VideoPhone that makes it easy to make a call. We were told in the movie 2001 that this would be implemented in payphones by now. So payphones are obsolete, but we&#8217;re still waiting for a no-brainer video phone that your grandma can use.</p>
<p><img src="http://7fff.com/wp-content/uploads/2011/12/touch-aiguru-sv1t.jpg" alt="touch-aiguru-sv1t" title="touch-aiguru-sv1t" width="554" height="329" class="aligncenter size-full wp-image-683" /></p>
<p>We acquired this for office use, and found it lacking. I took it home and tried to dump it on Craig&#8217;s List &#8211; no takers. Then it languished in my home office, and just recently I decided to give it another assessment. It still sucks.</p>
<p>Before I begin, let me say that this is a prime example of a product that should be a one-button interface: Call my favorite number. Would that be asking too much?</p>
<p>But everything about this product is wrong.</p>
<p>When I tap the green &#8220;phone&#8221; icon, it does what? Suggests that I enter a phone number. Hello! I want to use Skype! Remember? Phone numbers are great, but secondary. Please let me just browse my Skype contacts. To be sure, I can tap the right arrow to get to my contacts, but then I have to select that user, then tap &#8220;Video call.&#8221; Tap tap tap. This is wrong.</p>
<p>Call quality: Terrible. The video is always grainy and the updates are slow. Additionally, I have this thing plugged in with an Ethernet cable to avoid the vagaries of wireless. It constantly (really: always) tells me that the network connection is lost and/or that the network connection has &#8220;changed&#8221; (no it hasn&#8217;t), and I lose my call or have to interact with the machine.</p>
<p>There are no options in the &#8220;Advanced&#8221; settings to probe connection quality, insist that the Ethernet connection be favored over wireless (or used alone).</p>
<p>Finally: The greatest sin: Some of these issues could be fixed with updated software. As of this writing (14-Dec-2011) I have the latest software, dated . . . well, I&#8217;d like to tell you, but to do that, the phone has to connect, and as of right now it&#8217;s &#8220;attempting&#8221; to connect. But not succeeding. Sheesh. Anyway, the date of last update was October, 2010. There is nothing I can find at ASUS Support with guidelines for troubleshooting, etc.</p>
<p>It is hard for me to imagine how ASUS screwed this up as badly as it did. All this thing is, is a repackaged laptop. The user interface gaffes I can forgive if the performance was great. But it isn&#8217;t.</p>
<p>Do not buy this product.</p>
<p>Meanwhile, someone should introduce an awesome Skype VideoPhone. I am sure you are saying: Use an iPad. But there are great use cases for a dedicated device, the chief one being that you want to <strong><em>use</em></strong> your iPad while on a call, or you want to <strong><em>share</em></strong> the call with others. Perhaps you&#8217;ll tell me just to buy a 2nd iPad. Yeah, I could do that, but where&#8217;s my &#8220;call my most frequently used number&#8221; button? And so on.</p>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/-lCUxCvpD_E" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/12/14/asus-aiguru-sv1t-skype-videophone-worst-consumer-electronics-product-of-2011-and-2010-and-maybe-2009/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/12/14/asus-aiguru-sv1t-skype-videophone-worst-consumer-electronics-product-of-2011-and-2010-and-maybe-2009/</feedburner:origLink></item>
		<item>
		<title>Correlate time zone abbreviations with time zone names</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/fCgmRg4dBd0/</link>
		<comments>http://7fff.com/2011/11/07/correlate-time-zone-abbreviations-with-time-zone-names/#comments</comments>
		<pubDate>Mon, 07 Nov 2011 20:09:28 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=672</guid>
		<description><![CDATA[There has got to be any easier way.


require 'rubygems'
require 'tzinfo'
require 'active_support'
require 'active_support/values/time_zone'

time_zone_names = [ 'Eastern Time (US &#38; Canada)', 'Central Time (US &#38; Canada)', 'Paris' ]
abbrev_to_zone = time_zone_names.inject({}) do &#124;map, name&#124;
  map[ActiveSupport::TimeZone.find_tzinfo(name).current_period.abbreviation.to_s] = name; map
end

p Time.now.zone
p abbrev_to_zone[Time.now.zone]
p abbrev_to_zone['CET']

]]></description>
			<content:encoded><![CDATA[<p></p><p>There has got to be any easier way.</p>
<pre name="code" class="ruby">

require 'rubygems'
require 'tzinfo'
require 'active_support'
require 'active_support/values/time_zone'

time_zone_names = [ 'Eastern Time (US &amp; Canada)', 'Central Time (US &amp; Canada)', 'Paris' ]
abbrev_to_zone = time_zone_names.inject({}) do |map, name|
  map[ActiveSupport::TimeZone.find_tzinfo(name).current_period.abbreviation.to_s] = name; map
end

p Time.now.zone
p abbrev_to_zone[Time.now.zone]
p abbrev_to_zone['CET']
</pre>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/fCgmRg4dBd0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/11/07/correlate-time-zone-abbreviations-with-time-zone-names/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/11/07/correlate-time-zone-abbreviations-with-time-zone-names/</feedburner:origLink></item>
		<item>
		<title>Rails 3.0 to 3.1 gotcha: Changed ActiveSupport::JSON.backend</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/wprtxJpPZnY/</link>
		<comments>http://7fff.com/2011/10/06/rails-3-0-to-3-1-gotcha-changed-activesupportjson-backend/#comments</comments>
		<pubDate>Thu, 06 Oct 2011 18:10:02 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=669</guid>
		<description><![CDATA[Ow.
In Rails 3.0, the default ActiveSupport::JSON backend is YAML.
In Rails 3.1, it&#8217;s YAJL.
They deserialize JSON times differently. In 3.0 . . .


ruby-1.9.2-p290 :001 &#62; ActiveSupport::JSON.decode( '{&#34;at&#34;: &#34;2011-10-13T12:00:00+00:00&#34;}' )
 =&#62; {&#34;at&#34;=&#62;2011-10-13 07:00:00 -0500} 

See the lack of quotation marks around what &#8220;at&#8221; points to? It&#8217;s not a String. YAML converts the value to an actual DateTime. [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Ow.</p>
<p>In Rails 3.0, the default ActiveSupport::JSON backend is YAML.</p>
<p>In Rails 3.1, it&#8217;s YAJL.</p>
<p>They deserialize JSON times differently. In 3.0 . . .</p>
<pre name="code" class="ruby">

ruby-1.9.2-p290 :001 &gt; ActiveSupport::JSON.decode( '{&quot;at&quot;: &quot;2011-10-13T12:00:00+00:00&quot;}' )
 =&gt; {&quot;at&quot;=&gt;2011-10-13 07:00:00 -0500} 
</pre>
<p>See the lack of quotation marks around what &#8220;at&#8221; points to? It&#8217;s not a String. YAML converts the value to an actual DateTime. (There a DATE_REGEX defined in ActiveSupport::JSON that does the matching.</p>
<p>But in 3.1:</p>
<pre name="code" class="ruby">

ruby-1.9.2-p290 :001 &gt; ActiveSupport::JSON.decode( '{&quot;at&quot;: &quot;2011-10-13T12:00:00+00:00&quot;}' )
 =&gt; {&quot;at&quot;=&gt;&quot;2011-10-13T12:00:00+00:00&quot;} 
</pre>
<p>We had the yajl-ruby gem installed for development, so were getting the the Strings. Then on our staging server, we were getting the already-parsed time value, and were getting errors.</p>
<p>This strikes me as a substantive change. I&#8217;m not sure I&#8217;d call it a bug, but it was unexpected.</p>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/wprtxJpPZnY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/10/06/rails-3-0-to-3-1-gotcha-changed-activesupportjson-backend/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/10/06/rails-3-0-to-3-1-gotcha-changed-activesupportjson-backend/</feedburner:origLink></item>
		<item>
		<title>RSpec and let</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/bIKlzjyQmX8/</link>
		<comments>http://7fff.com/2011/10/02/rspec-and-let/#comments</comments>
		<pubDate>Sun, 02 Oct 2011 21:43:48 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=647</guid>
		<description><![CDATA[So here&#8217;s a good one. We had a spec that looked something like this:


require 'time'

class SomeModel
  def self.all
    @all &#124;&#124;= []
  end
  def self.create!(time)
   all &#60;&#60; new
  end
end

describe &#34;surprise&#34; do
  subject { SomeModel }
  let!(:now) { Time.now }
  ['9 AM', '10 AM', '11 [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>So here&#8217;s a good one. We had a spec that looked something like this:</p>
<pre name="code" class="ruby">

require 'time'

class SomeModel
  def self.all
    @all ||= []
  end
  def self.create!(time)
   all &lt;&lt; new
  end
end

describe &quot;surprise&quot; do
  subject { SomeModel }
  let!(:now) { Time.now }
  ['9 AM', '10 AM', '11 AM'].each do |hour|
    let!(:time) { Time.mktime(now.year, now.month, now.day, Time.parse(hour).hour) }
    let!(:some_model) { SomeModel.create!(time) }
  end
  its(:all) { should have(3).items }
end
</pre>
<p>(In the original code, rather than check that the <code>.all</code> method returned a enumeration with 3 items, we were describing the behavior of a different object to see if it could do something when three collaborating objects had already been created via factories.)</p>
<p>The spec told us this:</p>
<pre name="code" class="ruby">

     Failure/Error: its(:all) { should have(3).items }
       expected 3 items, got 1
</pre>
<p>That&#8217;s funny. How come we don&#8217;t have three items? Let&#8217;s try a bit of puts debugging. We&#8217;ll dump the value of hour just inside the block passed to <code>#each</code>. And we get:</p>
<pre name="code" class="ruby">

hour: 9 AM
hour: 10 AM
hour: 11 AM
</pre>
<p>Well that&#8217;s expected. Gee, maybe our collaborator is buggy (and maybe we should be mocking it anyway, but let that pass). Let&#8217;s show the hour inside of what the let defines:</p>
<pre name="code" class="ruby">

  ['9 AM', '10 AM', '11 AM'].each do |hour|
    puts &quot;hour: #{hour}&quot;
    let!(:time) { puts &quot;hour inside time: #{hour}&quot;; Time.mktime(now.year, now.month, now.day, Time.parse(hour).hour) }
    let!(:some_model) { SomeModel.create!(time) }
  end
</pre>
<p>What do we get?</p>
<pre name="code" class="ruby">

hour: 9 AM
hour: 10 AM
hour: 11 AM
hour inside time: 11 AM
F
</pre>
<p>Interesting.</p>
<p>The problem is that we hadn&#8217;t internalized properly the documentation for the RSpec <code>let</code> (and <code>let!</code>) helper. The documentation says: &#8220;Use <code>let</code> to define a memoized helper method. The value will be cached across multiple calls in the same example but not across examples. Note that <code>let</code> is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked. You can use <code>let!</code> to force the method&#8217;s invocation before each example&#8221; (<a href="https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/let-and-let">https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/let-and-let</a>).</p>
<p>The key phrase is &#8220;define a . . . method.&#8221; So what our code is doing above is simply redefining the <code>#time</code> and <code>#some_model</code> methods over and over. Then, since the execution is deferred, we would get the <code>#time</code> method returning &#8216;11 AM&#8217; and the <code>#some_model</code> method instantiating only one <code>SomeModel</code>.</p>
<p>Before we touch on one way to fix this, let&#8217;s look at some of the implications of what let does. For one thing, let&#8217;s remember that the method will only be executed once (since it&#8217;s memoized). For example:</p>
<pre name="code" class="ruby">

describe &quot;no surprise&quot; do
  let(:x) { puts &quot;x defined to return 'a'&quot;; &quot;a&quot; }
  let(:x) { puts &quot;x defined to return 'b'&quot;; &quot;b&quot; }
  it &quot;should have the expected setup value&quot; do
    x.should == &quot;b&quot;
    x.should == &quot;b&quot;
  end
end
</pre>
<p>When this spec is run, we are going to see &#8220;x defined to return &#8216;b&#8217;&#8221; once. We don&#8217;t see &#8220;x defined to return &#8216;a&#8217;&#8221; because the execution is deferred, and the second <code>let(:x)</code> wipes out the first. Since the result is memoized, we only see &#8220;x defined to return &#8216;b&#8217;&#8221; get printed out once:</p>
<pre name="code" class="ruby">

$ rspec spec/example2_spec.rb
x defined to return 'b'
.

Finished in 0.00034 seconds
1 example, 0 failures
</pre>
<p>Additionally, since <code>let</code> defines methods, and since execution of the methods is deferred until after they have all been defined, you can have earlier methods depend on later methods:</p>
<pre name="code" class="ruby">

describe &quot;no surprise&quot; do
  let(:x) { y + 1 }
  let(:y) { 1 }
  it &quot;should not matter in what order these methods are defined&quot; do
    x.should == 2
  end
end
</pre>
<p>So why the confusion? I think there are a couple of reasons. The main thing is that word &#8220;let.&#8221; It sounds as though we are assigning a value to a key. It is not uncommon to see sequences of lets like so:</p>
<pre name="code" class="ruby">

let!(:last) { Factory(:user, last_name: 'zzz', first_name: 'aaa') }
let!(:center) { Factory(:user, last_name: 'aaa', first_name: 'abc') }
let!(:first) { Factory(:user, last_name: 'aaa', first_name: 'aabc') }
</pre>
<p>So let looks something like an assignment. Why is it called let? I don&#8217;t know. Maybe it&#8217;s supposed to feel a bit like Common Lisp where let is a stand-in for lambda. Maybe the thought was that if &#8220;assigns&#8221; or &#8220;set&#8221; was used, it would be ambiguous given how these terms are used elsewhere in Ruby and Rails.</p>
<p>If you ask me, a name such as &#8220;dm&#8221; (for define_method) would have been more appropriate. Then noobs (like me &#8212; I&#8217;ve always used <code>Test::Unit</code>) would wonder what it stands for, look it up in the documentation, and get some insight. &#8220;let&#8221; is just too abstract.</p>
<p>The original problem? If we must use <code>let</code>, perhaps define an <code>Array</code> with the <code>let</code>:</p>
<pre name="code" class="ruby">

describe &quot;surprise&quot; do
  subject { SomeModel }
  let!(:now) { Time.now }
  let!(:some_models) do
    ['9 AM', '10 AM', '11 AM'].map do |hour|
      SomeModel.create!(Time.mktime(now.year, now.month, now.day, Time.parse(hour).hour))
    end
  end
  its(:all) { should have(3).items }
end
</pre>
<p>One last thing. Discussions in books are a bit misleading, or don&#8217;t tell the truth soon enough. For instance, in Noel Rappin&#8217;s book <em>Rails Test Prescriptions</em> (which is not only excellent but actually funny), he introduces let by saying: &#8220;Using let(), you can make a variable available within the current example group[.] . . . The symbol can then be called as if it was a local variable&#8221; (p. 193). Well, this is true, but by saying that &#8220;you can make a variable available,&#8221; it sounds like variable assignment &#8212; which it just isn&#8217;t. Among other things, just saying that makes one thing that the order of the let&#8217;s is important in terms of the values that will eventually be assigned. Thankfully he says just a wee bit later, &#8220;a let() call is syntactic sugar for defining a method and memoizing the the result.&#8221; Yes. But this is one of those cases where easing into the truth can be harmful.</p>
<p>The <em>RSpec Book</em> (Chelimsky, et al.) does a similar thing, rhetorically: &#8220;When the code in a before block is only creating instance variables and assigning them values, which is most of the time, we can use RSpec&#8217;s let() method instead&#8221; (p. 55). Again, this is a bit slippery. It&#8217;s not like creating instance variables at all. It&#8217;s like calling a method. Which, of course, Chelimsky immediately explains.</p>
<p>The upshot:</p>
<ol>
<li>Don&#8217;t put your calls to <code>let</code> inside of loops! (If you must, have the calls to <code>let</code> define different symbols.)</li>
<li>Don&#8217;t assume that the methods will run in the order of the <code>let</code>s.</li>
</ol>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/bIKlzjyQmX8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/10/02/rspec-and-let/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/10/02/rspec-and-let/</feedburner:origLink></item>
		<item>
		<title>Comcast drives me nuts</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/_fELg1ebiSI/</link>
		<comments>http://7fff.com/2011/09/28/comcast-drives-me-nuts/#comments</comments>
		<pubDate>Wed, 28 Sep 2011 12:49:08 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Annoyance]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=643</guid>
		<description><![CDATA[At the beginning of the summer, I had numerous reboots of my cable modem. Typically this would happen when I was making heavy use of the network. It could happen 5 times an hour. Of course I called Comcast. They had a lot of suggestions: Let&#8217;s re-flash your modem; upgrade the software on your router; [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>At the beginning of the summer, I had numerous reboots of my cable modem. Typically this would happen when I was making heavy use of the network. It could happen 5 times an hour. Of course I called Comcast. They had a lot of suggestions: Let&#8217;s re-flash your modem; upgrade the software on your router; try a different router. I suggested: Maybe it&#8217;s the link from my house to upstream? Comcast: No, we&#8217;ve checked that. They were responsive (good response from one of their Twitter helpers) . . . but couldn&#8217;t make it work.</p>
<p>Then about 3 weeks ago (unbidden from me), Comcast told me that I wouldn&#8217;t have service for a couple of days. They did something.</p>
<p>Now? The network runs like a charm. I had switched routers somewhere back in there, so, cautiously, I put my fastest router back on, and it&#8217;s worked like a charm. The reboot after Sept. 20 was from a manual reboot.</p>
<p><img src="http://7fff.com/wp-content/uploads/2011/09/Untitled.png" alt="Untitled" title="Untitled" width="600" /></p>
<p>Comcast! What is wrong with you? Why is it always like this?</p>
<p>You&#8217;re going to get disintermediated, Comcast!</p>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/_fELg1ebiSI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/09/28/comcast-drives-me-nuts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/09/28/comcast-drives-me-nuts/</feedburner:origLink></item>
		<item>
		<title>US Airways Mastercard error message</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/84uqfqB2XHs/</link>
		<comments>http://7fff.com/2011/09/26/us-airways-mastercard-error-message/#comments</comments>
		<pubDate>Tue, 27 Sep 2011 02:11:00 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=638</guid>
		<description><![CDATA[One of the worse credit card sites on the Internet. How &#8217;bout this? I would guess that &#8220;1-1027&#8243; is an error number you report to an operator, rather than a suggested value range.

]]></description>
			<content:encoded><![CDATA[<p></p><p>One of the worse credit card sites on the Internet. How &#8217;bout this? I would guess that &#8220;1-1027&#8243; is an error number you report to an operator, rather than a suggested value range.</p>
<p><img src="http://7fff.com/wp-content/uploads/2011/09/dopes.png" alt="dopes" title="dopes" width="600" class="aligncenter" /></p>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/84uqfqB2XHs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/09/26/us-airways-mastercard-error-message/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/09/26/us-airways-mastercard-error-message/</feedburner:origLink></item>
		<item>
		<title>Best way to test Ruby class methods?</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/RK87D4iIj3I/</link>
		<comments>http://7fff.com/2011/09/07/best-way-to-test-ruby-class-methods/#comments</comments>
		<pubDate>Wed, 07 Sep 2011 14:59:50 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=628</guid>
		<description><![CDATA[Let&#8217;s say that we have a Ruby object with some class methods. In my case, we want to be able to inject a driver at the class level that all instances will use:


class Mod::Thingy
  def self.driver
  end
  def self.driver=(d)
  end
end

And let&#8217;s say that .driver has a default value. So the class [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>Let&#8217;s say that we have a Ruby object with some class methods. In my case, we want to be able to inject a driver at the class level that all instances will use:</p>
<pre name="code" class="ruby">

class Mod::Thingy
  def self.driver
  end
  def self.driver=(d)
  end
end
</pre>
<p>And let&#8217;s say that .driver has a default value. So the class looks more like this:</p>
<pre name="code" class="ruby">

class Mod::Thingy
  def self.driver
    @driver || DefaultDriver
  end
  def self.driver=(d)
    @driver = d
  end
end
</pre>
<p>Now here&#8217;s the dealio. Let&#8217;s say that we test for the default value first. So we check <code>Mod::Thingy.driver == DefaultDriver</code>. Then we test the setter. We do <code>Mod::Thingy.driver = OtherDriver</code>, and then check the return value of <code>Mod::Thingy.driver</code>. Fair enough.</p>
<p>But what if we ran the examples in reverse? Now we do <code>Mod::Thingy.driver = OtherDriver</code> first, check it; but now in our second test, we can&#8217;t test the default value, because the state of <code>Mod::Thingy</code> has been changed.</p>
<p>Strategies:</p>
<ol>
<li>
We might use RSpec&#8217;s <code>around</code> feature to save the state of <code>Mod::Thingy.driver</code> and restore it afterwards. What I don&#8217;t like about this is that now the around method has to know all about all of the setters at the class level. Meanwhile, it is possible to imagine objects that are difficult to set back to their default state: Perhaps the class methods have order dependencies themselves. This would make the test need to know a lot about the inner state of the object.
</li>
<li>
We might provide a <code>.reset!</code> method on the object. But this seems a bit nutty: Add a method just to make testing easier?
</li>
<li>
What about this in an RSpec <code>before(:each)</code> block:</p>
<pre name="code" class="ruby">

    Mod.send(:remove_const, 'Thingy')
    load &quot;#{PROJECT_ROOT}/lib/mod/thingy.rb&quot;
</pre>
<p>Now we&#8217;re getting a nice clean class. To me, (3) is more parallel to the testing convention of new&#8217;ing up an object instance for the test.
</li>
</ol>
<p>Thoughts?</p>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/RK87D4iIj3I" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/09/07/best-way-to-test-ruby-class-methods/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/09/07/best-way-to-test-ruby-class-methods/</feedburner:origLink></item>
		<item>
		<title>Spying on Ruby’s Net::HTTP</title>
		<link>http://feedproxy.google.com/~r/7fff-ThinkMaxValue/~3/9m8oHePEouk/</link>
		<comments>http://7fff.com/2011/08/04/spying-on-rubys-nethttp/#comments</comments>
		<pubDate>Thu, 04 Aug 2011 15:03:58 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://7fff.com/?p=623</guid>
		<description><![CDATA[I&#8217;ve heard two people complain that they can&#8217;t see OAuth HTTP behavior and get unexpected 401s. If you want to see what&#8217;s going on at the transport level, here&#8217;s a monkeypatch (depends on ActiveSupport for the alias_method_chain) just for you:


class Net::HTTP

  def initialize_with_debug(*args, &#38;block)
    initialize_without_debug(*args, &#38;block)
    set_debug_output $stderr
 [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>I&#8217;ve heard two people complain that they can&#8217;t see OAuth HTTP behavior and get unexpected 401s. If you want to see what&#8217;s going on at the transport level, here&#8217;s a monkeypatch (depends on ActiveSupport for the alias_method_chain) just for you:</p>
<pre name="code" class="ruby">

class Net::HTTP

  def initialize_with_debug(*args, &amp;block)
    initialize_without_debug(*args, &amp;block)
    set_debug_output $stderr
  end

  alias_method_chain :initialize, :debug
end
</pre>
<img src="http://feeds.feedburner.com/~r/7fff-ThinkMaxValue/~4/9m8oHePEouk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://7fff.com/2011/08/04/spying-on-rubys-nethttp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://7fff.com/2011/08/04/spying-on-rubys-nethttp/</feedburner:origLink></item>
	</channel>
</rss>

