From 87714836e328ca3f98c93a451be26bfd35e804b7 Mon Sep 17 00:00:00 2001 From: Zachary Anker Date: Tue, 12 Sep 2017 12:52:15 -0700 Subject: Allow initializing a chain of protos using only a hash --- ruby/ext/google/protobuf_c/message.c | 36 +++++++++++++++++++++++++++++------- ruby/ext/google/protobuf_c/storage.c | 13 ++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) (limited to 'ruby/ext') diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 29911140..9d5349a3 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -217,20 +217,32 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) { return Qtrue; } +VALUE create_submsg_from_hash(const upb_fielddef *f, VALUE hash) { + const upb_def *d = upb_fielddef_subdef(f); + assert(d != NULL); + + VALUE descriptor = get_def_obj(d); + VALUE msgclass = rb_funcall(descriptor, rb_intern("msgclass"), 0, NULL); + + VALUE args[1] = { hash }; + return rb_class_new_instance(1, args, msgclass); +} + int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) { MessageHeader* self; - VALUE method_str; - char* name; + char *name; const upb_fielddef* f; TypedData_Get_Struct(_self, MessageHeader, &Message_type, self); - if (!SYMBOL_P(key)) { + if (TYPE(key) == T_STRING) { + name = RSTRING_PTR(key); + } else if (TYPE(key) == T_SYMBOL) { + name = RSTRING_PTR(rb_id2str(SYM2ID(key))); + } else { rb_raise(rb_eArgError, - "Expected symbols as hash keys in initialization map."); + "Expected string or symbols as hash keys when initializing proto from hash."); } - method_str = rb_id2str(SYM2ID(key)); - name = RSTRING_PTR(method_str); f = upb_msgdef_ntofz(self->descriptor->msgdef, name); if (f == NULL) { rb_raise(rb_eArgError, @@ -248,6 +260,7 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) { Map_merge_into_self(map, val); } else if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) { VALUE ary; + VALUE entry; if (TYPE(val) != T_ARRAY) { rb_raise(rb_eArgError, @@ -255,9 +268,18 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) { } ary = layout_get(self->descriptor->layout, Message_data(self), f); for (int i = 0; i < RARRAY_LEN(val); i++) { - RepeatedField_push(ary, rb_ary_entry(val, i)); + entry = rb_ary_entry(val, i); + if (TYPE(entry) == T_HASH && upb_fielddef_issubmsg(f)) { + entry = create_submsg_from_hash(f, entry); + } + + RepeatedField_push(ary, entry); } } else { + if (TYPE(val) == T_HASH && upb_fielddef_issubmsg(f)) { + val = create_submsg_from_hash(f, val); + } + layout_set(self->descriptor->layout, Message_data(self), f, val); } return 0; diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 3ff2bda6..24064dfd 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -176,6 +176,15 @@ void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class, break; } case UPB_TYPE_STRING: + if (CLASS_OF(value) == rb_cSymbol) { + value = rb_funcall(value, rb_intern("to_s"), 0, NULL); + } else if (CLASS_OF(value) != rb_cString) { + rb_raise(rb_eTypeError, "Invalid argument for string field."); + } + + DEREF(memory, VALUE) = native_slot_encode_and_freeze_string(type, value); + break; + case UPB_TYPE_BYTES: { if (CLASS_OF(value) != rb_cString) { rb_raise(rb_eTypeError, "Invalid argument for string field."); @@ -197,7 +206,9 @@ void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class, } case UPB_TYPE_ENUM: { int32_t int_val = 0; - if (!is_ruby_num(value) && TYPE(value) != T_SYMBOL) { + if (TYPE(value) == T_STRING) { + value = rb_funcall(value, rb_intern("to_sym"), 0, NULL); + } else if (!is_ruby_num(value) && TYPE(value) != T_SYMBOL) { rb_raise(rb_eTypeError, "Expected number or symbol type for enum field."); } -- cgit v1.2.3