protobuf 3.10.8 → 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 +30 -11
- data/.rubocop_todo.yml +462 -60
- data/CHANGES.md +31 -0
- data/Gemfile +22 -0
- 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/buffer.rb +2 -2
- 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 +9 -27
- 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/field/string_field_spec.rb +0 -2
- data/spec/lib/protobuf/generators/service_generator_spec.rb +23 -0
- data/spec/lib/protobuf/message_spec.rb +0 -2
- 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 +49 -77
|
@@ -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
|
data/lib/protobuf/rpc/buffer.rb
CHANGED
|
@@ -44,7 +44,7 @@ module Protobuf
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
def set_data(data) # rubocop:disable
|
|
47
|
+
def set_data(data) # rubocop:disable Naming/AccessorMethodName
|
|
48
48
|
@data = data.to_s
|
|
49
49
|
@size = @data.size
|
|
50
50
|
end
|
|
@@ -61,7 +61,7 @@ module Protobuf
|
|
|
61
61
|
@flush
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
def get_data_size # rubocop:disable
|
|
64
|
+
def get_data_size # rubocop:disable Naming/AccessorMethodName
|
|
65
65
|
if @size == 0 || @data.match(SIZE_REGEX)
|
|
66
66
|
sliced_size = @data.slice!(SIZE_REGEX)
|
|
67
67
|
@size = sliced_size.delete('-').to_i unless sliced_size.nil?
|
data/lib/protobuf/rpc/service.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'active_support/core_ext/class/subclasses'
|
|
1
2
|
require 'protobuf/logging'
|
|
2
3
|
require 'protobuf/message'
|
|
3
4
|
require 'protobuf/rpc/client'
|
|
@@ -65,15 +66,18 @@ module Protobuf
|
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
# An array of defined service classes that contain implementation
|
|
68
|
-
# code
|
|
69
|
+
# code. Walks the full descendant tree so intermediate parents
|
|
70
|
+
# (e.g. ApplicationService) don't hide the real services from
|
|
71
|
+
# discovery.
|
|
69
72
|
def self.implemented_services
|
|
70
|
-
classes =
|
|
73
|
+
classes = descendants.select do |subclass|
|
|
71
74
|
subclass.rpcs.any? do |(name, _)|
|
|
72
75
|
subclass.method_defined? name
|
|
73
76
|
end
|
|
74
77
|
end
|
|
75
78
|
|
|
76
|
-
classes
|
|
79
|
+
# Dev reloads can leave nameless/duplicate classes in `descendants`.
|
|
80
|
+
classes.map(&:name).compact.uniq
|
|
77
81
|
end
|
|
78
82
|
|
|
79
83
|
# Shorthand call to configure, passing a string formatted as hostname:port
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'active_support/core_ext/class/subclasses'
|
|
2
|
+
|
|
1
3
|
module Protobuf
|
|
2
4
|
module Rpc
|
|
3
5
|
module ServiceFilters
|
|
@@ -34,23 +36,51 @@ module Protobuf
|
|
|
34
36
|
@rescue_filters ||= {}
|
|
35
37
|
end
|
|
36
38
|
|
|
39
|
+
# A subclass gets the filters of its parent, so a base class such as
|
|
40
|
+
# ApplicationService can hold shared filters. A filter that the parent
|
|
41
|
+
# adds later also goes to existing subclasses (after their own filters).
|
|
42
|
+
def inherited(subclass)
|
|
43
|
+
super
|
|
44
|
+
subclass.inherit_filters_from(self)
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
def rescue_from(*ex_klasses, &block)
|
|
38
48
|
options = ex_klasses.last.is_a?(Hash) ? ex_klasses.pop : {}
|
|
39
49
|
callable = options.delete(:with) { block }
|
|
40
50
|
fail ArgumentError, 'Option :with missing from rescue_from options' if callable.nil?
|
|
41
|
-
ex_klasses.each
|
|
51
|
+
ex_klasses.each do |ex_klass|
|
|
52
|
+
inherited_callable = rescue_filters[ex_klass]
|
|
53
|
+
rescue_filters[ex_klass] = callable
|
|
54
|
+
descendants.each do |klass|
|
|
55
|
+
# A subclass keeps a handler that it defined itself.
|
|
56
|
+
current = klass.rescue_filters[ex_klass]
|
|
57
|
+
klass.rescue_filters[ex_klass] = callable if current.nil? || current.equal?(inherited_callable)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
42
60
|
end
|
|
43
61
|
|
|
44
|
-
|
|
62
|
+
protected
|
|
45
63
|
|
|
46
|
-
def
|
|
64
|
+
def inherit_filters_from(parent)
|
|
65
|
+
parent.filters.each { |type, list| filters[type] = list.dup }
|
|
66
|
+
parent.defined_filters.each { |type, set| defined_filters[type] = set.dup }
|
|
67
|
+
rescue_filters.merge!(parent.rescue_filters)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def defined_filters
|
|
71
|
+
@defined_filters ||= Hash.new { |h, k| h[k] = Set.new }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def add_filter(type, filter, options)
|
|
47
75
|
return if filter_defined?(type, filter)
|
|
48
76
|
filters[type] << options.merge(:callable => filter)
|
|
49
77
|
remember_filter(type, filter)
|
|
50
78
|
end
|
|
51
79
|
|
|
52
|
-
|
|
53
|
-
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def define_filter(type, filter, options = {})
|
|
83
|
+
([self] + descendants).each { |klass| klass.add_filter(type, filter, options) }
|
|
54
84
|
end
|
|
55
85
|
|
|
56
86
|
# Check to see if the filter has been defined.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Protobuf
|
|
2
|
+
# Requires every Ruby file below a directory, in sorted order. Protobuf::Railtie
|
|
3
|
+
# uses it when the main autoloader cannot eager-load the services directory
|
|
4
|
+
# (classic autoloader, Zeitwerk < 2.6.2, or a path outside the main loader).
|
|
5
|
+
#
|
|
6
|
+
# It uses `require`, not `load`. Zeitwerk tracks `require` of the files it
|
|
7
|
+
# manages, and a file never executes two times.
|
|
8
|
+
module ServiceLoader
|
|
9
|
+
DEFAULT_GLOB = '**/*.rb'.freeze
|
|
10
|
+
|
|
11
|
+
def self.load_services(path, glob = DEFAULT_GLOB)
|
|
12
|
+
return [] if path.nil?
|
|
13
|
+
dir = ::File.expand_path(path.to_s)
|
|
14
|
+
return [] unless ::File.directory?(dir)
|
|
15
|
+
|
|
16
|
+
::Dir.glob(::File.join(dir, glob)).sort.each do |file|
|
|
17
|
+
require file
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/protobuf/version.rb
CHANGED
data/lib/protobuf.rb
CHANGED
|
@@ -44,6 +44,10 @@ require 'protobuf/enum'
|
|
|
44
44
|
require 'protobuf/message'
|
|
45
45
|
require 'protobuf/descriptors'
|
|
46
46
|
|
|
47
|
+
# Rails runs this hook when the application class is defined. That works even
|
|
48
|
+
# when protobuf loads before Rails, as `rpc_server` does.
|
|
49
|
+
::ActiveSupport.on_load(:before_configuration) { require 'protobuf/railtie' }
|
|
50
|
+
|
|
47
51
|
module Protobuf
|
|
48
52
|
|
|
49
53
|
class << self
|
|
@@ -114,6 +118,7 @@ end
|
|
|
114
118
|
unless ENV.key?('PB_NO_NETWORKING')
|
|
115
119
|
require 'protobuf/rpc/client'
|
|
116
120
|
require 'protobuf/rpc/service'
|
|
121
|
+
require 'protobuf/rpc/application_service'
|
|
117
122
|
|
|
118
123
|
env_connector_type = ENV.fetch('PB_CLIENT_TYPE') do
|
|
119
124
|
:socket
|
data/protobuf.gemspec
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
1
|
$LOAD_PATH.push ::File.expand_path("../lib", __FILE__)
|
|
3
2
|
require "protobuf/version"
|
|
4
3
|
|
|
@@ -19,39 +18,22 @@ require "protobuf/version"
|
|
|
19
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
20
19
|
s.require_paths = ["lib"]
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
s.add_dependency "activesupport", '>=
|
|
21
|
+
s.required_ruby_version = '>= 3.1'
|
|
22
|
+
|
|
23
|
+
s.add_dependency "activesupport", '>= 6.0'
|
|
25
24
|
s.add_dependency 'middleware'
|
|
26
25
|
s.add_dependency 'thor'
|
|
27
26
|
s.add_dependency 'thread_safe'
|
|
28
27
|
|
|
28
|
+
# Ruby 4.0 no longer loads benchmark from the standard library.
|
|
29
|
+
s.add_development_dependency 'benchmark'
|
|
29
30
|
s.add_development_dependency 'benchmark-ips'
|
|
30
31
|
s.add_development_dependency 'ffi-rzmq'
|
|
31
|
-
s.add_development_dependency '
|
|
32
|
-
s.add_development_dependency '
|
|
33
|
-
s.add_development_dependency
|
|
34
|
-
s.add_development_dependency "
|
|
32
|
+
s.add_development_dependency 'railties', '>= 6.0'
|
|
33
|
+
s.add_development_dependency 'rake', '~> 13.0'
|
|
34
|
+
s.add_development_dependency 'rspec', '~> 3.5'
|
|
35
|
+
s.add_development_dependency "rubocop", "~> 0.81.0"
|
|
35
36
|
s.add_development_dependency 'simplecov'
|
|
36
37
|
s.add_development_dependency 'timecop'
|
|
37
38
|
s.add_development_dependency 'yard'
|
|
38
|
-
|
|
39
|
-
# debuggers only work in MRI
|
|
40
|
-
if RUBY_ENGINE.to_sym == :ruby
|
|
41
|
-
# we don't support MRI < 1.9.3
|
|
42
|
-
pry_debugger = if RUBY_VERSION < '2.0.0'
|
|
43
|
-
'pry-debugger'
|
|
44
|
-
else
|
|
45
|
-
'pry-byebug'
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
s.add_development_dependency pry_debugger
|
|
49
|
-
s.add_development_dependency 'pry-stack_explorer'
|
|
50
|
-
|
|
51
|
-
s.add_development_dependency 'varint'
|
|
52
|
-
s.add_development_dependency 'ruby-prof'
|
|
53
|
-
elsif RUBY_PLATFORM =~ /java/i
|
|
54
|
-
s.add_development_dependency 'fast_blank_java'
|
|
55
|
-
s.add_development_dependency 'pry'
|
|
56
|
-
end
|
|
57
39
|
end
|