protocol-grpc 0.1.0 → 0.3.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +1 -0
- data/lib/protocol/grpc/body/readable_body.rb +15 -62
- data/lib/protocol/grpc/call.rb +6 -1
- data/lib/protocol/grpc/header.rb +16 -4
- data/lib/protocol/grpc/interface.rb +38 -13
- data/lib/protocol/grpc/metadata.rb +16 -2
- data/lib/protocol/grpc/version.rb +2 -1
- data/readme.md +10 -0
- data/releases.md +10 -0
- data.tar.gz.sig +2 -0
- metadata +31 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a889e5eb3f6b74e8609a27a4667b62611ab8dc0515d24d9ecf70c4fee7e25b11
|
|
4
|
+
data.tar.gz: 56b5bc9e9d8a767db66ead55bcc97404e8092ef6a75a008d4bd893624391b808
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6aab2ee9f75421070a6348c39da680c7cd320e31021b9492f48d48832ab4caf580ffa77113a792e336cd87158890d22ed12d9e4650079954b33c99c7da85e422
|
|
7
|
+
data.tar.gz: 67c0e25c06ed8f5c8ad431233506f6133655d64a6e546d9767c74049d85b0fa6b54e37384e15e6458c429f0431779fad96c8b01e30821acd8f7952a260ccf3de
|
checksums.yaml.gz.sig
ADDED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# Copyright, 2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require "protocol/http"
|
|
7
|
+
require "protocol/http/body/wrapper"
|
|
7
8
|
require "zlib"
|
|
8
9
|
|
|
9
10
|
module Protocol
|
|
@@ -12,56 +13,36 @@ module Protocol
|
|
|
12
13
|
module Body
|
|
13
14
|
# Represents a readable body for gRPC messages with length-prefixed framing.
|
|
14
15
|
# This is the standard readable body for gRPC - all gRPC responses use message framing.
|
|
15
|
-
|
|
16
|
+
# Wraps the underlying HTTP body and transforms raw chunks into decoded gRPC messages.
|
|
17
|
+
class ReadableBody < Protocol::HTTP::Body::Wrapper
|
|
18
|
+
def self.wrap(message, **options)
|
|
19
|
+
if body = message.body
|
|
20
|
+
message.body = self.new(body, **options)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return message.body
|
|
24
|
+
end
|
|
25
|
+
|
|
16
26
|
# Initialize a new readable body for gRPC messages.
|
|
17
27
|
# @parameter body [Protocol::HTTP::Body::Readable] The underlying HTTP body
|
|
18
28
|
# @parameter message_class [Class | Nil] Protobuf message class with .decode method.
|
|
19
29
|
# If `nil`, returns raw binary data (useful for channel adapters)
|
|
20
30
|
# @parameter encoding [String | Nil] Compression encoding (from grpc-encoding header)
|
|
21
31
|
def initialize(body, message_class: nil, encoding: nil)
|
|
22
|
-
|
|
32
|
+
super(body)
|
|
23
33
|
@message_class = message_class
|
|
24
34
|
@encoding = encoding
|
|
25
35
|
@buffer = String.new.force_encoding(Encoding::BINARY)
|
|
26
|
-
@closed = false
|
|
27
36
|
end
|
|
28
37
|
|
|
29
|
-
# @attribute [Protocol::HTTP::Body::Readable] The underlying HTTP body.
|
|
30
|
-
attr_reader :body
|
|
31
|
-
|
|
32
38
|
# @attribute [String | Nil] The compression encoding.
|
|
33
39
|
attr_reader :encoding
|
|
34
40
|
|
|
35
|
-
# Close the input body.
|
|
36
|
-
# @parameter error [Exception | Nil] Optional error that caused the close
|
|
37
|
-
# @returns [Nil]
|
|
38
|
-
def close(error = nil)
|
|
39
|
-
@closed = true
|
|
40
|
-
|
|
41
|
-
if @body
|
|
42
|
-
@body.close(error)
|
|
43
|
-
@body = nil
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
nil
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Check if the stream has been closed.
|
|
50
|
-
# @returns [Boolean] `true` if the stream is closed, `false` otherwise
|
|
51
|
-
def closed?
|
|
52
|
-
@closed or @body.nil?
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Check if there are any input chunks remaining.
|
|
56
|
-
# @returns [Boolean] `true` if the body is empty, `false` otherwise
|
|
57
|
-
def empty?
|
|
58
|
-
@body.nil?
|
|
59
|
-
end
|
|
60
|
-
|
|
61
41
|
# Read the next gRPC message.
|
|
42
|
+
# Overrides Wrapper#read to transform raw HTTP body chunks into decoded gRPC messages.
|
|
62
43
|
# @returns [Object | String | Nil] Decoded message, raw binary, or `Nil` if stream ended
|
|
63
44
|
def read
|
|
64
|
-
return nil if
|
|
45
|
+
return nil if @body.nil? || @body.empty?
|
|
65
46
|
|
|
66
47
|
# Read 5-byte prefix: 1 byte compression flag + 4 bytes length
|
|
67
48
|
prefix = read_exactly(5)
|
|
@@ -87,24 +68,6 @@ module Protocol
|
|
|
87
68
|
end
|
|
88
69
|
end
|
|
89
70
|
|
|
90
|
-
# Enumerate all messages until finished, then invoke {close}.
|
|
91
|
-
# @yields {|message| ...} The block to call with each message.
|
|
92
|
-
def each
|
|
93
|
-
return to_enum unless block_given?
|
|
94
|
-
|
|
95
|
-
error = nil
|
|
96
|
-
begin
|
|
97
|
-
while (message = read)
|
|
98
|
-
yield message
|
|
99
|
-
end
|
|
100
|
-
rescue StandardError => e
|
|
101
|
-
error = e
|
|
102
|
-
raise
|
|
103
|
-
ensure
|
|
104
|
-
close(error)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
71
|
private
|
|
109
72
|
|
|
110
73
|
# Read exactly n bytes from the underlying body.
|
|
@@ -113,28 +76,18 @@ module Protocol
|
|
|
113
76
|
def read_exactly(n)
|
|
114
77
|
# Fill buffer until we have enough data:
|
|
115
78
|
while @buffer.bytesize < n
|
|
116
|
-
return nil if
|
|
79
|
+
return nil if @body.nil? || @body.empty?
|
|
117
80
|
|
|
118
81
|
# Read chunk from underlying body:
|
|
119
82
|
chunk = @body.read
|
|
120
83
|
|
|
121
84
|
if chunk.nil?
|
|
122
85
|
# End of stream:
|
|
123
|
-
if @body && !@closed
|
|
124
|
-
@body.close
|
|
125
|
-
@closed = true
|
|
126
|
-
end
|
|
127
86
|
return nil
|
|
128
87
|
end
|
|
129
88
|
|
|
130
89
|
# Append to buffer:
|
|
131
90
|
@buffer << chunk.force_encoding(Encoding::BINARY)
|
|
132
|
-
|
|
133
|
-
# Check if body is empty and close if needed:
|
|
134
|
-
if @body.empty?
|
|
135
|
-
@body.close
|
|
136
|
-
@closed = true
|
|
137
|
-
end
|
|
138
91
|
end
|
|
139
92
|
|
|
140
93
|
# Extract the required data:
|
data/lib/protocol/grpc/call.rb
CHANGED
|
@@ -12,9 +12,11 @@ module Protocol
|
|
|
12
12
|
class Call
|
|
13
13
|
# Initialize a new RPC call context.
|
|
14
14
|
# @parameter request [Protocol::HTTP::Request] The HTTP request
|
|
15
|
+
# @parameter response [Protocol::HTTP::Response | Nil] The HTTP response (for setting metadata and trailers)
|
|
15
16
|
# @parameter deadline [Async::Deadline | Nil] Deadline for the call
|
|
16
|
-
def initialize(request, deadline: nil)
|
|
17
|
+
def initialize(request, response = nil, deadline: nil)
|
|
17
18
|
@request = request
|
|
19
|
+
@response = response
|
|
18
20
|
@deadline = deadline
|
|
19
21
|
@cancelled = false
|
|
20
22
|
end
|
|
@@ -22,6 +24,9 @@ module Protocol
|
|
|
22
24
|
# @attribute [Protocol::HTTP::Request] The underlying HTTP request.
|
|
23
25
|
attr_reader :request
|
|
24
26
|
|
|
27
|
+
# @attribute [Protocol::HTTP::Response | Nil] The HTTP response.
|
|
28
|
+
attr_reader :response
|
|
29
|
+
|
|
25
30
|
# @attribute [Async::Deadline | Nil] The deadline for this call.
|
|
26
31
|
attr_reader :deadline
|
|
27
32
|
|
data/lib/protocol/grpc/header.rb
CHANGED
|
@@ -20,9 +20,9 @@ module Protocol
|
|
|
20
20
|
class Status
|
|
21
21
|
# Initialize the status header with the given value.
|
|
22
22
|
#
|
|
23
|
-
# @parameter value [String, Integer] The status code as a string or
|
|
23
|
+
# @parameter value [String, Integer, Array] The status code as a string, integer, or array (takes first element).
|
|
24
24
|
def initialize(value)
|
|
25
|
-
@value = value
|
|
25
|
+
@value = normalize_value(value)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Get the status code as an integer.
|
|
@@ -40,12 +40,24 @@ module Protocol
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Merge another status value (takes the new value, as status should only appear once)
|
|
43
|
-
# @parameter value [String, Integer] The new status code
|
|
43
|
+
# @parameter value [String, Integer, Array] The new status code
|
|
44
44
|
def <<(value)
|
|
45
|
-
@value = value
|
|
45
|
+
@value = normalize_value(value)
|
|
46
46
|
self
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Normalize a value to an integer status code.
|
|
52
|
+
# Handles arrays (from external clients), strings, and integers.
|
|
53
|
+
# @parameter value [String, Integer, Array] The raw value
|
|
54
|
+
# @returns [Integer] The normalized status code
|
|
55
|
+
def normalize_value(value)
|
|
56
|
+
# Handle Array case (may occur with external clients)
|
|
57
|
+
actual_value = value.is_a?(Array) ? value.flatten.compact.first : value
|
|
58
|
+
actual_value.to_i
|
|
59
|
+
end
|
|
60
|
+
|
|
49
61
|
# Whether this header is acceptable in HTTP trailers.
|
|
50
62
|
# The `grpc-status` header can appear in trailers as per the gRPC specification.
|
|
51
63
|
# @returns [Boolean] `true`, as grpc-status can appear in trailers.
|
|
@@ -7,16 +7,23 @@ require_relative "methods"
|
|
|
7
7
|
|
|
8
8
|
module Protocol
|
|
9
9
|
module GRPC
|
|
10
|
-
# RPC method definition
|
|
11
|
-
RPC = Struct.new(:request_class, :response_class, :streaming, :method, keyword_init: true) do
|
|
12
|
-
def initialize(request_class:, response_class:, streaming: :unary, method: nil)
|
|
13
|
-
super
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
10
|
# Represents an interface definition for gRPC methods.
|
|
18
11
|
# Can be used by both client stubs and server implementations.
|
|
19
12
|
class Interface
|
|
13
|
+
# RPC method definition
|
|
14
|
+
RPC = Struct.new(:request_class, :response_class, :streaming, :method, keyword_init: true) do
|
|
15
|
+
def initialize(request_class:, response_class:, streaming: :unary, method: nil)
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Check if this RPC is a streaming RPC (server, client, or bidirectional).
|
|
20
|
+
# Server-side handlers for streaming RPCs are expected to block until all messages are sent.
|
|
21
|
+
# @returns [Boolean] `true` if streaming, `false` if unary
|
|
22
|
+
def streaming?
|
|
23
|
+
streaming != :unary
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
20
27
|
# Hook called when a subclass is created.
|
|
21
28
|
# Initializes the RPC hash for the subclass.
|
|
22
29
|
# @parameter subclass [Class] The subclass being created
|
|
@@ -33,6 +40,9 @@ module Protocol
|
|
|
33
40
|
# @parameter streaming [Symbol] Streaming type (:unary, :server_streaming, :client_streaming, :bidirectional)
|
|
34
41
|
# @parameter method [Symbol | Nil] Optional explicit Ruby method name (snake_case). If not provided, automatically converts PascalCase to snake_case.
|
|
35
42
|
def self.rpc(name, **options)
|
|
43
|
+
# Ensure snake_case method name is always available
|
|
44
|
+
options[:method] ||= pascal_case_to_snake_case(name.to_s).to_sym
|
|
45
|
+
|
|
36
46
|
@rpcs[name] = RPC.new(**options)
|
|
37
47
|
end
|
|
38
48
|
|
|
@@ -44,12 +54,15 @@ module Protocol
|
|
|
44
54
|
klass = self
|
|
45
55
|
while klass && klass != Interface
|
|
46
56
|
if klass.instance_variable_defined?(:@rpcs)
|
|
47
|
-
rpc = klass.instance_variable_get(:@rpcs)[name]
|
|
48
|
-
|
|
57
|
+
if rpc = klass.instance_variable_get(:@rpcs)[name]
|
|
58
|
+
return rpc
|
|
59
|
+
end
|
|
49
60
|
end
|
|
50
61
|
klass = klass.superclass
|
|
51
62
|
end
|
|
52
|
-
|
|
63
|
+
|
|
64
|
+
# Not found:
|
|
65
|
+
return nil
|
|
53
66
|
end
|
|
54
67
|
|
|
55
68
|
# Get all RPC definitions from this class and all parent classes.
|
|
@@ -69,21 +82,33 @@ module Protocol
|
|
|
69
82
|
all_rpcs
|
|
70
83
|
end
|
|
71
84
|
|
|
72
|
-
# @attribute [String] The service name (e.g., "hello.Greeter").
|
|
73
|
-
attr :name
|
|
74
|
-
|
|
75
85
|
# Initialize a new interface instance.
|
|
76
86
|
# @parameter name [String] Service name
|
|
77
87
|
def initialize(name)
|
|
78
88
|
@name = name
|
|
79
89
|
end
|
|
80
90
|
|
|
91
|
+
# @attribute [String] The service name (e.g., "hello.Greeter").
|
|
92
|
+
attr :name
|
|
93
|
+
|
|
81
94
|
# Build gRPC path for a method.
|
|
82
95
|
# @parameter method_name [String, Symbol] Method name in PascalCase (e.g., :SayHello)
|
|
83
96
|
# @returns [String] gRPC path with PascalCase method name
|
|
84
97
|
def path(method_name)
|
|
85
98
|
Methods.build_path(@name, method_name.to_s)
|
|
86
99
|
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
# Convert PascalCase to snake_case.
|
|
104
|
+
# @parameter pascal_case [String] PascalCase string (e.g., "SayHello")
|
|
105
|
+
# @returns [String] snake_case string (e.g., "say_hello")
|
|
106
|
+
def self.pascal_case_to_snake_case(pascal_case)
|
|
107
|
+
pascal_case
|
|
108
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') # Insert underscore before capital letters followed by lowercase
|
|
109
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2') # Insert underscore between lowercase/digit and uppercase
|
|
110
|
+
.downcase
|
|
111
|
+
end
|
|
87
112
|
end
|
|
88
113
|
end
|
|
89
114
|
end
|
|
@@ -33,8 +33,22 @@ module Protocol
|
|
|
33
33
|
status.to_i
|
|
34
34
|
else
|
|
35
35
|
# Fallback for when header policy isn't used
|
|
36
|
-
|
|
37
|
-
status_value.
|
|
36
|
+
# Handle Array case (may occur with external clients)
|
|
37
|
+
status_value = if status.is_a?(Array)
|
|
38
|
+
# Flatten and take first non-nil value, recursively handle nested arrays
|
|
39
|
+
flattened = status.flatten.compact.first
|
|
40
|
+
# If still an array, take first element
|
|
41
|
+
flattened.is_a?(Array) ? flattened.first : flattened
|
|
42
|
+
else
|
|
43
|
+
status
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Convert to string then integer to handle various types
|
|
47
|
+
# Handle case where status_value might still be an array somehow
|
|
48
|
+
if status_value.is_a?(Array)
|
|
49
|
+
status_value = status_value.first
|
|
50
|
+
end
|
|
51
|
+
status_value.to_s.to_i
|
|
38
52
|
end
|
|
39
53
|
end
|
|
40
54
|
|
data/readme.md
CHANGED
|
@@ -28,6 +28,16 @@ Please see the [project documentation](https://socketry.github.io/protocol-grpc/
|
|
|
28
28
|
|
|
29
29
|
Please see the [project releases](https://socketry.github.io/protocol-grpc/releases/index) for all releases.
|
|
30
30
|
|
|
31
|
+
### v0.3.0
|
|
32
|
+
|
|
33
|
+
- **Breaking**: `Protocol::GRPC::Call` now takes a `response` object parameter instead of separate `response_headers`.
|
|
34
|
+
- **Breaking**: Removed `Call#response_headers` method. Use `call.response.headers` directly.
|
|
35
|
+
- Added `RPC#streaming?` method to check if an RPC is streaming.
|
|
36
|
+
|
|
37
|
+
### v0.2.0
|
|
38
|
+
|
|
39
|
+
- `RPC#method` is always defined (snake case).
|
|
40
|
+
|
|
31
41
|
### v0.1.0
|
|
32
42
|
|
|
33
43
|
- Initial design.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.3.0
|
|
4
|
+
|
|
5
|
+
- **Breaking**: `Protocol::GRPC::Call` now takes a `response` object parameter instead of separate `response_headers`.
|
|
6
|
+
- **Breaking**: Removed `Call#response_headers` method. Use `call.response.headers` directly.
|
|
7
|
+
- Added `RPC#streaming?` method to check if an RPC is streaming.
|
|
8
|
+
|
|
9
|
+
## v0.2.0
|
|
10
|
+
|
|
11
|
+
- `RPC#method` is always defined (snake case).
|
|
12
|
+
|
|
3
13
|
## v0.1.0
|
|
4
14
|
|
|
5
15
|
- Initial design.
|
data.tar.gz.sig
ADDED
metadata
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-grpc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
8
8
|
bindir: bin
|
|
9
|
-
cert_chain:
|
|
9
|
+
cert_chain:
|
|
10
|
+
- |
|
|
11
|
+
-----BEGIN CERTIFICATE-----
|
|
12
|
+
MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
|
|
13
|
+
ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
|
|
14
|
+
CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
|
|
15
|
+
MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
|
|
16
|
+
MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
|
|
17
|
+
bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
|
|
18
|
+
igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
|
|
19
|
+
9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
|
|
20
|
+
sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
|
|
21
|
+
e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
|
|
22
|
+
XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
|
|
23
|
+
RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
|
|
24
|
+
tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
|
|
25
|
+
zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
|
|
26
|
+
xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
|
27
|
+
BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
|
|
28
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
|
|
29
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
|
|
30
|
+
cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
|
|
31
|
+
xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
|
|
32
|
+
c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
|
|
33
|
+
8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
|
|
34
|
+
JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
|
|
35
|
+
eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
|
|
36
|
+
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
|
37
|
+
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
|
38
|
+
-----END CERTIFICATE-----
|
|
10
39
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
40
|
dependencies:
|
|
12
41
|
- !ruby/object:Gem::Dependency
|
metadata.gz.sig
ADDED
|
Binary file
|