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>'
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.
Most helpful comment
TL:DR: documentation, you will need to use the instance method
#replaceinstead of+=.I ran into similar issue recently and was able to solved it by replacing
+=with the#replacemethod. As such, you will need to dotest.repeated_custom.replace([custom_value])instead oftest.repeated_custom += [custom_value]in your code. I hope that help.