diff --git a/.travis.yml b/.travis.yml index 015168e..ccc4a3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ sudo: false language: ruby cache: bundler rvm: - - 2.6.3 + - 3.2.1 before_install: - - gem install bundler -v 2.0.2 + - gem install bundler -v 2.3.7 script: - bundle exec rspec diff --git a/Gemfile.lock b/Gemfile.lock index 123e550..704b077 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,27 +7,27 @@ PATH GEM remote: https://rubygems.org/ specs: - coderay (1.1.2) - diff-lcs (1.3) - google-protobuf (3.11.4) - method_source (0.9.2) - pry (0.12.2) - coderay (~> 1.1.0) - method_source (~> 0.9.0) - rake (13.0.1) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.1) - rspec-support (~> 3.9.1) - rspec-expectations (3.9.0) + coderay (1.1.3) + diff-lcs (1.5.0) + google-protobuf (3.25.1) + method_source (1.0.0) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + rake (13.1.0) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-mocks (3.9.1) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.2) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) PLATFORMS ruby diff --git a/lib/pb.rb b/lib/pb.rb index 62f08fc..5d47483 100644 --- a/lib/pb.rb +++ b/lib/pb.rb @@ -212,13 +212,34 @@ def to_proto_one(klass, v) type_info = field_types[k.to_s] next if type_info.nil? # Ignore unknown field - if type_info.type == :message - params[k] = to_proto(type_info.subtype.msgclass, vv) + params[k] = serialize(type_info, vv) + end + + klass.new(params) + end + + # klass is special _MapEntry_ message class + def to_proto_map(klass, v) + key_descriptor = klass.descriptor.entries.find { |e| e.name == 'key' } + value_descriptor = klass.descriptor.entries.find { |e| e.name == 'value' } + v.to_h do |k, vv| + [ + serialize(key_descriptor, k), + serialize(value_descriptor, vv) + ] + end + end + + def serialize(descriptor, value) + if descriptor.type == :message + if descriptor.subtype.options.map_entry + to_proto_map(descriptor.subtype.msgclass, value) else - params[k] = to_primitive(type_info.type, vv) + to_proto(descriptor.subtype.msgclass, value) end + else + to_primitive(descriptor.type, value) end - klass.new(params) end # @param [Symbol] type The type of protobuf field. e.g. :enum, :int64, diff --git a/spec/pb_spec.rb b/spec/pb_spec.rb index 75f7121..0d923cc 100644 --- a/spec/pb_spec.rb +++ b/spec/pb_spec.rb @@ -300,6 +300,67 @@ ) ) end + + end + context "when message entry is a map" do + let(:proto_class_a) { + klass_b = proto_class_b # Assign to variable + + # Use random name to avoid conflict + proto_class_name = "proto_class_a_#{Array.new(20) { rand(('a'.ord)..('z'.ord)).chr }.join}" + + Google::Protobuf::DescriptorPool.generated_pool.build do + add_message proto_class_name do + map :map_with_primitive_value, :string, :string, 1 + map :map_with_message_value, :string, :message, 2, klass_b.descriptor.name + map :map_with_builtin_value, :string, :message, 3, "google.protobuf.Timestamp" + end + end + + Google::Protobuf::DescriptorPool.generated_pool.lookup(proto_class_name).msgclass + } + let(:proto_class_b) { + # Use random name to avoid conflict + proto_class_name = "proto_class_b_#{Array.new(20) { rand(('a'.ord)..('z'.ord)).chr }.join}" + + Google::Protobuf::DescriptorPool.generated_pool.build do + add_message proto_class_name do + optional :id, :int64, 1 + end + end + + Google::Protobuf::DescriptorPool.generated_pool.lookup(proto_class_name).msgclass + } + + it "returns proto object" do + expect(Pb.to_proto(proto_class_a, {})).to eq proto_class_a.new + + expect(Pb.to_proto(proto_class_a, { + map_with_primitive_value: { + "foo": "bar", + }, + map_with_message_value: { + "foo": { + "id": 123 + }, + }, + map_with_builtin_value: { + "foo": "2019-02-03T00:00:00+09:00", + } + })).to eq proto_class_a.new({ + map_with_primitive_value: { + "foo": "bar", + }, + map_with_message_value: { + "foo": { + "id": 123 + }, + }, + map_with_builtin_value: { + "foo": Google::Protobuf::Timestamp.new(seconds: Time.new(2019, 2, 3).to_i), + } + }) + end end end end