Skip to content

Commit

Permalink
Merge pull request #52 from wantedly/fetburner/lazy-default-mask
Browse files Browse the repository at this point in the history
Generate the default mask lazily to prevent infinite recursions
  • Loading branch information
fetburner authored Sep 7, 2023
2 parents dfefdef + 9760257 commit acb3ffd
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 6 deletions.
2 changes: 1 addition & 1 deletion lib/pb/serializable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def self.included(base)
# @param with [
# Google::Protobuf::FieldMask,
# Array<(Symbol, Hash)>,
# Hash{Symbol=>(Array,Symbol,Hash)},
# Hash{Symbol=>(Array,Symbol,Hash,Proc)},
# ]
# Specifies the list of fields to be serialized in the Proto message object.
# `nil` means that all fields defined in .proto will be serialized.
Expand Down
12 changes: 7 additions & 5 deletions lib/pb/serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def build_default_mask(descriptor)
"google.protobuf.BoolValue" ,
"google.protobuf.BytesValue" then m << fd.name.to_sym
else
m << { fd.name.to_sym => build_default_mask(fd.subtype) }
m << { fd.name.to_sym => -> { build_default_mask(fd.subtype) } }
end
else
m << fd.name.to_sym
Expand All @@ -102,22 +102,24 @@ def parse_field_mask(field_mask)
end
end

# @param input [Google::Protobuf::FieldMask, Symbol, Array<(Symbol,Hash)>, Hash{Symbol=>(Array,Symbol,Hash)}]
# @return [Hash{Symbol=>(Array,Hash)}]
# @param input [Google::Protobuf::FieldMask, Symbol, Array<(Symbol,Hash)>, Hash{Symbol=>(Array,Symbol,Hash,Proc)}, Proc]
# @return [Hash{Symbol=>(Array,Hash,Proc)}]
def normalize_mask(input)
if input.kind_of?(Google::Protobuf::FieldMask)
input = parse_field_mask(input)
end

normalized = {}

input = input.call if input.kind_of?(Proc)
input = [input] if input.kind_of?(Hash)

normalized = {}
Array(input).each do |el|
case el
when Symbol
normalized[el] ||= []
when Hash
el.each do |k, v|
v = v.call if v.kind_of?(Proc)
v = [v] if v.kind_of?(Hash)
normalized[k] ||= []
normalized[k].push(*Array(v))
Expand Down
33 changes: 33 additions & 0 deletions spec/examples/recursive_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
RSpec.describe 'recursive models' do
module self::Sandbox
class StringListSerializer < Pb::Serializer::Base
message TestFixture::Recursive::StringList

attribute :car
attribute :cdr, allow_nil: true, serializer: StringListSerializer
end

StringList = Struct.new(:car, :cdr)
end

let(:sandbox) { self.class::Sandbox }

it "serializes recursive ruby object into protobuf type" do
l = sandbox::StringList.new(
"Alpha",
sandbox::StringList.new(
"Bravo",
sandbox::StringList.new(
"Charlie",
nil,
),
),
)
pb = sandbox::StringListSerializer.new(l).to_pb

expect(pb).to be_a(TestFixture::Recursive::StringList)
expect(pb.car).to eq "Alpha"
expect(pb.cdr.car).to eq "Bravo"
expect(pb.cdr.cdr.car).to eq "Charlie"
end
end
10 changes: 10 additions & 0 deletions spec/fixtures/recursive_model.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
syntax = "proto3";

package example.recursive;

option ruby_package = "TestFixture::Recursive";

message StringList {
string car = 1;
StringList cdr = 2;
}
38 changes: 38 additions & 0 deletions spec/fixtures/recursive_model_pb.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit acb3ffd

Please sign in to comment.