twirp 1.2.0 → 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 83302ec929694b2ac96f016518ad5585de1fa65e
4
- data.tar.gz: c79097cde99163118952b003b1402332f83b8c7f
2
+ SHA256:
3
+ metadata.gz: 3f17d89c29073da7ebffa780c9e8577e10f2537e28900344d8a055e098aa5f3d
4
+ data.tar.gz: 2932fb1abec1db6b14c3b39e8b7d2f81e318c0bdc02639d83a99bdfacd26001f
5
5
  SHA512:
6
- metadata.gz: ba357830275b3b4af11bc6f7319118a17a4405c338592ccb26293a0fed95622612b7f6d167214254eb6b0c05664d91f2ca5c847aeceb171e7c3dd0395f73d7ec
7
- data.tar.gz: '05581b8cb43081e0e0103f6e3c50af6dfa0280b67355307418d287086e0c109e335bd0708f84eed4561eca7c288f1aa98884e71524a797ad07fe081a737efa3f'
6
+ metadata.gz: aca9edc089a15abfe4426880e8db8a8e2cac3f47ff056703e686dc14a3fe879a952a2b96c2de8bb06301b7406262601102c29fe48af6f9ead8775e9e2c76e35b
7
+ data.tar.gz: 10fc31c496e2fa4020ff9a643e08aa9ba333b0baa2324d1a42444ca6916d672a449eeef7ab22e674de81cc7a3e906d82fc97736714dfab29a999bfb4dd191f75
data/README.md CHANGED
@@ -2,19 +2,21 @@
2
2
 
3
3
  [Twirp is a protocol](https://twitchtv.github.io/twirp/docs/spec_v5.html) for routing and serialization of services defined in a [.proto file](https://developers.google.com/protocol-buffers/docs/proto3), allowing easy implementation of RPC services with auto-generated clients in different languages.
4
4
 
5
- The [canonical implementation](https://github.com/twitchtv/twirp) is in Golang. The Twirp-Ruby project in this repository is the Ruby implementation.
5
+ The [canonical implementation](https://github.com/twitchtv/twirp) is in Golang. The Twirp-Ruby project is the official implementation in Ruby for both server and clients.
6
6
 
7
7
 
8
8
  ## Install
9
9
 
10
10
  Add `gem "twirp"` to your Gemfile, or install with `gem install twirp`.
11
11
 
12
+ To auto-generate Ruby code from a proto file, use the `protoc` plugin and the `--ruby_out` option ([see Wiki page](https://github.com/twitchtv/twirp-ruby/wiki/Code-Generation)).
13
+
12
14
 
13
15
  ## Documentation
14
16
 
15
- [Go to the Wiki](https://github.com/twitchtv/twirp-ruby/wiki).
17
+ [On the wiki](https://github.com/twitchtv/twirp-ruby/wiki).
16
18
 
17
19
 
18
20
  ## Contributing
19
21
 
20
- [Go to the CONTRIBUTING file](CONTRIBUTING.md).
22
+ [On the CONTRIBUTING file](CONTRIBUTING.md).
@@ -24,6 +24,7 @@ module Twirp
24
24
 
25
25
  package = opts[:package].to_s
26
26
  service = opts[:service].to_s
27
+ @strict = opts.fetch( :strict, false )
27
28
  raise ArgumentError.new("Missing option :service") if service.empty?
28
29
  @service_full_name = package.empty? ? service : "#{package}.#{service}"
29
30
  end
@@ -33,7 +34,8 @@ module Twirp
33
34
  def rpc(rpc_method, attrs={}, req_opts=nil)
34
35
  body = Encoding.encode_json(attrs)
35
36
 
36
- resp = self.class.make_http_request(@conn, @service_full_name, rpc_method, Encoding::JSON, req_opts, body)
37
+ encoding = @strict ? Encoding::JSON_STRICT : Encoding::JSON
38
+ resp = self.class.make_http_request(@conn, @service_full_name, rpc_method, encoding, req_opts, body)
37
39
  if resp.status != 200
38
40
  return ClientResp.new(nil, self.class.error_from_response(resp))
39
41
  end
@@ -17,13 +17,18 @@ module Twirp
17
17
 
18
18
  module Encoding
19
19
  JSON = "application/json"
20
+ # An opt-in content type useful when curling or manually testing a twirp
21
+ # service. This will fail if unknown fields are encountered. The return
22
+ # content type will be application/json.
23
+ JSON_STRICT = "application/json; strict=true"
20
24
  PROTO = "application/protobuf"
21
25
 
22
26
  class << self
23
27
 
24
28
  def decode(bytes, msg_class, content_type)
25
29
  case content_type
26
- when JSON then msg_class.decode_json(bytes)
30
+ when JSON then msg_class.decode_json(bytes, ignore_unknown_fields: true)
31
+ when JSON_STRICT then msg_class.decode_json(bytes, ignore_unknown_fields: false)
27
32
  when PROTO then msg_class.decode(bytes)
28
33
  else raise ArgumentError.new("Invalid content_type")
29
34
  end
@@ -31,7 +36,7 @@ module Twirp
31
36
 
32
37
  def encode(msg_obj, msg_class, content_type)
33
38
  case content_type
34
- when JSON then msg_class.encode_json(msg_obj)
39
+ when JSON, JSON_STRICT then msg_class.encode_json(msg_obj, emit_defaults: true)
35
40
  when PROTO then msg_class.encode(msg_obj)
36
41
  else raise ArgumentError.new("Invalid content_type")
37
42
  end
@@ -46,7 +51,7 @@ module Twirp
46
51
  end
47
52
 
48
53
  def valid_content_type?(content_type)
49
- content_type == JSON || content_type == PROTO
54
+ content_type == JSON || content_type == PROTO || content_type == JSON_STRICT
50
55
  end
51
56
 
52
57
  def valid_content_types
@@ -16,15 +16,16 @@ module Twirp
16
16
  # Valid Twirp error codes and their mapping to related HTTP status.
17
17
  # This can also be used to check if a code is valid (check if not nil).
18
18
  ERROR_CODES_TO_HTTP_STATUS = {
19
- canceled: 408, # RequestTimeout
20
- invalid_argument: 400, # BadRequest
21
- deadline_exceeded: 408, # RequestTimeout
19
+ canceled: 408, # Request Timeout
20
+ invalid_argument: 400, # Bad Request
21
+ malformed: 400, # Bad Request
22
+ deadline_exceeded: 408, # Request Timeout
22
23
  not_found: 404, # Not Found
23
24
  bad_route: 404, # Not Found
24
25
  already_exists: 409, # Conflict
25
26
  permission_denied: 403, # Forbidden
26
27
  unauthenticated: 401, # Unauthorized
27
- resource_exhausted: 403, # Forbidden
28
+ resource_exhausted: 429, # Too Many Requests
28
29
  failed_precondition: 412, # Precondition Failed
29
30
  aborted: 409, # Conflict
30
31
  out_of_range: 400, # Bad Request
@@ -49,10 +50,10 @@ module Twirp
49
50
  ERROR_CODES_TO_HTTP_STATUS.key? code # one of the valid symbols
50
51
  end
51
52
 
52
- # Use this constructors to ensure the errors have valid error codes. Example:
53
+ # Code constructors to ensure valid error codes. Example:
53
54
  # Twirp::Error.internal("boom")
54
- # Twirp::Error.invalid_argument("foo is mandatory", argument: "foo")
55
- # Twirp::Error.permission_denied("thou shall not pass!", target: "Balrog")
55
+ # Twirp::Error.invalid_argument("foo is mandatory", mymeta: "foobar")
56
+ # Twirp::Error.permission_denied("Thou shall not pass!", target: "Balrog")
56
57
  ERROR_CODES.each do |code|
57
58
  define_singleton_method code do |msg, meta=nil|
58
59
  new(code, msg, meta)
@@ -62,7 +63,7 @@ module Twirp
62
63
  # Wrap another error as a Twirp::Error :internal.
63
64
  def self.internal_with(err)
64
65
  twerr = internal err.message, cause: err.class.name
65
- twerr.cause = err
66
+ twerr.cause = err # availabe in error hook for inspection, but not in the response
66
67
  twerr
67
68
  end
68
69
 
@@ -80,6 +81,7 @@ module Twirp
80
81
  @meta = validate_meta(meta)
81
82
  end
82
83
 
84
+ # Key-value representation of the error. Can be directly serialized into JSON.
83
85
  def to_h
84
86
  h = {
85
87
  code: @code,
@@ -23,10 +23,19 @@ module Twirp
23
23
  extend ServiceDSL
24
24
 
25
25
  class << self
26
- # Raise exceptions instead of handling them with exception_raised hooks.
26
+
27
+ # Whether to raise exceptions instead of handling them with exception_raised hooks.
27
28
  # Useful during tests to easily debug and catch unexpected exceptions.
28
- # Default false.
29
- attr_accessor :raise_exceptions
29
+ attr_accessor :raise_exceptions # Default: false
30
+
31
+ # Rack response with a Twirp::Error
32
+ def error_response(twerr)
33
+ status = Twirp::ERROR_CODES_TO_HTTP_STATUS[twerr.code]
34
+ headers = {'Content-Type' => Encoding::JSON} # Twirp errors are always JSON, even if the request was protobuf
35
+ resp_body = Encoding.encode_json(twerr.to_h)
36
+ [status, headers, [resp_body]]
37
+ end
38
+
30
39
  end
31
40
 
32
41
  def initialize(handler)
@@ -98,29 +107,30 @@ module Twirp
98
107
  private
99
108
 
100
109
  # Parse request and fill env with rpc data.
101
- # Returns a bad_route error if something went wrong.
110
+ # Returns a bad_route error if could not be properly routed to a Twirp method.
111
+ # Returns a malformed error if could not decode the body (either bad JSON or bad Protobuf)
102
112
  def route_request(rack_env, env)
103
113
  rack_request = Rack::Request.new(rack_env)
104
114
 
105
115
  if rack_request.request_method != "POST"
106
- return bad_route_error("HTTP request method must be POST", rack_request)
116
+ return route_err(:bad_route, "HTTP request method must be POST", rack_request)
107
117
  end
108
118
 
109
119
  content_type = rack_request.get_header("CONTENT_TYPE")
110
120
  if !Encoding.valid_content_type?(content_type)
111
- return bad_route_error("Unexpected Content-Type: #{content_type.inspect}. Content-Type header must be one of #{Encoding.valid_content_types.inspect}", rack_request)
121
+ return route_err(:bad_route, "Unexpected Content-Type: #{content_type.inspect}. Content-Type header must be one of #{Encoding.valid_content_types.inspect}", rack_request)
112
122
  end
113
123
  env[:content_type] = content_type
114
124
 
115
125
  path_parts = rack_request.fullpath.split("/")
116
126
  if path_parts.size < 3 || path_parts[-2] != self.full_name
117
- return bad_route_error("Invalid route. Expected format: POST {BaseURL}/#{self.full_name}/{Method}", rack_request)
127
+ return route_err(:bad_route, "Invalid route. Expected format: POST {BaseURL}/#{self.full_name}/{Method}", rack_request)
118
128
  end
119
129
  method_name = path_parts[-1]
120
130
 
121
131
  base_env = self.class.rpcs[method_name]
122
132
  if !base_env
123
- return bad_route_error("Invalid rpc method #{method_name.inspect}", rack_request)
133
+ return route_err(:bad_route, "Invalid rpc method #{method_name.inspect}", rack_request)
124
134
  end
125
135
  env.merge!(base_env) # :rpc_method, :input_class, :output_class
126
136
 
@@ -132,7 +142,7 @@ module Twirp
132
142
  if e.is_a?(Google::Protobuf::ParseError)
133
143
  error_msg += ": #{e.message.strip}"
134
144
  end
135
- return bad_route_error(error_msg, rack_request)
145
+ return route_err(:malformed, error_msg, rack_request)
136
146
  end
137
147
 
138
148
  env[:input] = input
@@ -140,12 +150,11 @@ module Twirp
140
150
  return
141
151
  end
142
152
 
143
- def bad_route_error(msg, req)
144
- Twirp::Error.bad_route msg, twirp_invalid_route: "#{req.request_method} #{req.fullpath}"
153
+ def route_err(code, msg, req)
154
+ Twirp::Error.new code, msg, twirp_invalid_route: "#{req.request_method} #{req.fullpath}"
145
155
  end
146
156
 
147
157
 
148
-
149
158
  # Call handler method and return a Protobuf Message or a Twirp::Error.
150
159
  def call_handler(env)
151
160
  m = env[:ruby_method]
@@ -181,11 +190,7 @@ module Twirp
181
190
  def error_response(twerr, env)
182
191
  begin
183
192
  @on_error.each{|hook| hook.call(twerr, env) }
184
-
185
- status = Twirp::ERROR_CODES_TO_HTTP_STATUS[twerr.code]
186
- resp_body = Encoding.encode_json(twerr.to_h)
187
- [status, error_response_headers, [resp_body]]
188
-
193
+ self.class.error_response(twerr)
189
194
  rescue => e
190
195
  return exception_response(e, env)
191
196
  end
@@ -193,6 +198,7 @@ module Twirp
193
198
 
194
199
  def exception_response(e, env)
195
200
  raise e if self.class.raise_exceptions
201
+
196
202
  begin
197
203
  @exception_raised.each{|hook| hook.call(e, env) }
198
204
  rescue => hook_e
@@ -200,13 +206,7 @@ module Twirp
200
206
  end
201
207
 
202
208
  twerr = Twirp::Error.internal_with(e)
203
- resp_body = Encoding.encode_json(twerr.to_h)
204
- [500, error_response_headers, [resp_body]]
205
- end
206
-
207
- def error_response_headers
208
- # Twirp errors are always JSON, even if the request was protobuf
209
- {'Content-Type' => Encoding::JSON}
209
+ self.class.error_response(twerr)
210
210
  end
211
211
 
212
212
  end
@@ -12,5 +12,5 @@
12
12
  # permissions and limitations under the License.
13
13
 
14
14
  module Twirp
15
- VERSION = "1.2.0"
15
+ VERSION = "1.7.2"
16
16
  end
@@ -29,6 +29,20 @@ class ClientJSONTest < Minitest::Test
29
29
  assert_equal 3, resp.data["blah_resp"]
30
30
  end
31
31
 
32
+ def test_client_json_strict_encoding
33
+ c = Twirp::ClientJSON.new(conn_stub("/my.pkg.Talking/Blah") {|req|
34
+ assert_equal "application/json; strict=true", req.request_headers['Content-Type']
35
+ assert_equal '{"blah1":1,"blah2":2}', req.body # body is json
36
+
37
+ [200, {}, '{"blah_resp": 3}']
38
+ }, package: "my.pkg", service: "Talking", strict: true)
39
+
40
+ resp = c.rpc :Blah, blah1: 1, blah2: 2
41
+ assert_nil resp.error
42
+ refute_nil resp.data
43
+ assert_equal 3, resp.data["blah_resp"]
44
+ end
45
+
32
46
  def test_client_json_error
33
47
  c = Twirp::ClientJSON.new(conn_stub("/Foo/Foomo") {|req|
34
48
  [400, {}, '{"code": "invalid_argument", "msg": "dont like empty"}']
@@ -3,9 +3,9 @@ require 'minitest/autorun'
3
3
  require_relative '../lib/twirp/error'
4
4
 
5
5
  class TestErrorCodes < Minitest::Test
6
-
6
+
7
7
  def test_error_codes
8
- assert_equal 17, Twirp::ERROR_CODES.size
8
+ assert_equal 18, Twirp::ERROR_CODES.size
9
9
 
10
10
  # all codes should be symbols
11
11
  Twirp::ERROR_CODES.each do |code|
@@ -13,13 +13,13 @@ class TestErrorCodes < Minitest::Test
13
13
  end
14
14
 
15
15
  # check some codes
16
- assert_includes Twirp::ERROR_CODES, :internal
16
+ assert_includes Twirp::ERROR_CODES, :internal
17
17
  assert_includes Twirp::ERROR_CODES, :not_found
18
18
  assert_includes Twirp::ERROR_CODES, :invalid_argument
19
19
  end
20
20
 
21
21
  def test_codes_to_http_status
22
- assert_equal 17, Twirp::ERROR_CODES_TO_HTTP_STATUS.size
22
+ assert_equal 18, Twirp::ERROR_CODES_TO_HTTP_STATUS.size
23
23
 
24
24
  assert_equal 404, Twirp::ERROR_CODES_TO_HTTP_STATUS[:not_found]
25
25
  assert_equal 500, Twirp::ERROR_CODES_TO_HTTP_STATUS[:internal]
@@ -52,7 +52,7 @@ class TestTwirpError < Minitest::Test
52
52
  end
53
53
 
54
54
  def test_invalid_constructor # Make sure that only supported codes are implemented (prevent bad metaprogramming)
55
- assert_raises NoMethodError do
55
+ assert_raises NoMethodError do
56
56
  Twirp::invalid_code_error "should fail"
57
57
  end
58
58
  end
@@ -67,7 +67,7 @@ class TestTwirpError < Minitest::Test
67
67
  def test_new_with_valid_metadata
68
68
  err = Twirp::Error.new(:internal, "woops", "meta" => "data", "for this" => "error")
69
69
  assert_equal "data", err.meta["meta"]
70
- assert_equal "error", err.meta["for this"]
70
+ assert_equal "error", err.meta["for this"]
71
71
  assert_nil err.meta["something else"]
72
72
 
73
73
  err = Twirp::Error.new(:internal, "woops", meta: "data")
@@ -78,11 +78,11 @@ class TestTwirpError < Minitest::Test
78
78
  def test_invalid_metadata
79
79
  Twirp::Error.new(:internal, "woops") # ensure the base case doesn't error
80
80
 
81
- assert_raises ArgumentError do
81
+ assert_raises ArgumentError do
82
82
  Twirp::Error.new(:internal, "woops", "string key" => :non_string_value)
83
83
  end
84
84
 
85
- assert_raises ArgumentError do
85
+ assert_raises ArgumentError do
86
86
  Twirp::Error.new(:internal, "woops", "valid key" => "valid val", "bad_one" => 666)
87
87
  end
88
88
  end
@@ -91,8 +91,8 @@ class TestTwirpError < Minitest::Test
91
91
  # returns a hash with attributes
92
92
  err = Twirp::Error.new(:internal, "err msg", "key" => "val")
93
93
  assert_equal({code: :internal, msg: "err msg", meta: {"key" => "val"}}, err.to_h)
94
-
95
- # skips meta if not included
94
+
95
+ # skips meta if not included
96
96
  err = Twirp::Error.new(:internal, "err msg")
97
97
  assert_equal({code: :internal, msg: "err msg"}, err.to_h)
98
98
  end
@@ -12,6 +12,15 @@ class ServiceTest < Minitest::Test
12
12
  Example::Haberdasher.raise_exceptions = true # configure for testing to make debugging easier
13
13
  end
14
14
 
15
+ # Class method to make a Rack response with a Twirp errpr
16
+ def test_service_error_response
17
+ twerr = Twirp::Error.invalid_argument('foo')
18
+ resp = Twirp::Service.error_response(twerr)
19
+ assert_equal 400, resp[0]
20
+ assert_equal 'application/json', resp[1]['Content-Type']
21
+ assert_equal '{"code":"invalid_argument","msg":"foo"}', resp[2][0]
22
+ end
23
+
15
24
  # The rpc DSL should properly build the base Twirp environment for each rpc method.
16
25
  def test_rpcs_accessor
17
26
  assert_equal 1, Example::Haberdasher.rpcs.size
@@ -55,6 +64,24 @@ class ServiceTest < Minitest::Test
55
64
  assert_equal({"inches" => 10, "color" => "white"}, JSON.parse(body[0]))
56
65
  end
57
66
 
67
+ def test_successful_json_strict_request_emit_defaults
68
+ rack_env = json_strict_req "/example.Haberdasher/MakeHat", inches: 0 # default int value
69
+ status, headers, body = haberdasher_service.call(rack_env)
70
+
71
+ assert_equal 200, status
72
+ assert_equal 'application/json; strict=true', headers['Content-Type']
73
+ assert_equal({"inches" => 0, "color" => "white"}, JSON.parse(body[0]))
74
+ end
75
+
76
+ def test_successful_json_request_emit_defaults
77
+ rack_env = json_req "/example.Haberdasher/MakeHat", inches: 0 # default int value
78
+ status, headers, body = haberdasher_service.call(rack_env)
79
+
80
+ assert_equal 200, status
81
+ assert_equal 'application/json', headers['Content-Type']
82
+ assert_equal({"inches" => 0, "color" => "white"}, JSON.parse(body[0]))
83
+ end
84
+
58
85
  def test_successful_proto_request
59
86
  rack_env = proto_req "/example.Haberdasher/MakeHat", Example::Size.new(inches: 10)
60
87
  status, headers, body = haberdasher_service.call(rack_env)
@@ -136,10 +163,10 @@ class ServiceTest < Minitest::Test
136
163
  method: "POST", input: 'bad json', "CONTENT_TYPE" => "application/json"
137
164
  status, headers, body = haberdasher_service.call(rack_env)
138
165
 
139
- assert_equal 404, status
166
+ assert_equal 400, status
140
167
  assert_equal 'application/json', headers['Content-Type']
141
168
  assert_equal({
142
- "code" => 'bad_route',
169
+ "code" => 'malformed',
143
170
  "msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/json: ' +
144
171
  "Error occurred during parsing: Parse error at 'bad json'",
145
172
  "meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
@@ -151,16 +178,39 @@ class ServiceTest < Minitest::Test
151
178
  method: "POST", input: 'bad protobuf', "CONTENT_TYPE" => "application/protobuf"
152
179
  status, headers, body = haberdasher_service.call(rack_env)
153
180
 
154
- assert_equal 404, status
181
+ assert_equal 400, status
155
182
  assert_equal 'application/json', headers['Content-Type']
156
183
  assert_equal({
157
- "code" => 'bad_route',
184
+ "code" => 'malformed',
158
185
  "msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/protobuf: ' +
159
186
  'Error occurred during parsing: Unexpected EOF inside skipped data',
160
187
  "meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
161
188
  }, JSON.parse(body[0]))
162
189
  end
163
190
 
191
+ def test_json_request_ignores_unknown_fields
192
+ rack_env = json_req "/example.Haberdasher/MakeHat", inches: 10, fake: 3
193
+ status, headers, body = haberdasher_service.call(rack_env)
194
+
195
+ assert_equal 200, status
196
+ assert_equal 'application/json', headers['Content-Type']
197
+ assert_equal({"inches" => 10, "color" => "white"}, JSON.parse(body[0]))
198
+ end
199
+
200
+ def test_json_strict_request_fails_unknown_fields
201
+ rack_env = json_strict_req "/example.Haberdasher/MakeHat", inches: 10, fake: 3
202
+ status, headers, body = haberdasher_service.call(rack_env)
203
+
204
+ assert_equal 400, status
205
+ assert_equal 'application/json', headers['Content-Type']
206
+ assert_equal({
207
+ "code" => 'malformed',
208
+ "msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/json; strict=true: ' +
209
+ "Error occurred during parsing: No such field: fake",
210
+ "meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
211
+ }, JSON.parse(body[0]))
212
+ end
213
+
164
214
  def test_bad_route_triggers_on_error_hooks
165
215
  svc = haberdasher_service
166
216
 
@@ -782,6 +832,12 @@ class ServiceTest < Minitest::Test
782
832
  "CONTENT_TYPE" => "application/json"
783
833
  end
784
834
 
835
+ def json_strict_req(path, attrs)
836
+ Rack::MockRequest.env_for path, method: "POST",
837
+ input: JSON.generate(attrs),
838
+ "CONTENT_TYPE" => "application/json; strict=true"
839
+ end
840
+
785
841
  def proto_req(path, proto_message)
786
842
  Rack::MockRequest.env_for path, method: "POST",
787
843
  input: proto_message.class.encode(proto_message),
@@ -19,10 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.required_ruby_version = '>= 1.9'
22
- spec.add_runtime_dependency 'google-protobuf', '~> 3.0', '>= 3.0.0'
23
- spec.add_runtime_dependency 'faraday', '~> 0' # for clients
22
+ spec.add_runtime_dependency 'google-protobuf', '~> 3.0', '>= 3.7.0'
23
+ spec.add_runtime_dependency 'faraday', '< 2' # for clients
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 1'
26
26
  spec.add_development_dependency 'minitest', '>= 5'
27
27
  spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rack', '>= 2.2.3'
28
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twirp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyrus A. Forbes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-03-11 00:00:00.000000000 Z
12
+ date: 2020-11-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: google-protobuf
@@ -20,7 +20,7 @@ dependencies:
20
20
  version: '3.0'
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 3.0.0
23
+ version: 3.7.0
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,21 +30,21 @@ dependencies:
30
30
  version: '3.0'
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 3.0.0
33
+ version: 3.7.0
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: faraday
36
36
  requirement: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - "<"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '2'
41
41
  type: :runtime
42
42
  prerelease: false
43
43
  version_requirements: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - "<"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '2'
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: bundler
50
50
  requirement: !ruby/object:Gem::Requirement
@@ -87,6 +87,20 @@ dependencies:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: rack
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 2.2.3
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 2.2.3
90
104
  description: Twirp is a simple RPC framework with protobuf service definitions. The
91
105
  Twirp gem provides native support for Ruby.
92
106
  email:
@@ -134,15 +148,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
148
  - !ruby/object:Gem::Version
135
149
  version: '0'
136
150
  requirements: []
137
- rubyforge_project:
138
- rubygems_version: 2.6.8
151
+ rubygems_version: 3.0.3
139
152
  signing_key:
140
153
  specification_version: 4
141
154
  summary: Twirp services in Ruby.
142
155
  test_files:
143
- - test/client_json_test.rb
144
156
  - test/client_test.rb
145
157
  - test/error_test.rb
146
- - test/fake_services.rb
147
158
  - test/license_header_test.rb
159
+ - test/fake_services.rb
148
160
  - test/service_test.rb
161
+ - test/client_json_test.rb