c4am95 c4am95 - 6 months ago 10
Ruby Question

How to detect when a Ruby object is the right-hand side of a comparison?

I want to create an object in Ruby that can lazily execute some code when it is accessed, e.g. when it is inspected or compared. It's easy enough to intercept messages with

method_missing
(which would handle
object > 5
), but this doesn't cover all attempts to access the object.

To illustrate:

class Proxy
def initialize(obj)
@object = obj
end

def do_something
self
end

def do_something_else
self
end

def method_missing(msg, *args)
puts "I'm here #{msg}"
# Do something here
@object.send(msg, *args)
end
end


Usage:

proxy = Proxy.new(5)
proxy.do_something.do_something_else

proxy > 2
# I'm here >
# true

2 > proxy
# ArgumentError: comparison of Fixnum with Proxy failed


I'm wondering if there's an implementation of
Proxy
such that
2
actually sees
5
instead of the object.

Answer

No, you can't "detect when a Ruby object is the right-hand side of a comparison." I'm going to infer from this, hopefully correctly, that the problem you're actually trying to solve is how to use a Ruby object in the right-hand side of a comparison with an object of a different type.

The answer is to define a coerce method. This method will be called automatically when the object is on the right-hand side of a comparison operator and an "incompatible" object is on the left-hand side. The LHS object will be supplied as an argument, and it's expected to return an array with two elements: "New" LHS and RHS values that are compatible. Using your Proxy example:

class Proxy
  def initialize(obj)
    @object = obj
  end

  def coerce(other)
    if other.is_a?(Fixnum)
      return [ other, @object.to_i ]
    end
    super
  end
end

proxy = Proxy.new(5)

2 > proxy
# => false

Of course, your coerce method will likely be more complex if you want your Proxy class to be able to "wrap" values other than Fixnums. For example, what will you do if @object is a String and other is a Float?

And, of course, this only handles when the object is the RHS. To make it work as the LHS as well you'll want to use the Comparable mixin.

Comments