cotweet-fakeweb 1.3.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 (58) hide show
  1. data/.autotest +5 -0
  2. data/.gitignore +7 -0
  3. data/CHANGELOG +215 -0
  4. data/LICENSE.txt +19 -0
  5. data/README.rdoc +189 -0
  6. data/Rakefile +67 -0
  7. data/fakeweb.gemspec +126 -0
  8. data/lib/fake_web.rb +215 -0
  9. data/lib/fake_web/ext/net_http.rb +72 -0
  10. data/lib/fake_web/registry.rb +127 -0
  11. data/lib/fake_web/responder.rb +122 -0
  12. data/lib/fake_web/response.rb +10 -0
  13. data/lib/fake_web/stub_socket.rb +15 -0
  14. data/lib/fake_web/utility.rb +90 -0
  15. data/lib/fakeweb.rb +2 -0
  16. data/test/fixtures/google_response_from_curl +12 -0
  17. data/test/fixtures/google_response_with_transfer_encoding +17 -0
  18. data/test/fixtures/google_response_without_transfer_encoding +11 -0
  19. data/test/fixtures/test_example.txt +1 -0
  20. data/test/fixtures/test_txt_file +3 -0
  21. data/test/test_allow_net_connect.rb +168 -0
  22. data/test/test_deprecations.rb +54 -0
  23. data/test/test_fake_authentication.rb +92 -0
  24. data/test/test_fake_web.rb +590 -0
  25. data/test/test_fake_web_open_uri.rb +58 -0
  26. data/test/test_helper.rb +90 -0
  27. data/test/test_last_request.rb +29 -0
  28. data/test/test_missing_open_uri.rb +25 -0
  29. data/test/test_missing_pathname.rb +37 -0
  30. data/test/test_other_net_http_libraries.rb +36 -0
  31. data/test/test_precedence.rb +79 -0
  32. data/test/test_query_string.rb +45 -0
  33. data/test/test_regexes.rb +157 -0
  34. data/test/test_response_headers.rb +79 -0
  35. data/test/test_trailing_slashes.rb +53 -0
  36. data/test/test_utility.rb +83 -0
  37. data/test/vendor/right_http_connection-1.2.4/History.txt +59 -0
  38. data/test/vendor/right_http_connection-1.2.4/Manifest.txt +7 -0
  39. data/test/vendor/right_http_connection-1.2.4/README.txt +54 -0
  40. data/test/vendor/right_http_connection-1.2.4/Rakefile +103 -0
  41. data/test/vendor/right_http_connection-1.2.4/lib/net_fix.rb +160 -0
  42. data/test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb +435 -0
  43. data/test/vendor/right_http_connection-1.2.4/setup.rb +1585 -0
  44. data/test/vendor/samuel-0.2.1/.document +5 -0
  45. data/test/vendor/samuel-0.2.1/.gitignore +5 -0
  46. data/test/vendor/samuel-0.2.1/LICENSE +20 -0
  47. data/test/vendor/samuel-0.2.1/README.rdoc +70 -0
  48. data/test/vendor/samuel-0.2.1/Rakefile +62 -0
  49. data/test/vendor/samuel-0.2.1/VERSION +1 -0
  50. data/test/vendor/samuel-0.2.1/lib/samuel.rb +52 -0
  51. data/test/vendor/samuel-0.2.1/lib/samuel/net_http.rb +10 -0
  52. data/test/vendor/samuel-0.2.1/lib/samuel/request.rb +96 -0
  53. data/test/vendor/samuel-0.2.1/samuel.gemspec +69 -0
  54. data/test/vendor/samuel-0.2.1/test/request_test.rb +193 -0
  55. data/test/vendor/samuel-0.2.1/test/samuel_test.rb +42 -0
  56. data/test/vendor/samuel-0.2.1/test/test_helper.rb +66 -0
  57. data/test/vendor/samuel-0.2.1/test/thread_test.rb +32 -0
  58. metadata +167 -0
data/fakeweb.gemspec ADDED
@@ -0,0 +1,126 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{cotweet-fakeweb}
8
+ s.version = "1.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Chris Kampmeier", "Blaine Cook"]
12
+ s.date = %q{2010-08-22}
13
+ s.description = %q{FakeWeb is a helper for faking web requests in Ruby. It works at a global level, without modifying code or writing extensive stubs.}
14
+ s.email = ["chris@kampers.net", "romeda@gmail.com"]
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".autotest",
21
+ ".gitignore",
22
+ "CHANGELOG",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "fakeweb.gemspec",
27
+ "lib/fake_web.rb",
28
+ "lib/fake_web/ext/net_http.rb",
29
+ "lib/fake_web/registry.rb",
30
+ "lib/fake_web/responder.rb",
31
+ "lib/fake_web/response.rb",
32
+ "lib/fake_web/stub_socket.rb",
33
+ "lib/fake_web/utility.rb",
34
+ "lib/fakeweb.rb",
35
+ "test/fixtures/google_response_from_curl",
36
+ "test/fixtures/google_response_with_transfer_encoding",
37
+ "test/fixtures/google_response_without_transfer_encoding",
38
+ "test/fixtures/test_example.txt",
39
+ "test/fixtures/test_txt_file",
40
+ "test/test_allow_net_connect.rb",
41
+ "test/test_deprecations.rb",
42
+ "test/test_fake_authentication.rb",
43
+ "test/test_fake_web.rb",
44
+ "test/test_fake_web_open_uri.rb",
45
+ "test/test_helper.rb",
46
+ "test/test_last_request.rb",
47
+ "test/test_missing_open_uri.rb",
48
+ "test/test_missing_pathname.rb",
49
+ "test/test_other_net_http_libraries.rb",
50
+ "test/test_precedence.rb",
51
+ "test/test_query_string.rb",
52
+ "test/test_regexes.rb",
53
+ "test/test_response_headers.rb",
54
+ "test/test_trailing_slashes.rb",
55
+ "test/test_utility.rb",
56
+ "test/vendor/right_http_connection-1.2.4/History.txt",
57
+ "test/vendor/right_http_connection-1.2.4/Manifest.txt",
58
+ "test/vendor/right_http_connection-1.2.4/README.txt",
59
+ "test/vendor/right_http_connection-1.2.4/Rakefile",
60
+ "test/vendor/right_http_connection-1.2.4/lib/net_fix.rb",
61
+ "test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb",
62
+ "test/vendor/right_http_connection-1.2.4/setup.rb",
63
+ "test/vendor/samuel-0.2.1/.document",
64
+ "test/vendor/samuel-0.2.1/.gitignore",
65
+ "test/vendor/samuel-0.2.1/LICENSE",
66
+ "test/vendor/samuel-0.2.1/README.rdoc",
67
+ "test/vendor/samuel-0.2.1/Rakefile",
68
+ "test/vendor/samuel-0.2.1/VERSION",
69
+ "test/vendor/samuel-0.2.1/lib/samuel.rb",
70
+ "test/vendor/samuel-0.2.1/lib/samuel/net_http.rb",
71
+ "test/vendor/samuel-0.2.1/lib/samuel/request.rb",
72
+ "test/vendor/samuel-0.2.1/samuel.gemspec",
73
+ "test/vendor/samuel-0.2.1/test/request_test.rb",
74
+ "test/vendor/samuel-0.2.1/test/samuel_test.rb",
75
+ "test/vendor/samuel-0.2.1/test/test_helper.rb",
76
+ "test/vendor/samuel-0.2.1/test/thread_test.rb"
77
+ ]
78
+ s.homepage = %q{http://github.com/chrisk/fakeweb}
79
+ s.rdoc_options = ["--charset=UTF-8"]
80
+ s.require_paths = ["lib"]
81
+ s.rubyforge_project = %q{fakeweb}
82
+ s.rubygems_version = %q{1.3.7}
83
+ s.summary = %q{A tool for faking responses to HTTP requests}
84
+ s.test_files = [
85
+ "test/test_allow_net_connect.rb",
86
+ "test/test_deprecations.rb",
87
+ "test/test_fake_authentication.rb",
88
+ "test/test_fake_web.rb",
89
+ "test/test_fake_web_open_uri.rb",
90
+ "test/test_helper.rb",
91
+ "test/test_last_request.rb",
92
+ "test/test_missing_open_uri.rb",
93
+ "test/test_missing_pathname.rb",
94
+ "test/test_other_net_http_libraries.rb",
95
+ "test/test_precedence.rb",
96
+ "test/test_query_string.rb",
97
+ "test/test_regexes.rb",
98
+ "test/test_response_headers.rb",
99
+ "test/test_trailing_slashes.rb",
100
+ "test/test_utility.rb",
101
+ "test/vendor/right_http_connection-1.2.4/lib/net_fix.rb",
102
+ "test/vendor/right_http_connection-1.2.4/lib/right_http_connection.rb",
103
+ "test/vendor/right_http_connection-1.2.4/setup.rb",
104
+ "test/vendor/samuel-0.2.1/lib/samuel/net_http.rb",
105
+ "test/vendor/samuel-0.2.1/lib/samuel/request.rb",
106
+ "test/vendor/samuel-0.2.1/lib/samuel.rb",
107
+ "test/vendor/samuel-0.2.1/test/request_test.rb",
108
+ "test/vendor/samuel-0.2.1/test/samuel_test.rb",
109
+ "test/vendor/samuel-0.2.1/test/test_helper.rb",
110
+ "test/vendor/samuel-0.2.1/test/thread_test.rb"
111
+ ]
112
+
113
+ if s.respond_to? :specification_version then
114
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
115
+ s.specification_version = 3
116
+
117
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
118
+ s.add_development_dependency(%q<mocha>, [">= 0.9.5"])
119
+ else
120
+ s.add_dependency(%q<mocha>, [">= 0.9.5"])
121
+ end
122
+ else
123
+ s.add_dependency(%q<mocha>, [">= 0.9.5"])
124
+ end
125
+ end
126
+
data/lib/fake_web.rb ADDED
@@ -0,0 +1,215 @@
1
+ require 'singleton'
2
+
3
+ require 'fake_web/ext/net_http'
4
+ require 'fake_web/registry'
5
+ require 'fake_web/response'
6
+ require 'fake_web/responder'
7
+ require 'fake_web/stub_socket'
8
+ require 'fake_web/utility'
9
+
10
+ FakeWeb::Utility.record_loaded_net_http_replacement_libs
11
+ FakeWeb::Utility.puts_warning_for_net_http_around_advice_libs_if_needed
12
+
13
+ module FakeWeb
14
+
15
+ # Returns the version string for the copy of FakeWeb you have loaded.
16
+ VERSION = '1.3.0'
17
+
18
+ # Resets the FakeWeb Registry. This will force all subsequent web requests to
19
+ # behave as real requests.
20
+ def self.clean_registry
21
+ Registry.instance.clean_registry
22
+ end
23
+
24
+ # Enables or disables real HTTP connections for requests that don't match
25
+ # registered URIs.
26
+ #
27
+ # If you set <tt>FakeWeb.allow_net_connect = false</tt> and subsequently try
28
+ # to make a request to a URI you haven't registered with #register_uri, a
29
+ # NetConnectNotAllowedError will be raised. This is handy when you want to
30
+ # make sure your tests are self-contained, or want to catch the scenario
31
+ # when a URI is changed in implementation code without a corresponding test
32
+ # change.
33
+ #
34
+ # When <tt>FakeWeb.allow_net_connect = true</tt> (the default), requests to
35
+ # URIs not stubbed with FakeWeb are passed through to Net::HTTP.
36
+ #
37
+ # If you assign a +String+, +URI+, or +Regexp+ object, unstubbed requests
38
+ # will be allowed if they match that value. This is useful when you want to
39
+ # allow access to a local server for integration testing, while still
40
+ # preventing your tests from using the internet.
41
+ def self.allow_net_connect=(allowed)
42
+ case allowed
43
+ when String, URI, Regexp
44
+ @allow_all_connections = false
45
+ Registry.instance.register_passthrough_uri(allowed)
46
+ else
47
+ @allow_all_connections = allowed
48
+ Registry.instance.remove_passthrough_uri
49
+ end
50
+ end
51
+
52
+ # Enable pass-through to Net::HTTP by default.
53
+ self.allow_net_connect = true
54
+
55
+ # Returns +true+ if requests to URIs not registered with FakeWeb are passed
56
+ # through to Net::HTTP for normal processing (the default). Returns +false+
57
+ # if an exception is raised for these requests.
58
+ #
59
+ # If you've assigned a +String+, +URI+, or +Regexp+ to
60
+ # <tt>FakeWeb.allow_net_connect=</tt>, you must supply a URI to check
61
+ # against that filter. Otherwise, an ArgumentError will be raised.
62
+ def self.allow_net_connect?(uri = nil)
63
+ if Registry.instance.passthrough_uri_map.any?
64
+ raise ArgumentError, "You must supply a URI to test" if uri.nil?
65
+ Registry.instance.passthrough_uri_matches?(uri)
66
+ else
67
+ @allow_all_connections
68
+ end
69
+ end
70
+
71
+ # This exception is raised if you set <tt>FakeWeb.allow_net_connect =
72
+ # false</tt> and subsequently try to make a request to a URI you haven't
73
+ # stubbed.
74
+ class NetConnectNotAllowedError < StandardError; end;
75
+
76
+ # This exception is raised if a Net::HTTP request matches more than one of
77
+ # the stubs you've registered. To fix the problem, remove a duplicate
78
+ # registration or disambiguate any regular expressions by making them more
79
+ # specific.
80
+ class MultipleMatchingURIsError < StandardError; end;
81
+
82
+ # call-seq:
83
+ # FakeWeb.register_uri(method, uri, options)
84
+ #
85
+ # Register requests using the HTTP method specified by the symbol +method+
86
+ # for +uri+ to be handled according to +options+. If you specify the method
87
+ # <tt>:any</tt>, the response will be reigstered for any request for +uri+.
88
+ # +uri+ can be a +String+, +URI+, or +Regexp+ object. +options+ must be either
89
+ # a +Hash+ or an +Array+ of +Hashes+ (see below), which must contain one of
90
+ # these two keys:
91
+ #
92
+ # <tt>:body</tt>::
93
+ # A string which is used as the body of the response. If the string refers
94
+ # to a valid filesystem path, the contents of that file will be read and used
95
+ # as the body of the response instead. (This used to be two options,
96
+ # <tt>:string</tt> and <tt>:file</tt>, respectively. These are now deprecated.)
97
+ # <tt>:response</tt>::
98
+ # Either a <tt>Net::HTTPResponse</tt>, an +IO+, or a +String+ which is used
99
+ # as the full response for the request.
100
+ #
101
+ # The easier way by far is to pass the <tt>:response</tt> option to
102
+ # +register_uri+ as a +String+ or an (open for reads) +IO+ object which
103
+ # will be used as the complete HTTP response, including headers and body.
104
+ # If the string points to a readable file, this file will be used as the
105
+ # content for the request.
106
+ #
107
+ # To obtain a complete response document, you can use the +curl+ command,
108
+ # like so:
109
+ #
110
+ # curl -i http://example.com > response_from_example.com
111
+ #
112
+ # which can then be used in your test environment like so:
113
+ #
114
+ # FakeWeb.register_uri(:get, "http://example.com", :response => "response_from_example.com")
115
+ #
116
+ # See the <tt>Net::HTTPResponse</tt>
117
+ # documentation[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html]
118
+ # for more information on creating custom response objects.
119
+ #
120
+ # +options+ may also be an +Array+ containing a list of the above-described
121
+ # +Hash+. In this case, FakeWeb will rotate through each response. You can
122
+ # optionally repeat a response more than once before rotating:
123
+ #
124
+ # <tt>:times</tt>::
125
+ # The number of times this response will be used before moving on to the
126
+ # next one. The last response will be repeated indefinitely, regardless of
127
+ # its <tt>:times</tt> parameter.
128
+ #
129
+ # Two optional arguments are also accepted:
130
+ #
131
+ # <tt>:status</tt>::
132
+ # Passing <tt>:status</tt> as a two-value array will set the response code
133
+ # and message. The defaults are <tt>200</tt> and <tt>OK</tt>, respectively.
134
+ # Example:
135
+ # FakeWeb.register_uri(:get, "http://example.com", :body => "Go away!", :status => [404, "Not Found"])
136
+ # <tt>:exception</tt>::
137
+ # The argument passed via <tt>:exception</tt> will be raised when the
138
+ # specified URL is requested. Any +Exception+ class is valid. Example:
139
+ # FakeWeb.register_uri(:get, "http://example.com", :exception => Net::HTTPError)
140
+ #
141
+ # If you're using the <tt>:body</tt> response type, you can pass additional
142
+ # options to specify the HTTP headers to be used in the response. Example:
143
+ #
144
+ # FakeWeb.register_uri(:get, "http://example.com/index.txt", :body => "Hello", :content_type => "text/plain")
145
+ #
146
+ # You can also pass an array of header values to include a header in the
147
+ # response more than once:
148
+ #
149
+ # FakeWeb.register_uri(:get, "http://example.com", :set_cookie => ["name=value", "example=1"])
150
+ def self.register_uri(*args)
151
+ case args.length
152
+ when 3
153
+ Registry.instance.register_uri(*args)
154
+ when 2
155
+ print_missing_http_method_deprecation_warning(*args)
156
+ Registry.instance.register_uri(:any, *args)
157
+ else
158
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 3)")
159
+ end
160
+ end
161
+
162
+ # call-seq:
163
+ # FakeWeb.response_for(method, uri)
164
+ #
165
+ # Returns the faked Net::HTTPResponse object associated with +method+ and +uri+.
166
+ def self.response_for(*args, &block) #:nodoc: :yields: response
167
+ case args.length
168
+ when 2
169
+ Registry.instance.response_for(*args, &block)
170
+ when 1
171
+ print_missing_http_method_deprecation_warning(*args)
172
+ Registry.instance.response_for(:any, *args, &block)
173
+ else
174
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
175
+ end
176
+ end
177
+
178
+ # call-seq:
179
+ # FakeWeb.registered_uri?(method, uri)
180
+ #
181
+ # Returns true if a +method+ request for +uri+ is registered with FakeWeb.
182
+ # Specify a method of <tt>:any</tt> to check against all HTTP methods.
183
+ def self.registered_uri?(*args)
184
+ case args.length
185
+ when 2
186
+ Registry.instance.registered_uri?(*args)
187
+ when 1
188
+ print_missing_http_method_deprecation_warning(*args)
189
+ Registry.instance.registered_uri?(:any, *args)
190
+ else
191
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
192
+ end
193
+ end
194
+
195
+ # Returns the request object from the last request made via Net::HTTP.
196
+ def self.last_request
197
+ @last_request
198
+ end
199
+
200
+ def self.last_request=(request) #:nodoc:
201
+ @last_request = request
202
+ end
203
+
204
+ private
205
+
206
+ def self.print_missing_http_method_deprecation_warning(*args)
207
+ method = caller.first.match(/`(.*?)'/)[1]
208
+ new_args = args.map { |a| a.inspect }.unshift(":any")
209
+ new_args.last.gsub!(/^\{|\}$/, "").gsub!("=>", " => ") if args.last.is_a?(Hash)
210
+ $stderr.puts
211
+ $stderr.puts "Deprecation warning: FakeWeb requires an HTTP method argument (or use :any). Try this:"
212
+ $stderr.puts " FakeWeb.#{method}(#{new_args.join(', ')})"
213
+ $stderr.puts "Called at #{caller[1]}"
214
+ end
215
+ end
@@ -0,0 +1,72 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'stringio'
4
+
5
+ module Net #:nodoc: all
6
+
7
+ class BufferedIO
8
+ def initialize_with_fakeweb(io, debug_output = nil)
9
+ @read_timeout = 60
10
+ @rbuf = ''
11
+ @debug_output = debug_output
12
+
13
+ @io = case io
14
+ when Socket, OpenSSL::SSL::SSLSocket, IO
15
+ io
16
+ when String
17
+ if !io.include?("\0") && File.exists?(io) && !File.directory?(io)
18
+ File.open(io, "r")
19
+ else
20
+ StringIO.new(io)
21
+ end
22
+ end
23
+ raise "Unable to create local socket" unless @io
24
+ end
25
+ alias_method :initialize_without_fakeweb, :initialize
26
+ alias_method :initialize, :initialize_with_fakeweb
27
+ end
28
+
29
+ class HTTP
30
+ class << self
31
+ def socket_type_with_fakeweb
32
+ FakeWeb::StubSocket
33
+ end
34
+ alias_method :socket_type_without_fakeweb, :socket_type
35
+ alias_method :socket_type, :socket_type_with_fakeweb
36
+ end
37
+
38
+ def request_with_fakeweb(request, body = nil, &block)
39
+ FakeWeb.last_request = request
40
+
41
+ uri = FakeWeb::Utility.request_uri_as_string(self, request)
42
+ method = request.method.downcase.to_sym
43
+
44
+ if FakeWeb.registered_uri?(method, uri)
45
+ @socket = Net::HTTP.socket_type.new
46
+ FakeWeb::Utility.produce_side_effects_of_net_http_request(request, body)
47
+ FakeWeb.response_for(method, uri, &block)
48
+ elsif FakeWeb.allow_net_connect?(uri)
49
+ connect_without_fakeweb
50
+ request_without_fakeweb(request, body, &block)
51
+ else
52
+ uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
53
+ raise FakeWeb::NetConnectNotAllowedError,
54
+ "Real HTTP connections are disabled. Unregistered request: #{request.method} #{uri}"
55
+ end
56
+ end
57
+ alias_method :request_without_fakeweb, :request
58
+ alias_method :request, :request_with_fakeweb
59
+
60
+
61
+ def connect_with_fakeweb
62
+ unless @@alredy_checked_for_net_http_replacement_libs ||= false
63
+ FakeWeb::Utility.puts_warning_for_net_http_replacement_libs_if_needed
64
+ @@alredy_checked_for_net_http_replacement_libs = true
65
+ end
66
+ nil
67
+ end
68
+ alias_method :connect_without_fakeweb, :connect
69
+ alias_method :connect, :connect_with_fakeweb
70
+ end
71
+
72
+ end
@@ -0,0 +1,127 @@
1
+ module FakeWeb
2
+ class Registry #:nodoc:
3
+ include Singleton
4
+
5
+ attr_accessor :uri_map, :passthrough_uri_map
6
+
7
+ def initialize
8
+ clean_registry
9
+ end
10
+
11
+ def clean_registry
12
+ self.uri_map = Hash.new { |hash, key| hash[key] = {} }
13
+ end
14
+
15
+ def register_uri(method, uri, options)
16
+ uri_map[normalize_uri(uri)][method] = [*[options]].flatten.collect do |option|
17
+ FakeWeb::Responder.new(method, uri, option, option[:times])
18
+ end
19
+ end
20
+
21
+ def registered_uri?(method, uri)
22
+ !responders_for(method, uri).empty?
23
+ end
24
+
25
+ def response_for(method, uri, &block)
26
+ responders = responders_for(method, uri)
27
+ return nil if responders.empty?
28
+
29
+ next_responder = responders.last
30
+ responders.each do |responder|
31
+ if responder.times and responder.times > 0
32
+ responder.times -= 1
33
+ next_responder = responder
34
+ break
35
+ end
36
+ end
37
+
38
+ next_responder.response(&block)
39
+ end
40
+
41
+ def register_passthrough_uri(uri)
42
+ self.passthrough_uri_map = {normalize_uri(uri) => {:any => true}}
43
+ end
44
+
45
+ def remove_passthrough_uri
46
+ self.passthrough_uri_map = {}
47
+ end
48
+
49
+ def passthrough_uri_matches?(uri)
50
+ uri = normalize_uri(uri)
51
+ uri_map_matches(passthrough_uri_map, :any, uri, URI) ||
52
+ uri_map_matches(passthrough_uri_map, :any, uri, Regexp)
53
+ end
54
+
55
+ private
56
+
57
+ def responders_for(method, uri)
58
+ uri = normalize_uri(uri)
59
+
60
+ uri_map_matches(uri_map, method, uri, URI) ||
61
+ uri_map_matches(uri_map, :any, uri, URI) ||
62
+ uri_map_matches(uri_map, method, uri, Regexp) ||
63
+ uri_map_matches(uri_map, :any, uri, Regexp) ||
64
+ []
65
+ end
66
+
67
+ def uri_map_matches(map, method, uri, type_to_check = URI)
68
+ uris_to_check = variations_of_uri_as_strings(uri)
69
+
70
+ matches = map.select { |registered_uri, method_hash|
71
+ registered_uri.is_a?(type_to_check) && method_hash.has_key?(method)
72
+ }.select { |registered_uri, method_hash|
73
+ if type_to_check == URI
74
+ uris_to_check.include?(registered_uri.to_s)
75
+ elsif type_to_check == Regexp
76
+ uris_to_check.any? { |u| u.match(registered_uri) }
77
+ end
78
+ }
79
+
80
+ if matches.size > 1
81
+ raise MultipleMatchingURIsError,
82
+ "More than one registered URI matched this request: #{method.to_s.upcase} #{uri}"
83
+ end
84
+
85
+ matches.map { |_, method_hash| method_hash[method] }.first
86
+ end
87
+
88
+
89
+ def variations_of_uri_as_strings(uri_object)
90
+ normalized_uri = normalize_uri(uri_object.dup)
91
+ normalized_uri_string = normalized_uri.to_s
92
+
93
+ variations = [normalized_uri_string]
94
+
95
+ # if the port is implied in the original, add a copy with an explicit port
96
+ if normalized_uri.default_port == normalized_uri.port
97
+ variations << normalized_uri_string.sub(
98
+ /#{Regexp.escape(normalized_uri.request_uri)}$/,
99
+ ":#{normalized_uri.port}#{normalized_uri.request_uri}")
100
+ end
101
+
102
+ variations
103
+ end
104
+
105
+ def normalize_uri(uri)
106
+ return uri if uri.is_a?(Regexp)
107
+ normalized_uri =
108
+ case uri
109
+ when URI then uri
110
+ when String
111
+ uri = 'http://' + uri unless uri.match('^https?://')
112
+ URI.parse(uri)
113
+ end
114
+ normalized_uri.query = sort_query_params(normalized_uri.query)
115
+ normalized_uri.normalize
116
+ end
117
+
118
+ def sort_query_params(query)
119
+ if query.nil? || query.empty?
120
+ nil
121
+ else
122
+ query.split('&').sort.join('&')
123
+ end
124
+ end
125
+
126
+ end
127
+ end