Skip to content

Instantly share code, notes, and snippets.

@xunker
Last active March 14, 2024 22:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xunker/6c92e3fff3d81a8245caf2ee4994f884 to your computer and use it in GitHub Desktop.
Save xunker/6c92e3fff3d81a8245caf2ee4994f884 to your computer and use it in GitHub Desktop.
Ruby IntegerWithIndifferentComparison - An experiment to show how to make an Integer-like class in Ruby without subclassing the Integer class directly
# IntegerWithIndifferentComparison
# --------------------------------
# https://gist.github.com/xunker/6c92e3fff3d81a8245caf2ee4994f884
#
# An experiment to show how to make an Integer-like class in Ruby without
# subclassing the Integer class directly.
#
# Instances of this class will behave like an Integer, but will allow direct
# equality comparison with Strings and other objects without explicit casting.
#
# Examples:
# > i = IntegerWithIndifferentComparison.new(2)
# > [2, '2', 2.0, "2.0", Rational(4,2)].all?{|other| i == other }
# > # => true
# >
# > i == '3'
# > # => false
#
# Nitty-Gritty
# ============
#
# You're not supposed to subclass Integer directly, it says so in the docs:
# https://ruby-doc.org/core-2.7.1/Numeric.html:
#
# > There can only ever be one instance of the integer 1, for example. Ruby
# > ensures this by preventing instantiation. If duplication is attempted, the
# > same instance is returned.
# >
# > For this reason, Numeric should be used when defining other numeric
# > classes.
# >
# > Classes which inherit from Numeric must implement coerce, which returns a
# > two-member Array containing an object that has been coerced into an
# > instance of the new class and self (see coerce).
# >
# > Inheriting classes should also implement arithmetic operator methods (+, -,
# > * and /) and the <=> operator (see Comparable). These methods may rely on
# > coerce to ensure interoperability with instances of other numeric classes.
#
# In this class, we **DON'T** implement the arithmetic operators _or_ #coerce.
# Instead, subclass `Numeric` and then we use #method_missing to delate all of
# those methods to an Integer (#to_i) version of ourselves.
class IntegerWithIndifferentComparison < Numeric
attr_reader :value
def initialize(val)
@value = val.to_i
end
# We want this to behave like an integer, but we're not supposed to subclass Integer directly.
# Use method missing to redirect everything to our Integer instance variable instead
def method_missing(method_name, *args, &block)
if @value.respond_to?(method_name)
@value.send(method_name, *args, &block)
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
@value.respond_to?(method_name) || super
end
# :== is an alias for :eql?
def eql?(other)
@value == other.to_i
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment