Skip to content

Commit 191bfcb

Browse files
committed
Define Kernel#instance_variables_to_inspect
[Bug #21718] Otherwise objects that don't define it, but define a fairly liberal `method_missing` method will run into errors that are hard to understand: ```ruby class Foo def method_missing(name, ...) name end end p Foo.new.inspect ``` ``` 'Kernel#inspect': wrong argument type Symbol (expected Array) (TypeError) from ../test.rb:7:in '<main>' ```
1 parent 8eaefd9 commit 191bfcb

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

object.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -854,14 +854,21 @@ rb_obj_inspect(VALUE obj)
854854
{
855855
VALUE ivars = rb_check_funcall(obj, id_instance_variables_to_inspect, 0, 0);
856856
st_index_t n = 0;
857-
if (UNDEF_P(ivars)) {
857+
if (UNDEF_P(ivars) || NIL_P(ivars)) {
858858
n = rb_ivar_count(obj);
859859
ivars = Qnil;
860860
}
861-
else if (!NIL_P(ivars)) {
862-
Check_Type(ivars, T_ARRAY);
861+
else if (RB_TYPE_P(ivars, T_ARRAY)) {
863862
n = RARRAY_LEN(ivars);
864863
}
864+
else {
865+
rb_raise(
866+
rb_eTypeError,
867+
"Expected #instance_variables_to_inspect to return an Array or nil, but it returned %"PRIsVALUE,
868+
rb_obj_class(ivars)
869+
);
870+
}
871+
865872
if (n > 0) {
866873
VALUE c = rb_class_name(CLASS_OF(obj));
867874
VALUE args[2] = {
@@ -875,6 +882,13 @@ rb_obj_inspect(VALUE obj)
875882
}
876883
}
877884

885+
/* :nodoc: */
886+
static VALUE
887+
rb_obj_instance_variables_to_inspect(VALUE obj)
888+
{
889+
return Qnil;
890+
}
891+
878892
static VALUE
879893
class_or_module_required(VALUE c)
880894
{
@@ -4535,6 +4549,7 @@ InitVM_Object(void)
45354549

45364550
rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0);
45374551
rb_define_method(rb_mKernel, "inspect", rb_obj_inspect, 0);
4552+
rb_define_private_method(rb_mKernel, "instance_variables_to_inspect", rb_obj_instance_variables_to_inspect, 0);
45384553
rb_define_method(rb_mKernel, "methods", rb_obj_methods, -1); /* in class.c */
45394554
rb_define_method(rb_mKernel, "singleton_methods", rb_obj_singleton_methods, -1); /* in class.c */
45404555
rb_define_method(rb_mKernel, "protected_methods", rb_obj_protected_methods, -1); /* in class.c */

spec/ruby/core/kernel/inspect_spec.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,34 @@ class << obj
5757
inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
5858
inspected.should == "#<Object:0x00>"
5959
end
60+
61+
it "displays all instance variables if #instance_variables_to_inspect returns nil" do
62+
obj = Object.new
63+
obj.instance_eval do
64+
@host = "localhost"
65+
@user = "root"
66+
@password = "hunter2"
67+
end
68+
obj.singleton_class.class_eval do
69+
private def instance_variables_to_inspect = nil
70+
end
71+
72+
inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
73+
inspected.should == %{#<Object:0x00 @host="localhost", @user="root", @password="hunter2">}
74+
end
75+
76+
it "raises an error if #instance_variables_to_inspect returns an invalid value" do
77+
obj = Object.new
78+
obj.instance_eval do
79+
@host = "localhost"
80+
@user = "root"
81+
@password = "hunter2"
82+
end
83+
obj.singleton_class.class_eval do
84+
private def instance_variables_to_inspect = {}
85+
end
86+
87+
->{ obj.inspect }.should raise_error(TypeError, "Expected #instance_variables_to_inspect to return an Array or nil, but it returned Hash")
88+
end
6089
end
6190
end

0 commit comments

Comments
 (0)