class Interface

Represents an interface definition for gRPC methods. Can be used by both client stubs and server implementations.

Nested Classes and Modules

class RPC

RPC method definition

Definitions

def self.stream(message_class)

Helper method to mark a message type as streamed in RPC definitions. Can be called directly within Interface subclasses without the Protocol::GRPC prefix.

Example: Define streaming RPCs

class MyService < Protocol::GRPC::Interface
  rpc :sum, stream(Num), Num              # client streaming
  rpc :fib, FibArgs, stream(Num)          # server streaming
  rpc :chat, stream(Msg), stream(Msg)     # bidirectional streaming
end

Signature

parameter message_class Class

The message class to mark as streamed

returns Streaming

A wrapper indicating this type is streamed

Implementation

def self.stream(message_class)
	Streaming.new(message_class)
end

def self.inherited(subclass)

Hook called when a subclass is created. Initializes the RPC hash for the subclass.

Signature

parameter subclass Class

The subclass being created

Implementation

def self.inherited(subclass)
	super
	
	subclass.instance_variable_set(:@rpcs, {})
end

def self.rpc(name, request_class = nil, response_class = nil, **options)

Define an RPC method.

Example: Using stream() decorator syntax

rpc :div, DivArgs, DivReply                      # unary
rpc :sum, stream(Num), Num                       # client streaming
rpc :fib, FibArgs, stream(Num)                   # server streaming
rpc :chat, stream(DivArgs), stream(DivReply)     # bidirectional streaming

Signature

parameter name Symbol

Method name in PascalCase (e.g., :SayHello, matching .proto file)

parameter request_class Class | Streaming

Request message class, optionally wrapped with stream()

parameter response_class Class | Streaming

Response message class, optionally wrapped with stream()

parameter streaming Symbol

Streaming type (:unary, :server_streaming, :client_streaming, :bidirectional) This is automatically inferred from stream() decorators if not explicitly provided

parameter method Symbol | Nil

Optional explicit Ruby method name (snake_case). If not provided, automatically converts PascalCase to snake_case.

Implementation

def self.rpc(name, request_class = nil, response_class = nil, **options)
	options[:name] = name
	
	# Check if request or response are wrapped with stream()
	request_streaming = request_class.is_a?(Streaming)
	response_streaming = response_class.is_a?(Streaming)
	
	# Unwrap StreamWrapper if present
	options[:request_class] ||= request_streaming ? request_class.message_class : request_class
	options[:response_class] ||= response_streaming ? response_class.message_class : response_class
	
	# Auto-detect streaming type from stream() decorators if not explicitly set
	if !options.key?(:streaming)
		if request_streaming && response_streaming
			options[:streaming] = :bidirectional
		elsif request_streaming
			options[:streaming] = :client_streaming
		elsif response_streaming
			options[:streaming] = :server_streaming
		else
			options[:streaming] = :unary
		end
	end
	
	# 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 name Symbol

Method name.

returns Protocol::GRPC::RPC | Nil

RPC definition or Nil if 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 name String

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_name String | Symbol

Method name in PascalCase (e.g., :SayHello)

returns String

gRPC path with PascalCase method name

Implementation

def path(method_name)
	Route.build(@name, method_name.to_s)
end

def self.pascal_case_to_snake_case(pascal_case)

Convert PascalCase to snake_case.

Signature

parameter pascal_case String

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