Class: Protobuf::Rpc::Service

Inherits:
Object
  • Object
show all
Includes:
Logger::LogMethods
Defined in:
lib/protobuf/rpc/service.rb

Constant Summary collapse

DEFAULT_LOCATION =
{
  :host => '127.0.0.1',
  :port => 9399 
}
NON_RPC_METHODS =

You MUST add the method name to this list if you are adding instance methods below, otherwise stuff will definitely break

%w( rpcs call_rpc on_rpc_failed rpc_failed request response method_missing async_responder on_send_response send_response log_signature )

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *params) ⇒ Object

If a method comes through that hasn't been found, and it is defined in the rpcs method list, we know that the rpc stub has been created, but no implementing method provides the functionality, so throw an appropriate error, otherwise go to super



127
128
129
130
131
132
133
134
135
136
# File 'lib/protobuf/rpc/service.rb', line 127

def method_missing m, *params
  if rpcs.key?(m)
    exc = MethodNotFound.new "#{self}##{m} was defined as a valid rpc method, but was not implemented."
    log_error exc.message
    raise exc
  else
    log_error "-------------- [#{log_signature}] %s#%s not rpc method, passing to super" % [self.class.name, m.to_s]
    super m, params
  end
end

Instance Attribute Details

#async_responderObject

Returns the value of attribute async_responder.



15
16
17
# File 'lib/protobuf/rpc/service.rb', line 15

def async_responder
  @async_responder
end

Class Method Details

.client(options = {}) ⇒ Object

Create a new client for the given service. See Client#initialize and ClientConnection::DEFAULT_OPTIONS for all available options.



68
69
70
71
72
73
74
75
76
# File 'lib/protobuf/rpc/service.rb', line 68

def client(options={})
  configure
  Client.new({
    :service => self,
    :async => false,
    :host => self.host,
    :port => self.port
  }.merge(options))
end

.configure(config = {}) ⇒ Object

Allows service-level configuration of location. Useful for system-startup configuration of a service so that any Clients using the Service.client sugar will not have to configure the location each time.



83
84
85
86
87
# File 'lib/protobuf/rpc/service.rb', line 83

def configure(config={})
  locations[self] ||= {}
  locations[self][:host] = config[:host] if config.key?(:host)
  locations[self][:port] = config[:port] if config.key?(:port)
end

.hostObject

The host location of the service



100
101
102
103
# File 'lib/protobuf/rpc/service.rb', line 100

def host
  configure
  locations[self][:host] || DEFAULT_LOCATION[:host]
end

.located_at(location) ⇒ Object

Shorthand call to configure, passing a string formatted as hostname:port e.g. 127.0.0.1:9933 e.g. localhost:0



93
94
95
96
97
# File 'lib/protobuf/rpc/service.rb', line 93

def located_at(location)
  return if location.nil? or location.downcase.strip !~ /[a-z0-9.]+:\d+/
  host, port = location.downcase.strip.split ':'
  configure :host => host, :port => port.to_i
end

.locationsObject

Shorthand for @locations class instance var



112
113
114
# File 'lib/protobuf/rpc/service.rb', line 112

def locations
  @locations ||= {}
end

.method_added(old) ⇒ Object

Override methods being added to the class If the method isn't already a private instance method, or it doesn't start with rpc_, or it isn't in the reserved method list (NON_RPC_METHODS), We want to remap the method such that we can wrap it in before and after behavior, most notably calling call_rpc against the method. See call_rpc for more info.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/protobuf/rpc/service.rb', line 36

def method_added(old)
  new_method = :"rpc_#{old}"
  return if private_instance_methods.include?(new_method) or old =~ /^rpc_/ or NON_RPC_METHODS.include?(old.to_s)
  
  alias_method new_method, old
  private new_method
  
  define_method(old) do |pb_request|
    call_rpc(old.to_sym, pb_request)
  end
rescue ArgumentError => e
  # Wrap a known issue where an instance method was defined in the class without
  # it being ignored with NON_RPC_METHODS. 
  raise ArgumentError, "#{e.message} (Note: This could mean that you need to add the method #{old} to the NON_RPC_METHODS list)"
end

.portObject

The port of the service on the destination server



106
107
108
109
# File 'lib/protobuf/rpc/service.rb', line 106

def port
  configure
  locations[self][:port] || DEFAULT_LOCATION[:port]
end

.rpc(method, request_type, response_type) ⇒ Object

Generated service classes should call this method on themselves to add rpc methods to the stack with a given request and response type



54
55
56
57
# File 'lib/protobuf/rpc/service.rb', line 54

def rpc(method, request_type, response_type)
  rpcs[self] ||= {}
  rpcs[self][method] = RpcMethod.new self, method, request_type, response_type
end

.rpcsObject

Shorthand for @rpcs class instance var



60
61
62
# File 'lib/protobuf/rpc/service.rb', line 60

def rpcs
  @rpcs ||= {}
end

Instance Method Details

#log_signatureObject



118
119
120
# File 'lib/protobuf/rpc/service.rb', line 118

def log_signature
  @log_signature ||= "service-#{self.class}"
end

#on_rpc_failed(&rpc_failure_cb) ⇒ Object

Callback register for the server when a service method calls rpc_failed. Called by Service#rpc_failed.



145
146
147
# File 'lib/protobuf/rpc/service.rb', line 145

def on_rpc_failed(&rpc_failure_cb)
  @rpc_failure_cb = rpc_failure_cb
end

#on_send_response(&responder) ⇒ Object

Callback register for the server to be notified when it is appropriate to generate a response to the client. Used in conjunciton with Service#send_response.



165
166
167
# File 'lib/protobuf/rpc/service.rb', line 165

def on_send_response(&responder)
  @responder = responder
end

#rpc_failed(message = "RPC Failed while executing service method #{@current_method}") ⇒ Object

Automatically fail a service method. NOTE: This shortcuts the @async_responder paradigm. There is not any way to get around this currently (and I'm not sure you should want to).



153
154
155
156
157
158
159
# File 'lib/protobuf/rpc/service.rb', line 153

def rpc_failed(message="RPC Failed while executing service method #{@current_method}")
  error_message = 'Unable to invoke rpc_failed, no failure callback is setup.' 
  log_and_raise_error(error_message) if @rpc_failure_cb.nil?
  error = message.is_a?(String) ? RpcFailed.new(message) : message
  log_warn "[#{log_signature}] RPC Failed: %s" % error.message
  @rpc_failure_cb.call(error)
end

#rpcsObject

Convenience wrapper around the rpc method list for a given class



139
140
141
# File 'lib/protobuf/rpc/service.rb', line 139

def rpcs
  self.class.rpcs[self.class]
end

#send_responseObject

Tell the server to generate response and send it to the client.

NOTE: If @async_responder is set to true, this MUST be called by the implementing service method, otherwise the connection will timeout since no data will be sent.



175
176
177
178
179
# File 'lib/protobuf/rpc/service.rb', line 175

def send_response
  error_message = "Unable to send response, responder is nil. It appears you aren't inside of an RPC request/response cycle."
  log_and_raise_error(error_message) if @responder.nil?
  @responder.call(@response)
end