Class: Twirp::Service

Inherits:
Object
  • Object
show all
Extended by:
ServiceDSL
Defined in:
lib/twirp/service.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ServiceDSL

package, package_name, rpc, rpcs, service, service_full_name, service_name

Constructor Details

#initialize(handler) ⇒ Service

Returns a new instance of Service.



42
43
44
45
46
47
48
49
# File 'lib/twirp/service.rb', line 42

def initialize(handler)
  @handler = handler

  @before = []
  @on_success = []
  @on_error = []
  @exception_raised = []
end

Class Attribute Details

.raise_exceptionsObject

Whether to raise exceptions instead of handling them with exception_raised hooks. Useful during tests to easily debug and catch unexpected exceptions.



30
31
32
# File 'lib/twirp/service.rb', line 30

def raise_exceptions
  @raise_exceptions
end

Class Method Details

.error_response(twerr) ⇒ Object

Rack response with a Twirp::Error



33
34
35
36
37
38
# File 'lib/twirp/service.rb', line 33

def error_response(twerr)
  status = Twirp::ERROR_CODES_TO_HTTP_STATUS[twerr.code]
  headers = {Rack::CONTENT_TYPE => Encoding::JSON} # Twirp errors are always JSON, even if the request was protobuf
  resp_body = Encoding.encode_json(twerr.to_h)
  [status, headers, [resp_body]]
end

Instance Method Details

#before(&block) ⇒ Object

Setup hook blocks.



52
# File 'lib/twirp/service.rb', line 52

def before(&block) @before << block; end

#call(rack_env) ⇒ Object

Rack app handler.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/twirp/service.rb', line 62

def call(rack_env)
  begin
    env = {}
    bad_route = route_request(rack_env, env)
    return error_response(bad_route, env) if bad_route

    @before.each do |hook|
      result = hook.call(rack_env, env)
      return error_response(result, env) if result.is_a? Twirp::Error
    end

    output = call_handler(env)
    return error_response(output, env) if output.is_a? Twirp::Error
    return success_response(output, env)

  rescue => e
    raise e if self.class.raise_exceptions
    begin
      @exception_raised.each{|hook| hook.call(e, env) }
    rescue => hook_e
      e = hook_e
    end

    twerr = Twirp::Error.internal_with(e)
    return error_response(twerr, env)
  end
end

#call_rpc(rpc_method, input = {}, env = {}) ⇒ Object

Call the handler method with input attributes or protobuf object. Returns a proto object (response) or a Twirp::Error. Hooks are not called and exceptions are raised instead of being wrapped. This is useful for unit testing the handler. The env should include fake data that is used by the handler, replicating middleware and before hooks.



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/twirp/service.rb', line 95

def call_rpc(rpc_method, input={}, env={})
  base_env = self.class.rpcs[rpc_method.to_s]
  return Twirp::Error.bad_route("Invalid rpc method #{rpc_method.to_s.inspect}") unless base_env

  env = env.merge(base_env)
  input = env[:input_class].new(input) if input.is_a? Hash
  env[:input] = input
  env[:content_type] ||= Encoding::PROTO
  env[:http_response_headers] = defined?(Rack::Headers) ? Rack::Headers.new : {}
  call_handler(env)
end

#exception_raised(&block) ⇒ Object



55
# File 'lib/twirp/service.rb', line 55

def exception_raised(&block) @exception_raised << block; end

#full_nameObject

Service full_name is needed to route http requests to this service.



58
# File 'lib/twirp/service.rb', line 58

def full_name; @full_name ||= self.class.service_full_name; end

#nameObject



59
# File 'lib/twirp/service.rb', line 59

def name; @name ||= self.class.service_name; end

#on_error(&block) ⇒ Object



54
# File 'lib/twirp/service.rb', line 54

def on_error(&block) @on_error << block; end

#on_success(&block) ⇒ Object



53
# File 'lib/twirp/service.rb', line 53

def on_success(&block) @on_success << block; end