[RAD stands for Ruby Ape Diaries, of which this is part IV.] That glob of letters in the title stands for “There’s More Than One Way To Do It”, and it comes from Perl culture. It is a distinguishing feature of Perl that if there’s something you need to do, not only does Perl have a way to do it, it has lots of different ways. Perl’s legion of evangelists, led by Larry Wall, argue that this is an advantage. Larry has a compelling argument by parallel with linguistics; natural languages usually have many ways of saying anything that’s worth saying. Not everyone agrees that this is a virtue; in particular, Pythonistas have historically grumbled. Ruby seems to fall into the Perl camp. And now, dear readers, I must confess to a bit of duplicity. When I wrote RAD III: Quack Like a URI, I knew that my example was perhaps not the purest possible example of duck typing, but my hidden agenda, namely gathering material for a TMTOWTDI essay, worked out well.
Ruby on Ruby · First off the mark was Sam Ruby, with Quack Squared, who argues convincingly for:
uri = AtomURI.check alleged_uri
return report_error(uri) unless uri.methods.include? :path
request = Net::HTTP::Get.new(uri.path)
But check his comments, where we find six other suggested approaches. The last is kind of startling:
uri = URI.new_with_app_check(alleged_uri) { |e| return report_error(e) }
I can see where they’re going, but something about it makes me nervous.
Burke on Ruby · Over at the ronin blog (cool URI BTW), Kerry Burke offers another elegant suggestion: return two values:
def some_method
return [true, "actual return value here"]
end
ok, value = some_method
return "this didn't work" unless ok
puts value
Morel on Ruby · Xavier Morel, who doesn’t have a blog, wrote:
I just read your “Quack like a URI” article, and I had a few things to tell you about it:
First of all, you shouldn’t use the class attribute to check for an object’s type, Ruby has the #kind_of? method which does the job much better as it also checks the whole inheritance tree. Let’s imagine that someone subclasses “String” and that you check for the Stringiness of the object, checking on #class won’t validate the user-generated class while #kind_of? will. Plus kind_of? being a predicate, your intent is much clearer:
uri.class == String
versus
uri.kind_of? String
Granted, in this case since you’re the one generating the data it doesn’t matter much, because you’re not subclassing strings.
Second, this is not duck-typing, duck-typing is testing what an object is able of, you’re checking for an object’s type. Using duck-typing would mean using #respond_to? (or just trying to send the message, and catch the exception), which checks whether or not an object responds to a message. In this case, you could check whether or not your response has a path, for example
if uri.respond_to? :path request = Net::HTTP::Get.new(uri.path) else report_error uri end
But a more Rubyful way might be to use a predicate: check implies that you’re just checking (for what?), while you’re performing many other operations besides checking for uri validity (generating a URI object, or creating an error message). Your code is therefore quite misleading/surprising, it lacks obviousness.
For example you could do something along the lines of:
if AtomURI::is_valid? alleged_uri # do stuff else # error end
Or even use blocks
AtomURI.if_valid alleged_uri do |uri| # do whatever you’re supposed to do with your URI object end
I wrote back disagreeing with Xavier’s claim that there was anything
incorrect about my class test; I wrote the atomURI.check
method
and I know that what’s coming back is either a URI or a String, and I
absolutely don’t want it rooting around the whole inheritance tree.
And Xavier, I said, TMTOWTDI.
Xavier wrote back:
And to quote Python, “There should be one—and preferably only one—obvious way to do it ... Although that way may not be obvious at first unless you’re Dutch.”