protobuf 3.10.9 → 3.10.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +106 -22
- data/.rubocop.yml +8 -0
- data/.rubocop_todo.yml +6 -0
- data/CHANGES.md +31 -0
- data/Gemfile +9 -10
- data/lib/protobuf/code_generator.rb +17 -5
- data/lib/protobuf/decoder.rb +33 -2
- data/lib/protobuf/field/base_field.rb +13 -5
- data/lib/protobuf/field/base_field_object_definitions.rb +40 -6
- data/lib/protobuf/field/bool_field.rb +2 -1
- data/lib/protobuf/field/int32_field.rb +12 -0
- data/lib/protobuf/field/sint32_field.rb +10 -0
- data/lib/protobuf/field/uint32_field.rb +9 -0
- data/lib/protobuf/generators/printable.rb +10 -1
- data/lib/protobuf/message/serialization.rb +4 -4
- data/lib/protobuf/railtie.rb +48 -0
- data/lib/protobuf/rpc/application_service.rb +12 -0
- data/lib/protobuf/rpc/service.rb +7 -3
- data/lib/protobuf/rpc/service_filters.rb +35 -5
- data/lib/protobuf/service_loader.rb +21 -0
- data/lib/protobuf/version.rb +1 -1
- data/lib/protobuf.rb +5 -0
- data/protobuf.gemspec +6 -3
- data/spec/encoding/packed_decoding_spec.rb +233 -0
- data/spec/encoding/wire_compat_decoding_spec.rb +113 -0
- data/spec/functional/railtie_boot_spec.rb +84 -0
- data/spec/lib/protobuf/field/bool_field_spec.rb +21 -0
- data/spec/lib/protobuf/generators/service_generator_spec.rb +23 -0
- data/spec/lib/protobuf/railtie_spec.rb +86 -0
- data/spec/lib/protobuf/rpc/application_service_spec.rb +57 -0
- data/spec/lib/protobuf/rpc/service_filters_inheritance_spec.rb +105 -0
- data/spec/lib/protobuf/service_loader_spec.rb +88 -0
- metadata +51 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8096ad0d169fe7be73b2cce91ed6b45fc29ce8377f6599468ea4b165f3cba5fc
|
|
4
|
+
data.tar.gz: f562a6ffed606efb06c22f429859d3c6f34a95024e12775b5fe500242f4af760
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 759ac6f9f38a1eb1a462cbaa8b7f8c172563fe85963fa51e8557d37464def8dd486b66a4e272593b6a9cb10f6094df3848678b139dd8fe15fd057f3a1d50d512
|
|
7
|
+
data.tar.gz: eacef1704e921e60280621d4cea12396c43a20e597e0485dfb402c18cf2d918d110f33cfb88fec57d729eb1bdf797c15abef77216604c99b3dca845d55ac1e21
|
data/.circleci/config.yml
CHANGED
|
@@ -1,46 +1,130 @@
|
|
|
1
1
|
version: 2.1
|
|
2
2
|
|
|
3
3
|
jobs:
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
rubocop:
|
|
5
|
+
docker:
|
|
6
|
+
# RuboCop 0.81 does not load on Ruby 4.0 (it needs the benchmark gem).
|
|
7
|
+
- image: cimg/ruby:3.4
|
|
8
|
+
steps:
|
|
9
|
+
- checkout
|
|
10
|
+
- run:
|
|
11
|
+
name: Install gems
|
|
12
|
+
command: bundle install --jobs 4 --retry 3
|
|
13
|
+
- run:
|
|
14
|
+
name: RuboCop
|
|
15
|
+
command: bundle exec rubocop
|
|
16
|
+
|
|
6
17
|
test:
|
|
7
18
|
parameters:
|
|
8
|
-
ruby
|
|
19
|
+
ruby:
|
|
20
|
+
type: string
|
|
21
|
+
rails:
|
|
9
22
|
type: string
|
|
10
23
|
docker:
|
|
11
|
-
- image:
|
|
24
|
+
- image: cimg/ruby:<< parameters.ruby >>
|
|
25
|
+
environment:
|
|
26
|
+
# Newer protoc versions reject the explicit `map_entry` option in the spec protos.
|
|
27
|
+
PROTOC_VERSION: "3.16.0"
|
|
28
|
+
RAILS_VERSION: "~> << parameters.rails >>.0"
|
|
12
29
|
steps:
|
|
30
|
+
- checkout
|
|
31
|
+
- run:
|
|
32
|
+
name: Install ZeroMQ
|
|
33
|
+
command: sudo apt-get update && sudo apt-get install -y libzmq3-dev
|
|
13
34
|
- run:
|
|
14
|
-
name: Install
|
|
35
|
+
name: Install protoc
|
|
15
36
|
command: |
|
|
16
|
-
archive=protoc-
|
|
17
|
-
curl -
|
|
18
|
-
|
|
19
|
-
sudo
|
|
20
|
-
|
|
37
|
+
archive="protoc-${PROTOC_VERSION}-linux-x86_64"
|
|
38
|
+
curl -sSL -o "/tmp/${archive}.zip" \
|
|
39
|
+
"https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${archive}.zip"
|
|
40
|
+
sudo unzip -q -o -d /usr/local "/tmp/${archive}.zip" 'bin/*' 'include/*'
|
|
41
|
+
sudo chmod -R a+rX /usr/local/bin/protoc /usr/local/include/google
|
|
42
|
+
protoc --version
|
|
43
|
+
# Gemfile.lock is not committed, so each job resolves gems for its own
|
|
44
|
+
# Ruby and RAILS_VERSION.
|
|
45
|
+
- run:
|
|
46
|
+
name: Install gems
|
|
47
|
+
command: bundle install --jobs 4 --retry 3
|
|
48
|
+
- run:
|
|
49
|
+
name: Compile protos
|
|
50
|
+
command: bundle exec rake compile:spec compile:rpc
|
|
21
51
|
- run:
|
|
22
|
-
|
|
52
|
+
name: Check compiled protos are committed
|
|
53
|
+
command: git diff --exit-code
|
|
23
54
|
- run:
|
|
24
|
-
|
|
55
|
+
name: Run specs
|
|
56
|
+
command: bundle exec rspec
|
|
57
|
+
|
|
58
|
+
test-jruby:
|
|
59
|
+
parameters:
|
|
60
|
+
jruby:
|
|
61
|
+
type: string
|
|
62
|
+
rails:
|
|
63
|
+
type: string
|
|
64
|
+
docker:
|
|
65
|
+
- image: jruby:<< parameters.jruby >>
|
|
66
|
+
environment:
|
|
67
|
+
PROTOC_VERSION: "3.16.0"
|
|
68
|
+
RAILS_VERSION: "~> << parameters.rails >>.0"
|
|
69
|
+
JRUBY_OPTS: "--dev"
|
|
70
|
+
steps:
|
|
71
|
+
# The jruby image has no git, so install it before checkout.
|
|
25
72
|
- run:
|
|
26
|
-
|
|
27
|
-
command:
|
|
73
|
+
name: Install system packages
|
|
74
|
+
command: apt-get update && apt-get install -y git unzip build-essential libzmq3-dev
|
|
28
75
|
- checkout
|
|
29
76
|
- run:
|
|
30
|
-
|
|
77
|
+
name: Install protoc
|
|
78
|
+
command: |
|
|
79
|
+
archive="protoc-${PROTOC_VERSION}-linux-x86_64"
|
|
80
|
+
curl -sSL -o "/tmp/${archive}.zip" \
|
|
81
|
+
"https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/${archive}.zip"
|
|
82
|
+
unzip -q -o -d /usr/local "/tmp/${archive}.zip" 'bin/*' 'include/*'
|
|
83
|
+
protoc --version
|
|
84
|
+
- run:
|
|
85
|
+
name: Install gems
|
|
86
|
+
command: bundle install --jobs 4 --retry 3
|
|
87
|
+
- run:
|
|
88
|
+
name: Compile protos
|
|
89
|
+
command: bundle exec rake compile:spec compile:rpc
|
|
31
90
|
- run:
|
|
32
|
-
|
|
91
|
+
name: Check compiled protos are committed
|
|
92
|
+
command: git diff --exit-code
|
|
33
93
|
- run:
|
|
34
|
-
|
|
94
|
+
name: Run specs
|
|
95
|
+
command: bundle exec rspec
|
|
35
96
|
|
|
36
97
|
workflows:
|
|
37
98
|
build_and_test:
|
|
38
99
|
jobs:
|
|
100
|
+
- rubocop
|
|
39
101
|
- test:
|
|
102
|
+
name: test-ruby-<< matrix.ruby >>-rails-<< matrix.rails >>
|
|
103
|
+
matrix:
|
|
104
|
+
parameters:
|
|
105
|
+
ruby: ["3.1", "3.2", "3.3", "3.4", "4.0"]
|
|
106
|
+
rails: ["6.0", "6.1", "7.0", "7.1", "7.2", "8.0", "8.1"]
|
|
107
|
+
exclude:
|
|
108
|
+
# Rails 8.0+ requires Ruby 3.2+.
|
|
109
|
+
- { ruby: "3.1", rails: "8.0" }
|
|
110
|
+
- { ruby: "3.1", rails: "8.1" }
|
|
111
|
+
# Rails 6.x does not load on Ruby 3.4+: it needs bigdecimal and
|
|
112
|
+
# logger, which are no longer default gems, and does not declare them.
|
|
113
|
+
- { ruby: "3.4", rails: "6.0" }
|
|
114
|
+
- { ruby: "3.4", rails: "6.1" }
|
|
115
|
+
- { ruby: "4.0", rails: "6.0" }
|
|
116
|
+
- { ruby: "4.0", rails: "6.1" }
|
|
117
|
+
- test-jruby:
|
|
118
|
+
name: test-jruby-<< matrix.jruby >>-rails-<< matrix.rails >>
|
|
40
119
|
matrix:
|
|
41
120
|
parameters:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
121
|
+
# JRuby 9.4 is Ruby 3.1; JRuby 10.0 is Ruby 3.4.
|
|
122
|
+
jruby: ["9.4", "10.0"]
|
|
123
|
+
rails: ["6.1", "7.1", "7.2", "8.0", "8.1"]
|
|
124
|
+
exclude:
|
|
125
|
+
# Rails 8.0+ requires Ruby 3.2+.
|
|
126
|
+
- { jruby: "9.4", rails: "8.0" }
|
|
127
|
+
- { jruby: "9.4", rails: "8.1" }
|
|
128
|
+
# Rails 6.x does not load on Ruby 3.4 (base64 and bigdecimal are
|
|
129
|
+
# no longer default gems).
|
|
130
|
+
- { jruby: "10.0", rails: "6.1" }
|
data/.rubocop.yml
CHANGED
|
@@ -2,6 +2,9 @@ inherit_from: .rubocop_todo.yml
|
|
|
2
2
|
|
|
3
3
|
AllCops:
|
|
4
4
|
DisplayCopNames: true
|
|
5
|
+
# The newest version RuboCop 0.81 knows. Without it, RuboCop reads the Ruby
|
|
6
|
+
# version from .ruby-version or Gemfile.lock and fails on Ruby 3.x.
|
|
7
|
+
TargetRubyVersion: 2.7
|
|
5
8
|
Exclude:
|
|
6
9
|
- 'spec/support/protos/*.pb.rb'
|
|
7
10
|
- 'varint_prof.rb'
|
|
@@ -79,3 +82,8 @@ Style/TrivialAccessors:
|
|
|
79
82
|
Style/Encoding:
|
|
80
83
|
Exclude:
|
|
81
84
|
- '**/*.pb.rb'
|
|
85
|
+
|
|
86
|
+
# TargetRubyVersion (2.7) cannot match required_ruby_version (>= 3.1), because
|
|
87
|
+
# RuboCop 0.81 does not know Ruby 3.x.
|
|
88
|
+
Gemspec/RequiredRubyVersion:
|
|
89
|
+
Enabled: false
|
data/.rubocop_todo.yml
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
+
# RuboCop 0.81 validates this file alone, before it merges it into
|
|
10
|
+
# .rubocop.yml, so it needs its own TargetRubyVersion. Keep it equal to the
|
|
11
|
+
# value in .rubocop.yml.
|
|
12
|
+
AllCops:
|
|
13
|
+
TargetRubyVersion: 2.7
|
|
14
|
+
|
|
9
15
|
# Offense count: 1
|
|
10
16
|
# Cop supports --auto-correct.
|
|
11
17
|
# Configuration parameters: TreatCommentsAsGroupSeparators, Include.
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Stable (3.8.x)
|
|
2
2
|
|
|
3
|
+
3.10.10
|
|
4
|
+
-------
|
|
5
|
+
- Decode repeated scalar fields by the wire type of each record, not by the
|
|
6
|
+
`packed` declaration. Packed input (the proto3 default in Go, Java, C++ and
|
|
7
|
+
google-protobuf) for a field without `packed` no longer decodes bools as
|
|
8
|
+
`false`, raises on integers and enums, or drops all but the first
|
|
9
|
+
fixed32/float/double value. (#438, #330, #414)
|
|
10
|
+
- Skip a record whose wire type does not match its known field, as the
|
|
11
|
+
reference parsers do. Before, such a record could decode to a wrong value
|
|
12
|
+
or raise `NoMethodError`/`TypeError` for string, bytes, message, and map
|
|
13
|
+
fields.
|
|
14
|
+
- Decode any non-zero bool varint as `true`.
|
|
15
|
+
- An `int32`, `uint32`, or `sint32` field keeps the low 32 bits of a wider
|
|
16
|
+
varint, as the reference parsers do. Before, a peer that sent an int64 value
|
|
17
|
+
for the field (a compatible schema change) made the whole decode raise
|
|
18
|
+
`TypeError`.
|
|
19
|
+
- Skip a group (wire types 3 and 4, from proto2 groups or editions `DELIMITED`
|
|
20
|
+
encoding) as an unknown field. Before, the decode raised
|
|
21
|
+
`Protobuf::InvalidWireType`.
|
|
22
|
+
- Support Ruby 4.0. CircleCI also tests Ruby 4.0, JRuby 9.4, and JRuby 10.0.
|
|
23
|
+
- Require Ruby >= 3.1 and ActiveSupport >= 6.0.
|
|
24
|
+
- Add `Protobuf::Rpc::ApplicationService`, a base class for application services.
|
|
25
|
+
- `Service.implemented_services` walks all descendants, not only direct subclasses.
|
|
26
|
+
- Add `Protobuf::Railtie`. Set `config.protobuf.services_path` to load that
|
|
27
|
+
directory when the RPC server binds, so dynamic discovery sees lazily loaded
|
|
28
|
+
services.
|
|
29
|
+
- Service filters and `rescue_from` handlers now pass from a parent class to
|
|
30
|
+
its subclasses.
|
|
31
|
+
- Add `PB_SERVICE_PARENT` to set the parent class of generated services.
|
|
32
|
+
- CircleCI tests Ruby 3.1 to 3.4 with Rails 6.0 to 8.1, and runs RuboCop.
|
|
33
|
+
|
|
3
34
|
|
|
4
35
|
3.10.0
|
|
5
36
|
------
|
data/Gemfile
CHANGED
|
@@ -2,19 +2,18 @@ source 'https://rubygems.org'
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
+
# CI sets RAILS_VERSION (for example "~> 6.1.0") to test each supported Rails.
|
|
6
|
+
# railties is also a gemspec development dependency, so only activesupport is
|
|
7
|
+
# pinned here and railties follows it.
|
|
8
|
+
if (rails_version = ENV['RAILS_VERSION'])
|
|
9
|
+
gem 'activesupport', rails_version
|
|
10
|
+
end
|
|
11
|
+
|
|
5
12
|
group :development do
|
|
6
13
|
# debuggers only work in MRI
|
|
7
14
|
if RUBY_ENGINE.to_sym == :ruby
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
elsif RUBY_VERSION < '2.4.0'
|
|
11
|
-
gem 'pry', '~> 0.12.0'
|
|
12
|
-
gem 'pry-byebug'
|
|
13
|
-
else
|
|
14
|
-
gem 'pry', '~> 0.13.0'
|
|
15
|
-
gem 'pry-byebug', '~> 3.9.0'
|
|
16
|
-
end
|
|
17
|
-
|
|
15
|
+
gem 'pry'
|
|
16
|
+
gem 'pry-byebug'
|
|
18
17
|
gem 'pry-stack_explorer'
|
|
19
18
|
|
|
20
19
|
gem 'ruby-prof'
|
|
@@ -63,7 +63,14 @@ module Protobuf
|
|
|
63
63
|
def define_set_method!
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
# Same rules as the set methods in BaseFieldObjectDefinitions: a packable
|
|
67
|
+
# repeated field decodes by the record's wire type, not its declaration.
|
|
68
|
+
def set_without_options(message_instance, bytes, record_wire_type = nil)
|
|
69
|
+
if repeated? && packable?
|
|
70
|
+
return set_packable_without_options(message_instance[name], bytes, record_wire_type)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
return if record_wire_type && record_wire_type != wire_type
|
|
67
74
|
return message_instance[name] = decode(bytes) unless repeated?
|
|
68
75
|
|
|
69
76
|
if map?
|
|
@@ -75,9 +82,14 @@ module Protobuf
|
|
|
75
82
|
return hash[entry.key]
|
|
76
83
|
end
|
|
77
84
|
|
|
78
|
-
|
|
85
|
+
message_instance[name] << decode(bytes)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def set_packable_without_options(array, bytes, record_wire_type)
|
|
89
|
+
record_wire_type ||= packed? ? ::Protobuf::WireType::LENGTH_DELIMITED : wire_type
|
|
90
|
+
return array << decode(bytes) if record_wire_type == wire_type
|
|
91
|
+
return unless record_wire_type == ::Protobuf::WireType::LENGTH_DELIMITED
|
|
79
92
|
|
|
80
|
-
array = message_instance[name]
|
|
81
93
|
stream = StringIO.new(bytes)
|
|
82
94
|
|
|
83
95
|
if wire_type == ::Protobuf::WireType::VARINT
|
|
@@ -92,7 +104,7 @@ module Protobuf
|
|
|
92
104
|
# Sets a MessageField that is known to be an option.
|
|
93
105
|
# We must allow fields to be set one at a time, as option syntax allows us to
|
|
94
106
|
# set each field within the option using a separate "option" line.
|
|
95
|
-
def set_with_options(message_instance, bytes)
|
|
107
|
+
def set_with_options(message_instance, bytes, record_wire_type = nil)
|
|
96
108
|
if message_instance[name].is_a?(::Protobuf::Message)
|
|
97
109
|
gp = Google::Protobuf
|
|
98
110
|
if message_instance.is_a?(gp::EnumOptions) || message_instance.is_a?(gp::EnumValueOptions) ||
|
|
@@ -109,7 +121,7 @@ module Protobuf
|
|
|
109
121
|
end
|
|
110
122
|
end
|
|
111
123
|
|
|
112
|
-
set_without_options(message_instance, bytes)
|
|
124
|
+
set_without_options(message_instance, bytes, record_wire_type)
|
|
113
125
|
end
|
|
114
126
|
alias_method :set, :set_with_options
|
|
115
127
|
|
data/lib/protobuf/decoder.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module Protobuf
|
|
2
2
|
class Decoder
|
|
3
3
|
|
|
4
|
-
# Read bytes from +stream+ and
|
|
4
|
+
# Read bytes from +stream+ and yield (tag, bytes, wire_type) for each field.
|
|
5
|
+
# A block that takes only (tag, bytes) still works: blocks drop extra arguments.
|
|
5
6
|
def self.decode_each_field(stream)
|
|
6
7
|
until stream.eof?
|
|
7
8
|
bits = Varint.decode(stream)
|
|
@@ -17,12 +18,42 @@ module Protobuf
|
|
|
17
18
|
stream.read(8)
|
|
18
19
|
elsif wire_type == ::Protobuf::WireType::FIXED32
|
|
19
20
|
stream.read(4)
|
|
21
|
+
elsif wire_type == ::Protobuf::WireType::START_GROUP
|
|
22
|
+
skip_group(stream, tag)
|
|
23
|
+
next
|
|
20
24
|
else
|
|
21
25
|
fail InvalidWireType, wire_type
|
|
22
26
|
end
|
|
23
27
|
|
|
24
|
-
yield(tag, bytes)
|
|
28
|
+
yield(tag, bytes, wire_type)
|
|
25
29
|
end
|
|
26
30
|
end
|
|
31
|
+
|
|
32
|
+
# Skip a group (wire types 3 and 4), with any groups inside it. This gem
|
|
33
|
+
# has no group fields, so the rule for an unknown field applies, as in the
|
|
34
|
+
# reference parsers: skip it. A group without its end raises InvalidWireType.
|
|
35
|
+
def self.skip_group(stream, group_tag)
|
|
36
|
+
open_tags = [group_tag]
|
|
37
|
+
until open_tags.empty?
|
|
38
|
+
fail InvalidWireType, ::Protobuf::WireType::START_GROUP if stream.eof?
|
|
39
|
+
|
|
40
|
+
bits = Varint.decode(stream)
|
|
41
|
+
wire_type = bits & 0x07
|
|
42
|
+
tag = bits >> 3
|
|
43
|
+
|
|
44
|
+
case wire_type
|
|
45
|
+
when ::Protobuf::WireType::VARINT then Varint.decode(stream)
|
|
46
|
+
when ::Protobuf::WireType::LENGTH_DELIMITED then stream.read(Varint.decode(stream))
|
|
47
|
+
when ::Protobuf::WireType::FIXED64 then stream.read(8)
|
|
48
|
+
when ::Protobuf::WireType::FIXED32 then stream.read(4)
|
|
49
|
+
when ::Protobuf::WireType::START_GROUP then open_tags.push(tag)
|
|
50
|
+
when ::Protobuf::WireType::END_GROUP
|
|
51
|
+
fail InvalidWireType, wire_type unless open_tags.pop == tag
|
|
52
|
+
else
|
|
53
|
+
fail InvalidWireType, wire_type
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
private_class_method :skip_group
|
|
27
58
|
end
|
|
28
59
|
end
|
|
@@ -287,8 +287,8 @@ module Protobuf
|
|
|
287
287
|
def define_set_method!
|
|
288
288
|
@set_method = if map?
|
|
289
289
|
OBJECT_MODULE::MapSetMethod.new(self)
|
|
290
|
-
elsif repeated? &&
|
|
291
|
-
OBJECT_MODULE::
|
|
290
|
+
elsif repeated? && packable?
|
|
291
|
+
OBJECT_MODULE::RepeatedPackableSetMethod.new(self)
|
|
292
292
|
elsif repeated?
|
|
293
293
|
OBJECT_MODULE::RepeatedNotPackedSetMethod.new(self)
|
|
294
294
|
else
|
|
@@ -296,8 +296,16 @@ module Protobuf
|
|
|
296
296
|
end
|
|
297
297
|
end
|
|
298
298
|
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
# Whether the field's type can use the packed encoding (scalar numeric
|
|
300
|
+
# types). This is about the type, not about the `packed` option.
|
|
301
|
+
def packable?
|
|
302
|
+
::Protobuf::Field::BaseField::PACKED_TYPES.include?(wire_type)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# +wire_type+ is the wire type of the record that +bytes+ came from. It is
|
|
306
|
+
# nil for callers that do not know it; then the field declaration decides.
|
|
307
|
+
def set(message_instance, bytes, wire_type = nil)
|
|
308
|
+
@set_method.call(message_instance, bytes, wire_type)
|
|
301
309
|
end
|
|
302
310
|
|
|
303
311
|
def tag_encoded
|
|
@@ -372,7 +380,7 @@ module Protobuf
|
|
|
372
380
|
end
|
|
373
381
|
|
|
374
382
|
def validate_packed_field
|
|
375
|
-
if packed? && !
|
|
383
|
+
if packed? && !packable?
|
|
376
384
|
fail "Can't use packed encoding for '#{type_class}' type"
|
|
377
385
|
end
|
|
378
386
|
end
|
|
@@ -116,9 +116,13 @@ module Protobuf
|
|
|
116
116
|
def initialize(selph)
|
|
117
117
|
@selph = selph
|
|
118
118
|
@name = selph.name
|
|
119
|
+
@wire_type = selph.wire_type
|
|
119
120
|
end
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
# A record whose wire type differs from the field's is skipped, as the
|
|
123
|
+
# protobuf spec and the reference parsers do.
|
|
124
|
+
def call(message_instance, bytes, wire_type = nil)
|
|
125
|
+
return if wire_type && wire_type != @wire_type
|
|
122
126
|
message_instance.set_field(@name, @selph.decode(bytes), true, @selph)
|
|
123
127
|
end
|
|
124
128
|
end
|
|
@@ -127,9 +131,11 @@ module Protobuf
|
|
|
127
131
|
def initialize(selph)
|
|
128
132
|
@selph = selph
|
|
129
133
|
@name = selph.name
|
|
134
|
+
@wire_type = selph.wire_type
|
|
130
135
|
end
|
|
131
136
|
|
|
132
|
-
def call(message_instance, bytes)
|
|
137
|
+
def call(message_instance, bytes, wire_type = nil)
|
|
138
|
+
return if wire_type && wire_type != @wire_type
|
|
133
139
|
hash = message_instance[@name]
|
|
134
140
|
entry = @selph.decode(bytes)
|
|
135
141
|
# decoded value could be nil for an
|
|
@@ -139,26 +145,54 @@ module Protobuf
|
|
|
139
145
|
end
|
|
140
146
|
end
|
|
141
147
|
|
|
148
|
+
# Repeated string, bytes, and message fields. These are never packed.
|
|
142
149
|
class RepeatedNotPackedSetMethod
|
|
143
150
|
def initialize(selph)
|
|
144
151
|
@selph = selph
|
|
145
152
|
@name = selph.name
|
|
153
|
+
@wire_type = selph.wire_type
|
|
146
154
|
end
|
|
147
155
|
|
|
148
|
-
def call(message_instance, bytes)
|
|
156
|
+
def call(message_instance, bytes, wire_type = nil)
|
|
157
|
+
return if wire_type && wire_type != @wire_type
|
|
149
158
|
message_instance[@name] << @selph.decode(bytes)
|
|
150
159
|
end
|
|
151
160
|
end
|
|
152
161
|
|
|
153
|
-
|
|
162
|
+
# Repeated scalar numeric fields (varint, fixed32, fixed64). The protobuf
|
|
163
|
+
# spec requires a parser to accept both the packed and the unpacked
|
|
164
|
+
# encoding, whatever the field declares. The wire type of each record
|
|
165
|
+
# selects the decoding: LENGTH_DELIMITED is a packed run, and the field's
|
|
166
|
+
# own wire type is one value. A record without a wire type (an old
|
|
167
|
+
# caller of BaseField#set) falls back to the declaration.
|
|
168
|
+
class RepeatedPackableSetMethod
|
|
169
|
+
LENGTH_DELIMITED = ::Protobuf::WireType::LENGTH_DELIMITED
|
|
170
|
+
|
|
154
171
|
def initialize(selph)
|
|
155
172
|
@selph = selph
|
|
156
173
|
@name = selph.name
|
|
157
174
|
@wire_type = selph.wire_type
|
|
175
|
+
@packed = selph.packed?
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def call(message_instance, bytes, wire_type = nil)
|
|
179
|
+
# Most common first: one unpacked value, as this gem encodes by default.
|
|
180
|
+
if wire_type == @wire_type
|
|
181
|
+
message_instance[@name] << @selph.decode(bytes)
|
|
182
|
+
elsif wire_type == LENGTH_DELIMITED
|
|
183
|
+
decode_packed(message_instance[@name], bytes)
|
|
184
|
+
elsif wire_type.nil?
|
|
185
|
+
if @packed
|
|
186
|
+
decode_packed(message_instance[@name], bytes)
|
|
187
|
+
else
|
|
188
|
+
message_instance[@name] << @selph.decode(bytes)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
158
191
|
end
|
|
159
192
|
|
|
160
|
-
|
|
161
|
-
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
def decode_packed(array, bytes)
|
|
162
196
|
stream = ::StringIO.new(bytes)
|
|
163
197
|
|
|
164
198
|
if @wire_type == ::Protobuf::WireType::VARINT
|
|
@@ -16,6 +16,18 @@ module Protobuf
|
|
|
16
16
|
INT32_MIN
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
##
|
|
20
|
+
# Public Instance Methods
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
# Keep the low 32 bits, as the reference parsers do. A peer that sends
|
|
24
|
+
# an int64 or uint64 value for this field (a compatible schema change)
|
|
25
|
+
# then decodes instead of raising.
|
|
26
|
+
def decode(value)
|
|
27
|
+
value &= 0xffff_ffff
|
|
28
|
+
value >= 0x8000_0000 ? value - 0x1_0000_0000 : value
|
|
29
|
+
end
|
|
30
|
+
|
|
19
31
|
end
|
|
20
32
|
end
|
|
21
33
|
end
|
|
@@ -16,6 +16,16 @@ module Protobuf
|
|
|
16
16
|
INT32_MIN
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
##
|
|
20
|
+
# Public Instance Methods
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
# Keep the low 32 bits before the zigzag decode, as the reference
|
|
24
|
+
# parsers do. A peer that sends a sint64 value then decodes.
|
|
25
|
+
def decode(value)
|
|
26
|
+
super(value & 0xffff_ffff)
|
|
27
|
+
end
|
|
28
|
+
|
|
19
29
|
end
|
|
20
30
|
end
|
|
21
31
|
end
|
|
@@ -82,12 +82,21 @@ module Protobuf
|
|
|
82
82
|
when :enum then
|
|
83
83
|
PARENT_CLASS_ENUM
|
|
84
84
|
when :service then
|
|
85
|
-
|
|
85
|
+
service_parent_class
|
|
86
86
|
else
|
|
87
87
|
fail "Unknown parent class type #{type}: #{caller[0..5].join("\n")}"
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
+
# PB_SERVICE_PARENT sets the parent class of generated services, for
|
|
92
|
+
# example `ApplicationService` or `::Protobuf::Rpc::ApplicationService`.
|
|
93
|
+
# The parent class must be loaded before the generated file.
|
|
94
|
+
#
|
|
95
|
+
def service_parent_class
|
|
96
|
+
parent = ENV['PB_SERVICE_PARENT'].to_s.strip
|
|
97
|
+
parent.empty? ? PARENT_CLASS_SERVICE : parent
|
|
98
|
+
end
|
|
99
|
+
|
|
91
100
|
# Print a class or module block, indicated by type.
|
|
92
101
|
# If a class, can be given a parent class to inherit from.
|
|
93
102
|
# If a block is given, call the block from within an indent block.
|
|
@@ -38,8 +38,8 @@ module Protobuf
|
|
|
38
38
|
# Decode the given stream into this message.
|
|
39
39
|
#
|
|
40
40
|
def decode_from(stream)
|
|
41
|
-
::Protobuf::Decoder.decode_each_field(stream) do |tag, bytes|
|
|
42
|
-
set_field_bytes(tag, bytes)
|
|
41
|
+
::Protobuf::Decoder.decode_each_field(stream) do |tag, bytes, wire_type|
|
|
42
|
+
set_field_bytes(tag, bytes, wire_type)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
self
|
|
@@ -75,9 +75,9 @@ module Protobuf
|
|
|
75
75
|
|
|
76
76
|
private
|
|
77
77
|
|
|
78
|
-
def set_field_bytes(tag, bytes)
|
|
78
|
+
def set_field_bytes(tag, bytes, wire_type = nil)
|
|
79
79
|
field = _protobuf_message_field[tag]
|
|
80
|
-
field.set(self, bytes) if field
|
|
80
|
+
field.set(self, bytes, wire_type) if field
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'rails/railtie'
|
|
2
|
+
require 'protobuf/service_loader'
|
|
3
|
+
|
|
4
|
+
module Protobuf
|
|
5
|
+
# RPC discovery (`Service.implemented_services`) walks `descendants`, which
|
|
6
|
+
# only sees loaded classes, and Zeitwerk loads lazily. This railtie loads the
|
|
7
|
+
# configured services directory one time, when the RPC server is about to
|
|
8
|
+
# bind. Web, console, and test processes never pay that cost.
|
|
9
|
+
#
|
|
10
|
+
# Opt in from config/application.rb:
|
|
11
|
+
#
|
|
12
|
+
# config.protobuf.services_path = 'app/services/rpc'
|
|
13
|
+
#
|
|
14
|
+
# A relative path resolves against Rails.root.
|
|
15
|
+
class Railtie < ::Rails::Railtie
|
|
16
|
+
config.protobuf = ::ActiveSupport::OrderedOptions.new
|
|
17
|
+
config.protobuf.services_path = nil
|
|
18
|
+
|
|
19
|
+
initializer 'protobuf.load_services' do |app|
|
|
20
|
+
::ActiveSupport::Notifications.subscribe('before_server_bind') do
|
|
21
|
+
::Protobuf::Railtie.load_services(app)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.services_path(app)
|
|
26
|
+
configured = app.config.protobuf.services_path.to_s
|
|
27
|
+
return if configured.empty?
|
|
28
|
+
|
|
29
|
+
::File.expand_path(configured, app.root.to_s)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.load_services(app)
|
|
33
|
+
path = services_path(app)
|
|
34
|
+
return unless path && ::File.directory?(path)
|
|
35
|
+
|
|
36
|
+
loader = ::Rails.autoloaders.main # nil with the classic autoloader
|
|
37
|
+
if loader.respond_to?(:eager_load_dir) # Zeitwerk 2.6.2+
|
|
38
|
+
begin
|
|
39
|
+
return loader.eager_load_dir(path)
|
|
40
|
+
rescue ::Zeitwerk::Error
|
|
41
|
+
# The main autoloader does not manage this path. Fall through.
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
::Protobuf::ServiceLoader.load_services(path)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'protobuf/rpc/service'
|
|
2
|
+
|
|
3
|
+
module Protobuf
|
|
4
|
+
module Rpc
|
|
5
|
+
# Base class for application services, like `ApplicationController`.
|
|
6
|
+
# Filters and `rescue_from` handlers defined here pass to every subclass.
|
|
7
|
+
# Generate services with `PB_SERVICE_PARENT=::Protobuf::Rpc::ApplicationService`
|
|
8
|
+
# to inherit from it.
|
|
9
|
+
class ApplicationService < Service
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|