webmock 1.7.10 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. data/.travis.yml +2 -2
  2. data/CHANGELOG.md +98 -24
  3. data/Gemfile +2 -3
  4. data/README.md +45 -4
  5. data/Rakefile +2 -2
  6. data/lib/webmock.rb +3 -0
  7. data/lib/webmock/api.rb +34 -6
  8. data/lib/webmock/http_lib_adapters/curb_adapter.rb +4 -41
  9. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +1 -1
  10. data/lib/webmock/http_lib_adapters/excon_adapter.rb +94 -0
  11. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +31 -4
  12. data/lib/webmock/http_lib_adapters/net_http.rb +2 -0
  13. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +4 -3
  14. data/lib/webmock/matchers/hash_including_matcher.rb +25 -0
  15. data/lib/webmock/rack_response.rb +8 -1
  16. data/lib/webmock/request_pattern.rb +108 -77
  17. data/lib/webmock/request_signature.rb +1 -0
  18. data/lib/webmock/stub_registry.rb +9 -8
  19. data/lib/webmock/version.rb +1 -1
  20. data/lib/webmock/webmock.rb +5 -2
  21. data/minitest/webmock_spec.rb +22 -2
  22. data/spec/acceptance/curb/curb_spec_helper.rb +12 -2
  23. data/spec/acceptance/em_http_request/em_http_request_spec.rb +42 -33
  24. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +4 -2
  25. data/spec/acceptance/excon/excon_spec.rb +15 -0
  26. data/spec/acceptance/excon/excon_spec_helper.rb +37 -0
  27. data/spec/acceptance/net_http/net_http_spec.rb +7 -0
  28. data/spec/acceptance/net_http/net_http_spec_helper.rb +3 -1
  29. data/spec/acceptance/patron/patron_spec.rb +12 -3
  30. data/spec/acceptance/patron/patron_spec_helper.rb +2 -2
  31. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +3 -3
  32. data/spec/acceptance/shared/callbacks.rb +22 -6
  33. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +21 -0
  34. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +10 -11
  35. data/spec/acceptance/shared/precedence_of_stubs.rb +1 -1
  36. data/spec/acceptance/shared/request_expectations.rb +49 -3
  37. data/spec/acceptance/shared/returning_declared_responses.rb +9 -21
  38. data/spec/acceptance/shared/stubbing_requests.rb +80 -4
  39. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +1 -1
  40. data/spec/acceptance/webmock_shared.rb +11 -8
  41. data/spec/spec_helper.rb +3 -3
  42. data/spec/support/my_rack_app.rb +25 -1
  43. data/spec/support/webmock_server.rb +9 -6
  44. data/spec/unit/rack_response_spec.rb +18 -0
  45. data/spec/unit/request_pattern_spec.rb +205 -96
  46. data/spec/unit/request_signature_spec.rb +36 -34
  47. data/spec/unit/util/uri_spec.rb +14 -2
  48. data/test/shared_test.rb +31 -2
  49. data/webmock.gemspec +9 -7
  50. metadata +86 -73
@@ -0,0 +1,21 @@
1
+ shared_context "complex cross-concern behaviors" do |*adapter_info|
2
+ it 'allows a response with multiple values for the same header to be recorded and played back exactly as-is' do
3
+ WebMock.allow_net_connect!
4
+
5
+ recorded_response = nil
6
+ WebMock.after_request { |_,r| recorded_response = r }
7
+ real_response = http_request(:get, webmock_server_url)
8
+
9
+ stub_request(:get, webmock_server_url).to_return(
10
+ :status => recorded_response.status,
11
+ :body => recorded_response.body,
12
+ :headers => recorded_response.headers
13
+ )
14
+
15
+ played_back_response = http_request(:get, webmock_server_url)
16
+
17
+ played_back_response.headers.keys.should include('Set-Cookie')
18
+ played_back_response.should == real_response
19
+ end
20
+ end
21
+
@@ -1,4 +1,4 @@
1
- shared_context "enabled and disabled webmock" do
1
+ shared_context "enabled and disabled webmock" do |*adapter_info|
2
2
  describe "when webmock is disabled" do
3
3
  before(:each) do
4
4
  WebMock.disable!
@@ -41,28 +41,27 @@ end
41
41
 
42
42
  shared_context "disabled WebMock" do
43
43
  it "should not register executed requests" do
44
- http_request(:get, "http://www.example.com/")
45
- a_request(:get, "http://www.example.com/").should_not have_been_made
44
+ http_request(:get, webmock_server_url)
45
+ a_request(:get, webmock_server_url).should_not have_been_made
46
46
  end
47
47
 
48
48
  it "should not block unstubbed requests" do
49
49
  lambda {
50
- http_request(:get, "http://www.example.com/")
50
+ http_request(:get, webmock_server_url)
51
51
  }.should_not raise_error
52
52
  end
53
53
 
54
54
  it "should return real response even if there are stubs" do
55
55
  stub_request(:get, /.*/).to_return(:body => "x")
56
- http_request(:get, "http://www.example.com/").
57
- status.should == "302"
56
+ http_request(:get, webmock_server_url).body.should == "hello world"
58
57
  end
59
58
 
60
59
  it "should not invoke any callbacks" do
61
60
  WebMock.reset_callbacks
62
- stub_request(:get, "http://www.example.com/")
61
+ stub_request(:get, webmock_server_url)
63
62
  @called = nil
64
63
  WebMock.after_request { @called = 1 }
65
- http_request(:get, "http://www.example.com/")
64
+ http_request(:get, webmock_server_url)
66
65
  @called.should == nil
67
66
  end
68
67
  end
@@ -70,8 +69,8 @@ end
70
69
  shared_context "enabled WebMock" do
71
70
  it "should register executed requests" do
72
71
  WebMock.allow_net_connect!
73
- http_request(:get, "http://www.example.com/")
74
- a_request(:get, "http://www.example.com/").should have_been_made
72
+ http_request(:get, webmock_server_url)
73
+ a_request(:get, webmock_server_url).should have_been_made
75
74
  end
76
75
 
77
76
  it "should block unstubbed requests" do
@@ -90,7 +89,7 @@ shared_context "enabled WebMock" do
90
89
  WebMock.reset_callbacks
91
90
  @called = nil
92
91
  WebMock.after_request { @called = 1 }
93
- http_request(:get, "http://www.example.com/")
92
+ http_request(:get, webmock_server_url)
94
93
  @called.should == 1
95
94
  end
96
95
  end
@@ -1,4 +1,4 @@
1
- shared_context "precedence of stubs" do
1
+ shared_context "precedence of stubs" do |*adapter_info|
2
2
  describe "when choosing a matching request stub" do
3
3
  it "should use the last declared matching request stub" do
4
4
  stub_request(:get, "www.example.com").to_return(:body => "abc")
@@ -1,4 +1,4 @@
1
- shared_context "request expectations" do
1
+ shared_context "request expectations" do |*adapter_info|
2
2
  describe "when request expectations are set" do
3
3
  describe "when net connect is not allowed" do
4
4
  before(:each) do
@@ -150,6 +150,13 @@ shared_context "request expectations" do
150
150
  a_request(:get, "www.example.com/?x=3").with(:query => {"a" => ["b", "c"]}).should have_been_made
151
151
  }.should_not raise_error
152
152
  end
153
+
154
+ it "should satisfy expectation if the request was executed with only part query params declared as a hash in a query option" do
155
+ lambda {
156
+ http_request(:get, "http://www.example.com/?a[]=b&a[]=c&b=1")
157
+ a_request(:get, "www.example.com").with(:query => hash_including({"a" => ["b", "c"]})).should have_been_made
158
+ }.should_not raise_error
159
+ end
153
160
  end
154
161
 
155
162
  it "should fail if request was made more times than expected" do
@@ -209,7 +216,7 @@ shared_context "request expectations" do
209
216
 
210
217
  describe "when expected reqest body is declared as a hash" do
211
218
  let(:body_hash) { {:a => '1', :b => 'five', 'c' => {'d' => ['e', 'f']}} }
212
- let(:fail_message) {%r(The request POST http://www.example.com/ with body \{"a"=>"1", "b"=>"five", "c"=>\{"d"=>\["e", "f"\]\}\} was expected to execute 1 time but it executed 0 times)}
219
+ let(:fail_message) {%r(The request POST http://www.example.com/ with body .+ was expected to execute 1 time but it executed 0 times)}
213
220
 
214
221
  describe "when request is made with url encoded body matching hash" do
215
222
  it "should satisfy expectation" do
@@ -290,6 +297,26 @@ shared_context "request expectations" do
290
297
  end
291
298
  end
292
299
 
300
+ describe "when expected reqest body is declared as a partial hash matcher" do
301
+ let(:body_hash) { hash_including({:a => '1', 'c' => {'d' => ['e', 'f']}}) }
302
+ let(:fail_message) {%r(The request POST http://www.example.com/ with body hash_including(.+) was expected to execute 1 time but it executed 0 times)}
303
+
304
+ describe "when request is made with url encoded body matching hash" do
305
+ it "should satisfy expectation" do
306
+ lambda {
307
+ http_request(:post, "http://www.example.com/", :body => 'a=1&c[d][]=e&c[d][]=f&b=five')
308
+ a_request(:post, "www.example.com").with(:body => body_hash).should have_been_made
309
+ }.should_not raise_error
310
+ end
311
+
312
+ it "should fail if request is executed with url encoded body not matching hash" do
313
+ lambda {
314
+ http_request(:post, "http://www.example.com/", :body => 'c[d][]=f&a=1&c[d][]=e')
315
+ a_request(:post, "www.example.com").with(:body => body_hash).should have_been_made
316
+ }.should fail_with(fail_message)
317
+ end
318
+ end
319
+ end
293
320
 
294
321
  describe "when request with headers is expected" do
295
322
  it "should satisfy expectation if request was executed with the same headers" do
@@ -410,7 +437,7 @@ shared_context "request expectations" do
410
437
  end
411
438
  end
412
439
 
413
- describe "with authentication" do
440
+ describe "with authentication", :unless => (adapter_info.include?(:no_url_auth)) do
414
441
  before(:each) do
415
442
  stub_request(:any, "http://user:pass@www.example.com")
416
443
  stub_request(:any, "http://user:pazz@www.example.com")
@@ -541,6 +568,25 @@ shared_context "request expectations" do
541
568
  }.should fail_with(%r(The request POST http://www.example.com/ with given block was expected to execute 1 time but it executed 0 times))
542
569
  end
543
570
  end
571
+
572
+ describe "when expectation is declared using assert_requested" do
573
+ it "should satisfy expectation if requests was made" do
574
+ stub_http = stub_http_request(:get, "http://www.example.com")
575
+ lambda {
576
+ http_request(:get, "http://www.example.com/")
577
+ assert_requested(stub_http, :times => 1)
578
+ assert_requested(stub_http)
579
+ }.should_not raise_error
580
+ end
581
+
582
+ it "should fail if request expected not to be made was not wade" do
583
+ stub_http = stub_http_request(:get, "http://www.example.com")
584
+ lambda {
585
+ http_request(:get, "http://www.example.com/")
586
+ assert_not_requested(stub_http)
587
+ }.should fail_with(%r(The request GET http://www.example.com/ was expected to execute 0 times but it executed 1 time))
588
+ end
589
+ end
544
590
  end
545
591
 
546
592
 
@@ -1,6 +1,6 @@
1
1
  class MyException < StandardError; end;
2
2
 
3
- shared_context "declared responses" do
3
+ shared_context "declared responses" do |*adapter_info|
4
4
  describe "when request stub declares that request should raise exception" do
5
5
  it "should raise exception" do
6
6
  stub_request(:get, "www.example.com").to_raise(MyException)
@@ -72,13 +72,10 @@ shared_context "declared responses" do
72
72
  http_request(:get, "http://www.example.com/").status.should == "500"
73
73
  end
74
74
 
75
- it "should return response with declared status message" do
75
+ it "should return response with declared status message", :unless => (adapter_info.include?(:no_status_message)) do
76
76
  stub_request(:get, "www.example.com").to_return(:status => [500, "Internal Server Error"])
77
77
  response = http_request(:get, "http://www.example.com/")
78
- # not supported by em-http-request, it always returns "unknown" for http_reason
79
- unless http_library == :em_http_request
80
- response.message.should == "Internal Server Error"
81
- end
78
+ response.message.should == "Internal Server Error"
82
79
  end
83
80
 
84
81
  it "should return response with a default status code" do
@@ -86,13 +83,10 @@ shared_context "declared responses" do
86
83
  http_request(:get, "http://www.example.com/").status.should == "200"
87
84
  end
88
85
 
89
- it "should return default response with empty message if response was not declared" do
86
+ it "should return default response with empty message if response was not declared", :unless => (adapter_info.include?(:no_status_message)) do
90
87
  stub_request(:get, "www.example.com")
91
88
  response = http_request(:get, "http://www.example.com/")
92
- # not supported by em-http-request, it always returns "unknown" for http_reason
93
- unless http_library == :em_http_request
94
- response.message.should == ""
95
- end
89
+ response.message.should == ""
96
90
  end
97
91
 
98
92
  describe "when response body was declared as IO" do
@@ -186,11 +180,8 @@ shared_context "declared responses" do
186
180
  @response.status.should == "202"
187
181
  end
188
182
 
189
- it "should return recorded status message" do
190
- # not supported by em-http-request, it always returns "unknown" for http_reason
191
- unless http_library == :em_http_request
192
- @response.message.should == "OK"
193
- end
183
+ it "should return recorded status message", :unless => (adapter_info.include?(:no_status_message)) do
184
+ @response.message.should == "OK"
194
185
  end
195
186
 
196
187
  it "should ensure file is closed" do
@@ -223,11 +214,8 @@ shared_context "declared responses" do
223
214
  @response.status.should == "202"
224
215
  end
225
216
 
226
- it "should return recorded status message" do
227
- # not supported by em-http-request, it always returns "unknown" for http_reason
228
- unless http_library == :em_http_request
229
- @response.message.should == "OK"
230
- end
217
+ it "should return recorded status message", :unless => (adapter_info.include?(:no_status_message)) do
218
+ @response.message.should == "OK"
231
219
  end
232
220
  end
233
221
 
@@ -1,4 +1,4 @@
1
- shared_examples_for "stubbing requests" do
1
+ shared_examples_for "stubbing requests" do |*adapter_info|
2
2
  describe "when requests are stubbed" do
3
3
  describe "based on uri" do
4
4
  it "should return stubbed response even if request have escaped parameters" do
@@ -32,6 +32,11 @@ shared_examples_for "stubbing requests" do
32
32
  stub_request(:get, "www.example.com/?x=3").with(:query => {"a" => ["b", "c"]}).to_return(:body => "abc")
33
33
  http_request(:get, "http://www.example.com/?x=3&a[]=b&a[]=c").body.should == "abc"
34
34
  end
35
+
36
+ it "should return stubbed response when stub expects only part of query params" do
37
+ stub_request(:get, "www.example.com").with(:query => hash_including({"a" => ["b", "c"]})).to_return(:body => "abc")
38
+ http_request(:get, "http://www.example.com/?a[]=b&a[]=c&b=1").body.should == "abc"
39
+ end
35
40
  end
36
41
 
37
42
  describe "based on method" do
@@ -111,7 +116,7 @@ shared_examples_for "stubbing requests" do
111
116
  lambda {
112
117
  http_request(
113
118
  :post, "http://www.example.com/",
114
- :body => 'c[d][]=f&a=1&c[d][]=e').status.should == "200"
119
+ :body => 'c[d][]=f&a=1&c[d][]=e')
115
120
  }.should raise_error(WebMock::NetConnectNotAllowedError, %r(Real HTTP connections are disabled. Unregistered request: POST http://www.example.com/ with body 'c\[d\]\[\]=f&a=1&c\[d\]\[\]=e'))
116
121
  end
117
122
  end
@@ -169,6 +174,37 @@ shared_examples_for "stubbing requests" do
169
174
  end
170
175
  end
171
176
  end
177
+
178
+ describe "when body is declared as partial hash matcher" do
179
+ before(:each) do
180
+ stub_request(:post, "www.example.com").
181
+ with(:body => hash_including({:a => '1', 'c' => {'d' => ['e', 'f']} }))
182
+ end
183
+
184
+ describe "for request with url encoded body" do
185
+ it "should match request if hash matches body" do
186
+ http_request(
187
+ :post, "http://www.example.com/",
188
+ :body => 'a=1&c[d][]=e&c[d][]=f&b=five').status.should == "200"
189
+ end
190
+
191
+ it "should not match if hash doesn't match url encoded body" do
192
+ lambda {
193
+ http_request(
194
+ :post, "http://www.example.com/",
195
+ :body => 'c[d][]=f&a=1&c[d][]=e').status
196
+ }.should raise_error
197
+ end
198
+ end
199
+
200
+ describe "for request with json body and content type is set to json" do
201
+ it "should match if hash matches body" do
202
+ http_request(
203
+ :post, "http://www.example.com/", :headers => {'Content-Type' => 'application/json'},
204
+ :body => "{\"a\":\"1\",\"c\":{\"d\":[\"e\",\"f\"]},\"b\":\"five\"}").status.should == "200"
205
+ end
206
+ end
207
+ end
172
208
  end
173
209
 
174
210
  describe "based on headers" do
@@ -259,7 +295,7 @@ shared_examples_for "stubbing requests" do
259
295
  end
260
296
  end
261
297
 
262
- describe "when stubbing request with basic authentication" do
298
+ describe "when stubbing request with basic authentication", :unless => (adapter_info.include?(:no_url_auth)) do
263
299
  it "should match if credentials are the same" do
264
300
  stub_request(:get, "user:pass@www.example.com")
265
301
  http_request(:get, "http://user:pass@www.example.com/").status.should == "200"
@@ -289,7 +325,7 @@ shared_examples_for "stubbing requests" do
289
325
 
290
326
  describe "when stubbing request with a global hook" do
291
327
  after(:each) do
292
- WebMock::StubRegistry.instance.global_stub = nil
328
+ WebMock::StubRegistry.instance.global_stubs.clear
293
329
  end
294
330
 
295
331
  it 'returns the response returned by the hook' do
@@ -335,6 +371,46 @@ shared_examples_for "stubbing requests" do
335
371
  http_request(:get, "http://www.example.com/")
336
372
  call_count.should == 1
337
373
  end
374
+
375
+ it 'supports multiple global stubs; the first registered one that returns a non-nil value determines the stub' do
376
+ stub_invocation_order = []
377
+ WebMock.globally_stub_request do |request|
378
+ stub_invocation_order << :nil_stub
379
+ nil
380
+ end
381
+
382
+ WebMock.globally_stub_request do |request|
383
+ stub_invocation_order << :hash_stub
384
+ { :body => "global stub body" }
385
+ end
386
+
387
+ http_request(:get, "http://www.example.com/").body.should == "global stub body"
388
+ stub_invocation_order.should eq([:nil_stub, :hash_stub])
389
+ end
390
+
391
+ [:before, :after].each do |before_or_after|
392
+ context "when there is also a non-global registered stub #{before_or_after} the global stub" do
393
+ def stub_non_globally
394
+ stub_request(:get, "www.example.com").to_return(:body => 'non-global stub body')
395
+ end
396
+
397
+ define_method :register_stubs do |block|
398
+ stub_non_globally if before_or_after == :before
399
+ WebMock.globally_stub_request(&block)
400
+ stub_non_globally if before_or_after == :after
401
+ end
402
+
403
+ it 'uses the response from the global stub if the block returns a non-nil value' do
404
+ register_stubs(lambda { |req| { :body => 'global stub body' } })
405
+ http_request(:get, "http://www.example.com/").body.should == "global stub body"
406
+ end
407
+
408
+ it 'uses the response from the non-global stub if the block returns a nil value' do
409
+ register_stubs(lambda { |req| nil })
410
+ http_request(:get, "http://www.example.com/").body.should == "non-global stub body"
411
+ end
412
+ end
413
+ end
338
414
  end
339
415
 
340
416
  describe "when stubbing request with a block evaluated on request" do
@@ -11,7 +11,7 @@ module TyphoeusHydraSpecHelper
11
11
  :method => method,
12
12
  :body => options[:body],
13
13
  :headers => options[:headers],
14
- :timeout => 15000 # milliseconds
14
+ :timeout => 25000 # milliseconds
15
15
  }
16
16
  )
17
17
  raise FakeTyphoeusHydraError.new if response.code.to_s == "0"
@@ -6,6 +6,7 @@ require 'acceptance/shared/request_expectations'
6
6
  require 'acceptance/shared/stubbing_requests'
7
7
  require 'acceptance/shared/allowing_and_disabling_net_connect'
8
8
  require 'acceptance/shared/precedence_of_stubs'
9
+ require 'acceptance/shared/complex_cross_concern_behaviors'
9
10
 
10
11
  unless defined? SAMPLE_HEADERS
11
12
  SAMPLE_HEADERS = { "Content-Length" => "8888", "Accept" => "application/json" }
@@ -13,7 +14,7 @@ unless defined? SAMPLE_HEADERS
13
14
  NOT_ESCAPED_PARAMS = "z='Stop!' said Fred&x=ab c"
14
15
  end
15
16
 
16
- shared_examples "with WebMock" do
17
+ shared_examples "with WebMock" do |*adapter_info|
17
18
  describe "with WebMock" do
18
19
  let(:webmock_server_url) {"http://#{WebMockServer.instance.host_with_port}/"}
19
20
  before(:each) do
@@ -21,18 +22,20 @@ shared_examples "with WebMock" do
21
22
  WebMock.reset!
22
23
  end
23
24
 
24
- include_context "allowing and disabling net connect"
25
+ include_context "allowing and disabling net connect", *adapter_info
25
26
 
26
- include_context "stubbing requests"
27
+ include_context "stubbing requests", *adapter_info
27
28
 
28
- include_context "declared responses"
29
+ include_context "declared responses", *adapter_info
29
30
 
30
- include_context "precedence of stubs"
31
+ include_context "precedence of stubs", *adapter_info
31
32
 
32
- include_context "request expectations"
33
+ include_context "request expectations", *adapter_info
33
34
 
34
- include_context "callbacks"
35
+ include_context "callbacks", *adapter_info
35
36
 
36
- include_context "enabled and disabled webmock"
37
+ include_context "enabled and disabled webmock", *adapter_info
38
+
39
+ include_context "complex cross-concern behaviors", *adapter_info
37
40
  end
38
41
  end
@@ -30,11 +30,11 @@ RSpec.configure do |config|
30
30
 
31
31
  config.filter_run_excluding :without_webmock => true
32
32
 
33
- config.before(:all) do
34
- WebMockServer.instance.start
33
+ config.before(:suite) do
34
+ WebMockServer.instance.start unless WebMockServer.instance.started
35
35
  end
36
36
 
37
- config.after(:all) do
37
+ config.after(:suite) do
38
38
  WebMockServer.instance.stop
39
39
  end
40
40
 
@@ -1,6 +1,17 @@
1
1
  require 'rack'
2
2
 
3
3
  class MyRackApp
4
+ class NonArrayResponse
5
+ # The rack response body need not implement #join,
6
+ # but it must implement #each. It need not be an Array.
7
+ # ActionDispatch::Response, for example, exercises that fact.
8
+ # See: http://rack.rubyforge.org/doc/SPEC.html
9
+
10
+ def each(*args, &blk)
11
+ ["This is not in an array!"].each(*args, &blk)
12
+ end
13
+ end
14
+
4
15
  def self.call(env)
5
16
  case env.values_at('REQUEST_METHOD', 'PATH_INFO')
6
17
  when ['GET', '/']
@@ -8,6 +19,10 @@ class MyRackApp
8
19
  when ['GET', '/greet']
9
20
  name = env['QUERY_STRING'][/name=([^&]*)/, 1] || "World"
10
21
  [200, {}, ["Hello, #{name}"]]
22
+ when ['GET', '/non_array_response']
23
+ [200, {}, NonArrayResponse.new]
24
+ when ['GET', '/locked']
25
+ [200, {}, ["Single threaded response."]]
11
26
  when ['POST', '/greet']
12
27
  name = env["rack.input"].read[/name=([^&]*)/, 1] || "World"
13
28
  [200, {}, ["Good to meet you, #{name}!"]]
@@ -15,4 +30,13 @@ class MyRackApp
15
30
  [404, {}, ['']]
16
31
  end
17
32
  end
18
- end
33
+ end
34
+
35
+ class MyLockedRackApp
36
+ MUTEX = Mutex.new
37
+
38
+ def self.call(env)
39
+ lock = Rack::Lock.new(MyRackApp, MUTEX)
40
+ lock.call(env)
41
+ end
42
+ end