aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--NEWS3
-rw-r--r--internal.h1
-rw-r--r--object.c9
-rw-r--r--struct.c28
-rw-r--r--test/ruby/test_struct.rb7
6 files changed, 54 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 0c5d05b82d..8ed37cf96d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Mon Nov 16 18:21:52 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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 <nobu@ruby-lang.org>
* compile.c (iseq_peephole_optimize): optimize tail calls on aref
diff --git a/NEWS b/NEWS
index 6724aaf402..a7d2da980a 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 600ad8e7d3..7e6f26db11 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 ed5d12ea2c..5023f64b0d 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 60ef6d8cfd..d8c781efa6 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 f51432cfd6..780d344b8e 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -353,6 +353,13 @@ module TestStruct
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