proto_turf 0.0.2 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a059f77cfc5a504cf09140a84a578a3609ef40c83074897f8dda0eb42bcc011e
4
- data.tar.gz: 1f0156bec33ddd18a48d90a3500b20066bf3a99a57627ef5a811e4efe439a60f
3
+ metadata.gz: d406d02e3db35efe7976a07238831d78ffeefc9692b6082c240aaf61983ec8be
4
+ data.tar.gz: 6ed547fb924c5d14526e22523417397b0b8b64682d4a79ca5aa78284ae940703
5
5
  SHA512:
6
- metadata.gz: 6d7d01d659d17256f3cb3bcba5abb9a1b5634c0e423cb7a0b603855ddbd3c26abb882ca8ae23e651fa5b1e7d39286ff024933fb31c058b57a6077d5b6a4f37ae
7
- data.tar.gz: eab1c0e1e9b220a1eb1a9adf2b969adde64298d248816faffa9ed96f15d0ca565997d9d7e201f8d4fef5fe7cf3d128d971720b3393532fb083704bf1ca640774
6
+ metadata.gz: 8bd87219da56f1a9c555265dde48c7b11e86986b3e14ce4f849747054e7e5dc1c51ac64b43b584894f2215320580a72a9a159f0e94c56b868d99fc4c6ed0cc05
7
+ data.tar.gz: 43d9f128249dfc33e6f8db72a44541cc69a665e168075de1af6f40564e24c0bbdcc12587f1bdb4744ffe2606c3ff66cdc495ca08f06a6a7223c6882626d0b9be
@@ -0,0 +1,18 @@
1
+ name: Lint
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - uses: actions/checkout@v3
12
+ - name: Set up Ruby ${{ matrix.ruby }}
13
+ uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: 3.4
16
+ bundler-cache: true
17
+ - name: Run standardrb
18
+ run: bundle exec standardrb
@@ -0,0 +1,31 @@
1
+ name: Release Gem
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ tags:
7
+ - 'v*.*.*' # Matches semantic versioning tags like v1.0.0
8
+ workflow_dispatch: # Allows manual triggering of the workflow
9
+
10
+ jobs:
11
+ push:
12
+ name: Push gem to RubyGems.org
13
+ runs-on: ubuntu-latest
14
+
15
+ permissions:
16
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
17
+ contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
18
+
19
+ steps:
20
+ # Set up
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ persist-credentials: false
24
+ - name: Set up Ruby
25
+ uses: ruby/setup-ruby@v1
26
+ with:
27
+ bundler-cache: true
28
+ ruby-version: ruby
29
+
30
+ # Release
31
+ - uses: rubygems/release-gem@v1
@@ -0,0 +1,22 @@
1
+ name: Test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby: [3.1, 3.2, 3.3, 3.4]
13
+
14
+ steps:
15
+ - uses: actions/checkout@v3
16
+ - name: Set up Ruby ${{ matrix.ruby }}
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler-cache: true
21
+ - name: Build and test with RSpec
22
+ run: bundle exec rspec
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/CHANGELOG.md CHANGED
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## UNRELEASED
9
9
 
10
- # 0.0.2 - 2025-8-14
10
+ # 0.0.4 - 2025-08-19
11
+
12
+ * Changed emitting of protobuf schemas from file-based to generated from descriptors.
13
+
14
+ # 0.0.3 - 2025-08-15
11
15
 
12
16
  * Initial release.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,110 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ proto_turf (0.0.4)
5
+ excon
6
+ google-protobuf
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.8.7)
12
+ public_suffix (>= 2.0.2, < 7.0)
13
+ ast (2.4.3)
14
+ bigdecimal (3.2.2)
15
+ crack (1.0.0)
16
+ bigdecimal
17
+ rexml
18
+ diff-lcs (1.6.1)
19
+ excon (1.2.9)
20
+ logger
21
+ google-protobuf (4.30.2-arm64-darwin)
22
+ bigdecimal
23
+ rake (>= 13)
24
+ google-protobuf (4.30.2-x86_64-linux)
25
+ bigdecimal
26
+ rake (>= 13)
27
+ hashdiff (1.2.0)
28
+ json (2.13.2)
29
+ language_server-protocol (3.17.0.5)
30
+ lint_roller (1.1.0)
31
+ logger (1.7.0)
32
+ parallel (1.27.0)
33
+ parser (3.3.9.0)
34
+ ast (~> 2.4.1)
35
+ racc
36
+ prism (1.4.0)
37
+ public_suffix (6.0.2)
38
+ racc (1.8.1)
39
+ rainbow (3.1.1)
40
+ rake (13.3.0)
41
+ regexp_parser (2.11.2)
42
+ rexml (3.4.1)
43
+ rspec (3.13.0)
44
+ rspec-core (~> 3.13.0)
45
+ rspec-expectations (~> 3.13.0)
46
+ rspec-mocks (~> 3.13.0)
47
+ rspec-core (3.13.3)
48
+ rspec-support (~> 3.13.0)
49
+ rspec-expectations (3.13.4)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.13.0)
52
+ rspec-mocks (3.13.3)
53
+ diff-lcs (>= 1.2.0, < 2.0)
54
+ rspec-support (~> 3.13.0)
55
+ rspec-support (3.13.3)
56
+ rubocop (1.75.8)
57
+ json (~> 2.3)
58
+ language_server-protocol (~> 3.17.0.2)
59
+ lint_roller (~> 1.1.0)
60
+ parallel (~> 1.10)
61
+ parser (>= 3.3.0.2)
62
+ rainbow (>= 2.2.2, < 4.0)
63
+ regexp_parser (>= 2.9.3, < 3.0)
64
+ rubocop-ast (>= 1.44.0, < 2.0)
65
+ ruby-progressbar (~> 1.7)
66
+ unicode-display_width (>= 2.4.0, < 4.0)
67
+ rubocop-ast (1.46.0)
68
+ parser (>= 3.3.7.2)
69
+ prism (~> 1.4)
70
+ rubocop-performance (1.25.0)
71
+ lint_roller (~> 1.1)
72
+ rubocop (>= 1.75.0, < 2.0)
73
+ rubocop-ast (>= 1.38.0, < 2.0)
74
+ ruby-progressbar (1.13.0)
75
+ standard (1.50.0)
76
+ language_server-protocol (~> 3.17.0.2)
77
+ lint_roller (~> 1.0)
78
+ rubocop (~> 1.75.5)
79
+ standard-custom (~> 1.0.0)
80
+ standard-performance (~> 1.8)
81
+ standard-custom (1.0.2)
82
+ lint_roller (~> 1.0)
83
+ rubocop (~> 1.50)
84
+ standard-performance (1.8.0)
85
+ lint_roller (~> 1.1)
86
+ rubocop-performance (~> 1.25.0)
87
+ standardrb (1.0.1)
88
+ standard
89
+ unicode-display_width (3.1.5)
90
+ unicode-emoji (~> 4.0, >= 4.0.4)
91
+ unicode-emoji (4.0.4)
92
+ webmock (3.25.1)
93
+ addressable (>= 2.8.0)
94
+ crack (>= 0.3.2)
95
+ hashdiff (>= 0.4.0, < 2.0.0)
96
+
97
+ PLATFORMS
98
+ arm64-darwin
99
+ x86_64-linux
100
+
101
+ DEPENDENCIES
102
+ bundler (~> 2.0)
103
+ proto_turf!
104
+ rake (~> 13.0)
105
+ rspec (~> 3.2)
106
+ standardrb
107
+ webmock
108
+
109
+ BUNDLED WITH
110
+ 2.6.7
data/README.md CHANGED
@@ -27,7 +27,7 @@ Example usage:
27
27
  ```ruby
28
28
  require 'proto_turf'
29
29
 
30
- proto_turf = ProtoTurf.new(registry_url: 'http://localhost:8081')
30
+ proto_turf = ProtoTurf.new(registry_url: 'http://localhost:8081', schema_paths: ['path/to/protos'])
31
31
  message = MyProto::MyMessage.new(field1: 'value1', field2: 42)
32
32
  encoded = proto_turf.encode(message, subject: 'my-subject')
33
33
 
@@ -38,7 +38,15 @@ decoded_proto_message = proto_turf.decode(encoded_string)
38
38
 
39
39
  If you're using [buf](https://buf.build/) to manage your Protobuf definitions, you should run `buf export` before using `proto_turf` to ensure that all the dependencies are available as `.proto` files in your project. The actual proto text is needed when registering the schema with the Schema Registry.
40
40
 
41
+ Because `buf export` overwrites/deletes existing files, you should run it into a different directory and provide both as `schema_paths` to the `ProtoTurf` constructor.
42
+
41
43
  ## Notes about usage
42
44
 
43
45
  * When decoding, this library does *not* attempt to fully parse the Proto definition stored on the schema registry and generate dynamic classes. Instead, it simply parses out the package and message and assumes that the reader has the message available in the descriptor pool. Any compatibility issues should be detected through normal means, i.e. just by instantiating the message and seeing if any errors are raised.
44
46
 
47
+ ### Regenerating test protos
48
+ Run the following to regenerate:
49
+
50
+ ```sh
51
+ protoc -I spec/schemas --ruby_out=spec/gen --ruby_opt=paths=source_relative spec/schemas/**/*.proto
52
+ ```
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
@@ -1,5 +1,4 @@
1
1
  class ProtoTurf::CachedConfluentSchemaRegistry
2
-
3
2
  # @param upstream [ProtoTurf::ConfluentSchemaRegistry]
4
3
  def initialize(upstream)
5
4
  @upstream = upstream
@@ -9,7 +8,7 @@ class ProtoTurf::CachedConfluentSchemaRegistry
9
8
  end
10
9
 
11
10
  # Delegate the following methods to the upstream
12
- %i(subject_versions schema_subject_versions).each do |name|
11
+ %i[subject_versions schema_subject_versions].each do |name|
13
12
  define_method(name) do |*args|
14
13
  instance_variable_get(:@upstream).send(name, *args)
15
14
  end
@@ -29,14 +28,14 @@ class ProtoTurf::CachedConfluentSchemaRegistry
29
28
  return @versions_by_subject_and_id[key] if @versions_by_subject_and_id[key]
30
29
 
31
30
  results = @upstream.schema_subject_versions(id)
32
- @versions_by_subject_and_id[key] = results&.find { |r| r['subject'] == subject }&.dig('version')
31
+ @versions_by_subject_and_id[key] = results&.find { |r| r["subject"] == subject }&.dig("version")
33
32
  end
34
33
 
35
34
  # @param subject [String] the subject to check
36
35
  # @param schema [String] the schema text to check
37
36
  # @return [Boolean] true if we know the schema has been registered for that subject.
38
37
  def registered?(subject, schema)
39
- @ids_by_schema[[subject, schema]].present?
38
+ @ids_by_schema[[subject, schema]] && !@ids_by_schema[[subject, schema]].empty?
40
39
  end
41
40
 
42
41
  # @param subject [String] the subject to register the schema under
@@ -47,5 +46,4 @@ class ProtoTurf::CachedConfluentSchemaRegistry
47
46
 
48
47
  @ids_by_schema[key] ||= @upstream.register(subject, schema, references: references)
49
48
  end
50
-
51
49
  end
@@ -1,8 +1,7 @@
1
- require 'excon'
1
+ require "excon"
2
2
 
3
3
  class ProtoTurf
4
4
  class ConfluentSchemaRegistry
5
-
6
5
  CONTENT_TYPE = "application/vnd.schemaregistry.v1+json".freeze
7
6
 
8
7
  def initialize(
@@ -24,7 +23,7 @@ class ProtoTurf
24
23
  retry_limit: nil
25
24
  )
26
25
  @path_prefix = path_prefix
27
- @schema_context_prefix = schema_context.nil? ? '' : ":.#{schema_context}:"
26
+ @schema_context_prefix = schema_context.nil? ? "" : ":.#{schema_context}:"
28
27
  @schema_context_options = schema_context.nil? ? {} : {query: {subject: @schema_context_prefix}}
29
28
  @logger = logger
30
29
  headers = Excon.defaults[:headers].merge(
@@ -57,7 +56,7 @@ class ProtoTurf
57
56
  # @return [String] the schema string stored in the registry for the given id
58
57
  def fetch(id)
59
58
  @logger.info "Fetching schema with id #{id}"
60
- data = get("/schemas/ids/#{id}", idempotent: true, **@schema_context_options, )
59
+ data = get("/schemas/ids/#{id}", idempotent: true, **@schema_context_options)
61
60
  data.fetch("schema")
62
61
  end
63
62
 
@@ -73,9 +72,9 @@ class ProtoTurf
73
72
  # @return [Integer] the ID of the registered schema
74
73
  def register(subject, schema, references: [])
75
74
  data = post("/subjects/#{@schema_context_prefix}#{CGI.escapeURIComponent(subject)}/versions",
76
- body: { schemaType: 'PROTOBUF',
77
- references: references,
78
- schema: schema.to_s }.to_json)
75
+ body: {schemaType: "PROTOBUF",
76
+ references: references,
77
+ schema: schema.to_s}.to_json)
79
78
 
80
79
  id = data.fetch("id")
81
80
 
@@ -105,7 +104,7 @@ class ProtoTurf
105
104
  end
106
105
 
107
106
  def request(path, **options)
108
- options = { expects: 200 }.merge!(options)
107
+ options = {expects: 200}.merge!(options)
109
108
  path = File.join(@path_prefix, path) unless @path_prefix.nil?
110
109
  response = @connection.request(path: path, **options)
111
110
  JSON.parse(response.body)
@@ -113,6 +112,5 @@ class ProtoTurf
113
112
  @logger.error("Error while requesting #{path}: #{e.response.body}")
114
113
  raise
115
114
  end
116
-
117
115
  end
118
116
  end
@@ -0,0 +1,315 @@
1
+ class ProtoTurf
2
+ module ProtoText
3
+ ParseInfo = Struct.new(:writer, :package, :message) do
4
+ %i[write write_indent write_line writenl indent dedent].each do |method|
5
+ define_method(method) do |*args|
6
+ writer.send(method, *args)
7
+ end
8
+ end
9
+ end
10
+
11
+ class Writer < StringIO
12
+ def initialize(...)
13
+ super
14
+ @indent = 0
15
+ end
16
+
17
+ def write_indent(str)
18
+ @indent.times { write(" ") }
19
+ write(str)
20
+ end
21
+
22
+ def write_line(line, nl = 1)
23
+ write_indent(line)
24
+ nl.times { writenl }
25
+ end
26
+
27
+ def writenl
28
+ write("\n")
29
+ end
30
+
31
+ def indent
32
+ @indent += 2
33
+ end
34
+
35
+ def dedent
36
+ @indent -= 2
37
+ end
38
+ end
39
+
40
+ class << self
41
+ def fetch(message_name)
42
+ name = message_name.start_with?(".") ? message_name[1..] : message_name
43
+ Google::Protobuf::DescriptorPool.generated_pool.lookup(name)
44
+ end
45
+
46
+ def output(file_descriptor)
47
+ writer = Writer.new
48
+ info = ParseInfo.new(writer, file_descriptor.package)
49
+ writer.write_line("syntax = \"#{file_descriptor.syntax}\";", 2)
50
+ writer.write_line("package #{file_descriptor.package};")
51
+ writer.writenl
52
+ found = false
53
+ file_descriptor.options.to_h.each do |name, value|
54
+ found = true
55
+ writer.write_line("option #{name} = #{value.to_json};")
56
+ end
57
+ writer.writenl if found
58
+
59
+ found = false
60
+ file_descriptor.dependency.each do |dependency|
61
+ found = true
62
+ writer.write_line("import \"#{dependency}\";")
63
+ end
64
+ writer.writenl if found
65
+
66
+ writer.writenl if write_options(info, file_descriptor)
67
+ writer.writenl if write_extensions(info, file_descriptor)
68
+
69
+ file_descriptor.enum_type.each do |enum_type|
70
+ write_enum(info, enum_type)
71
+ end
72
+ file_descriptor.message_type.each do |message_type|
73
+ write_message(info, message_type)
74
+ end
75
+ file_descriptor.service.each do |service|
76
+ write_service(info, service)
77
+ end
78
+ writer.string
79
+ end
80
+
81
+ def write_extensions(info, descriptor)
82
+ descriptor.extension.each do |extension|
83
+ info.write_line("extend #{extension.extendee[1..]} {")
84
+ info.indent
85
+ write_field(info, extension)
86
+ info.dedent
87
+ info.write_line("}")
88
+ end
89
+ descriptor.extension.any?
90
+ end
91
+
92
+ def write_reserved(writer, descriptor)
93
+ reserved = descriptor.reserved_range.map do |range|
94
+ (range.start == range.end - 1) ? range.start.to_s : "#{range.start} to #{range.end - 1}"
95
+ end
96
+ found = false
97
+ if reserved.any?
98
+ found = true
99
+ writer.write_line("reserved #{reserved.join(", ")};")
100
+ end
101
+ if descriptor.reserved_name.any?
102
+ found = true
103
+ writer.write_line("reserved #{descriptor.reserved_name.map(&:to_json).join(", ")};")
104
+ end
105
+ writer.writenl if found
106
+ end
107
+
108
+ def write_imports(writer, file_descriptor)
109
+ writer.writenl
110
+ file_descriptor.dependency.each do |dependency|
111
+ writer.write_line("import \"#{dependency}\";")
112
+ end
113
+ file_descriptor.public_dependency.each do |public_dependency|
114
+ writer.write_line("import public \"#{public_dependency}\";")
115
+ end
116
+ file_descriptor.option_dependency.each do |option_dependency|
117
+ writer.write_line("import weak \"#{option_dependency}\";")
118
+ end
119
+ writer.writenl
120
+ end
121
+
122
+ def write_message(info, message_type)
123
+ info.message = message_type
124
+ info.write_indent("message ")
125
+ info.write("#{message_type.name} {")
126
+ info.writenl
127
+ info.indent
128
+
129
+ write_options(info, message_type)
130
+ write_reserved(info, message_type)
131
+
132
+ message_type.enum_type.each do |enum|
133
+ info.writenl
134
+ write_enum(info, enum)
135
+ end
136
+ message_type.field.each do |field|
137
+ write_field(info, field)
138
+ end
139
+ message_type.extension.each do |extension|
140
+ write_field(info, extension)
141
+ end
142
+ write_oneofs(info, message_type)
143
+ message_type.nested_type.each do |subtype|
144
+ info.writenl
145
+ write_message(info, subtype)
146
+ end
147
+ info.dedent
148
+ info.write_line("}")
149
+ end
150
+
151
+ def field_type(info, field_type)
152
+ case field_type.type
153
+ when :TYPE_INT32
154
+ "int32"
155
+ when :TYPE_INT64
156
+ "int64"
157
+ when :TYPE_UINT32
158
+ "uint32"
159
+ when :TYPE_UINT64
160
+ "uint64"
161
+ when :TYPE_SINT32
162
+ "sint32"
163
+ when :TYPE_SINT64
164
+ "sint64"
165
+ when :TYPE_FIXED32
166
+ "fixed32"
167
+ when :TYPE_FIXED64
168
+ "fixed64"
169
+ when :TYPE_SFIXED32
170
+ "sfixed32"
171
+ when :TYPE_SFIXED64
172
+ "sfixed64"
173
+ when :TYPE_FLOAT
174
+ "float"
175
+ when :TYPE_DOUBLE
176
+ "double"
177
+ when :TYPE_BOOL
178
+ "bool"
179
+ when :TYPE_STRING
180
+ "string"
181
+ when :TYPE_BYTES
182
+ "bytes"
183
+ when :TYPE_ENUM, :TYPE_MESSAGE
184
+ # remove leading .
185
+ type = fetch(field_type.type_name[1..])
186
+ name = type.name.sub("#{info.package}.#{info.message.name}.", "")
187
+ name.sub("#{info.package}.", "")
188
+ end
189
+ end
190
+
191
+ def write_field(info, field, oneof: false)
192
+ info.write_indent("")
193
+ return if !oneof && field.has_oneof_index?
194
+
195
+ klass = nil
196
+ if field.type_name && field.type_name != ""
197
+ klass = fetch(field.type_name).to_proto
198
+ end
199
+
200
+ if field.proto3_optional
201
+ info.write("optional ")
202
+ elsif field.label == :LABEL_REPEATED && !klass&.options&.map_entry
203
+ info.write("repeated ")
204
+ end
205
+
206
+ if klass&.options&.map_entry
207
+ info.write("map<#{field_type(info, klass.field[0])}, #{field_type(info, klass.field[1])}>")
208
+ else
209
+ info.write(field_type(info, field).to_s)
210
+ end
211
+ info.write(" #{field.name} = #{field.number}")
212
+
213
+ write_field_options(info, field)
214
+ info.write(";")
215
+ info.writenl
216
+ end
217
+
218
+ def write_field_options(info, field)
219
+ return unless field.options
220
+
221
+ info.write(" [")
222
+ info.write(field.options.to_h.map { |name, value| "#{name} = #{value}" }.join(", "))
223
+ write_options(info, field, include_option_label: false)
224
+ info.write("]")
225
+ end
226
+
227
+ def write_oneofs(info, message)
228
+ message.oneof_decl.each_with_index do |oneof, i|
229
+ next if oneof.name.start_with?("_") &&
230
+ message.field.any? { |f| f.proto3_optional && f.name == oneof.name[1..] }
231
+ info.write_line("oneof #{oneof.name} {")
232
+ info.indent
233
+ message.field.select { |f| f.has_oneof_index? && f.oneof_index == i }.each do |field|
234
+ write_field(info, field, oneof: true)
235
+ end
236
+ info.dedent
237
+ info.write_line("}")
238
+ end
239
+ end
240
+
241
+ def write_enum(info, enum_type)
242
+ info.write("enum ")
243
+ info.write("#{enum_type.name} {")
244
+ info.writenl
245
+ info.indent
246
+ write_reserved(info, enum_type)
247
+ enum_type.value.each do |value|
248
+ info.write_line("#{value.name} = #{value.number};")
249
+ end
250
+ info.dedent
251
+ info.write_line("}")
252
+ info.writenl
253
+ end
254
+
255
+ def method_type(package, name)
256
+ output = name.sub("#{package}.", "")
257
+ output = output[1..] if output.start_with?(".")
258
+ output
259
+ end
260
+
261
+ def write_service(info, service)
262
+ info.write_line("service #{service.name} {")
263
+ info.indent
264
+ service["method"].each do |method|
265
+ info.write_indent("rpc #{method.name}(#{method_type(info.package, method.input_type)}) ")
266
+ info.write("returns (#{method_type(info.package, method.output_type)}) {")
267
+ info.writenl
268
+ info.indent
269
+ if method.options
270
+ write_options(info, method)
271
+ end
272
+ info.dedent
273
+ info.write_line("};")
274
+ end
275
+ info.dedent
276
+ info.write_line("}")
277
+ end
278
+
279
+ # @return [Boolean] true if any options were written
280
+ def write_options(info, descriptor, include_option_label: true)
281
+ # unfortunately there doesn't seem to be a way to get the full list of options without
282
+ # resorting to to_json.
283
+ json = JSON.parse(descriptor.options.to_json)
284
+ return if !json || json.empty?
285
+
286
+ found = false
287
+ json.keys.each do |name|
288
+ option_name = name.tr("[]", "")
289
+ ext = fetch(option_name)
290
+ next if ext.nil?
291
+
292
+ found = true
293
+ options = ext.get(descriptor.options)
294
+ if include_option_label
295
+ info.write_indent("option (#{option_name}) =")
296
+ else
297
+ info.write("(#{option_name}) = ")
298
+ end
299
+ if options.respond_to?(:to_h)
300
+ lines = JSON.pretty_generate(options.to_h).lines(chomp: true)
301
+ lines.each_with_index do |line, i|
302
+ info.write_indent(line)
303
+ info.writenl if i < lines.length - 1
304
+ end
305
+ info.write(";")
306
+ else
307
+ info.write(options.to_json)
308
+ end
309
+ info.writenl
310
+ end
311
+ found
312
+ end
313
+ end
314
+ end
315
+ end
@@ -1,3 +1,3 @@
1
1
  class ProtoTurf
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/proto_turf.rb CHANGED
@@ -1,6 +1,11 @@
1
- require 'logger'
2
- require 'proto_turf/confluent_schema_registry'
3
- require 'proto_turf/cached_confluent_schema_registry'
1
+ require "logger"
2
+ require "google/protobuf"
3
+ require "google/protobuf/well_known_types"
4
+ require "google/protobuf/descriptor_pb"
5
+ require "json"
6
+ require "proto_turf/confluent_schema_registry"
7
+ require "proto_turf/cached_confluent_schema_registry"
8
+ require "proto_turf/proto_text"
4
9
 
5
10
  class ProtoTurf
6
11
  # Provides a way to encode and decode messages without having to embed schemas
@@ -15,14 +20,12 @@ class ProtoTurf
15
20
  # https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
16
21
  MAGIC_BYTE = [0].pack("C").freeze
17
22
 
18
-
19
23
  # Instantiate a new ProtoTurf instance with the given configuration.
20
24
  #
21
25
  # registry - A schema registry object that responds to all methods in the
22
26
  # ProtoTurf::ConfluentSchemaRegistry interface.
23
27
  # registry_url - The String URL of the schema registry that should be used.
24
28
  # schema_context - Schema registry context name (optional)
25
- # schemas_path - The String file system path where local schemas are stored.
26
29
  # registry_path_prefix - The String URL path prefix used to namespace schema registry requests (optional).
27
30
  # logger - The Logger that should be used to log information (optional).
28
31
  # proxy - Forward the request via proxy (optional).
@@ -40,7 +43,6 @@ class ProtoTurf
40
43
  registry: nil,
41
44
  registry_url: nil,
42
45
  schema_context: nil,
43
- schemas_path: nil,
44
46
  registry_path_prefix: nil,
45
47
  logger: nil,
46
48
  proxy: nil,
@@ -57,7 +59,6 @@ class ProtoTurf
57
59
  retry_limit: nil
58
60
  )
59
61
  @logger = logger || Logger.new($stderr)
60
- @path = schemas_path
61
62
  @registry = registry || ProtoTurf::CachedConfluentSchemaRegistry.new(
62
63
  ProtoTurf::ConfluentSchemaRegistry.new(
63
64
  registry_url,
@@ -101,7 +102,7 @@ class ProtoTurf
101
102
  stream.write([id].pack("N"))
102
103
 
103
104
  _, indexes = find_index(message.class.descriptor.to_proto,
104
- file_descriptor.to_proto.message_type)
105
+ file_descriptor.to_proto.message_type)
105
106
 
106
107
  if indexes == [0]
107
108
  write_int(stream, 0)
@@ -138,7 +139,7 @@ class ProtoTurf
138
139
  end
139
140
 
140
141
  # The schema id is a 4-byte big-endian integer.
141
- schema_id = stream.read(4).unpack("N").first
142
+ schema_id = stream.read(4).unpack1("N")
142
143
 
143
144
  # For now, we're only going to support a single message per schema. See
144
145
  # https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format
@@ -161,7 +162,7 @@ class ProtoTurf
161
162
 
162
163
  private
163
164
 
164
- def find_index(descriptor, messages, indexes=[])
165
+ def find_index(descriptor, messages, indexes = [])
165
166
  messages.each_with_index do |sub_descriptor, i|
166
167
  if sub_descriptor == descriptor
167
168
  indexes.push(i)
@@ -221,7 +222,7 @@ class ProtoTurf
221
222
  raise "Could not find schema for #{full_name}. Make sure the corresponding .proto file has been compiled and loaded."
222
223
  end
223
224
  path = find_descriptor(indexes, descriptor.file_descriptor.to_proto.message_type)
224
- correct_message = Google::Protobuf::DescriptorPool.generated_pool.lookup("#{package}.#{path.join('.')}")
225
+ correct_message = Google::Protobuf::DescriptorPool.generated_pool.lookup("#{package}.#{path.join(".")}")
225
226
  correct_message.msgclass.decode(encoded)
226
227
  end
227
228
 
@@ -230,7 +231,7 @@ class ProtoTurf
230
231
  return if @registry.registered?(file_descriptor.name, subject)
231
232
 
232
233
  # register dependencies first
233
- dependencies = file_descriptor.to_proto.dependency.to_a.reject { |d| d.start_with?('google/protobuf/') }
234
+ dependencies = file_descriptor.to_proto.dependency.to_a.reject { |d| d.start_with?("google/protobuf/") }
234
235
  versions = dependencies.map do |dependency|
235
236
  dependency_descriptor = @all_schemas[dependency]
236
237
  result = register_schema(dependency_descriptor, subject: dependency_descriptor.name)
@@ -238,33 +239,27 @@ class ProtoTurf
238
239
  end
239
240
 
240
241
  @registry.register(subject,
241
- schema_text(file_descriptor),
242
- references: dependencies.map.with_index do |dependency, i|
243
- {
244
- name: dependency,
245
- subject: dependency,
246
- version: versions[i]
247
- }
248
- end
249
- )
242
+ schema_text(file_descriptor),
243
+ references: dependencies.map.with_index do |dependency, i|
244
+ {
245
+ name: dependency,
246
+ subject: dependency,
247
+ version: versions[i]
248
+ }
249
+ end)
250
250
  end
251
251
 
252
252
  def schema_text(file_descriptor)
253
- filename = "#{@path}/#{file_descriptor.name}"
254
- File.exist?(filename) ? File.read(filename) : ""
253
+ ProtoTurf::ProtoText.output(file_descriptor.to_proto)
255
254
  end
256
255
 
257
256
  def load_schemas!
258
- all_messages = ObjectSpace.each_object(Class).select do |o|
259
- o < Google::Protobuf.const_get(:AbstractMessage)
260
- end.to_a
261
- all_messages.each do |m|
262
- file_desc = m.descriptor.file_descriptor
257
+ all_files = ObjectSpace.each_object(Google::Protobuf::FileDescriptor).to_a
258
+ all_files.each do |file_desc|
263
259
  file_path = file_desc.name
264
- next if file_path.start_with?('google/protobuf/') # skip built-in protos
260
+ next if file_path.start_with?("google/protobuf/") # skip built-in protos
265
261
 
266
262
  @all_schemas[file_path] = file_desc
267
263
  end
268
264
  end
269
-
270
265
  end
data/proto_turf.gemspec CHANGED
@@ -1,27 +1,28 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'proto_turf/version'
3
+ require "proto_turf/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "proto_turf"
8
- spec.version = ProtoTurf::VERSION
9
- spec.authors = ["Daniel Orner"]
10
- spec.email = ["daniel.orner@flipp.com"]
11
- spec.summary = "Support for Protobuf files in Confluent Schema Registry"
12
- spec.homepage = "https://github.com/flipp-oss/proto_turf"
13
- spec.license = "MIT"
6
+ spec.name = "proto_turf"
7
+ spec.version = ProtoTurf::VERSION
8
+ spec.authors = ["Daniel Orner"]
9
+ spec.email = ["daniel.orner@flipp.com"]
10
+ spec.summary = "Support for Protobuf files in Confluent Schema Registry"
11
+ spec.homepage = "https://github.com/flipp-oss/proto_turf"
12
+ spec.license = "MIT"
14
13
 
15
14
  spec.metadata["rubygems_mfa_required"] = "true"
16
15
 
17
- spec.files = `git ls-files -z`.split("\x0")
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
18
  spec.require_paths = ["lib"]
21
19
 
22
20
  spec.add_dependency "google-protobuf"
21
+ spec.add_dependency "excon"
23
22
 
24
23
  spec.add_development_dependency "bundler", "~> 2.0"
25
24
  spec.add_development_dependency "rake", "~> 13.0"
26
25
  spec.add_development_dependency "rspec", "~> 3.2"
26
+ spec.add_development_dependency "webmock"
27
+ spec.add_development_dependency "standardrb"
27
28
  end
@@ -0,0 +1,36 @@
1
+ RSpec.describe "encoding" do
2
+ let(:proto_turf) do
3
+ ProtoTurf.new(
4
+ registry_url: "http://localhost:8081"
5
+ )
6
+ end
7
+
8
+ it "should decode a simple message" do
9
+ schema = File.read("#{__dir__}/schemas/simple/simple.proto")
10
+ stub = stub_request(:get, "http://localhost:8081/schemas/ids/15")
11
+ .to_return_json(body: {schema: schema})
12
+ msg = Simple::V1::SimpleMessage.new(name: "my name")
13
+ encoded = "\u0000\u0000\u0000\u0000\u000F\u0000" + msg.to_proto
14
+ expect(proto_turf.decode(encoded)).to eq(msg)
15
+
16
+ # if we do it again we should not see any more requests
17
+ expect(proto_turf.decode(encoded)).to eq(msg)
18
+
19
+ expect(stub).to have_been_requested.once
20
+ end
21
+
22
+ it "should decode a complex message" do
23
+ schema = File.read("#{__dir__}/schemas/referenced/referer.proto")
24
+ stub = stub_request(:get, "http://localhost:8081/schemas/ids/20")
25
+ .to_return_json(body: {schema: schema})
26
+ msg = Referenced::V1::MessageB::MessageBA.new(
27
+ simple: Simple::V1::SimpleMessage.new(name: "my name")
28
+ )
29
+ encoded = "\u0000\u0000\u0000\u0000\u0014\u0004\u0002\u0000" + msg.to_proto
30
+ expect(proto_turf.decode(encoded)).to eq(msg)
31
+
32
+ # if we do it again we should not see any more requests
33
+ expect(proto_turf.decode(encoded)).to eq(msg)
34
+ expect(stub).to have_been_requested.once
35
+ end
36
+ end
@@ -0,0 +1,57 @@
1
+ RSpec.describe "encoding" do
2
+ let(:proto_turf) do
3
+ ProtoTurf.new(
4
+ registry_url: "http://localhost:8081"
5
+ )
6
+ end
7
+
8
+ it "should encode a simple message" do
9
+ schema = File.read("#{__dir__}/schemas/simple/simple.proto")
10
+ stub = stub_request(:post, "http://localhost:8081/subjects/simple/versions")
11
+ .with(body: {"schemaType" => "PROTOBUF",
12
+ "references" => [],
13
+ "schema" => schema}).to_return_json(body: {id: 15})
14
+ msg = Simple::V1::SimpleMessage.new(name: "my name")
15
+ encoded = proto_turf.encode(msg, subject: "simple")
16
+ expect(encoded).to eq("\u0000\u0000\u0000\u0000\u000F\u0000" + msg.to_proto)
17
+
18
+ # if we do it again we should not see any more requests
19
+ encoded2 = proto_turf.encode(msg, subject: "simple")
20
+ expect(encoded2).to eq(encoded)
21
+
22
+ expect(stub).to have_been_requested.once
23
+ end
24
+
25
+ it "should encode a complex message" do
26
+ schema = File.read("#{__dir__}/schemas/referenced/referer.proto")
27
+ dep_schema = File.read("#{__dir__}/schemas/simple/simple.proto")
28
+ dep_stub = stub_request(:post, "http://localhost:8081/subjects/simple%2Fsimple.proto/versions")
29
+ .with(body: {"schemaType" => "PROTOBUF",
30
+ "references" => [],
31
+ "schema" => dep_schema}).to_return_json(body: {id: 15})
32
+ version_stub = stub_request(:get, "http://localhost:8081/schemas/ids/15/versions")
33
+ .to_return_json(body: [{version: 1, subject: "simple/simple.proto"}])
34
+ stub = stub_request(:post, "http://localhost:8081/subjects/referenced/versions")
35
+ .with(body: {"schemaType" => "PROTOBUF",
36
+ "references" => [
37
+ {
38
+ name: "simple/simple.proto",
39
+ subject: "simple/simple.proto",
40
+ version: 1
41
+ }
42
+ ],
43
+ "schema" => schema}).to_return_json(body: {id: 20})
44
+ msg = Referenced::V1::MessageB::MessageBA.new(
45
+ simple: Simple::V1::SimpleMessage.new(name: "my name")
46
+ )
47
+ encoded = proto_turf.encode(msg, subject: "referenced")
48
+ expect(encoded).to eq("\u0000\u0000\u0000\u0000\u0014\u0004\u0002\u0000" + msg.to_proto)
49
+
50
+ # if we do it again we should not see any more requests
51
+ encoded2 = proto_turf.encode(msg, subject: "referenced")
52
+ expect(encoded2).to eq(encoded)
53
+ expect(stub).to have_been_requested.once
54
+ expect(dep_stub).to have_been_requested.once
55
+ expect(version_stub).to have_been_requested.once
56
+ end
57
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
4
+ # source: everything/everything.proto
5
+
6
+ require "google/protobuf"
7
+
8
+ require "simple/simple_pb"
9
+ require "google/protobuf/descriptor_pb"
10
+
11
+ descriptor_data = "\n\x1b\x65verything/everything.proto\x12\reverything.v1\x1a\x13simple/simple.proto\x1a google/protobuf/descriptor.proto\"*\n\x0e\x46oreignMessage\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"\xc0\x12\n\x0cTestAllTypes\x12\x16\n\x0eoptional_int32\x18\x01 \x01(\x05\x12\x16\n\x0eoptional_int64\x18\x02 \x01(\x03\x12\x17\n\x0foptional_uint32\x18\x03 \x01(\r\x12\x17\n\x0foptional_uint64\x18\x04 \x01(\x04\x12\x17\n\x0foptional_sint32\x18\x05 \x01(\x11\x12\x17\n\x0foptional_sint64\x18\x06 \x01(\x12\x12\x18\n\x10optional_fixed32\x18\x07 \x01(\x07\x12\x18\n\x10optional_fixed64\x18\x08 \x01(\x06\x12\x19\n\x11optional_sfixed32\x18\t \x01(\x0f\x12\x19\n\x11optional_sfixed64\x18\n \x01(\x10\x12\x16\n\x0eoptional_float\x18\x0b \x01(\x02\x12\x17\n\x0foptional_double\x18\x0c \x01(\x01\x12\x15\n\roptional_bool\x18\r \x01(\x08\x12\x17\n\x0foptional_string\x18\x0e \x01(\t\x12\x16\n\x0eoptional_bytes\x18\x0f \x01(\x0c\x12J\n\x17optional_nested_message\x18\x12 \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessage\x12?\n\x18optional_foreign_message\x18\x13 \x01(\x0b\x32\x1d.everything.v1.ForeignMessage\x12\x39\n\x17optional_import_message\x18\x14 \x01(\x0b\x32\x18.simple.v1.SimpleMessage\x12\x44\n\x14optional_nested_enum\x18\x15 \x01(\x0e\x32&.everything.v1.TestAllTypes.NestedEnum\x12\x39\n\x15optional_foreign_enum\x18\x16 \x01(\x0e\x32\x1a.everything.v1.ForeignEnum\x12\x33\n\x14optional_import_enum\x18\x17 \x01(\x0e\x32\x15.simple.v1.SimpleEnum\x12!\n\x15optional_string_piece\x18\x18 \x01(\tB\x02\x08\x02\x12\x19\n\roptional_cord\x18\x19 \x01(\tB\x02\x08\x01\x12\x1f\n\x13optional_bytes_cord\x18V \x01(\x0c\x42\x02\x08\x01\x12L\n\x15optional_lazy_message\x18\x1b \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02(\x01\x12W\n optional_unverified_lazy_message\x18\x1c \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02x\x01\x12\x16\n\x0erepeated_int32\x18\x1f \x03(\x05\x12\x16\n\x0erepeated_int64\x18 \x03(\x03\x12\x17\n\x0frepeated_uint32\x18! \x03(\r\x12\x17\n\x0frepeated_uint64\x18\" \x03(\x04\x12\x17\n\x0frepeated_sint32\x18# \x03(\x11\x12\x17\n\x0frepeated_sint64\x18$ \x03(\x12\x12\x18\n\x10repeated_fixed32\x18% \x03(\x07\x12\x18\n\x10repeated_fixed64\x18& \x03(\x06\x12\x19\n\x11repeated_sfixed32\x18' \x03(\x0f\x12\x19\n\x11repeated_sfixed64\x18( \x03(\x10\x12\x16\n\x0erepeated_float\x18) \x03(\x02\x12\x17\n\x0frepeated_double\x18* \x03(\x01\x12\x15\n\rrepeated_bool\x18+ \x03(\x08\x12\x17\n\x0frepeated_string\x18, \x03(\t\x12\x16\n\x0erepeated_bytes\x18- \x03(\x0c\x12J\n\x17repeated_nested_message\x18\x30 \x03(\x0b\x32).everything.v1.TestAllTypes.NestedMessage\x12?\n\x18repeated_foreign_message\x18\x31 \x03(\x0b\x32\x1d.everything.v1.ForeignMessage\x12\x39\n\x17repeated_import_message\x18\x32 \x03(\x0b\x32\x18.simple.v1.SimpleMessage\x12\x44\n\x14repeated_nested_enum\x18\x33 \x03(\x0e\x32&.everything.v1.TestAllTypes.NestedEnum\x12\x39\n\x15repeated_foreign_enum\x18\x34 \x03(\x0e\x32\x1a.everything.v1.ForeignEnum\x12\x33\n\x14repeated_import_enum\x18\x35 \x03(\x0e\x32\x15.simple.v1.SimpleEnum\x12!\n\x15repeated_string_piece\x18\x36 \x03(\tB\x02\x08\x02\x12\x19\n\rrepeated_cord\x18\x37 \x03(\tB\x02\x08\x01\x12L\n\x15repeated_lazy_message\x18\x39 \x03(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02(\x01\x12\x16\n\x0coneof_uint32\x18o \x01(\rH\x00\x12I\n\x14oneof_nested_message\x18p \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageH\x00\x12\x16\n\x0coneof_string\x18q \x01(\tH\x00\x12\x15\n\x0boneof_bytes\x18r \x01(\x0cH\x00\x12\x18\n\noneof_cord\x18s \x01(\tB\x02\x08\x01H\x00\x12 \n\x12oneof_string_piece\x18t \x01(\tB\x02\x08\x02H\x00\x12R\n\x19oneof_lazy_nested_message\x18u \x01(\x0b\x32).everything.v1.TestAllTypes.NestedMessageB\x02(\x01H\x00\x1a\"\n\rNestedMessage\x12\x11\n\x02\x62\x62\x18\x01 \x01(\x05\x42\x05\x90\x82\x19\xd2\t\x1a\x1a\n\rOptionalGroup\x12\t\n\x01\x61\x18\x11 \x01(\x05\x1a\x1a\n\rRepeatedGroup\x12\t\n\x01\x61\x18/ \x01(\x05\"'\n\nNestedEnum\x12\x07\n\x03\x46OO\x10\x00\x12\x07\n\x03\x42\x41R\x10\x02\x12\x07\n\x03\x42\x41Z\x10\x03\x42\r\n\x0boneof_field*/\n\x0b\x46oreignEnum\x12\x0f\n\x0b\x46OREIGN_FOO\x10\x00\x12\x0f\n\x0b\x46OREIGN_BAR\x10\x01\x32Y\n\x0bTestService\x12J\n\nTestMethod\x12\x1b.everything.v1.TestAllTypes\x1a\x1d.everything.v1.ForeignMessage\"\x00:4\n\x0bsome_option\x12\x1d.google.protobuf.FieldOptions\x18\xa2\x90\x03 \x01(\x05\x62\x06proto3"
12
+
13
+ pool = Google::Protobuf::DescriptorPool.generated_pool
14
+ pool.add_serialized_file(descriptor_data)
15
+
16
+ module Everything
17
+ module V1
18
+ ForeignMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.ForeignMessage").msgclass
19
+ TestAllTypes = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes").msgclass
20
+ TestAllTypes::NestedMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.NestedMessage").msgclass
21
+ TestAllTypes::OptionalGroup = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.OptionalGroup").msgclass
22
+ TestAllTypes::RepeatedGroup = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.RepeatedGroup").msgclass
23
+ TestAllTypes::NestedEnum = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.TestAllTypes.NestedEnum").enummodule
24
+ ForeignEnum = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("everything.v1.ForeignEnum").enummodule
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
4
+ # source: referenced/referer.proto
5
+
6
+ require "google/protobuf"
7
+
8
+ require "simple/simple_pb"
9
+
10
+ descriptor_data = "\n\x18referenced/referer.proto\x12\rreferenced.v1\x1a\x13simple/simple.proto\"j\n\x08MessageA\x1a\x43\n\tMessageAA\x12\x0c\n\x04name\x18\x01 \x01(\t\x12(\n\x06simple\x18\x02 \x01(\x0b\x32\x18.simple.v1.SimpleMessage\x1a\x19\n\tMessageAB\x12\x0c\n\x04name\x18\x01 \x01(\t\"j\n\x08MessageB\x1a\x43\n\tMessageBA\x12\x0c\n\x04name\x18\x01 \x01(\t\x12(\n\x06simple\x18\x02 \x01(\x0b\x32\x18.simple.v1.SimpleMessage\x1a\x19\n\tMessageBB\x12\x0c\n\x04name\x18\x01 \x01(\tb\x06proto3"
11
+
12
+ pool = Google::Protobuf::DescriptorPool.generated_pool
13
+ pool.add_serialized_file(descriptor_data)
14
+
15
+ module Referenced
16
+ module V1
17
+ MessageA = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageA").msgclass
18
+ MessageA::MessageAA = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageA.MessageAA").msgclass
19
+ MessageA::MessageAB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageA.MessageAB").msgclass
20
+ MessageB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageB").msgclass
21
+ MessageB::MessageBA = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageB.MessageBA").msgclass
22
+ MessageB::MessageBB = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("referenced.v1.MessageB.MessageBB").msgclass
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
4
+ # source: simple/simple.proto
5
+
6
+ require "google/protobuf"
7
+
8
+ descriptor_data = "\n\x13simple/simple.proto\x12\tsimple.v1\"\x1d\n\rSimpleMessage\x12\x0c\n\x04name\x18\x01 \x01(\t*,\n\nSimpleEnum\x12\x0e\n\nSIMPLE_FOO\x10\x00\x12\x0e\n\nSIMPLE_BAR\x10\x01\x62\x06proto3"
9
+
10
+ pool = Google::Protobuf::DescriptorPool.generated_pool
11
+ pool.add_serialized_file(descriptor_data)
12
+
13
+ module Simple
14
+ module V1
15
+ SimpleMessage = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("simple.v1.SimpleMessage").msgclass
16
+ SimpleEnum = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("simple.v1.SimpleEnum").enummodule
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ RSpec.describe ProtoTurf::ProtoText do
2
+ it "should output as expected" do
3
+ output = described_class.output(Everything::V1::TestAllTypes.descriptor.file_descriptor.to_proto)
4
+
5
+ expected = File.read("#{__dir__}/schemas/everything/everything.proto")
6
+ expect(output).to eq(expected)
7
+ end
8
+ end
@@ -0,0 +1,105 @@
1
+ syntax = "proto3";
2
+
3
+ package everything.v1;
4
+
5
+ import "simple/simple.proto";
6
+ import "google/protobuf/descriptor.proto";
7
+
8
+ extend google.protobuf.FieldOptions {
9
+ int32 some_option = 51234;
10
+ }
11
+
12
+ enum ForeignEnum {
13
+ FOREIGN_FOO = 0;
14
+ FOREIGN_BAR = 1;
15
+ }
16
+
17
+ message ForeignMessage {
18
+ int32 id = 1;
19
+ string name = 2;
20
+ }
21
+ message TestAllTypes {
22
+
23
+ enum NestedEnum {
24
+ FOO = 0;
25
+ BAR = 2;
26
+ BAZ = 3;
27
+ }
28
+
29
+ int32 optional_int32 = 1;
30
+ int64 optional_int64 = 2;
31
+ uint32 optional_uint32 = 3;
32
+ uint64 optional_uint64 = 4;
33
+ sint32 optional_sint32 = 5;
34
+ sint64 optional_sint64 = 6;
35
+ fixed32 optional_fixed32 = 7;
36
+ fixed64 optional_fixed64 = 8;
37
+ sfixed32 optional_sfixed32 = 9;
38
+ sfixed64 optional_sfixed64 = 10;
39
+ float optional_float = 11;
40
+ double optional_double = 12;
41
+ bool optional_bool = 13;
42
+ string optional_string = 14;
43
+ bytes optional_bytes = 15;
44
+ NestedMessage optional_nested_message = 18;
45
+ ForeignMessage optional_foreign_message = 19;
46
+ simple.v1.SimpleMessage optional_import_message = 20;
47
+ NestedEnum optional_nested_enum = 21;
48
+ ForeignEnum optional_foreign_enum = 22;
49
+ simple.v1.SimpleEnum optional_import_enum = 23;
50
+ string optional_string_piece = 24 [ctype = STRING_PIECE];
51
+ string optional_cord = 25 [ctype = CORD];
52
+ bytes optional_bytes_cord = 86 [ctype = CORD];
53
+ NestedMessage optional_lazy_message = 27 [lazy = true];
54
+ NestedMessage optional_unverified_lazy_message = 28 [unverified_lazy = true];
55
+ repeated int32 repeated_int32 = 31;
56
+ repeated int64 repeated_int64 = 32;
57
+ repeated uint32 repeated_uint32 = 33;
58
+ repeated uint64 repeated_uint64 = 34;
59
+ repeated sint32 repeated_sint32 = 35;
60
+ repeated sint64 repeated_sint64 = 36;
61
+ repeated fixed32 repeated_fixed32 = 37;
62
+ repeated fixed64 repeated_fixed64 = 38;
63
+ repeated sfixed32 repeated_sfixed32 = 39;
64
+ repeated sfixed64 repeated_sfixed64 = 40;
65
+ repeated float repeated_float = 41;
66
+ repeated double repeated_double = 42;
67
+ repeated bool repeated_bool = 43;
68
+ repeated string repeated_string = 44;
69
+ repeated bytes repeated_bytes = 45;
70
+ repeated NestedMessage repeated_nested_message = 48;
71
+ repeated ForeignMessage repeated_foreign_message = 49;
72
+ repeated simple.v1.SimpleMessage repeated_import_message = 50;
73
+ repeated NestedEnum repeated_nested_enum = 51;
74
+ repeated ForeignEnum repeated_foreign_enum = 52;
75
+ repeated simple.v1.SimpleEnum repeated_import_enum = 53;
76
+ repeated string repeated_string_piece = 54 [ctype = STRING_PIECE];
77
+ repeated string repeated_cord = 55 [ctype = CORD];
78
+ repeated NestedMessage repeated_lazy_message = 57 [lazy = true];
79
+ oneof oneof_field {
80
+ uint32 oneof_uint32 = 111;
81
+ NestedMessage oneof_nested_message = 112;
82
+ string oneof_string = 113;
83
+ bytes oneof_bytes = 114;
84
+ string oneof_cord = 115 [ctype = CORD];
85
+ string oneof_string_piece = 116 [ctype = STRING_PIECE];
86
+ NestedMessage oneof_lazy_nested_message = 117 [lazy = true];
87
+ }
88
+
89
+ message NestedMessage {
90
+ int32 bb = 1 [(everything.v1.some_option) = 1234
91
+ ];
92
+ }
93
+
94
+ message OptionalGroup {
95
+ int32 a = 17;
96
+ }
97
+
98
+ message RepeatedGroup {
99
+ int32 a = 47;
100
+ }
101
+ }
102
+ service TestService {
103
+ rpc TestMethod(TestAllTypes) returns (ForeignMessage) {
104
+ };
105
+ }
@@ -0,0 +1,28 @@
1
+ syntax = "proto3";
2
+
3
+ package referenced.v1;
4
+
5
+ import "simple/simple.proto";
6
+
7
+ message MessageA {
8
+
9
+ message MessageAA {
10
+ string name = 1;
11
+ simple.v1.SimpleMessage simple = 2;
12
+ }
13
+
14
+ message MessageAB {
15
+ string name = 1;
16
+ }
17
+ }
18
+ message MessageB {
19
+
20
+ message MessageBA {
21
+ string name = 1;
22
+ simple.v1.SimpleMessage simple = 2;
23
+ }
24
+
25
+ message MessageBB {
26
+ string name = 1;
27
+ }
28
+ }
@@ -0,0 +1,12 @@
1
+ syntax = "proto3";
2
+
3
+ package simple.v1;
4
+
5
+ enum SimpleEnum {
6
+ SIMPLE_FOO = 0;
7
+ SIMPLE_BAR = 1;
8
+ }
9
+
10
+ message SimpleMessage {
11
+ string name = 1;
12
+ }
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
4
+ $LOAD_PATH.unshift(File.expand_path("gen", __dir__))
5
+ Dir["#{__dir__}/gen/**/*.rb"].each { |file| require file }
6
+ require "proto_turf"
7
+ require "webmock/rspec"
8
+
9
+ RSpec.configure do |config|
10
+ config.full_backtrace = true
11
+
12
+ config.shared_context_metadata_behavior = :apply_to_host_groups
13
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proto_turf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Orner
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-08-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: google-protobuf
@@ -24,6 +23,20 @@ dependencies:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: excon
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
27
40
  - !ruby/object:Gem::Dependency
28
41
  name: bundler
29
42
  requirement: !ruby/object:Gem::Requirement
@@ -66,27 +79,71 @@ dependencies:
66
79
  - - "~>"
67
80
  - !ruby/object:Gem::Version
68
81
  version: '3.2'
69
- description:
82
+ - !ruby/object:Gem::Dependency
83
+ name: webmock
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: standardrb
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
70
110
  email:
71
111
  - daniel.orner@flipp.com
72
112
  executables: []
73
113
  extensions: []
74
114
  extra_rdoc_files: []
75
115
  files:
116
+ - ".github/workflows/lint.yml"
117
+ - ".github/workflows/release.yml"
118
+ - ".github/workflows/test.yml"
119
+ - ".rspec"
76
120
  - CHANGELOG.md
121
+ - Gemfile
122
+ - Gemfile.lock
77
123
  - LICENSE
78
124
  - README.md
125
+ - Rakefile
79
126
  - lib/proto_turf.rb
80
127
  - lib/proto_turf/cached_confluent_schema_registry.rb
81
128
  - lib/proto_turf/confluent_schema_registry.rb
129
+ - lib/proto_turf/proto_text.rb
82
130
  - lib/proto_turf/version.rb
83
131
  - proto_turf.gemspec
132
+ - spec/decoding_spec.rb
133
+ - spec/encoding_spec.rb
134
+ - spec/gen/everything/everything_pb.rb
135
+ - spec/gen/referenced/referer_pb.rb
136
+ - spec/gen/simple/simple_pb.rb
137
+ - spec/proto_text_spec.rb
138
+ - spec/schemas/everything/everything.proto
139
+ - spec/schemas/referenced/referer.proto
140
+ - spec/schemas/simple/simple.proto
141
+ - spec/spec_helper.rb
84
142
  homepage: https://github.com/flipp-oss/proto_turf
85
143
  licenses:
86
144
  - MIT
87
145
  metadata:
88
146
  rubygems_mfa_required: 'true'
89
- post_install_message:
90
147
  rdoc_options: []
91
148
  require_paths:
92
149
  - lib
@@ -101,8 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
158
  - !ruby/object:Gem::Version
102
159
  version: '0'
103
160
  requirements: []
104
- rubygems_version: 3.4.10
105
- signing_key:
161
+ rubygems_version: 3.6.9
106
162
  specification_version: 4
107
163
  summary: Support for Protobuf files in Confluent Schema Registry
108
164
  test_files: []