diff options
-rw-r--r-- | hash.c | 49 | ||||
-rw-r--r-- | test/ruby/test_hash.rb | 24 |
2 files changed, 73 insertions, 0 deletions
@@ -1861,6 +1861,52 @@ rb_hash_s_try_convert(VALUE dummy, VALUE hash) return rb_check_hash_type(hash); } +/* + * call-seq: + * Hash.ruby2_keywords_hash?(hash) -> true or false + * + * Checks if a given hash is flagged by Module#ruby2_keywords (or + * Proc#ruby2_keywords). + * This method is not for casual use; debugging, researching, and + * some truly necessary cases like serialization of arguments. + * + * ruby2_keywords def foo(*args) + * Hash.ruby2_keywords_hash?(args.last) + * end + * foo(k: 1) #=> true + * foo({k: 1}) #=> false + */ +static VALUE +rb_hash_s_ruby2_keywords_hash_p(VALUE dummy, VALUE hash) +{ + Check_Type(hash, T_HASH); + return (RHASH(hash)->basic.flags & RHASH_PASS_AS_KEYWORDS) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * Hash.ruby2_keywords_hash(hash) -> hash + * + * Duplicates a given hash and adds a ruby2_keywords flag. + * This method is not for casual use; debugging, researching, and + * some truly necessary cases like deserialization of arguments. + * + * h = {k: 1} + * h = Hash.ruby2_keywords_hash(h) + * def foo(k: 42) + * k + * end + * foo(*[h]) #=> 1 with neither a warning or an error + */ +static VALUE +rb_hash_s_ruby2_keywords_hash(VALUE dummy, VALUE hash) +{ + Check_Type(hash, T_HASH); + hash = rb_hash_dup(hash); + RHASH(hash)->basic.flags |= RHASH_PASS_AS_KEYWORDS; + return hash; +} + struct rehash_arg { VALUE hash; st_table *tbl; @@ -6339,6 +6385,9 @@ Init_Hash(void) rb_define_method(rb_cHash, "deconstruct_keys", rb_hash_deconstruct_keys, 1); + rb_define_singleton_method(rb_cHash, "ruby2_keywords_hash?", rb_hash_s_ruby2_keywords_hash_p, 1); + rb_define_singleton_method(rb_cHash, "ruby2_keywords_hash", rb_hash_s_ruby2_keywords_hash, 1); + /* Document-class: ENV * * ENV is a hash-like accessor for environment variables. diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index ccc3355930..304bd585a4 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1740,4 +1740,28 @@ class TestHash < Test::Unit::TestCase super end end + + ruby2_keywords def get_flagged_hash(*args) + args.last + end + + def check_flagged_hash(k: :NG) + k + end + + def test_ruby2_keywords_hash? + flagged_hash = get_flagged_hash(k: 1) + assert_equal(true, Hash.ruby2_keywords_hash?(flagged_hash)) + assert_equal(false, Hash.ruby2_keywords_hash?({})) + assert_raise(TypeError) { Hash.ruby2_keywords_hash?(1) } + end + + def test_ruby2_keywords_hash! + hash = {k: 1} + assert_equal(false, Hash.ruby2_keywords_hash?(hash)) + hash = Hash.ruby2_keywords_hash(hash) + assert_equal(true, Hash.ruby2_keywords_hash?(hash)) + assert_equal(1, check_flagged_hash(*[hash])) + assert_raise(TypeError) { Hash.ruby2_keywords_hash(1) } + end end |