I come across this in Rails source code:
class Object
def duplicable?
true
end
end
class NilClass
begin
nil.dup
rescue TypeError
def duplicable?
false
end
end
end
With this code, even after dup is removed from an object, that object responds to duplicable? with true.
I think it can be rewritten to a simpler code like:
class Object
def duplicable?
repond_to?(:dup)
end
end
What is the merit of defining duplicable? using begin...rescue?
What is the merit of defining
duplicable?usingbegin...rescue?
Ruby before 2.4 raised a TypeError when attempting to nil.dup:
$ rbenv local 2.3.0
$ ruby --version
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]
$ ruby -e 'p nil.dup'
-e:1:in `dup': can't dup NilClass (TypeError)
from -e:1:in `<main>'
Starting with Ruby 2.4, nil.dup just returns itself:
$ rbenv local 2.4.0
$ ruby --version
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin15]
$ ruby -e 'p nil.dup'
nil
Putting the method definition inside rescue ensures that the method is only defined for Ruby versions which raise the TypeError.
I think it can be rewritten to a simpler code like: [...]
Simply checking whether the receiver responds to dup doesn't work, because nil – being an Object – does respond to dup, even in 2.3. The TypeError is (was) raised from within Object#dup:
VALUE rb_obj_dup(VALUE obj)
{
VALUE dup;
if (rb_special_const_p(obj)) {
rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj));
}
// ...
}
nil responds to dup explicitly throwing the TypeError (which has, in turn, nothing to do with NoMethodError.) [Correction: had responded to dup before 2.4, credits go to @Stefan.]
NilClass.instance_method(:dup)
#⇒ #<UnboundMethod: NilClass(Kernel)#dup>
The goal is to respond to duplicable? with false unless NilClass#dup is overwritten by another monkey patcher in the city. [Correction: read “another monkey patcher” as “Matz” :)]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With