Ruby: fixed Message#to_h for map fields.

This commit is contained in:
Josh Haberman 2017-03-14 14:27:16 -07:00
parent 43f2db776c
commit 9c6b8cb9bf
4 changed files with 55 additions and 2 deletions

View File

@ -652,6 +652,35 @@ VALUE Map_hash(VALUE _self) {
return INT2FIX(h);
}
/*
* call-seq:
* Map.to_h => {}
*
* Returns a Ruby Hash object containing all the values within the map
*/
VALUE Map_to_h(VALUE _self) {
Map* self = ruby_to_Map(_self);
VALUE hash = rb_hash_new();
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
VALUE key = table_key_to_ruby(
self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
VALUE value = native_slot_get(self->value_type,
self->value_type_class,
mem);
if (self->value_type == UPB_TYPE_MESSAGE) {
value = Message_to_h(value);
}
rb_hash_aset(hash, key, value);
}
return hash;
}
/*
* call-seq:
* Map.inspect => string
@ -804,6 +833,8 @@ void Map_register(VALUE module) {
rb_define_method(klass, "dup", Map_dup, 0);
rb_define_method(klass, "==", Map_eq, 1);
rb_define_method(klass, "hash", Map_hash, 0);
rb_define_method(klass, "to_hash", Map_to_h, 0);
rb_define_method(klass, "to_h", Map_to_h, 0);
rb_define_method(klass, "inspect", Map_inspect, 0);
rb_define_method(klass, "merge", Map_merge, 1);
rb_include_module(klass, rb_mEnumerable);

View File

@ -394,7 +394,12 @@ VALUE Message_inspect(VALUE _self) {
return str;
}
/*
* call-seq:
* Message.to_h => {}
*
* Returns the message as a Ruby Hash object, with keys as symbols.
*/
VALUE Message_to_h(VALUE _self) {
MessageHeader* self;
VALUE hash;
@ -410,8 +415,13 @@ VALUE Message_to_h(VALUE _self) {
VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self),
field);
VALUE msg_key = ID2SYM(rb_intern(upb_fielddef_name(field)));
if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
if (upb_fielddef_ismap(field)) {
msg_value = Map_to_h(msg_value);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
msg_value = RepeatedField_to_ary(msg_value);
} else if (msg_value != Qnil &&
upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
msg_value = Message_to_h(msg_value);
}
rb_hash_aset(hash, msg_key, msg_value);
}

View File

@ -424,6 +424,7 @@ VALUE Map_dup(VALUE _self);
VALUE Map_deep_copy(VALUE _self);
VALUE Map_eq(VALUE _self, VALUE _other);
VALUE Map_hash(VALUE _self);
VALUE Map_to_h(VALUE _self);
VALUE Map_inspect(VALUE _self);
VALUE Map_merge(VALUE _self, VALUE hashmap);
VALUE Map_merge_into_self(VALUE _self, VALUE hashmap);
@ -496,6 +497,7 @@ VALUE Message_deep_copy(VALUE _self);
VALUE Message_eq(VALUE _self, VALUE _other);
VALUE Message_hash(VALUE _self);
VALUE Message_inspect(VALUE _self);
VALUE Message_to_h(VALUE _self);
VALUE Message_index(VALUE _self, VALUE field_name);
VALUE Message_index_set(VALUE _self, VALUE field_name, VALUE value);
VALUE Message_descriptor(VALUE klass);

View File

@ -927,6 +927,16 @@ module BasicTest
:repeated_uint64=>[]
}
assert_equal expected_result, m.to_h
m = MapMessage.new(
:map_string_int32 => {"a" => 1, "b" => 2},
:map_string_msg => {"a" => TestMessage2.new(:foo => 1),
"b" => TestMessage2.new(:foo => 2)})
expected_result = {
:map_string_int32=>{"a"=>1, "b"=>2},
:map_string_msg=>{"a"=>{:foo=>1}, "b"=>{:foo=>2}}
}
assert_equal expected_result, m.to_h
end