The problem
You need to ensure inputs are of a core type with context-specific extra semantics.
The approach
Define new implicit conversion protocols mimicking Ruby’s native protocols such as #to_path.
Explanation
Ruby defines a number of protocols for converting objects into core types such as String, Array, and Integer. But there may come a time when the core protocols don’t capture the conversion semantics your apps or libraries need.
Consider a 2D drawing library. Points on the canvas are identified by X/Y pairs. For simplicity, these pairs are simply two-element arrays of integers.
Ruby defines #to_a and #to_ary for converting to =Array=s. But that doesn’t really capture intent of converting to an X/Y pair. Just like the #to_path conversion used by File.open, even though we are converting to a core type we’d like to add a little more meaning to the conversion call. We’d also like to make it possible for an object to have a coordinate conversion even if otherwise it doesn’t really make sense for it to have a general Array conversion.
In order to capture this input requirement, we define the #to_coords conversion protocol. Here’s a method which uses the protocol:
# origin and ending should both be [x,y] pairs, or should # define #to_coords to convert to an [x,y] pair def draw_line(start, endpoint) start = start.to_coords if start.respond_to?(:to_coords) start = start.to_ary # ... end
Later, we decide to encapsulate coordinate points in their own Point class, enabling us to attach extra information like the name of the point. We define a #to_coords method on this class:
class Point attr_reader :x, :y, :name def initialize(x, y, name=nil) @x, @y, @name = x, y, name end def to_coords [x,y] end end
We can now use either raw X/Y pairs or Point objects interchangeably:
start = Point.new(23, 37)
endpoint = [45,89]
draw_line(start, endpoint)
But the #to_coords protocol isn’t limited to classes defined in our own library. Client code which defines classes with coordinates can also define #to_coords conversions. By documenting the protocol, we open up our methods to interoperate with client objects which we had no inkling of at the time of writing.





