From 20690026a7dd7622c7d824de71e337a68a293b63 Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 16 Nov 2015 09:21:56 +0000 Subject: [PATCH] struct.c: dig * object.c (rb_obj_dig): dig in nested structs too. * struct.c (rb_struct_dig): new method Struct#dig. [Feature #11688] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52596 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 +++++++ NEWS | 3 +++ internal.h | 1 + object.c | 9 ++++++++- struct.c | 28 ++++++++++++++++++++++++++++ test/ruby/test_struct.rb | 7 +++++++ 6 files changed, 54 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 0c5d05b82dff35..8ed37cf96dffbc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Mon Nov 16 18:21:52 2015 Nobuyoshi Nakada + + * object.c (rb_obj_dig): dig in nested structs too. + + * struct.c (rb_struct_dig): new method Struct#dig. + [Feature #11688] + Mon Nov 16 17:41:33 2015 Nobuyoshi Nakada * compile.c (iseq_peephole_optimize): optimize tail calls on aref diff --git a/NEWS b/NEWS index 6724aaf40257a8..a7d2da980aed94 100644 --- a/NEWS +++ b/NEWS @@ -95,6 +95,9 @@ with all sufficient information, see the ChangeLog file. Backtrace doesn't show each methods (show block lines directly). TracePoint also ignore these calls. [Feature #11569] +* Struct + * Struct#dig [Feature #11686] + * Thread * Thread#name, Thread#name= are added to handle thread names [Feature #11251] diff --git a/internal.h b/internal.h index 600ad8e7d35d95..7e6f26db1121f2 100644 --- a/internal.h +++ b/internal.h @@ -1141,6 +1141,7 @@ VALUE rb_cstr_intern(const char *ptr, long len, rb_encoding *enc); /* struct.c */ VALUE rb_struct_init_copy(VALUE copy, VALUE s); +VALUE rb_struct_lookup(VALUE s, VALUE idx); /* time.c */ struct timeval rb_time_timeval(VALUE); diff --git a/object.c b/object.c index ed5d12ea2c9653..5023f64b0db5be 100644 --- a/object.c +++ b/object.c @@ -3165,7 +3165,7 @@ dig_basic_p(VALUE obj, struct dig_method *cache) VALUE rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound) { - struct dig_method hash = {Qnil}, ary = {Qnil}; + struct dig_method hash = {Qnil}, ary = {Qnil}, strt = {Qnil}; for (; argc > 0; ++argv, --argc) { if (!SPECIAL_CONST_P(obj)) { @@ -3181,6 +3181,13 @@ rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound) obj = rb_ary_at(obj, *argv); continue; } + break; + case T_STRUCT: + if (dig_basic_p(obj, &strt)) { + obj = rb_struct_lookup(obj, *argv); + continue; + } + break; } } return rb_check_funcall_default(obj, id_dig, argc, argv, notfound); diff --git a/struct.c b/struct.c index 60ef6d8cfdf41f..d8c781efa6e3a8 100644 --- a/struct.c +++ b/struct.c @@ -923,6 +923,23 @@ rb_struct_aset(VALUE s, VALUE idx, VALUE val) return val; } +FUNC_MINIMIZED(VALUE rb_struct_lookup(VALUE s, VALUE idx)); +NOINLINE(static VALUE rb_struct_lookup_default(VALUE s, VALUE idx, VALUE notfound)); + +VALUE +rb_struct_lookup(VALUE s, VALUE idx) +{ + return rb_struct_lookup_default(s, idx, Qnil); +} + +static VALUE +rb_struct_lookup_default(VALUE s, VALUE idx, VALUE notfound) +{ + int i = rb_struct_pos(s, &idx); + if (i < 0) return notfound; + return RSTRUCT_GET(s, i); +} + static VALUE struct_entry(VALUE s, long n) { @@ -1109,6 +1126,16 @@ rb_struct_size(VALUE s) return LONG2FIX(RSTRUCT_LEN(s)); } +static VALUE +rb_struct_dig(int argc, VALUE *argv, VALUE self) +{ + rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); + self = rb_struct_lookup(self, *argv); + if (!--argc) return self; + ++argv; + return rb_obj_dig(argc, argv, self, Qnil); +} + /* * A Struct is a convenient way to bundle a number of attributes together, * using accessor methods, without having to write an explicit class. @@ -1166,6 +1193,7 @@ InitVM_Struct(void) rb_define_method(rb_cStruct, "values_at", rb_struct_values_at, -1); rb_define_method(rb_cStruct, "members", rb_struct_members_m, 0); + rb_define_method(rb_cStruct, "dig", rb_struct_dig, -1); } #undef rb_intern diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb index f51432cfd6b379..780d344b8ecaa5 100644 --- a/test/ruby/test_struct.rb +++ b/test/ruby/test_struct.rb @@ -353,6 +353,13 @@ def test_setter_method_returns_value assert_equal "[Bug #9353]", x.send(:a=, "[Bug #9353]") end + def test_dig + klass = @Struct.new(:a) + o = klass.new(klass.new({b: [1, 2, 3]})) + assert_equal(1, o.dig(:a, :a, :b, 0)) + assert_nil(o.dig(:b, 0)) + end + class TopStruct < Test::Unit::TestCase include TestStruct