Open classes are one of my favorites. For example, this:
# try.rb
class TryObjectMock
public_instance_methods.reject { |x| x =~ /^__/ }.each { |x| undef_method(x) }
def method_missing(symbol, *args)
end
end
class Object
def try
self.nil? ? TryObjectMock.new : self
end
end
makes code that handles nil
able objects clearer:
irb(main):001:0> require 'try'
irb(main):002:0> puts "5".try.to_i
5
irb(main):003:0> puts nil.try.to_i
nil
irb(main):004:0> puts [1, 2].try.find { |x| x == 2 }.try + 3
5
irb(main):005:0> puts [1, 2].try.find { |x| x == 3 }.try + 3
nil
irb(main):006:0> puts nil.try.find { |x| x == 3 }.try + 3
nil
irb(main):007:0>
It avoids a lot of if-else
s:
irb(main):007:0> a = [1, 2]
irb(main):008:0> puts(if a
irb(main):009:2> if b = a.find { |x| x == 2 }
irb(main):010:3> b + 3
irb(main):011:3> end
irb(main):012:2> end)
5
irb(main):013:0> c = "5"
irb(main):014:0> puts(c ? c.to_i : nil)
5
The trick works by defining the method try
for every class in the object space. When it's called it returns a special object when the object is nil
(nil
is an instance of NilClass
, which is a subclass of Object
!) or the object itself otherwise. That special object simply returns nil
for every method called (through the empty method_missing
implementation). Note that it also works for operators, since they are methods of the left-side object.