Support hashes for struct initializers (#5716)
* support hashes for struct initalizers
* convert hash keys to string
* update tests
* add extra asserts
diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c
index cf1d6e3..fb25efe 100644
--- a/ruby/ext/google/protobuf_c/map.c
+++ b/ruby/ext/google/protobuf_c/map.c
@@ -71,6 +71,9 @@
case UPB_TYPE_BYTES:
case UPB_TYPE_STRING:
// Strings: use string content directly.
+ if (TYPE(key) == T_SYMBOL) {
+ key = rb_id2str(SYM2ID(key));
+ }
Check_Type(key, T_STRING);
key = native_slot_encode_and_freeze_string(self->key_type, key);
*out_key = RSTRING_PTR(key);
@@ -397,6 +400,11 @@
void* mem;
key = table_key(self, key, keybuf, &keyval, &length);
+ if (TYPE(value) == T_HASH) {
+ VALUE args[1] = { value };
+ value = rb_class_new_instance(1, args, self->value_type_class);
+ }
+
mem = value_memory(&v);
native_slot_set("", self->value_type, self->value_type_class, mem, value);
diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb
index 9ec738b..f1b1c6f 100644
--- a/ruby/tests/basic.rb
+++ b/ruby/tests/basic.rb
@@ -204,6 +204,20 @@
end
end
+ def test_map_field_with_symbol
+ m = MapMessage.new
+ assert m.map_string_int32 == {}
+ assert m.map_string_msg == {}
+
+ m = MapMessage.new(
+ :map_string_int32 => {a: 1, "b" => 2},
+ :map_string_msg => {a: TestMessage2.new(:foo => 1),
+ b: TestMessage2.new(:foo => 10)})
+ assert_equal 1, m.map_string_int32[:a]
+ assert_equal 2, m.map_string_int32[:b]
+ assert_equal 10, m.map_string_msg[:b].foo
+ end
+
def test_map_inspect
m = MapMessage.new(
:map_string_int32 => {"a" => 1, "b" => 2},
diff --git a/ruby/tests/well_known_types_test.rb b/ruby/tests/well_known_types_test.rb
index f35f7b1..3eafe09 100644
--- a/ruby/tests/well_known_types_test.rb
+++ b/ruby/tests/well_known_types_test.rb
@@ -139,4 +139,58 @@
assert any.is(Google::Protobuf::Timestamp)
assert_equal ts, any.unpack(Google::Protobuf::Timestamp)
end
+
+ def test_struct_init
+ s = Google::Protobuf::Struct.new(fields: {'a' => Google::Protobuf::Value.new({number_value: 4.4})})
+ assert_equal 4.4, s['a']
+
+ s = Google::Protobuf::Struct.new(fields: {'a' => {number_value: 2.2}})
+ assert_equal 2.2, s['a']
+
+ s = Google::Protobuf::Struct.new(fields: {a: {number_value: 1.1}})
+ assert_equal 1.1, s[:a]
+ end
+
+ def test_struct_nested_init
+ s = Google::Protobuf::Struct.new(
+ fields: {
+ 'a' => {string_value: 'A'},
+ 'b' => {struct_value: {
+ fields: {
+ 'x' => {list_value: {values: [{number_value: 1.0}, {string_value: "ok"}]}},
+ 'y' => {bool_value: true}}}
+ },
+ 'c' => {struct_value: {}}
+ }
+ )
+ assert_equal 'A', s['a']
+ assert_equal 'A', s[:a]
+ expected_b_x = [Google::Protobuf::Value.new(number_value: 1.0), Google::Protobuf::Value.new(string_value: "ok")]
+ assert_equal expected_b_x, s['b']['x'].values
+ assert_equal expected_b_x, s[:b][:x].values
+ assert_equal expected_b_x, s['b'][:x].values
+ assert_equal expected_b_x, s[:b]['x'].values
+ assert_equal true, s['b']['y']
+ assert_equal true, s[:b][:y]
+ assert_equal true, s[:b]['y']
+ assert_equal true, s['b'][:y]
+ assert_equal Google::Protobuf::Struct.new, s['c']
+ assert_equal Google::Protobuf::Struct.new, s[:c]
+
+ s = Google::Protobuf::Struct.new(
+ fields: {
+ a: {string_value: 'Eh'},
+ b: {struct_value: {
+ fields: {
+ y: {bool_value: false}}}
+ }
+ }
+ )
+ assert_equal 'Eh', s['a']
+ assert_equal 'Eh', s[:a]
+ assert_equal false, s['b']['y']
+ assert_equal false, s[:b][:y]
+ assert_equal false, s['b'][:y]
+ assert_equal false, s[:b]['y']
+ end
end