c4am95
c4am95

Reputation: 87

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.

Upvotes: 3

Views: 89

Answers (1)

Jordan Running
Jordan Running

Reputation: 106037

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.

Upvotes: 1

Related Questions