magicprotorb 0.1.0
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 +7 -0
- data/.rubocop.yml +39 -0
- data/Cargo.lock +517 -0
- data/Cargo.toml +6 -0
- data/DESIGN.md +163 -0
- data/README.md +124 -0
- data/Rakefile +36 -0
- data/ext/magicprotorb_native/Cargo.toml +22 -0
- data/ext/magicprotorb_native/extconf.rb +8 -0
- data/ext/magicprotorb_native/src/lib.rs +35 -0
- data/lib/magicprotorb/include_path.rb +43 -0
- data/lib/magicprotorb/loader.rb +49 -0
- data/lib/magicprotorb/naming.rb +33 -0
- data/lib/magicprotorb/registrar.rb +96 -0
- data/lib/magicprotorb/require_hook.rb +45 -0
- data/lib/magicprotorb/service_builder.rb +62 -0
- data/lib/magicprotorb/version.rb +5 -0
- data/lib/magicprotorb.rb +55 -0
- data/sig/magicprotorb.rbs +29 -0
- metadata +106 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Magicprotorb
|
|
4
|
+
# Synthesizes gRPC Service/Stub classes from the service descriptors in a
|
|
5
|
+
# FileDescriptorProto — the runtime equivalent of a generated
|
|
6
|
+
# `*_services_pb.rb`. Requires the optional `grpc` gem, loaded lazily so that
|
|
7
|
+
# message-only users never pay for it.
|
|
8
|
+
module ServiceBuilder
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def build(file)
|
|
12
|
+
require "grpc"
|
|
13
|
+
package = file.package
|
|
14
|
+
file.service.each { |service| build_service(package, service) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def build_service(package, service)
|
|
18
|
+
# Use to_h to read the repeated `method` field: ServiceDescriptorProto#method
|
|
19
|
+
# collides with Ruby's Object#method.
|
|
20
|
+
descriptor = service.to_h
|
|
21
|
+
name = descriptor[:name]
|
|
22
|
+
full_name = qualify(package, name)
|
|
23
|
+
|
|
24
|
+
service_class = define_service_class(full_name, descriptor[:method] || [])
|
|
25
|
+
|
|
26
|
+
mod = Registrar.ensure_module(Naming.package_modules(package) + [Naming.constant_name(name)])
|
|
27
|
+
mod.const_set(:Service, service_class) unless mod.const_defined?(:Service, false)
|
|
28
|
+
mod.const_set(:Stub, service_class.rpc_stub_class) unless mod.const_defined?(:Stub, false)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def define_service_class(full_name, methods)
|
|
32
|
+
pool = Google::Protobuf::DescriptorPool.generated_pool
|
|
33
|
+
|
|
34
|
+
Class.new do
|
|
35
|
+
include GRPC::GenericService
|
|
36
|
+
|
|
37
|
+
self.marshal_class_method = :encode
|
|
38
|
+
self.unmarshal_class_method = :decode
|
|
39
|
+
self.service_name = full_name
|
|
40
|
+
|
|
41
|
+
methods.each do |method|
|
|
42
|
+
request = pool.lookup(ServiceBuilder.strip(method[:input_type])).msgclass
|
|
43
|
+
response = pool.lookup(ServiceBuilder.strip(method[:output_type])).msgclass
|
|
44
|
+
# stream(...) is provided by GenericService's DSL inside the class body.
|
|
45
|
+
request = stream(request) if method[:client_streaming]
|
|
46
|
+
response = stream(response) if method[:server_streaming]
|
|
47
|
+
rpc method[:name].to_sym, request, response
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def qualify(package, name)
|
|
53
|
+
package.nil? || package.empty? ? name : "#{package}.#{name}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Descriptor type references are fully-qualified with a leading dot
|
|
57
|
+
# (".greet.HelloRequest"); the pool is keyed without it.
|
|
58
|
+
def strip(type_name)
|
|
59
|
+
type_name.sub(/\A\./, "")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/magicprotorb.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "google/protobuf"
|
|
4
|
+
require "google/protobuf/descriptor_pb"
|
|
5
|
+
|
|
6
|
+
require_relative "magicprotorb/version"
|
|
7
|
+
|
|
8
|
+
module Magicprotorb
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
|
|
11
|
+
# Raised when the native compiler rejects a .proto (syntax error, unresolved
|
|
12
|
+
# import, etc.).
|
|
13
|
+
class CompileError < Error; end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The Rust compiler extension (defines Magicprotorb::Compiler._compile).
|
|
17
|
+
require_relative "magicprotorb/magicprotorb_native"
|
|
18
|
+
|
|
19
|
+
require_relative "magicprotorb/naming"
|
|
20
|
+
require_relative "magicprotorb/include_path"
|
|
21
|
+
require_relative "magicprotorb/registrar"
|
|
22
|
+
require_relative "magicprotorb/service_builder"
|
|
23
|
+
require_relative "magicprotorb/loader"
|
|
24
|
+
require_relative "magicprotorb/require_hook"
|
|
25
|
+
|
|
26
|
+
module Magicprotorb
|
|
27
|
+
# Public, programmatic entry points. The primary interface is the require hook
|
|
28
|
+
# installed below; these mirror it for callers who prefer an explicit API.
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
# The include roots currently searched for protos (MAGICPROTORB_PATH then
|
|
33
|
+
# $LOAD_PATH), highest priority first.
|
|
34
|
+
def include_paths
|
|
35
|
+
IncludePath.roots
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Programmatic equivalent of `require "magicprotorb/<proto>_pb"`. Accepts a
|
|
39
|
+
# canonical proto path with or without the .proto extension.
|
|
40
|
+
def import(proto)
|
|
41
|
+
Loader.load_messages(normalize(proto))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Programmatic equivalent of `require "magicprotorb/<proto>_services_pb"`.
|
|
45
|
+
def import_services(proto)
|
|
46
|
+
Loader.load_services(normalize(proto))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def normalize(proto)
|
|
50
|
+
proto.end_with?(".proto") ? proto : "#{proto}.proto"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Install the import hook. After this, `require "magicprotorb/foo/bar_pb"` works.
|
|
55
|
+
Kernel.prepend(Magicprotorb::RequireHook)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Magicprotorb
|
|
2
|
+
VERSION: String
|
|
3
|
+
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Raised when the native compiler rejects a .proto.
|
|
8
|
+
class CompileError < Error
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Compiles greet/hello.proto and defines its message/enum constants.
|
|
12
|
+
# Equivalent to `require "magicprotorb/greet/hello_pb"`.
|
|
13
|
+
def self.import: (String proto) -> void
|
|
14
|
+
|
|
15
|
+
# As import, plus synthesizes the gRPC Service/Stub.
|
|
16
|
+
# Equivalent to `require "magicprotorb/greet/hello_services_pb"`.
|
|
17
|
+
def self.import_services: (String proto) -> void
|
|
18
|
+
|
|
19
|
+
# The include roots currently searched (MAGICPROTORB_PATH, then $LOAD_PATH).
|
|
20
|
+
def self.include_paths: () -> Array[String]
|
|
21
|
+
|
|
22
|
+
def self.normalize: (String proto) -> String
|
|
23
|
+
|
|
24
|
+
# The Rust extension's compiler entry point.
|
|
25
|
+
class Compiler
|
|
26
|
+
# Returns a serialized FileDescriptorSet (binary string).
|
|
27
|
+
def self._compile: (String proto_path, Array[String] include_dirs) -> String
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: magicprotorb
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sam
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-29 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: google-protobuf
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.21'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '5.0'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '3.21'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.0'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: rb_sys
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.9'
|
|
40
|
+
type: :runtime
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.9'
|
|
47
|
+
description: |
|
|
48
|
+
magicprotorb lets you `require "magicprotorb/foo/bar_pb"` and have foo/bar.proto
|
|
49
|
+
compiled to descriptors and registered at require time. The dotted require path
|
|
50
|
+
mirrors the canonical proto path 1:1, so the require name, the file location, and
|
|
51
|
+
the descriptor name can never drift apart. A small Rust extension (built on the
|
|
52
|
+
pure-Rust protox compiler) turns .proto text into a FileDescriptorSet, which is
|
|
53
|
+
then registered through the stock protobuf DescriptorPool — making the resulting
|
|
54
|
+
message classes indistinguishable from generated ones.
|
|
55
|
+
email:
|
|
56
|
+
executables: []
|
|
57
|
+
extensions:
|
|
58
|
+
- ext/magicprotorb_native/extconf.rb
|
|
59
|
+
extra_rdoc_files: []
|
|
60
|
+
files:
|
|
61
|
+
- ".rubocop.yml"
|
|
62
|
+
- Cargo.lock
|
|
63
|
+
- Cargo.toml
|
|
64
|
+
- DESIGN.md
|
|
65
|
+
- README.md
|
|
66
|
+
- Rakefile
|
|
67
|
+
- ext/magicprotorb_native/Cargo.toml
|
|
68
|
+
- ext/magicprotorb_native/extconf.rb
|
|
69
|
+
- ext/magicprotorb_native/src/lib.rs
|
|
70
|
+
- lib/magicprotorb.rb
|
|
71
|
+
- lib/magicprotorb/include_path.rb
|
|
72
|
+
- lib/magicprotorb/loader.rb
|
|
73
|
+
- lib/magicprotorb/naming.rb
|
|
74
|
+
- lib/magicprotorb/registrar.rb
|
|
75
|
+
- lib/magicprotorb/require_hook.rb
|
|
76
|
+
- lib/magicprotorb/service_builder.rb
|
|
77
|
+
- lib/magicprotorb/version.rb
|
|
78
|
+
- sig/magicprotorb.rbs
|
|
79
|
+
homepage: https://github.com/grpyc/magicproto-rb
|
|
80
|
+
licenses:
|
|
81
|
+
- MIT
|
|
82
|
+
metadata:
|
|
83
|
+
homepage_uri: https://github.com/grpyc/magicproto-rb
|
|
84
|
+
source_code_uri: https://github.com/grpyc/magicproto-rb
|
|
85
|
+
rubygems_mfa_required: 'true'
|
|
86
|
+
post_install_message:
|
|
87
|
+
rdoc_options: []
|
|
88
|
+
require_paths:
|
|
89
|
+
- lib
|
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: 3.0.0
|
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: '0'
|
|
100
|
+
requirements: []
|
|
101
|
+
rubygems_version: 3.3.7
|
|
102
|
+
signing_key:
|
|
103
|
+
specification_version: 4
|
|
104
|
+
summary: Import .proto files directly in Ruby — no protoc, no generated _pb.rb, no
|
|
105
|
+
build step.
|
|
106
|
+
test_files: []
|