mruby-hash-ext: add Hash#deconstruct_keys for pattern matching

Implement new method for Ruby 2.7+ pattern matching compatibility.
Handles nil (return self) and array (extract keys) arguments.
Enables modern case/in pattern matching syntax in mruby.

Co-authored-by: Atlassian Rovo Dev
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-07-01 16:02:42 +09:00
parent 85b1e56279
commit 6e79fed77b
2 changed files with 74 additions and 0 deletions
+49
View File
@@ -247,6 +247,54 @@ hash_key(mrb_state *mrb, mrb_value hash)
return search.found ? search.result : mrb_nil_value();
}
/*
* call-seq:
* hsh.deconstruct_keys(keys) -> hash
*
* Returns a hash containing the contents of hsh for the given keys.
*
* If keys is nil, returns the hash itself.
* If keys is an array, returns a new hash containing only the specified keys.
*
* h = { a: 1, b: 2, c: 3, d: 4 }
* h.deconstruct_keys([:a, :c]) #=> { a: 1, c: 3 }
* h.deconstruct_keys(nil) #=> { a: 1, b: 2, c: 3, d: 4 }
* h.deconstruct_keys([:x, :y]) #=> { x: nil, y: nil }
*/
static mrb_value
hash_deconstruct_keys(mrb_state *mrb, mrb_value hash)
{
mrb_value keys;
mrb_value result;
mrb_int i, len;
mrb_get_args(mrb, "o", &keys);
/* If keys is nil, return the hash itself */
if (mrb_nil_p(keys)) {
return hash;
}
/* Keys must be an array */
if (!mrb_array_p(keys)) {
mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %C (expected Array or nil)",
mrb_obj_class(mrb, keys));
}
/* Create result hash */
result = mrb_hash_new(mrb);
len = RARRAY_LEN(keys);
/* Extract specified keys */
for (i = 0; i < len; i++) {
mrb_value key = mrb_ary_ref(mrb, keys, i);
mrb_value val = mrb_hash_get(mrb, hash, key);
mrb_hash_set(mrb, result, key, val);
}
return result;
}
void
mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
{
@@ -257,6 +305,7 @@ mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
mrb_define_method_id(mrb, h, MRB_SYM(slice), hash_slice, MRB_ARGS_ANY());
mrb_define_method_id(mrb, h, MRB_SYM(except), hash_except, MRB_ARGS_ANY());
mrb_define_method_id(mrb, h, MRB_SYM(key), hash_key, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, h, MRB_SYM(deconstruct_keys), hash_deconstruct_keys, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, h, MRB_OPSYM(aref), hash_s_create, MRB_ARGS_ANY());
}
+25
View File
@@ -298,3 +298,28 @@ assert("Hash#except") do
assert_equal({:a=>100}, h.except(:b, :c, :d))
assert_equal(h, h.except)
end
assert("Hash#deconstruct_keys") do
h = { a: 1, b: 2, c: 3, d: 4 }
# Test with specific keys
result = h.deconstruct_keys([:a, :c])
assert_equal({ a: 1, c: 3 }, result)
# Test with nil (return self)
result_nil = h.deconstruct_keys(nil)
assert_equal(h, result_nil)
assert_true(result_nil.equal?(h))
# Test with non-existent keys
result_missing = h.deconstruct_keys([:a, :x, :y])
assert_equal({ a: 1, x: nil, y: nil }, result_missing)
# Test with empty array
result_empty = h.deconstruct_keys([])
assert_equal({}, result_empty)
# Test error cases
assert_raise(TypeError) { h.deconstruct_keys("not_an_array") }
assert_raise(TypeError) { h.deconstruct_keys(123) }
end