Skip to content

Commit 95cefd6

Browse files
committed
First try at implementing patches.
1 parent 493eb76 commit 95cefd6

3 files changed

Lines changed: 544 additions & 1 deletion

File tree

411.diff

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
diff --git a/lib/protobuf/field/bytes_field.rb b/lib/protobuf/field/bytes_field.rb
2+
index 81a3634d..bce24c80 100644
3+
--- a/lib/protobuf/field/bytes_field.rb
4+
+++ b/lib/protobuf/field/bytes_field.rb
5+
@@ -48,7 +48,18 @@ def wire_type
6+
7+
def coerce!(value)
8+
case value
9+
- when String, Symbol
10+
+ when String
11+
+ if value.encoding == Encoding::ASCII_8BIT
12+
+ # This is a "binary" string
13+
+ value
14+
+ else
15+
+ # Assume the value is Base64 encoded (from JSON)
16+
+ # Ideally we'd do the Base64 decoding while processing the JSON,
17+
+ # but this is tricky to do since we don't know the protobuf field
18+
+ # types when we do that.
19+
+ Base64.decode64(value)
20+
+ end
21+
+ when Symbol
22+
value.to_s
23+
when NilClass
24+
nil
25+
diff --git a/lib/protobuf/field/enum_field.rb b/lib/protobuf/field/enum_field.rb
26+
index 6993faff..12867adf 100644
27+
--- a/lib/protobuf/field/enum_field.rb
28+
+++ b/lib/protobuf/field/enum_field.rb
29+
@@ -37,6 +37,11 @@ def coerce!(value)
30+
type_class.fetch(value) || fail(TypeError, "Invalid Enum value: #{value.inspect} for #{name}")
31+
end
32+
33+
+ def json_encode(value, options={})
34+
+ enum = type_class.enums.find { |e| e.to_i == value }
35+
+ enum.to_s(:name)
36+
+ end
37+
+
38+
private
39+
40+
##
41+
diff --git a/lib/protobuf/field/field_array.rb b/lib/protobuf/field/field_array.rb
42+
index eb1f29d9..47f9c379 100644
43+
--- a/lib/protobuf/field/field_array.rb
44+
+++ b/lib/protobuf/field/field_array.rb
45+
@@ -81,6 +81,8 @@ def normalize(value)
46+
47+
if field.is_a?(::Protobuf::Field::EnumField)
48+
field.type_class.fetch(value)
49+
+ elsif field.is_a?(::Protobuf::Field::BytesField)
50+
+ field.coerce!(value)
51+
elsif field.is_a?(::Protobuf::Field::MessageField) && value.is_a?(field.type_class)
52+
value
53+
elsif field.is_a?(::Protobuf::Field::MessageField) && value.respond_to?(:to_hash)
54+
diff --git a/lib/protobuf/message.rb b/lib/protobuf/message.rb
55+
index 06b26b94..d2b3f862 100644
56+
--- a/lib/protobuf/message.rb
57+
+++ b/lib/protobuf/message.rb
58+
@@ -21,6 +21,22 @@ def self.to_json
59+
name
60+
end
61+
62+
+ def self.from_json(json)
63+
+ fields = normalize_json(JSON.parse(json))
64+
+ new(fields)
65+
+ end
66+
+
67+
+ def self.normalize_json(ob)
68+
+ case ob
69+
+ when Array
70+
+ ob.map { |value| normalize_json(value) }
71+
+ when Hash
72+
+ Hash[*ob.flat_map { |key, value| [key.underscore, normalize_json(value)] }]
73+
+ else
74+
+ ob
75+
+ end
76+
+ end
77+
+
78+
##
79+
# Constructor
80+
#
81+
@@ -150,7 +166,7 @@ def to_json_hash(options = {})
82+
83+
# NB: to_json_hash_value should come before json_encode so as to handle
84+
# repeated fields without extra logic.
85+
- hashed_value = if value.respond_to?(:to_json_hash_value)
86+
+ hashed_value = if value.respond_to?(:to_json_hash_value) && !field.is_a?(::Protobuf::Field::EnumField)
87+
value.to_json_hash_value(options)
88+
elsif field.respond_to?(:json_encode)
89+
field.json_encode(value)
90+
diff --git a/spec/encoding/all_types_spec.rb b/spec/encoding/all_types_spec.rb
91+
index fbd38b46..04ddb866 100644
92+
--- a/spec/encoding/all_types_spec.rb
93+
+++ b/spec/encoding/all_types_spec.rb
94+
@@ -18,7 +18,7 @@
95+
:optional_double => 112,
96+
:optional_bool => true,
97+
:optional_string => "115",
98+
- :optional_bytes => "116",
99+
+ :optional_bytes => "116".force_encoding(Encoding::ASCII_8BIT),
100+
:optional_nested_message => Protobuf_unittest::TestAllTypes::NestedMessage.new(:bb => 118),
101+
:optional_foreign_message => Protobuf_unittest::ForeignMessage.new(:c => 119),
102+
:optional_import_message => Protobuf_unittest_import::ImportMessage.new(:d => 120),
103+
@@ -43,7 +43,7 @@
104+
:repeated_double => [212, 312],
105+
:repeated_bool => [true, false],
106+
:repeated_string => ["215", "315"],
107+
- :repeated_bytes => ["216", "316"],
108+
+ :repeated_bytes => ["216".force_encoding(Encoding::ASCII_8BIT), "316".force_encoding(Encoding::ASCII_8BIT)],
109+
:repeated_nested_message => [
110+
::Protobuf_unittest::TestAllTypes::NestedMessage.new(:bb => 218),
111+
::Protobuf_unittest::TestAllTypes::NestedMessage.new(:bb => 318),
112+
@@ -88,7 +88,7 @@
113+
:default_double => 412,
114+
:default_bool => false,
115+
:default_string => "415",
116+
- :default_bytes => "416",
117+
+ :default_bytes => "416".force_encoding(Encoding::ASCII_8BIT),
118+
:default_nested_enum => ::Protobuf_unittest::TestAllTypes::NestedEnum::FOO,
119+
:default_foreign_enum => ::Protobuf_unittest::ForeignEnum::FOREIGN_FOO,
120+
:default_import_enum => ::Protobuf_unittest_import::ImportEnum::IMPORT_FOO,
121+
diff --git a/spec/encoding/extreme_values_spec.rb b/spec/encoding/extreme_values_spec.rb
122+
index 477e695a..7f3d516f 100644
123+
Binary files a/spec/encoding/extreme_values_spec.rb and b/spec/encoding/extreme_values_spec.rb differ
124+
diff --git a/spec/lib/protobuf/field/enum_field_spec.rb b/spec/lib/protobuf/field/enum_field_spec.rb
125+
index cd72760d..c2e04eed 100644
126+
--- a/spec/lib/protobuf/field/enum_field_spec.rb
127+
+++ b/spec/lib/protobuf/field/enum_field_spec.rb
128+
@@ -23,4 +23,22 @@
129+
expect(message.decode(instance.encode).enum).to eq(-33)
130+
end
131+
end
132+
+
133+
+ # https://developers.google.com/protocol-buffers/docs/proto3#json
134+
+ describe '.{to_json, from_json}' do
135+
+ it 'serialises enum value as string' do
136+
+ instance = message.new(:enum => :POSITIVE)
137+
+ expect(instance.to_json).to eq('{"enum":"POSITIVE"}')
138+
+ end
139+
+
140+
+ it 'deserialises enum value as integer' do
141+
+ instance = message.from_json('{"enum":22}')
142+
+ expect(instance.enum).to eq(22)
143+
+ end
144+
+
145+
+ it 'deserialises enum value as string' do
146+
+ instance = message.from_json('{"enum":"NEGATIVE"}')
147+
+ expect(instance.enum).to eq(-33)
148+
+ end
149+
+ end
150+
end
151+
diff --git a/spec/lib/protobuf/message_spec.rb b/spec/lib/protobuf/message_spec.rb
152+
index 13110bc9..40b68a6d 100644
153+
--- a/spec/lib/protobuf/message_spec.rb
154+
+++ b/spec/lib/protobuf/message_spec.rb
155+
@@ -429,7 +429,7 @@
156+
specify { expect(subject.to_json).to eq '{"name":"Test Name","active":false}' }
157+
158+
context 'for byte fields' do
159+
- let(:bytes) { "\x06\x8D1HP\x17:b" }
160+
+ let(:bytes) { "\x06\x8D1HP\x17:b".force_encoding(Encoding::ASCII_8BIT) }
161+
162+
subject do
163+
::Test::ResourceFindRequest.new(:widget_bytes => [bytes])
164+
@@ -439,7 +439,7 @@
165+
end
166+
167+
context 'using lower camel case field names' do
168+
- let(:bytes) { "\x06\x8D1HP\x17:b" }
169+
+ let(:bytes) { "\x06\x8D1HP\x17:b".force_encoding(Encoding::ASCII_8BIT) }
170+
171+
subject do
172+
::Test::ResourceFindRequest.new(:widget_bytes => [bytes])
173+
@@ -449,6 +449,26 @@
174+
end
175+
end
176+
177+
+ describe '.from_json' do
178+
+ it 'decodes optional bytes field with base64' do
179+
+ expected_single_bytes = "\x06\x8D1HP\x17:b".unpack('C*')
180+
+ single_bytes = ::Test::ResourceFindRequest
181+
+ .from_json('{"singleBytes":"Bo0xSFAXOmI="}')
182+
+ .single_bytes.unpack('C*')
183+
+
184+
+ expect(single_bytes).to(eq(expected_single_bytes))
185+
+ end
186+
+
187+
+ it 'decodes repeated bytes field with base64' do
188+
+ expected_widget_bytes = ["\x06\x8D1HP\x17:b"].map { |s| s.unpack('C*') }
189+
+ widget_bytes = ::Test::ResourceFindRequest
190+
+ .from_json('{"widgetBytes":["Bo0xSFAXOmI="]}')
191+
+ .widget_bytes.map { |s| s.unpack('C*') }
192+
+
193+
+ expect(widget_bytes).to(eq(expected_widget_bytes))
194+
+ end
195+
+ end
196+
+
197+
describe '.to_json' do
198+
it 'returns the class name of the message for use in json encoding' do
199+
expect do
200+
diff --git a/spec/support/protos/resource.pb.rb b/spec/support/protos/resource.pb.rb
201+
index f81ef52f..e765b1ce 100644
202+
--- a/spec/support/protos/resource.pb.rb
203+
+++ b/spec/support/protos/resource.pb.rb
204+
@@ -72,6 +72,7 @@ class ResourceFindRequest
205+
optional :bool, :active, 2
206+
repeated :string, :widgets, 3
207+
repeated :bytes, :widget_bytes, 4
208+
+ optional :bytes, :single_bytes, 5
209+
end
210+
211+
class ResourceSleepRequest
212+
@@ -169,4 +170,3 @@ class ResourceService < ::Protobuf::Rpc::Service
213+
end
214+
215+
end
216+
-
217+
diff --git a/spec/support/protos/resource.proto b/spec/support/protos/resource.proto
218+
index 70b338b3..a5573e24 100644
219+
--- a/spec/support/protos/resource.proto
220+
+++ b/spec/support/protos/resource.proto
221+
@@ -47,6 +47,7 @@ message ResourceFindRequest {
222+
optional bool active = 2;
223+
repeated string widgets = 3;
224+
repeated bytes widget_bytes = 4;
225+
+ optional bytes single_bytes = 5;
226+
}
227+
228+
message ResourceSleepRequest {

0 commit comments

Comments
 (0)