From 7f8e7ec181dac0b2bfd24d5b74839b8b241a9949 Mon Sep 17 00:00:00 2001 From: Lukas Fittl Date: Mon, 1 Mar 2021 00:15:58 -0800 Subject: [PATCH 1/3] Ruby: Add support for proto3 json_name in compiler and field definitions --- ruby/ext/google/protobuf_c/defs.c | 48 +++++++++++++++---- .../protobuf/compiler/ruby/ruby_generator.cc | 5 ++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 1d912c1a2e1..635aac35fa6 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -868,6 +868,22 @@ static VALUE FieldDescriptor_default(VALUE _self) { return Convert_UpbToRuby(default_val, TypeInfo_get(self->fielddef), Qnil); } + +/* + * call-seq: + * FieldDescriptor.json_name => json_name + * + * Returns this field's json_name, as a Ruby string, or nil if not yet set. + */ +static VALUE FieldDescriptor_json_name(VALUE _self) { + FieldDescriptor* self = ruby_to_FieldDescriptor(_self); + const upb_fielddef *f = self->fielddef; + const char *json_name = upb_fielddef_jsonname(f); + if (json_name != NULL) + return rb_str_new2(json_name); + return Qnil; +} + /* * call-seq: * FieldDescriptor.label => label @@ -1043,6 +1059,7 @@ static void FieldDescriptor_register(VALUE module) { rb_define_method(klass, "name", FieldDescriptor_name, 0); rb_define_method(klass, "type", FieldDescriptor__type, 0); rb_define_method(klass, "default", FieldDescriptor_default, 0); + rb_define_method(klass, "json_name", FieldDescriptor_json_name, 0); rb_define_method(klass, "label", FieldDescriptor_label, 0); rb_define_method(klass, "number", FieldDescriptor_number, 0); rb_define_method(klass, "submsg_name", FieldDescriptor_submsg_name, 0); @@ -1750,6 +1767,19 @@ static void msgdef_add_field(VALUE msgbuilder_rb, upb_label_t label, VALUE name, field_proto, FileBuilderContext_strdup(self->file_builder, default_value)); } + + if (rb_funcall(options, rb_intern("key?"), 1, + ID2SYM(rb_intern("json_name"))) == Qtrue) { + VALUE json_name = + rb_hash_lookup(options, ID2SYM(rb_intern("json_name"))); + + /* Call #to_s since json_name is always a string in the descriptor. */ + json_name = rb_funcall(json_name, rb_intern("to_s"), 0); + + google_protobuf_FieldDescriptorProto_set_json_name( + field_proto, + FileBuilderContext_strdup(self->file_builder, json_name)); + } } if (oneof_index >= 0) { @@ -1899,18 +1929,20 @@ static VALUE MessageBuilderContext_required(int argc, VALUE* argv, */ static VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self) { - VALUE name, type, number, type_class; + VALUE name, type, number; + VALUE type_class, options = Qnil; - if (argc < 3) { - rb_raise(rb_eArgError, "Expected at least 3 arguments."); + rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options); + + // Allow passing (name, type, number, options) or + // (name, type, number, type_class, options) + if (argc == 4 && RB_TYPE_P(type_class, T_HASH)) { + options = type_class; + type_class = Qnil; } - name = argv[0]; - type = argv[1]; - number = argv[2]; - type_class = (argc > 3) ? argv[3] : Qnil; msgdef_add_field(_self, UPB_LABEL_REPEATED, name, type, number, type_class, - Qnil, -1, false); + options, -1, false); return Qnil; } diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc index a6935c76584..cca69de0872 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc @@ -220,6 +220,11 @@ void GenerateField(const FieldDescriptor* field, io::Printer* printer) { DefaultValueForField(field)); } + if (field->has_json_name()) { + printer->Print(", json_name: \"$json_name$\"", "json_name", + field->json_name()); + } + printer->Print("\n"); } } From 97c6004a5a6b9722b58a4800c0c7fd53473255d1 Mon Sep 17 00:00:00 2001 From: Lukas Fittl Date: Sat, 3 Apr 2021 18:42:43 -0700 Subject: [PATCH 2/3] Address review feedback --- ruby/ext/google/protobuf_c/defs.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 635aac35fa6..992f54bc4d6 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -879,9 +879,7 @@ static VALUE FieldDescriptor_json_name(VALUE _self) { FieldDescriptor* self = ruby_to_FieldDescriptor(_self); const upb_fielddef *f = self->fielddef; const char *json_name = upb_fielddef_jsonname(f); - if (json_name != NULL) - return rb_str_new2(json_name); - return Qnil; + return rb_str_new2(json_name); } /* @@ -1773,9 +1771,6 @@ static void msgdef_add_field(VALUE msgbuilder_rb, upb_label_t label, VALUE name, VALUE json_name = rb_hash_lookup(options, ID2SYM(rb_intern("json_name"))); - /* Call #to_s since json_name is always a string in the descriptor. */ - json_name = rb_funcall(json_name, rb_intern("to_s"), 0); - google_protobuf_FieldDescriptorProto_set_json_name( field_proto, FileBuilderContext_strdup(self->file_builder, json_name)); From ff3258d0542349c1cbbdf841d79a1c58776fdb90 Mon Sep 17 00:00:00 2001 From: Lukas Fittl Date: Sat, 3 Apr 2021 18:42:54 -0700 Subject: [PATCH 3/3] Add test for json_name functionality --- ruby/tests/encode_decode_test.rb | 6 ++++++ ruby/tests/generated_code.proto | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/ruby/tests/encode_decode_test.rb b/ruby/tests/encode_decode_test.rb index cce364d0d81..429ac433221 100755 --- a/ruby/tests/encode_decode_test.rb +++ b/ruby/tests/encode_decode_test.rb @@ -95,4 +95,10 @@ def test_encode_wrong_msg end end + def test_json_name + msg = A::B::C::TestJsonName.new(:value => 42) + json = msg.to_json + assert_match json, "{\"CustomJsonName\":42}" + end + end diff --git a/ruby/tests/generated_code.proto b/ruby/tests/generated_code.proto index e8116697d7a..bfdfa5aa780 100644 --- a/ruby/tests/generated_code.proto +++ b/ruby/tests/generated_code.proto @@ -83,3 +83,7 @@ message TestUnknown { map map_unknown = 67; int32 unknown_field = 89; } + +message TestJsonName { + int32 value = 1 [json_name = "CustomJsonName"]; +}