diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 4c4a23272..9b78a5169 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -317,6 +317,46 @@ mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, const mrb_value *arg return self; } +static mrb_value +mrb_struct_initialize_withKw(mrb_state *mrb, mrb_value hash, mrb_value self) +{ + mrb_value members = struct_members(mrb, self); + mrb_int member_count = num_members(mrb, self); + + /* Initialize members from hash, defaulting to nil */ + for (mrb_int i = 0; i < member_count; i++) { + mrb_value member = RARRAY_PTR(members)[i]; + if (mrb_hash_key_p(mrb, hash, member)) { + mrb_value val = mrb_hash_get(mrb, hash, member); + mrb_ary_set(mrb, self, i, val); + } + else { + mrb_ary_set(mrb, self, i, mrb_nil_value()); + } + } + + /* Check if all keys in the hash are valid members */ + mrb_value keys = mrb_hash_keys(mrb, hash); + mrb_value invalid_keys = mrb_ary_new(mrb); + + for (mrb_int i = 0; i < RARRAY_LEN(keys); i++) { + mrb_value key = RARRAY_PTR(keys)[i]; + mrb_value include_result = mrb_funcall(mrb, members, "include?", 1, key); + if (mrb_test(include_result) == FALSE) { + mrb_ary_push(mrb, invalid_keys, key); + } + } + + /* If there are any invalid keys, raise an error with all of them */ + if (RARRAY_LEN(invalid_keys) > 0) { + mrb_value keys_str = mrb_funcall(mrb, invalid_keys, "join", 1, mrb_str_new_lit(mrb, ", ")); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown keyword%s: %S", + RARRAY_LEN(invalid_keys) > 1 ? "s" : "", keys_str); + } + + return self; +} + static mrb_value mrb_struct_initialize(mrb_state *mrb, mrb_value self) { @@ -324,7 +364,13 @@ mrb_struct_initialize(mrb_state *mrb, mrb_value self) mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); - return mrb_struct_initialize_withArg(mrb, argc, argv, self); + + /* If we get a single hash argument, treat it as keyword arguments */ + if (argc == 1 && mrb_hash_p(argv[0])) { + return mrb_struct_initialize_withKw(mrb, argv[0], self); + } else { + return mrb_struct_initialize_withArg(mrb, argc, argv, self); + } } /* 15.2.18.4.9 */ diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb index d10dc866c..da41d04cb 100644 --- a/mrbgems/mruby-struct/test/struct.rb +++ b/mrbgems/mruby-struct/test/struct.rb @@ -229,3 +229,23 @@ assert 'method visibility with Struct' do c.new.bad! end end + +assert "Struct initialize with keyword arguments" do + c = Struct.new(:foo, :bar) + + o = c.new(foo: 1, bar: 2) + assert_equal 1, o.foo + assert_equal 2, o.bar + + o2 = c.new(bar: 1, foo: 2) + assert_equal 2, o2.foo + assert_equal 1, o2.bar + + o3 = c.new(foo: :test) + assert_equal :test, o3.foo + assert_equal nil, o3.bar + + assert_raise_with_message_pattern(ArgumentError, "unknown keywords: roo, baq") do + c.new(foo: 1, roo: nil, baq: :test) + end +end