Skip to content

Instantly share code, notes, and snippets.

@trans
Created May 2, 2012 06:04
Show Gist options
  • Save trans/2574264 to your computer and use it in GitHub Desktop.
Save trans/2574264 to your computer and use it in GitHub Desktop.
Make NilClass comparable
# This tiny framework simply makes nil comparable, such that
# all things (except itself) are greater than it.
#
# The main effect is this:
#
# nil <=> any_object #=> -1
#
# To enable a class to compare against nil (reverse of the above),
# simply prepend a nil comparsion into the #<=> method of that class.
# For example:
#
# class Foo
# def initialize( value )
# @value = value
# end
#
# def <=>( other )
# return 1 if other.nil? # nil comparison
# @value <=> other
# end
#
# end
#
# To do so for a pre-existing class with a pre-existing #<=> method, you'll
# need to overide the method. As an example let's make Numeric comparable to nil.
#
# class Numeric
# alias_method :compare_without_nil, :<=>
# def <=>( other )
# return 1 if other.nil?
# compare_without_nil( other )
# end
# end
#
# Or more conveniently:
#
# require 'facets/module/wrap_method'
#
# Numeric.wrap_method(:<=>) do |prev, other|
# return 1 if other.nil?
# prev.call(other)
# end
#
# NilCompariable is a mixin module that can do the above automatically.
# The including class should have #<=> defined, then simple include the
# mixin.
#
# class Numeric
# include NilComparable
# end
#
# == Important Consideration
#
# Changing this behavior for of NilClass to be comparable is not something
# to be done lightly. Potentially it could cause unexpected errors in
# other's code. So it is best to use this library only when you have full
# control of the code being executed.
#
# == Credits
#
# NilComparable is based on the original library from Paul Brannan's Ruby Treasures, circa 2005.
module NilComparable
def self.included(mod)
mod.class_eval %{
if method_defined?( :<=> )
alias_method :compare_without_nil, :<=>
else
def compare_without_nil( other )
raise TypeError, "Cannot compare \#{self.inspect} to \#{other.inspect}"
end
end
def <=>(other)
return 1 if other.nil?
compare_without_nil(other)
end
}
end
end
class NilClass #:nodoc:
include Comparable
# Any comparison against nil with return -1,
# except for nil itself which returns 0.
def <=>(other)
other.nil? ? 0 : -1
end
alias_method( :cmp, :<=> )
def succ(n=nil); nil; end
end
# IDEA: Use conversion to do comparison, i.e. NilClass#to_{class}.
require 'test/unit'
require 'nilcomparable'
class Mock < String
alias_method :compare_without_nil, :<=>
def <=>( other )
return 1 if other.nil?
compare_without_nil( other )
end
end
class TestNilClassComparable < Test::Unit::TestCase
def test001
assert_equal( 0, nil <=> nil )
assert_equal( -1, nil <=> 4 )
assert_equal( -1, nil <=> "a" )
assert_equal( -1, nil <=> Object.new )
assert_equal( 0, nil.cmp(nil) )
assert( nil < 4 )
end
def test002
m = Mock.new("A")
assert_equal( 1, m <=> nil )
assert_equal( -1, m <=> "B" )
#assert_equal( 1, m.cmp(nil) )
assert( m > nil )
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment