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