From 9c6b8cb9bfc7c31ec99566772246c9bc4317c57b Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Tue, 14 Mar 2017 14:27:16 -0700 Subject: Ruby: fixed Message#to_h for map fields. --- ruby/ext/google/protobuf_c/map.c | 31 +++++++++++++++++++++++++++++++ ruby/ext/google/protobuf_c/message.c | 14 ++++++++++++-- ruby/ext/google/protobuf_c/protobuf.h | 2 ++ ruby/tests/basic.rb | 10 ++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c index 12f1f9dd..4be54c39 100644 --- a/ruby/ext/google/protobuf_c/map.c +++ b/ruby/ext/google/protobuf_c/map.c @@ -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); diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 837a974b..29911140 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -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); } diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index d5ced567..520e9d9b 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -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); diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index ca81e3a5..ff7576b8 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -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 -- cgit v1.2.3 From 324a299a55409febb4c0e71a03ff3e50d191ca84 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 15 Mar 2017 10:35:15 -0700 Subject: Made formatting more consistent. --- ruby/tests/basic.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ruby') diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index ff7576b8..34251234 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -933,8 +933,8 @@ module BasicTest :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}} + :map_string_int32 => {"a" => 1, "b" => 2}, + :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}} } assert_equal expected_result, m.to_h end -- cgit v1.2.3