class Interface
Represents an interface definition for gRPC methods. Can be used by both client stubs and server implementations.
Definitions
RPC
RPC method definition
Implementation
RPC = Struct.new(:name, :request_class, :response_class, :streaming, :method, keyword_init: true) do
def initialize(name:, request_class:, response_class:, streaming: :unary, method: nil)
super
end
# Check if this RPC is a streaming RPC (server, client, or bidirectional).
# Server-side handlers for streaming RPCs are expected to block until all messages are sent.
# @returns [Boolean] `true` if streaming, `false` if unary
def streaming?
streaming != :unary
end
end
def self.inherited(subclass)
Hook called when a subclass is created. Initializes the RPC hash for the subclass.
Signature
-
parameter
subclassClass The subclass being created
Implementation
def self.inherited(subclass)
super
subclass.instance_variable_set(:@rpcs, {})
end
def self.rpc(name, **options)
Define an RPC method.
Signature
-
parameter
nameSymbol Method name in PascalCase (e.g., :SayHello, matching .proto file)
-
parameter
request_classClass Request message class
-
parameter
response_classClass Response message class
-
parameter
streamingSymbol Streaming type (:unary, :server_streaming, :client_streaming, :bidirectional)
-
parameter
methodSymbol | Nil Optional explicit Ruby method name (snake_case). If not provided, automatically converts PascalCase to snake_case.
Implementation
def self.rpc(name, **options)
options[:name] = name
# Ensure snake_case method name is always available
options[:method] ||= pascal_case_to_snake_case(name.to_s).to_sym
@rpcs[name] = RPC.new(**options)
end
def self.lookup_rpc(name)
Look up RPC definition for a method. Looks up the inheritance chain to find the RPC definition.
Signature
-
parameter
nameSymbol Method name.
-
returns
Protocol::GRPC::RPC | Nil RPC definition or
Nilif not found.
Implementation
def self.lookup_rpc(name)
klass = self
while klass && klass != Interface
if klass.instance_variable_defined?(:@rpcs)
if rpc = klass.instance_variable_get(:@rpcs)[name]
return rpc
end
end
klass = klass.superclass
end
# Not found:
return nil
end
def self.rpcs
Get all RPC definitions from this class and all parent classes.
Signature
-
returns
Hash All RPC definitions merged from inheritance chain
Implementation
def self.rpcs
all_rpcs = {}
klass = self
# Walk up the inheritance chain:
while klass && klass != Interface
if klass.instance_variable_defined?(:@rpcs)
all_rpcs.merge!(klass.instance_variable_get(:@rpcs))
end
klass = klass.superclass
end
all_rpcs
end
def initialize(name)
Initialize a new interface instance.
Signature
-
parameter
nameString Service name
Implementation
def initialize(name)
@name = name
end
attr :name
Signature
-
attribute
String The service name (e.g., "hello.Greeter").
def path(method_name)
Build gRPC path for a method.
Signature
-
parameter
method_nameString, Symbol Method name in PascalCase (e.g., :SayHello)
-
returns
String gRPC path with PascalCase method name
Implementation
def path(method_name)
Methods.build_path(@name, method_name.to_s)
end
def self.pascal_case_to_snake_case(pascal_case)
Convert PascalCase to snake_case.
Signature
-
parameter
pascal_caseString PascalCase string (e.g., "SayHello")
-
returns
String snake_case string (e.g., "say_hello")
Implementation
def self.pascal_case_to_snake_case(pascal_case)
pascal_case
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') # Insert underscore before capital letters followed by lowercase
.gsub(/([a-z\d])([A-Z])/, '\1_\2') # Insert underscore between lowercase/digit and uppercase
.downcase
end