aboutsummaryrefslogtreecommitdiffhomepage
path: root/ruby/ext
diff options
context:
space:
mode:
authorGravatar Joshua Haberman <jhaberman@gmail.com>2017-09-21 15:08:45 -0700
committerGravatar GitHub <noreply@github.com>2017-09-21 15:08:45 -0700
commit4fc75296c781e851b887da6d05fed722acf3373b (patch)
tree8e66ed46d842268594e18f6b33468997a9e4249f /ruby/ext
parent2b0ee3fdf62577708eea74db3c31853782ae4efe (diff)
parent633ef8bde974a236ef1bdec58c246c0a798d2aa9 (diff)
Merge pull request #3627 from zanker/zanker/add-submsg-hash-init
Allow initializing a chain of protos using only a hash in Ruby
Diffstat (limited to 'ruby/ext')
-rw-r--r--ruby/ext/google/protobuf_c/message.c35
-rw-r--r--ruby/ext/google/protobuf_c/storage.c13
2 files changed, 40 insertions, 8 deletions
diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c
index 9e8056e3..42910bfe 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,
@@ -255,9 +267,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));
+ VALUE 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.");
}