From 69d56ca4ed925a753665441c5151ac069c3096cb Mon Sep 17 00:00:00 2001 From: Jonathan Baker Date: Tue, 7 Jul 2026 15:50:42 -0400 Subject: [PATCH] Normalize FieldDescriptor#type; add #json_name and #default_value - #type: override the delegated type enum to a plain symbol for every field, dropping the TYPE_ prefix and downcasing (:TYPE_INT32 -> :int32, :TYPE_MESSAGE -> :message). descriptor.type is read in exactly one place (the normalization); the type predicates and map detection are defined in terms of #type. - #json_name: the protoc-computed JSON name. - #default_value: the proto2 default as a string, or nil when absent. --- lib/proto_plugin/field_descriptor.rb | 43 ++++++++++++++++++++-- test/proto_plugin/field_descriptor_test.rb | 30 +++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/lib/proto_plugin/field_descriptor.rb b/lib/proto_plugin/field_descriptor.rb index 207b767..5256c6c 100644 --- a/lib/proto_plugin/field_descriptor.rb +++ b/lib/proto_plugin/field_descriptor.rb @@ -86,21 +86,56 @@ def value # # @return [Boolean] def message? - type == :TYPE_MESSAGE && !map? + type == :message && !map? end # Returns true if the field is an enum type. # # @return [Boolean] def enum? - type == :TYPE_ENUM + type == :enum end # Returns true if the field is a group type. # # @return [Boolean] def group? - type == :TYPE_GROUP + type == :group + end + + # The field's type as a plain symbol. + # + # Normalizes the underlying `type` enum by removing its `TYPE_` prefix and + # downcasing, e.g. `:TYPE_INT32` becomes `:int32` and `:TYPE_MESSAGE` + # becomes `:message`. For `:message` and `:enum` fields, use + # {#type_descriptor} (or {#key}/{#value} for maps) to resolve the referenced + # type. + # + # @example + # field.type #=> :int32 + # field.type #=> :message + # + # @return [Symbol] + def type + descriptor.type.to_s.delete_prefix("TYPE_").downcase.to_sym + end + + # The JSON name of the field, as computed by `protoc` (the lowerCamelCase + # form unless overridden with the `json_name` option). + # + # @return [String] + def json_name + descriptor.json_name + end + + # The explicit default value declared for the field (proto2 only), as a + # string. Proto3 fields do not carry defaults. + # + # @return [String] the declared default + # @return [nil] if no default was declared + def default_value + value = descriptor.default_value + value unless value.nil? || value.empty? end # Returns true if the field is a scalar type (i.e. not a message, enum, @@ -183,7 +218,7 @@ def repeated_label? def map_entry return @map_entry if defined?(@map_entry) - @map_entry = if repeated_label? && type == :TYPE_MESSAGE + @map_entry = if repeated_label? && type == :message name = type_name.split(".").last proto = message.descriptor.nested_type.find do |n| n.name == name && n.options&.map_entry diff --git a/test/proto_plugin/field_descriptor_test.rb b/test/proto_plugin/field_descriptor_test.rb index 0f9902e..01d6af1 100644 --- a/test/proto_plugin/field_descriptor_test.rb +++ b/test/proto_plugin/field_descriptor_test.rb @@ -129,6 +129,36 @@ def test_synthetic_map_entries_excluded_from_messages assert_empty(digest.messages) end + def test_type_normalizes_the_enum + assert_equal(:uint64, @fields["id"].type) + assert_equal(:string, @fields["title"].type) + assert_equal(:message, @fields["author"].type) + assert_equal(:message, @fields["comments"].type) # repeated message + end + + def test_json_name + assert_equal("publishedAt", @fields["published_at"].json_name) + assert_equal("title", @fields["title"].json_name) + end + + def test_default_value + field = FieldDescriptor.new( + Google::Protobuf::FieldDescriptorProto.new( + name: "count", + type: :TYPE_INT32, + default_value: "42", + ), + @article, + @context, + ) + + assert_equal("42", field.default_value) + end + + def test_default_value_nil_when_absent + assert_nil(@fields["title"].default_value) + end + def test_oneof_membership event = @context.type_by_proto_name(".proto_plugin.fixtures.CommentEvent") fields = event.fields.each_with_object({}) do |field, hash|