Protobuf: Ruby: Repeated Field handling of custom types not working

Created on 5 Feb 2018  路  4Comments  路  Source: protocolbuffers/protobuf

I've found a bug that prevents the modification (or setting) of repeated fields for non-primitive types. I know that setting a repeated custom field at message initialization works, but that's not what we need to be doing. Below I've provided a minimal example showing what I've attempted.

I created a simple protobuf file with two repeated fields; one of a primtive type, and one of a custom type: repeated_test.proto

syntax = "proto3";             

message CustomField {          
  string name = 1;
  string value = 2;
}

message RepeatedTest {
  repeated string       repeated_primitive = 1;
  repeated CustomField  repeated_custom = 2;
}

Generated protobuf with protoc

$ protoc --version
libprotoc 3.5.1

$ protoc --ruby_out=generated/ *proto

generated/repeated_test_pb.rb

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: repeated_test.proto

require 'google/protobuf'

Google::Protobuf::DescriptorPool.generated_pool.build do
  add_message "CustomField" do
    optional :name, :string, 1
    optional :value, :string, 2
  end
  add_message "RepeatedTest" do
    repeated :repeated_primitive, :string, 1
    repeated :repeated_custom, :message, 2, "CustomField"
  end
end

CustomField = Google::Protobuf::DescriptorPool.generated_pool.lookup("CustomField").msgclass
RepeatedTest = Google::Protobuf::DescriptorPool.generated_pool.lookup("RepeatedTest").msgclass

Then I tried to use it (Ruby version of 2.2.2, google-protobuf version of 3.5.1.2)

2.2.2 :001 > test = RepeatedTest.new()
 => <RepeatedTest: repeated_primitive: [], repeated_custom: []>
2.2.2 :002 > custom_value = CustomField.new(name: "foo", value: "bar")
 => <CustomField: name: "foo", value: "bar"> 

# Appending to the primtive repeated field worked as expected
2.2.2 :003 > test.repeated_primitive += ["new_string"]
 => ["new_string"] 
2.2.2 :004 > test
 => <RepeatedTest: repeated_primitive: ["new_string"], repeated_custom: []> 

# But appending to the custom repeated field throws an error
2.2.2 :005 > test.repeated_custom += [custom_value]
TypeError: Repeated field array has wrong message/enum class
    from (irb):5:in `method_missing'
    from (irb):5
    from /Users/cgross/.rbenv/versions/2.2.2/bin/irb:11:in `<main>'

# I also tried pulling out the pre-generated repeated field object, adding to it and putting it back...
2.2.2 :006 > separate_repeated_custom = test.repeated_custom
 => [] 
2.2.2 :008 > separate_repeated_custom.class
 => Google::Protobuf::RepeatedField 

# I can add to it just fine...
2.2.2 :010 > separate_repeated_custom += [custom_value]
 => [<CustomField: name: "foo", value: "bar">] 
2.2.2 :011 > separate_repeated_custom.class
 => Google::Protobuf::RepeatedField 

# But when I try to put it back in (with assignment or appending), it throws the same error as before
2.2.2 :012 > test.repeated_custom = separate_repeated_custom
TypeError: Repeated field array has wrong message/enum class
    from (irb):12:in `method_missing'
    from (irb):12
    from /Users/cgross/.rbenv/versions/2.2.2/bin/irb:11:in `<main>'
2.2.2 :013 > test.repeated_custom += separate_repeated_custom
TypeError: Repeated field array has wrong message/enum class
    from (irb):13:in `method_missing'
    from (irb):13
    from /Users/cgross/.rbenv/versions/2.2.2/bin/irb:11:in `<main>'

enhancement ruby

Most helpful comment

TL:DR: documentation, you will need to use the instance method #replace instead of +=.

I ran into similar issue recently and was able to solved it by replacing += with the #replace method. As such, you will need to do test.repeated_custom.replace([custom_value]) instead of test.repeated_custom += [custom_value] in your code. I hope that help.

All 4 comments

Any chance of getting a reply on this?

TL:DR: documentation, you will need to use the instance method #replace instead of +=.

I ran into similar issue recently and was able to solved it by replacing += with the #replace method. As such, you will need to do test.repeated_custom.replace([custom_value]) instead of test.repeated_custom += [custom_value] in your code. I hope that help.

Thanks @RithyHuot, this worked for me. It'd be nice if the other operators worked, but whatevs. Perhaps this bit of information should be added to the google protobuf documentation pages.

#replace _replaces_ the existing contents, rather than appending them as += does, right? It looks like #concat does the trick, as far as I can tell. I would expect #push to behave similarly as well but I'm getting "wrong number of arguments (given 83, expected 1)" when I use push(*arr) and "Invalid type Array to assign to submessage field" when I use push(arr) so it looks like push is only intended to be used for a single submessage at a time.

Was this page helpful?
0 / 5 - 0 ratings