ivanvr-fakeweb 1.2.5.3
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.
- data/CHANGELOG +163 -0
- data/LICENSE.txt +281 -0
- data/README.rdoc +206 -0
- data/Rakefile +76 -0
- data/lib/fake_web/ext/net_http.rb +72 -0
- data/lib/fake_web/registry.rb +159 -0
- data/lib/fake_web/responder.rb +120 -0
- data/lib/fake_web/response.rb +10 -0
- data/lib/fake_web/stub_socket.rb +15 -0
- data/lib/fake_web/utility.rb +44 -0
- data/lib/fake_web.rb +173 -0
- data/lib/fakeweb.rb +2 -0
- data/test/fixtures/google_response_from_curl +12 -0
- data/test/fixtures/google_response_with_transfer_encoding +17 -0
- data/test/fixtures/google_response_without_transfer_encoding +11 -0
- data/test/fixtures/test_example.txt +1 -0
- data/test/fixtures/test_txt_file +3 -0
- data/test/test_allow_net_connect.rb +85 -0
- data/test/test_deprecations.rb +54 -0
- data/test/test_fake_authentication.rb +92 -0
- data/test/test_fake_web.rb +526 -0
- data/test/test_fake_web_open_uri.rb +58 -0
- data/test/test_helper.rb +74 -0
- data/test/test_missing_open_uri.rb +25 -0
- data/test/test_precedence.rb +79 -0
- data/test/test_query_string.rb +45 -0
- data/test/test_regexes.rb +152 -0
- data/test/test_response_headers.rb +73 -0
- data/test/test_trailing_slashes.rb +53 -0
- data/test/test_utility.rb +91 -0
- metadata +127 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
puts "Using ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}"
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'rake/gempackagetask'
|
|
5
|
+
require 'rake/testtask'
|
|
6
|
+
begin
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
rescue LoadError
|
|
9
|
+
puts "\nIt looks like you're using an old version of RDoc, but FakeWeb requires a newer one."
|
|
10
|
+
puts "You can try upgrading with `sudo gem install rdoc`.\n\n"
|
|
11
|
+
raise
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
task :default => :test
|
|
15
|
+
|
|
16
|
+
desc "Run All Tests"
|
|
17
|
+
Rake::TestTask.new :test do |test|
|
|
18
|
+
test.test_files = ["test/**/*.rb"]
|
|
19
|
+
test.verbose = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "Generate Documentation"
|
|
23
|
+
RDoc::Task.new do |rdoc|
|
|
24
|
+
rdoc.main = "README.rdoc"
|
|
25
|
+
rdoc.rdoc_dir = "doc"
|
|
26
|
+
rdoc.rdoc_files.include("README.rdoc", "CHANGELOG", "LICENSE.txt", "lib/*.rb")
|
|
27
|
+
rdoc.title = "FakeWeb API Documentation"
|
|
28
|
+
rdoc.options << '--line-numbers' << '--charset' << 'utf-8'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
desc %{Update ".manifest" with the latest list of project filenames. Respect\
|
|
32
|
+
.gitignore by excluding everything that git ignores. Update `files` and\
|
|
33
|
+
`test_files` arrays in "*.gemspec" file if it's present.}
|
|
34
|
+
task :manifest do
|
|
35
|
+
list = Dir['**/*'].sort
|
|
36
|
+
spec_file = Dir['*.gemspec'].first
|
|
37
|
+
list -= [spec_file] if spec_file
|
|
38
|
+
|
|
39
|
+
File.read('.gitignore').each_line do |glob|
|
|
40
|
+
glob = glob.chomp.sub(/^\//, '')
|
|
41
|
+
list -= Dir[glob]
|
|
42
|
+
list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
|
|
43
|
+
puts "excluding #{glob}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if spec_file
|
|
47
|
+
spec = File.read spec_file
|
|
48
|
+
spec.gsub!(/^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx) do
|
|
49
|
+
assignment = $1
|
|
50
|
+
bunch = $2 ? list.grep(/^test\//) : list
|
|
51
|
+
'%s%%w(%s)' % [assignment, bunch.join(' ')]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
File.open(spec_file, 'w') {|f| f << spec }
|
|
55
|
+
end
|
|
56
|
+
File.open('.manifest', 'w') {|f| f << list.join("\n") }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if RUBY_PLATFORM =~ /java/
|
|
60
|
+
puts "rcov support disabled (running under JRuby)."
|
|
61
|
+
elsif RUBY_VERSION =~ /^1\.9/
|
|
62
|
+
puts "rcov support disabled (running under Ruby 1.9)"
|
|
63
|
+
else
|
|
64
|
+
require 'rcov/rcovtask'
|
|
65
|
+
Rcov::RcovTask.new do |t|
|
|
66
|
+
t.test_files = FileList['test/**/test*.rb']
|
|
67
|
+
t.rcov_opts << "--sort coverage"
|
|
68
|
+
t.rcov_opts << "--exclude gems"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
spec = eval(File.read(File.join(File.dirname(__FILE__), "fakeweb.gemspec")))
|
|
73
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
|
74
|
+
pkg.need_tar_gz = true
|
|
75
|
+
pkg.need_zip = true
|
|
76
|
+
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
|
+
alias initialize_without_fakeweb initialize
|
|
9
|
+
def initialize(io, debug_output = nil)
|
|
10
|
+
@read_timeout = 60
|
|
11
|
+
@rbuf = ''
|
|
12
|
+
@debug_output = debug_output
|
|
13
|
+
|
|
14
|
+
@io = case io
|
|
15
|
+
when Socket, OpenSSL::SSL::SSLSocket, IO
|
|
16
|
+
io
|
|
17
|
+
when String
|
|
18
|
+
if !io.include?("\0") && File.exists?(io) && !File.directory?(io)
|
|
19
|
+
File.open(io, "r")
|
|
20
|
+
else
|
|
21
|
+
StringIO.new(io)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
raise "Unable to create local socket" unless @io
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class HTTP
|
|
29
|
+
class << self
|
|
30
|
+
alias socket_type_without_fakeweb socket_type
|
|
31
|
+
def socket_type
|
|
32
|
+
FakeWeb::StubSocket
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
alias request_without_fakeweb request
|
|
37
|
+
def request(request, body = nil, &block)
|
|
38
|
+
protocol = use_ssl? ? "https" : "http"
|
|
39
|
+
|
|
40
|
+
path = request.path
|
|
41
|
+
path = URI.parse(request.path).request_uri if request.path =~ /^http/
|
|
42
|
+
|
|
43
|
+
if request["authorization"] =~ /^Basic /
|
|
44
|
+
userinfo = FakeWeb::Utility.decode_userinfo_from_header(request["authorization"])
|
|
45
|
+
userinfo = FakeWeb::Utility.encode_unsafe_chars_in_userinfo(userinfo) + "@"
|
|
46
|
+
else
|
|
47
|
+
userinfo = ""
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
uri = "#{protocol}://#{userinfo}#{self.address}:#{self.port}#{path}"
|
|
51
|
+
method = request.method.downcase.to_sym
|
|
52
|
+
|
|
53
|
+
the_body = body || request.body
|
|
54
|
+
if FakeWeb.registered_uri?(method, uri, the_body)
|
|
55
|
+
@socket = Net::HTTP.socket_type.new
|
|
56
|
+
FakeWeb.response_for(method, uri, the_body, &block)
|
|
57
|
+
elsif FakeWeb.allow_net_connect?
|
|
58
|
+
connect_without_fakeweb
|
|
59
|
+
request_without_fakeweb(request, body, &block)
|
|
60
|
+
else
|
|
61
|
+
uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
|
|
62
|
+
raise FakeWeb::NetConnectNotAllowedError,
|
|
63
|
+
"Real HTTP connections are disabled. Unregistered request: #{request.method} #{uri} BODY: #{the_body}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
alias connect_without_fakeweb connect
|
|
68
|
+
def connect
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
module FakeWeb
|
|
2
|
+
class Registry #:nodoc:
|
|
3
|
+
include Singleton
|
|
4
|
+
|
|
5
|
+
attr_accessor :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
|
+
if uri_map[normalize_uri(uri)][method].nil?
|
|
17
|
+
uri_map[normalize_uri(uri)][method] = [*[options]].flatten.collect do |option|
|
|
18
|
+
FakeWeb::Responder.new(method, uri, option, option[:times])
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
existing = nil
|
|
22
|
+
uri_map[normalize_uri(uri)][method].each_with_index do |resp, i|
|
|
23
|
+
existing = i if resp.request_body == options[:request_body]
|
|
24
|
+
end
|
|
25
|
+
if existing
|
|
26
|
+
uri_map[normalize_uri(uri)][method][existing] = [*[options]].flatten.collect do |option|
|
|
27
|
+
FakeWeb::Responder.new(method, uri, option, option[:times])
|
|
28
|
+
end
|
|
29
|
+
else
|
|
30
|
+
uri_map[normalize_uri(uri)][method] += [*[options]].flatten.collect do |option|
|
|
31
|
+
FakeWeb::Responder.new(method, uri, option, option[:times])
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def registered_uri?(method, uri, request_body = nil)
|
|
38
|
+
!responses_for(method, uri, request_body).empty?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def response_for(method, uri, request_body = nil, &block)
|
|
42
|
+
responses = responses_for(method, uri, request_body)
|
|
43
|
+
return nil if responses.empty?
|
|
44
|
+
|
|
45
|
+
next_response = responses.last
|
|
46
|
+
responses.each do |response|
|
|
47
|
+
if response.times and response.times > 0
|
|
48
|
+
response.times -= 1
|
|
49
|
+
next_response = response
|
|
50
|
+
break
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
next_response.response(request_body, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def responses_for(method, uri, request_body = nil)
|
|
61
|
+
uri = normalize_uri(uri)
|
|
62
|
+
|
|
63
|
+
uri_map_matches(method, uri, URI, request_body) ||
|
|
64
|
+
uri_map_matches(:any, uri, URI, request_body) ||
|
|
65
|
+
uri_map_matches(method, uri, Regexp, request_body) ||
|
|
66
|
+
uri_map_matches(:any, uri, Regexp, request_body) ||
|
|
67
|
+
[]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def uri_map_matches(method, uri, type_to_check = URI, request_body = nil)
|
|
71
|
+
uris_to_check = variations_of_uri_as_strings(uri)
|
|
72
|
+
|
|
73
|
+
matches = uri_map.select { |registered_uri, method_hash|
|
|
74
|
+
registered_uri.is_a?(type_to_check) && method_hash.has_key?(method)
|
|
75
|
+
}.select { |registered_uri, method_hash|
|
|
76
|
+
if type_to_check == URI
|
|
77
|
+
uris_to_check.include?(registered_uri.to_s)
|
|
78
|
+
elsif type_to_check == Regexp
|
|
79
|
+
uris_to_check.any? { |u| u.match(registered_uri) }
|
|
80
|
+
end
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if matches.size > 1
|
|
84
|
+
raise MultipleMatchingURIsError,
|
|
85
|
+
"More than one registered URI matched this request: #{method.to_s.upcase} #{uri}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
match = matches.map { |_, method_hash| method_hash[method] }.first
|
|
89
|
+
if match.is_a?(Array) && match.first.is_a?(Array)
|
|
90
|
+
match = match.first
|
|
91
|
+
elsif match.is_a?(Array)
|
|
92
|
+
match = match.reject do |resp|
|
|
93
|
+
body = (resp.is_a?(Array) ? resp.first : resp).request_body
|
|
94
|
+
case body
|
|
95
|
+
when nil, String
|
|
96
|
+
body != request_body && !(body.nil? && (request_body.nil? || request_body == ''))
|
|
97
|
+
when Regexp
|
|
98
|
+
!(request_body =~ body)
|
|
99
|
+
else
|
|
100
|
+
body.to_s != request_body
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
match
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def variations_of_uri_as_strings(uri_object)
|
|
109
|
+
uris = []
|
|
110
|
+
normalized_uri = normalize_uri(uri_object)
|
|
111
|
+
|
|
112
|
+
# all orderings of query parameters
|
|
113
|
+
query = normalized_uri.query
|
|
114
|
+
if query.nil? || query.empty?
|
|
115
|
+
uris << normalized_uri
|
|
116
|
+
else
|
|
117
|
+
FakeWeb::Utility.simple_array_permutation(query.split('&')) do |p|
|
|
118
|
+
current_permutation = normalized_uri.dup
|
|
119
|
+
current_permutation.query = p.join('&')
|
|
120
|
+
uris << current_permutation
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
uri_strings = uris.map { |uri| uri.to_s }
|
|
125
|
+
|
|
126
|
+
# including and omitting the default port
|
|
127
|
+
if normalized_uri.default_port == normalized_uri.port
|
|
128
|
+
uri_strings += uris.map { |uri|
|
|
129
|
+
uri.to_s.sub(/#{Regexp.escape(normalized_uri.request_uri)}$/,
|
|
130
|
+
":#{normalized_uri.port}#{normalized_uri.request_uri}")
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
uri_strings
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def normalize_uri(uri)
|
|
138
|
+
return uri if uri.is_a?(Regexp)
|
|
139
|
+
normalized_uri =
|
|
140
|
+
case uri
|
|
141
|
+
when URI then uri
|
|
142
|
+
when String
|
|
143
|
+
uri = 'http://' + uri unless uri.match('^https?://')
|
|
144
|
+
URI.parse(uri)
|
|
145
|
+
end
|
|
146
|
+
normalized_uri.query = sort_query_params(normalized_uri.query)
|
|
147
|
+
normalized_uri.normalize
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def sort_query_params(query)
|
|
151
|
+
if query.nil? || query.empty?
|
|
152
|
+
nil
|
|
153
|
+
else
|
|
154
|
+
query.split('&').sort.join('&')
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module FakeWeb
|
|
2
|
+
class Responder #:nodoc:
|
|
3
|
+
|
|
4
|
+
attr_accessor :method, :uri, :options, :times, :request_body, :eval_body
|
|
5
|
+
KNOWN_OPTIONS = [:body, :exception, :response, :status].freeze
|
|
6
|
+
|
|
7
|
+
def initialize(method, uri, options, times)
|
|
8
|
+
self.method = method
|
|
9
|
+
self.uri = uri
|
|
10
|
+
self.options = options
|
|
11
|
+
self.times = times ? times : 1
|
|
12
|
+
self.request_body = options.delete(:request_body)
|
|
13
|
+
self.eval_body = options.delete(:eval_body) || false
|
|
14
|
+
|
|
15
|
+
if options.has_key?(:file) || options.has_key?(:string)
|
|
16
|
+
print_file_string_options_deprecation_warning
|
|
17
|
+
options[:body] = options.delete(:file) || options.delete(:string)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def response(request_body = nil, &block)
|
|
22
|
+
if has_baked_response?
|
|
23
|
+
response = baked_response
|
|
24
|
+
else
|
|
25
|
+
code, msg = meta_information
|
|
26
|
+
response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
|
|
27
|
+
the_body = body
|
|
28
|
+
if eval_body
|
|
29
|
+
@fakeweb = { :request_body => request_body, :responder => self }
|
|
30
|
+
the_body = eval(the_body)
|
|
31
|
+
end
|
|
32
|
+
response.instance_variable_set(:@body, the_body)
|
|
33
|
+
headers_extracted_from_options.each { |name, value| response[name] = value }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
response.instance_variable_set(:@read, true)
|
|
37
|
+
response.extend FakeWeb::Response
|
|
38
|
+
|
|
39
|
+
optionally_raise(response)
|
|
40
|
+
|
|
41
|
+
yield response if block_given?
|
|
42
|
+
|
|
43
|
+
response
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def headers_extracted_from_options
|
|
49
|
+
options.reject {|name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value|
|
|
50
|
+
[name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value]
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def body
|
|
55
|
+
return '' unless options.has_key?(:body)
|
|
56
|
+
|
|
57
|
+
if !options[:body].include?("\0") && File.exists?(options[:body]) && !File.directory?(options[:body])
|
|
58
|
+
File.read(options[:body])
|
|
59
|
+
else
|
|
60
|
+
options[:body]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def baked_response
|
|
65
|
+
resp = case options[:response]
|
|
66
|
+
when Net::HTTPResponse then options[:response]
|
|
67
|
+
when String
|
|
68
|
+
socket = Net::BufferedIO.new(options[:response])
|
|
69
|
+
r = Net::HTTPResponse.read_new(socket)
|
|
70
|
+
|
|
71
|
+
# Store the oiriginal transfer-encoding
|
|
72
|
+
saved_transfer_encoding = r.instance_eval {
|
|
73
|
+
@header['transfer-encoding'] if @header.key?('transfer-encoding')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# read the body of response.
|
|
77
|
+
r.instance_eval { @header['transfer-encoding'] = nil }
|
|
78
|
+
r.reading_body(socket, true) {}
|
|
79
|
+
|
|
80
|
+
# Delete the transfer-encoding key from r.@header if there wasn't one,
|
|
81
|
+
# else restore the saved_transfer_encoding.
|
|
82
|
+
if saved_transfer_encoding.nil?
|
|
83
|
+
r.instance_eval { @header.delete('transfer-encoding') }
|
|
84
|
+
else
|
|
85
|
+
r.instance_eval { @header['transfer-encoding'] = saved_transfer_encoding }
|
|
86
|
+
end
|
|
87
|
+
r
|
|
88
|
+
else raise StandardError, "Handler unimplemented for response #{options[:response]}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def has_baked_response?
|
|
93
|
+
options.has_key?(:response)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def optionally_raise(response)
|
|
97
|
+
return unless options.has_key?(:exception)
|
|
98
|
+
|
|
99
|
+
case options[:exception].to_s
|
|
100
|
+
when "Net::HTTPError", "OpenURI::HTTPError"
|
|
101
|
+
raise options[:exception].new('Exception from FakeWeb', response)
|
|
102
|
+
else
|
|
103
|
+
raise options[:exception].new('Exception from FakeWeb')
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def meta_information
|
|
108
|
+
options.has_key?(:status) ? options[:status] : [200, 'OK']
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def print_file_string_options_deprecation_warning
|
|
112
|
+
which = options.has_key?(:file) ? :file : :string
|
|
113
|
+
$stderr.puts
|
|
114
|
+
$stderr.puts "Deprecation warning: FakeWeb's :#{which} option has been renamed to :body."
|
|
115
|
+
$stderr.puts "Just replace :#{which} with :body in your FakeWeb.register_uri calls."
|
|
116
|
+
$stderr.puts "Called at #{caller[6]}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module FakeWeb
|
|
2
|
+
module Utility #:nodoc:
|
|
3
|
+
|
|
4
|
+
def self.decode_userinfo_from_header(header)
|
|
5
|
+
header.sub(/^Basic /, "").unpack("m").first
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.encode_unsafe_chars_in_userinfo(userinfo)
|
|
9
|
+
unsafe_in_userinfo = /[^#{URI::REGEXP::PATTERN::UNRESERVED};&=+$,]|^(#{URI::REGEXP::PATTERN::ESCAPED})/
|
|
10
|
+
userinfo.split(":").map { |part| URI.escape(part, unsafe_in_userinfo) }.join(":")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.strip_default_port_from_uri(uri)
|
|
14
|
+
case uri
|
|
15
|
+
when %r{^http://} then uri.sub(%r{:80(/|$)}, '\1')
|
|
16
|
+
when %r{^https://} then uri.sub(%r{:443(/|$)}, '\1')
|
|
17
|
+
else uri
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Array#permutation wrapper for 1.8.6-compatibility. It only supports the
|
|
22
|
+
# simple case that returns all permutations (so it doesn't take a numeric
|
|
23
|
+
# argument).
|
|
24
|
+
def self.simple_array_permutation(array, &block)
|
|
25
|
+
# use native implementation if it exists
|
|
26
|
+
return array.permutation(&block) if array.respond_to?(:permutation)
|
|
27
|
+
|
|
28
|
+
yield array if array.length <= 1
|
|
29
|
+
|
|
30
|
+
array.length.times do |i|
|
|
31
|
+
rest = array.dup
|
|
32
|
+
picked = rest.delete_at(i)
|
|
33
|
+
next if rest.empty?
|
|
34
|
+
|
|
35
|
+
simple_array_permutation(rest) do |part_of_rest|
|
|
36
|
+
yield [picked] + part_of_rest
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
array
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/fake_web.rb
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
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
|
+
module FakeWeb
|
|
11
|
+
|
|
12
|
+
# Resets the FakeWeb Registry. This will force all subsequent web requests to
|
|
13
|
+
# behave as real requests.
|
|
14
|
+
def self.clean_registry
|
|
15
|
+
Registry.instance.clean_registry
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Enables or disables real HTTP connections for requests that don't match
|
|
19
|
+
# registered URIs.
|
|
20
|
+
#
|
|
21
|
+
# If you set <tt>FakeWeb.allow_net_connect = false</tt> and subsequently try
|
|
22
|
+
# to make a request to a URI you haven't registered with #register_uri, a
|
|
23
|
+
# NetConnectNotAllowedError will be raised. This is handy when you want to
|
|
24
|
+
# make sure your tests are self-contained, or want to catch the scenario
|
|
25
|
+
# when a URI is changed in implementation code without a corresponding test
|
|
26
|
+
# change.
|
|
27
|
+
#
|
|
28
|
+
# When <tt>FakeWeb.allow_net_connect = true</tt> (the default), requests to
|
|
29
|
+
# URIs not stubbed with FakeWeb are passed through to Net::HTTP.
|
|
30
|
+
def self.allow_net_connect=(allowed)
|
|
31
|
+
@allow_net_connect = allowed
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Enable pass-through to Net::HTTP by default.
|
|
35
|
+
self.allow_net_connect = true
|
|
36
|
+
|
|
37
|
+
# Returns +true+ if requests to URIs not registered with FakeWeb are passed
|
|
38
|
+
# through to Net::HTTP for normal processing (the default). Returns +false+
|
|
39
|
+
# if an exception is raised for these requests.
|
|
40
|
+
def self.allow_net_connect?
|
|
41
|
+
@allow_net_connect
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# This exception is raised if you set <tt>FakeWeb.allow_net_connect =
|
|
45
|
+
# false</tt> and subsequently try to make a request to a URI you haven't
|
|
46
|
+
# stubbed.
|
|
47
|
+
class NetConnectNotAllowedError < StandardError; end;
|
|
48
|
+
|
|
49
|
+
# This exception is raised if a Net::HTTP request matches more than one of
|
|
50
|
+
# the stubs you've registered. To fix the problem, remove a duplicate
|
|
51
|
+
# registration or disambiguate any regular expressions by making them more
|
|
52
|
+
# specific.
|
|
53
|
+
class MultipleMatchingURIsError < StandardError; end;
|
|
54
|
+
|
|
55
|
+
# call-seq:
|
|
56
|
+
# FakeWeb.register_uri(method, uri, options)
|
|
57
|
+
#
|
|
58
|
+
# Register requests using the HTTP method specified by the symbol +method+
|
|
59
|
+
# for +uri+ to be handled according to +options+. If you specify the method
|
|
60
|
+
# <tt>:any</tt>, the response will be reigstered for any request for +uri+.
|
|
61
|
+
# +uri+ can be a +String+, +URI+, or +Regexp+ object. +options+ must be either
|
|
62
|
+
# a +Hash+ or an +Array+ of +Hashes+ (see below), which must contain one of
|
|
63
|
+
# these two keys:
|
|
64
|
+
#
|
|
65
|
+
# <tt>:body</tt>::
|
|
66
|
+
# A string which is used as the body of the response. If the string refers
|
|
67
|
+
# to a valid filesystem path, the contents of that file will be read and used
|
|
68
|
+
# as the body of the response instead. (This used to be two options,
|
|
69
|
+
# <tt>:string</tt> and <tt>:file</tt>, respectively. These are now deprecated.)
|
|
70
|
+
# <tt>:response</tt>::
|
|
71
|
+
# Either an <tt>Net::HTTPResponse</tt>, an +IO+, or a +String+ which is used
|
|
72
|
+
# as the full response for the request.
|
|
73
|
+
#
|
|
74
|
+
# The easier way by far is to pass the <tt>:response</tt> option to
|
|
75
|
+
# +register_uri+ as a +String+ or an (open for reads) +IO+ object which
|
|
76
|
+
# will be used as the complete HTTP response, including headers and body.
|
|
77
|
+
# If the string points to a readable file, this file will be used as the
|
|
78
|
+
# content for the request.
|
|
79
|
+
#
|
|
80
|
+
# To obtain a complete response document, you can use the +curl+ command,
|
|
81
|
+
# like so:
|
|
82
|
+
#
|
|
83
|
+
# curl -i http://www.example.com/ > response_for_www.example.com
|
|
84
|
+
#
|
|
85
|
+
# which can then be used in your test environment like so:
|
|
86
|
+
#
|
|
87
|
+
# FakeWeb.register_uri(:get, 'http://www.example.com/', :response => 'response_for_www.example.com')
|
|
88
|
+
#
|
|
89
|
+
# See the <tt>Net::HTTPResponse</tt>
|
|
90
|
+
# documentation[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html]
|
|
91
|
+
# for more information on creating custom response objects.
|
|
92
|
+
#
|
|
93
|
+
# +options+ may also be an +Array+ containing a list of the above-described
|
|
94
|
+
# +Hash+. In this case, FakeWeb will rotate through each provided response,
|
|
95
|
+
# you may optionally provide:
|
|
96
|
+
#
|
|
97
|
+
# <tt>:times</tt>::
|
|
98
|
+
# The number of times this response will be used. Decremented by one each time it's called.
|
|
99
|
+
# FakeWeb will use the final provided request indefinitely, regardless of its :times parameter.
|
|
100
|
+
#
|
|
101
|
+
# Two optional arguments are also accepted:
|
|
102
|
+
#
|
|
103
|
+
# <tt>:status</tt>::
|
|
104
|
+
# Passing <tt>:status</tt> as a two-value array will set the response code
|
|
105
|
+
# and message. The defaults are <tt>200</tt> and <tt>OK</tt>, respectively.
|
|
106
|
+
# Example:
|
|
107
|
+
# FakeWeb.register_uri("http://www.example.com/", :body => "Go away!", :status => [404, "Not Found"])
|
|
108
|
+
# <tt>:exception</tt>::
|
|
109
|
+
# The argument passed via <tt>:exception</tt> will be raised when the
|
|
110
|
+
# specified URL is requested. Any +Exception+ class is valid. Example:
|
|
111
|
+
# FakeWeb.register_uri('http://www.example.com/', :exception => Net::HTTPError)
|
|
112
|
+
#
|
|
113
|
+
# If you're using the <tt>:body</tt> response type, you can pass additional
|
|
114
|
+
# options to specify the HTTP headers to be used in the response. Example:
|
|
115
|
+
#
|
|
116
|
+
# FakeWeb.register_uri(:get, "http://example.com/index.txt", :body => "Hello", :content_type => "text/plain")
|
|
117
|
+
def self.register_uri(*args)
|
|
118
|
+
case args.length
|
|
119
|
+
when 3
|
|
120
|
+
Registry.instance.register_uri(*args)
|
|
121
|
+
when 2
|
|
122
|
+
print_missing_http_method_deprecation_warning(*args)
|
|
123
|
+
Registry.instance.register_uri(:any, *args)
|
|
124
|
+
else
|
|
125
|
+
raise ArgumentError.new("wrong number of arguments (#{args.length} for 3)")
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# call-seq:
|
|
130
|
+
# FakeWeb.response_for(method, uri)
|
|
131
|
+
#
|
|
132
|
+
# Returns the faked Net::HTTPResponse object associated with +method+ and +uri+.
|
|
133
|
+
def self.response_for(*args, &block) #:nodoc: :yields: response
|
|
134
|
+
case args.length
|
|
135
|
+
when 2..3
|
|
136
|
+
Registry.instance.response_for(*args, &block)
|
|
137
|
+
when 1
|
|
138
|
+
print_missing_http_method_deprecation_warning(*args)
|
|
139
|
+
Registry.instance.response_for(:any, *args, &block)
|
|
140
|
+
else
|
|
141
|
+
raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# call-seq:
|
|
146
|
+
# FakeWeb.registered_uri?(method, uri)
|
|
147
|
+
#
|
|
148
|
+
# Returns true if a +method+ request for +uri+ is registered with FakeWeb.
|
|
149
|
+
# Specify a method of <tt>:any</tt> to check for against all HTTP methods.
|
|
150
|
+
def self.registered_uri?(*args)
|
|
151
|
+
case args.length
|
|
152
|
+
when 2..3
|
|
153
|
+
Registry.instance.registered_uri?(*args)
|
|
154
|
+
when 1
|
|
155
|
+
print_missing_http_method_deprecation_warning(*args)
|
|
156
|
+
Registry.instance.registered_uri?(:any, *args)
|
|
157
|
+
else
|
|
158
|
+
raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
def self.print_missing_http_method_deprecation_warning(*args)
|
|
165
|
+
method = caller.first.match(/`(.*?)'/)[1]
|
|
166
|
+
new_args = args.map { |a| a.inspect }.unshift(":any")
|
|
167
|
+
new_args.last.gsub!(/^\{|\}$/, "").gsub!("=>", " => ") if args.last.is_a?(Hash)
|
|
168
|
+
$stderr.puts
|
|
169
|
+
$stderr.puts "Deprecation warning: FakeWeb requires an HTTP method argument (or use :any). Try this:"
|
|
170
|
+
$stderr.puts " FakeWeb.#{method}(#{new_args.join(', ')})"
|
|
171
|
+
$stderr.puts "Called at #{caller[1]}"
|
|
172
|
+
end
|
|
173
|
+
end
|
data/lib/fakeweb.rb
ADDED