DevFi podcast #1!

Peter Jones (good friend, awesome developer) and I started a podcast, DevFi, and we released our first episode just moments ago. We do a little high level talk about buying vs. building vs. forking, and then jump into an interview with Dan Berger, the guy who forked the Ruby interpreter to create the Sapphire programming language.

The podcast isn’t Ruby-centric (like this blog), but it should still be worthwhile. Hope you enjoy it. (We’re also taking comments and suggestions if you don’t enjoy it enough.)

Comments

Will Rubinius have fewer dark corners?

Section 3.4.2 of The Ruby Programming Language talks about defining (or overriding) the hash and eql? methods of a class in order to control how objects are treated as keys in a hash… except for strings. Strings are handled specially because they’re “mutable but commonly used as hash keys.”

So when someone asked today on comp.lang.ruby about case-insensitive hash access, the answer, sadly, could not be as simple (and possibly dangerous) as “Just override hash and eql? in String.” Or could it?

Well, I whipped up something quickly:

class String
  alias old_hash hash
  def hash
    self.downcase.old_hash
  end

  def eql?(other)
    self.hash == other.hash
  end
end

require test/unit

class TestCaseInsensitiveHashAccess < Test::Unit::TestCase
  def test_case_insensitivity
    h = {’a => 1, B => buzz‘}
    assert_equal(1, h[’A‘])
    assert_equal(’buzz‘, h[’b‘])
  end
end

I ran it a few times. Ruby failed. Ruby 1.9 failed. JRuby failed. But Rubinius passed.

And why shouldn’t it pass? For every other class, this sort of monkeying with hash and eql? will get you what you want: hash key access mayhem. Why not String?

Well, there might be something sinister hiding in the “mutable but commonly used as hash keys” description. Something subtle. Maybe it’s not subtle and I just haven’t been thinking very clearly. I don’t know.

It looks to me, in the short amount of time I’ve been thinking about this, that Rubinius is more consistently Ruby-ish than Ruby is (at least in this case). Are there more ways in which that’s true?

Comments

Evil: knowing when a method got passed the default value

Let’s say you’re crazy. Now, as a crazy person, you might have a method like this:

def greet_world(salutation=”Hello“)
  #{salutation} World!
end

You’re not so crazy that you can’t write working Ruby code. Anyway, it’s totally useful:

greet_world          # => “Hello World!”
greet_world(’Hi‘)    # => “Hi World!”
greet_world(’Yo‘)    # => “Yo World!”
greet_world(’Hola‘)  # => “Hola World!”

But then you see someone do this:

greet_world(’Hello‘)  # => “Hello World!”

And it drives you even crazier. Being totally crazy, you feel a compulsion to make fun of people who pass the default value for an argument of a method. But how in the world can you tell?

Well, it turns out (and you probably know this already) that the argument list of a method is a proper venue for executing code. You know this because you’ve seen this a thousand times: def something(time=Time.now). That Time.now happens when you call the method without that optional argument. It doesn’t happen when Ruby first parses the method and adds it to the method table for the class. That wouldn’t be too helpful.

So… you can execute arbitrary code in the method argument list. But what kind? What’s the scope? Well, anything you can do in a method body is fair game (and anything you can’t is not). You can’t define a class, for instance. And the scope? The method body itself.

You’re crazy, remember? But not so crazy that you don’t remember that every statement has a return value. Like assignment. Assignment returns a value. And you’ve got a place in the argument list that’s scoped with the method body. And you’ve got a deep desire to mock people who pass default values.

BOOM!

def greet_world(salutation=(salutation_not_given=’Hello‘))
  if salutation == Hello and not salutation_not_given
    YOU HAVE NO IMAGINATION!
  else
    #{salutation} World!
  end
end

greet_world(’Hello‘)  # => “YOU HAVE NO IMAGINATION!”
greet_world           # => “Hello World!”
greet_world(’Hi‘)     # => “Hi World!”

salutation_not_given is nil if you’ve given a value. It’s one of those things.

ANYWAY. Use that at your own peril.

Comments (4)

MountainWest RubyConf videos (including my lightning talk on GuessMethod)

Confreaks have started putting up more video from MountainWest RubyConf, and today saw the addition of the lightning talks from day one. I’d like to draw particular attention to the second entry of this list of who talked when about what:

  1. Binary Lottery in Shoes - Mike Moore - 0:10
  2. GuessMethod - Chris Shea (me!) - 4:35
  3. ActiveScaffold - Ed Moss - 8:51
  4. Migration Concordance - Josh Susser - 12:39
  5. Framework Components - Jeremy McAnally - 18:05
  6. Systems Building Systems with Puppet - Andrew Schafer - 20:43
  7. Multi-image Uploading with SWFUpload and Rails - David South - 27:14
  8. RubyAmp: Ruby Dev in TextMate Amplified - Tim Harper - 31:55
  9. Build a GUI App in 5 Minutes - David Koontz - 37:46

Lots of interesting stuff in there. And I encourage everyone to look at other videos from the conference. It was great.

Comments

GuessMethod 0.2.1

I gave a lightning talk about GuessMethod at MountainWest RubyConf, and then I looked at the code again. I figured it could use a little cleaning, so I took my steel wool to it.

I fixed up some of the documentation and the website, made the specs run again (they always passed, they just stopped running at some point… rspec change?), and rewrote the code that finds close matches of missing methods and constant names. And that last change (based on a naïve benchmark) doubled its speed (of course, iterating over an array once instead of twice will sort of do that).

So, if you need aggressive spell-checking in your irb or script/console sessions check it out:

001:0> Strin.tos
attention: replacing non-existant constant Strin with String for Object
attention: sending to_s instead of tos to String:Class
“String”

Or, if you sometimes can’t be bothered to correctly remember rake task names (and rake -T takes too long), use grake!

mvb:~/hot-rails-app cms$ grake db:migrat
(in /Users/cms/hot-rails-app)
attention: invoking task db:migrate instead of db:migrat

It might show up in Giles Bowkett’s utility_belt at some point, in case you’re into that and you can’t be bothered with this:

sudo gem install guessmethod

Comments

Salt Lake City

I’m in Salt Lake City for MountainWest RubyConf. Say hello. I’ll be wearing an Orioles cap and two hooded sweatshirts the whole time.

Comments (2)

Speeding up gem on OS X Leopard

Update: gem has changed for the better, and this is no longer necessary. Go ahead and gem update --system.

After upgrading to Leopard from Tiger, I noticed that certain network operations for some processes suddenly took forever. Most noticeably, when gem would update metadata. And I’m not the only one.

It’s not so bad anymore, and all I had to do was grab something from the standard library.

In gem (mine’s at /usr/local/bin/gem), add this line before any other code:

require 'resolv-replace'

Done. Seriously. gem will work much faster. Not as fast as you remember from Tiger, but getting there.

Discussions I’ve had suggest that Leopard isn’t caching DNS requests from certain processes, and letting Ruby handle that itself gets around this.

Update: Here’s the current state of my /usr/local/bin/gem:

#!/usr/local/bin/ruby
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++

require 'resolv-replace'

require 'rubygems'
require 'rubygems/gem_runner'

required_version = Gem::Requirement.new ">= 1.8.2"

unless required_version.satisfied_by? Gem::Version.new(RUBY_VERSION) then
  abort "Expected Ruby Version #{required_version}, was #{RUBY_VERSION}"
end

# We need to preserve the original ARGV to use for passing gem options
# to source gems.  If there is a -- in the line, strip all options after
# it...its for the source building process.
args = !ARGV.include?("--") ? ARGV.clone : ARGV[0...ARGV.index("--")]

Gem::GemRunner.new.run args

Comments (8)

You might be able to stop trying to call methods on nil

Correct me if I’m mistaken. Please.

The people behind andand, try, do_or_do_not, NilClass#method_missing, SafeNil, maybe, etc. seem to be trying to beautifully solve the problem of calling methods on objects that might be nil and not having Ruby complain about it. They might be chaining methods a lot or, when they check if an object is nil, they use the ternary conditional and (rightly) think it’s ugly.

As for these ternary conditional users, you can do this: @person.name if @person instead of @person ? @person.name : nil. It does what you need, looks great, and is highly readable.

Of course, that doesn’t help you if you’re chaining methods.

But (and this is what’s been on my mind), I suspect that all of these solutions value terseness over readability and flexibility. It’s a trade off I’m willing to make sometimes, but I think it’s unnecessary here.

For example, Reg Braithwaite’s example:

@phone = Location.find(:first, ...elided... ).andand.phone

We’re trying to get the phone number of a location, but maybe we won’t find the location. Seems normal on the surface. But these are the questions that run through my mind looking at this:

  1. All you need is the phone number?
  2. You’re sure you don’t need anything else from the Location object? (address, url, hours, etc.)
  3. It doesn’t mean anything if the location isn’t found?

I find it hard to believe that the answer to all these questions is “Yes”. I know this is example code, but it must be inspired from something, right?

I’d bet that instead of @phone = Location.find(:first, ...elided... ).andand.phone, later on you’d really wish you had this:

@location = Location.find(:first, ...elided... )

Maybe you’d discover your view (Reg’s example is ActiveResource-related, I’m running with that) needs more than just the phone number (who are you calling?). Or maybe you’d discover that if the location isn’t found, that that means something. Maybe you need to alert the user (unless @location ...).

It could be that I’m more hung up on this idea of meaning. The example for Ben’s maybe is:

if(customer && customer.order && customer.order.id==newest_customer_id)

I look at that and think: if there’s no customer, that means one thing; if there’s no order for the customer, that means something else; if the customer’s order’s id isn’t newest_customer_id, that means something else entirely; and if all these things are different, maybe they should be handled separately.

If you think about it that way, there’s no need to fiddle with nil or add methods to Object.

(I completely understand that this might just be an aesthetic difference, or that sometimes this apparent loss of flexibility is desirable, or that I might be wrong. Please narrow it down for me in the comments. Thanks.)

Comments (14)

A better try — chaining methods and nil

Of #try, #andand, and #do_or_do_not, the one I like best (and I don’t even like any of them that much, but that’s my next post) is #try.

What they are all basically for is chaining method calls when one of methods might return nil, resulting in a NoMethodError. Here’s Chris Wanstrath’s implementation:

class Object
  ##
  #   @person ? @person.name : nil
  # vs
  #   @person.try(:name)
  def try(method)
    send method if respond_to? method
  end
end

It’s almost right!

First of all, I’d argue that if #try is trying to be better than something, it had better be better than @person.name if @person. That looks and reads so nicely I can scarcely see why it needs replacing. @person.name if @person is so obviously better than @person ? @person.name : nil that I hope people are using that before they start thinking about monkey patching Object (which I hear is destroying Ruby). Anyway, I’d fix the documentation first.

But I think the biggest problem with #try is that it’s not doing what it says it’s doing. It’s not a replacement for @person ? @person.name : nil. That’s certainly not what the method is doing. Here’s what I’d do:

class Object
  ##
  #   @person.name if @person
  # vs
  #   @person.try(:name)
  def try(method)
    self.send(method) if self
  end
end

See? Much clearer; it does what it says. Using respond_to? means that the original #try is about something else; it’s about whether or not an object responds to a message. This #try is about sending a message to an object unless it’s nil or false, which is closer to what the original use case was… right?

But then it occurred to me that the issue is nil, not nil or false. The problem isn’t getting false in the middle of method chains, it’s nil. So maybe what #try should really be is this:

class Object
  ##
  #   @person.name unless @person.nil?
  # vs
  #   @person.try(:name)
  def try(method)
    self.send(method) unless self.nil?
  end
end

I might even use that.

Comments (7)

Boy, what ISN’T destroying Ruby these days?

What else?

(Note: Charles Oliver Nutter is helping when he tells us that Thread#raise, Thread#kill, and timeout.rb are broken.)

Comments (7)

« Previous entries