This post originated from an RSS feed registered with Ruby Buzz
by Jeremy Voorhis.
Original Post: Specifying Duck Types in Ruby
Feed Title: JVoorhis
Feed URL: http://feeds.feedburner.com/jvoorhis
Feed Description: JVoorhis is a Rubyist in northeast Ohio. He rambles about Ruby on Rails, development practices, other frameworks such as Django, and on other days he is just full of snark.
While prototyping a RESTful web service, I wrote the following code to allow Rails to parse a request’s parameters from a POST request containing JSON.
Theoretically, both of these should accomplish the same thing: assign an object that responds to the message call with an arity of 1 to Mime::JSON entry of the param_parsers hash. They lead to different results, however, because a case statement within the Rails HTTP request parsing code watches for an instance of Proc, instead of an object which responds to call.
As a proof of concept, I whipped up this DuckType class:
class DuckType
def initialize(*args)
@args = args
end
def ===(other)
@args.all? { |arg| other.respond_to?(arg) }
end
end
Now, we can replace this statement from cgi_methods.rb
case strategy = ActionController::Base.param_parsers[mime_type]
when Proc
strategy.call(raw_post_data)
# snip...
end
with the following:
Callable = DuckType.new :call
case strategy = ActionController::Base.param_parsers[mime_type]
when Callable
strategy.call(raw_post_data)
# snip...
end
As you can see, an instance of DuckType has a case comparison operator which asserts that all specified messages are supported, allowing you to focus on an object’s capabilities rather than its class.
My DuckType implementation in its current form is rather crude, but if enough people found this technique useful, I would consider properly packaging and extending it for general use.