summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKazuki Tsujimoto <kazuki@callcc.net>2019-11-08 11:37:07 +0900
committerKazuki Tsujimoto <kazuki@callcc.net>2019-11-08 11:37:46 +0900
commitd4da74ea786da7906fdb85e593593a9c6c11fe96 (patch)
tree98682735d82abf9c8d05bc5c718c00ee88d17248
parent766115010932d977142097f60c76dd20af73196e (diff)
Define Struct#deconstruct_keys
-rw-r--r--struct.c28
-rw-r--r--test/ruby/test_pattern_matching.rb24
-rw-r--r--test/ruby/test_struct.rb12
3 files changed, 64 insertions, 0 deletions
diff --git a/struct.c b/struct.c
index 273c719aec..f4ae094f00 100644
--- a/struct.c
+++ b/struct.c
@@ -944,6 +944,33 @@ rb_struct_to_h(VALUE s)
return h;
}
+static VALUE
+rb_struct_deconstruct_keys(VALUE s, VALUE keys)
+{
+ VALUE h;
+ long i;
+
+ if (NIL_P(keys)) {
+ return rb_struct_to_h(s);
+ }
+ if (UNLIKELY(!RB_TYPE_P(keys, T_ARRAY))) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Array or nil)",
+ rb_obj_class(keys));
+
+ }
+ h = rb_hash_new_with_size(RARRAY_LEN(keys));
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE key = RARRAY_AREF(keys, i);
+ int i = rb_struct_pos(s, &key);
+ if (i < 0) {
+ return rb_hash_new_with_size(0);
+ }
+ rb_hash_aset(h, key, RSTRUCT_GET(s, i));
+ }
+ return h;
+}
+
/* :nodoc: */
VALUE
rb_struct_init_copy(VALUE copy, VALUE s)
@@ -1357,6 +1384,7 @@ InitVM_Struct(void)
rb_define_method(rb_cStruct, "dig", rb_struct_dig, -1);
rb_define_method(rb_cStruct, "deconstruct", rb_struct_to_a, 0);
+ rb_define_method(rb_cStruct, "deconstruct_keys", rb_struct_deconstruct_keys, 1);
}
#undef rb_intern
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
index 0338f90a91..9af091a5e5 100644
--- a/test/ruby/test_pattern_matching.rb
+++ b/test/ruby/test_pattern_matching.rb
@@ -1230,6 +1230,30 @@ END
true
end
end
+
+ s = Struct.new(:a, :b, keyword_init: true)
+ assert_block do
+ case s[a: 0, b: 1]
+ in **r
+ r == {a: 0, b: 1}
+ end
+ end
+ assert_block do
+ s = Struct.new(:a, :b, keyword_init: true)
+ case s[a: 0, b: 1]
+ in a:, b:
+ a == 0 && b == 1
+ end
+ end
+ assert_block do
+ s = Struct.new(:a, :b, keyword_init: true)
+ case s[a: 0, b: 1]
+ in a:, c:
+ flunk
+ in b:
+ b == 1
+ end
+ end
end
################################################################
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index 22a6ce8241..06a8a1bb92 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -431,6 +431,18 @@ module TestStruct
}
end
+ def test_deconstruct_keys
+ klass = @Struct.new(:a, :b)
+ o = klass.new(1, 2)
+ assert_equal({a: 1, b: 2}, o.deconstruct_keys(nil))
+ assert_equal({a: 1, b: 2}, o.deconstruct_keys([:b, :a]))
+ assert_equal({a: 1}, o.deconstruct_keys([:a]))
+ assert_equal({}, o.deconstruct_keys([:a, :c]))
+ assert_raise(TypeError) {
+ o.deconstruct_keys(0)
+ }
+ end
+
class TopStruct < Test::Unit::TestCase
include TestStruct