fcoury-fakeweb 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,70 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ task :default => :test
7
+
8
+ desc "Run All Tests"
9
+ Rake::TestTask.new :test do |test|
10
+ test.test_files = ["test/**/*.rb"]
11
+ test.verbose = true
12
+ end
13
+
14
+ desc "Generate Documentation"
15
+ Rake::RDocTask.new do |rdoc|
16
+ rdoc.main = "README.rdoc"
17
+ rdoc.rdoc_dir = "doc"
18
+ rdoc.rdoc_files.include("README.rdoc", "CHANGELOG", "LICENSE.txt", "lib/*.rb")
19
+ rdoc.title = "FakeWeb API Documentation"
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.options << '--charset' << 'utf-8'
22
+ end
23
+
24
+ desc %{Update ".manifest" with the latest list of project filenames. Respect\
25
+ .gitignore by excluding everything that git ignores. Update `files` and\
26
+ `test_files` arrays in "*.gemspec" file if it's present.}
27
+ task :manifest do
28
+ list = Dir['**/*'].sort
29
+ spec_file = Dir['*.gemspec'].first
30
+ list -= [spec_file] if spec_file
31
+
32
+ File.read('.gitignore').each_line do |glob|
33
+ glob = glob.chomp.sub(/^\//, '')
34
+ list -= Dir[glob]
35
+ list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
36
+ puts "excluding #{glob}"
37
+ end
38
+
39
+ if spec_file
40
+ spec = File.read spec_file
41
+ spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
42
+ assignment = $1
43
+ bunch = $2 ? list.grep(/^test\//) : list
44
+ '%s%%w(%s)' % [assignment, bunch.join(' ')]
45
+ end
46
+
47
+ File.open(spec_file, 'w') {|f| f << spec }
48
+ end
49
+ File.open('.manifest', 'w') {|f| f << list.join("\n") }
50
+ end
51
+
52
+ if RUBY_PLATFORM =~ /java/
53
+ puts "rcov support disabled (running under JRuby)."
54
+ elsif RUBY_VERSION =~ /^1\.9/
55
+ puts "rcov support disabled (running under Ruby 1.9)"
56
+ else
57
+ require 'rcov/rcovtask'
58
+ Rcov::RcovTask.new do |t|
59
+ t.test_files = FileList['test/**/test*.rb']
60
+ t.rcov_opts << "--sort coverage"
61
+ t.rcov_opts << "--exclude gems"
62
+ t.rcov_opts << "--no-validator-links"
63
+ end
64
+ end
65
+
66
+ spec = eval(File.read(File.join(File.dirname(__FILE__), "fakeweb.gemspec")))
67
+ Rake::GemPackageTask.new(spec) do |pkg|
68
+ pkg.need_tar_gz = true
69
+ pkg.need_zip = true
70
+ end
data/lib/fake_web.rb ADDED
@@ -0,0 +1,176 @@
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
+
9
+ module FakeWeb
10
+
11
+ # Resets the FakeWeb Registry. This will force all subsequent web requests to
12
+ # behave as real requests.
13
+ def self.clean_registry
14
+ Registry.instance.clean_registry
15
+ end
16
+
17
+ # Enables or disables real HTTP connections for requests that don't match
18
+ # registered URIs.
19
+ #
20
+ # If you set <tt>FakeWeb.allow_net_connect = false</tt> and subsequently try
21
+ # to make a request to a URI you haven't registered with #register_uri, a
22
+ # NetConnectNotAllowedError will be raised. This is handy when you want to
23
+ # make sure your tests are self-contained, or want to catch the scenario
24
+ # when a URI is changed in implementation code without a corresponding test
25
+ # change.
26
+ #
27
+ # When <tt>FakeWeb.allow_net_connect = true</tt> (the default), requests to
28
+ # URIs not stubbed with FakeWeb are passed through to Net::HTTP.
29
+ def self.allow_net_connect=(allowed)
30
+ @allow_net_connect = allowed
31
+ end
32
+
33
+ # Enable pass-through to Net::HTTP by default.
34
+ self.allow_net_connect = true
35
+
36
+ # Returns +true+ if requests to URIs not registered with FakeWeb are passed
37
+ # through to Net::HTTP for normal processing (the default). Returns +false+
38
+ # if an exception is raised for these requests.
39
+ def self.allow_net_connect?
40
+ @allow_net_connect
41
+ end
42
+
43
+ # Enables or disables ignoring of query parameters for registered URIs.
44
+ #
45
+ # When you set <tt>FakeWeb.ignore_query_params = false</tt> (the default),
46
+ # all registered requests will fail unless they match the provided query
47
+ # parameters.
48
+ #
49
+ # When you set <tt>FakeWeb.ignore_query_params = true</tt>, though, any URIs
50
+ # that are supplied without query parameters will match the same URIs when
51
+ # they are requested, whether they have query parameters or not.
52
+ def self.ignore_query_params=(ignore)
53
+ @ignore_query_params = ignore
54
+ end
55
+
56
+ # Pay attention to query parameters by default.
57
+ self.ignore_query_params = false
58
+
59
+ # Returns +true+ if query parameters on a request should be ignored, or
60
+ # +false+ if matching query parameters are needed for the corresponding
61
+ # response to be supplied.
62
+ def self.ignore_query_params?
63
+ @ignore_query_params
64
+ end
65
+
66
+ # This exception is raised if you set <tt>FakeWeb.allow_net_connect =
67
+ # false</tt> and subsequently try to make a request to a URI you haven't
68
+ # stubbed.
69
+ class NetConnectNotAllowedError < StandardError; end;
70
+
71
+ # call-seq:
72
+ # FakeWeb.register_uri(method, uri, options)
73
+ # FakeWeb.register_uri(uri, options)
74
+ #
75
+ # Register requests using the HTTP method specified by the symbol +method+ for
76
+ # +uri+ to be handled according to +options+. If no +method+ is specified, or
77
+ # you explicitly specify <tt>:any</tt>, the response will be reigstered for
78
+ # any request for +uri+. +uri+ can be a +String+ or a +URI+ object. +options+
79
+ # must be either a +Hash+ or an +Array+ of +Hashes+ (see below) that must
80
+ # contain any one of the following keys:
81
+ #
82
+ # <tt>:string</tt>::
83
+ # Takes a +String+ argument that is returned as the body of the response.
84
+ # FakeWeb.register_uri(:get, 'http://example.com/', :string => "Hello World!")
85
+ # <tt>:file</tt>::
86
+ # Takes a valid filesystem path to a file that is slurped and returned as
87
+ # the body of the response.
88
+ # FakeWeb.register_uri(:post, 'http://example.com/', :file => "/tmp/my_response_body.txt")
89
+ # <tt>:response</tt>::
90
+ # Either an <tt>Net::HTTPResponse</tt>, an +IO+ or a +String+.
91
+ #
92
+ # The easier way by far is to pass the <tt>:response</tt> option to
93
+ # +register_uri+ as a +String+ or an (open for reads) +IO+ object which
94
+ # will be used as the complete HTTP response, including headers and body.
95
+ # If the string points to a readable file, this file will be used as the
96
+ # content for the request.
97
+ #
98
+ # To obtain a complete response document, you can use the +curl+ command,
99
+ # like so:
100
+ #
101
+ # curl -i http://www.example.com/ > response_for_www.example.com
102
+ #
103
+ # which can then be used in your test environment like so:
104
+ #
105
+ # FakeWeb.register_uri(:get, 'http://www.example.com/', :response => 'response_for_www.example.com')
106
+ #
107
+ # See the <tt>Net::HTTPResponse</tt>
108
+ # documentation[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html]
109
+ # for more information on creating custom response objects.
110
+ #
111
+ # +options+ may also be an +Array+ containing a list of the above-described +Hash+.
112
+ # In this case, FakeWeb will rotate through each provided response, you may optionally
113
+ # provide:
114
+ #
115
+ # <tt>:times</tt>::
116
+ # The number of times this response will be used. Decremented by one each time it's called.
117
+ # FakeWeb will use the final provided request indefinitely, regardless of its :times parameter.
118
+ #
119
+ # Two optional arguments are also accepted:
120
+ #
121
+ # <tt>:status</tt>::
122
+ # Passing <tt>:status</tt> as a two-value array will set the response code
123
+ # and message. The defaults are <tt>200</tt> and <tt>OK</tt>, respectively.
124
+ # Example:
125
+ # FakeWeb.register_uri('http://www.example.com/', :response => "Go away!", :status => [ 404, "Not Found" ])
126
+ # <tt>:exception</tt>::
127
+ # The argument passed via <tt>:exception</tt> will be raised when the
128
+ # specified URL is requested. Any +Exception+ class is valid. Example:
129
+ # FakeWeb.register_uri('http://www.example.com/', :exception => Net::HTTPError)
130
+ #
131
+ def self.register_uri(*args)
132
+ method = :any
133
+ case args.length
134
+ when 3 then method, uri, options = *args
135
+ when 2 then uri, options = *args
136
+ else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri, options)")
137
+ end
138
+
139
+ Registry.instance.register_uri(method, uri, options)
140
+ end
141
+
142
+ # call-seq:
143
+ # FakeWeb.response_for(method, uri)
144
+ # FakeWeb.response_for(uri)
145
+ #
146
+ # Returns the faked Net::HTTPResponse object associated with +uri+.
147
+ def self.response_for(*args, &block) #:nodoc: :yields: response
148
+ method = :any
149
+ case args.length
150
+ when 2 then method, uri = args
151
+ when 1 then uri = args.first
152
+ else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri)")
153
+ end
154
+
155
+ Registry.instance.response_for(method, uri, &block)
156
+ end
157
+
158
+ # call-seq:
159
+ # FakeWeb.registered_uri?(method, uri)
160
+ # FakeWeb.registered_uri?(uri)
161
+ #
162
+ # Returns true if +uri+ is registered with FakeWeb. You can optionally
163
+ # specify +method+ to limit the search to a certain HTTP method (or use
164
+ # <tt>:any</tt> to explicitly check against any method).
165
+ def self.registered_uri?(*args)
166
+ method = :any
167
+ case args.length
168
+ when 2 then method, uri = args
169
+ when 1 then uri = args.first
170
+ else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri)")
171
+ end
172
+
173
+ Registry.instance.registered_uri?(method, uri)
174
+ end
175
+
176
+ end
@@ -0,0 +1,62 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'stringio'
4
+
5
+ module Net #:nodoc: all
6
+
7
+ class BufferedIO
8
+ def initialize(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
+ File.exists?(io) ? File.open(io, "r") : StringIO.new(io)
18
+ end
19
+ raise "Unable to create local socket" unless @io
20
+ end
21
+ end
22
+
23
+ class HTTP
24
+ def self.socket_type
25
+ FakeWeb::StubSocket
26
+ end
27
+
28
+ alias :original_net_http_request :request
29
+ alias :original_net_http_connect :connect
30
+
31
+ def request(request, body = nil, &block)
32
+ protocol = use_ssl? ? "https" : "http"
33
+
34
+ path = request.path
35
+ path = URI.parse(request.path).request_uri if request.path =~ /^http/
36
+
37
+ if request['authorization'].nil?
38
+ userinfo = ""
39
+ else
40
+ userinfo = request['authorization'].sub(/^Basic /, "").unpack("m").first + "@"
41
+ end
42
+
43
+ uri = "#{protocol}://#{userinfo}#{self.address}:#{self.port}#{path}"
44
+ method = request.method.downcase.to_sym
45
+
46
+ if FakeWeb.registered_uri?(method, uri)
47
+ @socket = Net::HTTP.socket_type.new
48
+ FakeWeb.response_for(method, uri, &block)
49
+ elsif FakeWeb.allow_net_connect?
50
+ original_net_http_connect
51
+ original_net_http_request(request, body, &block)
52
+ else
53
+ raise FakeWeb::NetConnectNotAllowedError,
54
+ "Real HTTP connections are disabled. Unregistered request: #{request.method} #{uri}"
55
+ end
56
+ end
57
+
58
+ def connect
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,83 @@
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 do |hash, key|
13
+ hash[key] = Hash.new(&hash.default_proc)
14
+ end
15
+ end
16
+
17
+ def register_uri(method, uri, options)
18
+ uri_map[normalize_uri(uri)][method] = [*[options]].flatten.collect do |option|
19
+ FakeWeb::Responder.new(method, uri, option, option[:times])
20
+ end
21
+ end
22
+
23
+ def registered_uri?(method, uri)
24
+ normalized_uri = normalize_uri(uri)
25
+
26
+ uri_map[normalized_uri].has_key?(method) ||
27
+ uri_map[normalized_uri].has_key?(:any)
28
+ end
29
+
30
+ def registered_uri(method, uri)
31
+ uri = normalize_uri(uri)
32
+ registered = registered_uri?(method, uri)
33
+ if registered && uri_map[uri].has_key?(method)
34
+ uri_map[uri][method]
35
+ elsif registered
36
+ uri_map[uri][:any]
37
+ else
38
+ nil
39
+ end
40
+ end
41
+
42
+ def response_for(method, uri, &block)
43
+ responses = registered_uri(method, uri)
44
+ return nil if responses.nil?
45
+
46
+ next_response = responses.last
47
+ responses.each do |response|
48
+ if response.times and response.times > 0
49
+ response.times -= 1
50
+ next_response = response
51
+ break
52
+ end
53
+ end
54
+
55
+ next_response.response(&block)
56
+ end
57
+
58
+ private
59
+
60
+ def normalize_uri(uri)
61
+ normalized_uri =
62
+ case uri
63
+ when URI then uri
64
+ else
65
+ uri = 'http://' + uri unless uri.match('^https?://')
66
+ parsed_uri = URI.parse(uri)
67
+ parsed_uri.query = FakeWeb.ignore_query_params? ? nil :
68
+ sort_query_params(parsed_uri.query)
69
+ parsed_uri
70
+ end
71
+ normalized_uri.normalize
72
+ end
73
+
74
+ def sort_query_params(query)
75
+ if query.nil? || query.empty?
76
+ nil
77
+ else
78
+ query.split('&').sort.join('&')
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,99 @@
1
+ module FakeWeb
2
+ class Responder #:nodoc:
3
+
4
+ attr_accessor :method, :uri, :options, :times
5
+
6
+ def initialize(method, uri, options, times)
7
+ self.method = method
8
+ self.uri = uri
9
+ self.options = options
10
+ self.times = times ? times : 1
11
+ end
12
+
13
+ def response(&block)
14
+ if has_baked_response?
15
+ response = baked_response
16
+ else
17
+ code, msg = meta_information
18
+ response = Net::HTTPResponse.send(:response_class, code.to_s).new(uri, code.to_s, msg)
19
+ response.instance_variable_set(:@body, content)
20
+ end
21
+
22
+ response.instance_variable_set(:@read, true)
23
+ response.extend FakeWeb::Response
24
+
25
+ optionally_raise(response)
26
+
27
+ yield response if block_given?
28
+
29
+ response
30
+ end
31
+
32
+ private
33
+
34
+ def content
35
+ [ :file, :string ].each do |map_option|
36
+ next unless options.has_key?(map_option)
37
+ return self.send("#{map_option}_response", options[map_option])
38
+ end
39
+
40
+ return ''
41
+ end
42
+
43
+ def file_response(path)
44
+ IO.read(path)
45
+ end
46
+
47
+ def string_response(string)
48
+ string
49
+ end
50
+
51
+ def baked_response
52
+ resp = case options[:response]
53
+ when Net::HTTPResponse then options[:response]
54
+ when String
55
+ socket = Net::BufferedIO.new(options[:response])
56
+ r = Net::HTTPResponse.read_new(socket)
57
+
58
+ # Store the oiriginal transfer-encoding
59
+ saved_transfer_encoding = r.instance_eval {
60
+ @header['transfer-encoding'] if @header.key?('transfer-encoding')
61
+ }
62
+
63
+ # read the body of response.
64
+ r.instance_eval { @header['transfer-encoding'] = nil }
65
+ r.reading_body(socket, true) {}
66
+
67
+ # Delete the transfer-encoding key from r.@header if there wasn't one,
68
+ # else restore the saved_transfer_encoding.
69
+ if saved_transfer_encoding.nil?
70
+ r.instance_eval { @header.delete('transfer-encoding') }
71
+ else
72
+ r.instance_eval { @header['transfer-encoding'] = saved_transfer_encoding }
73
+ end
74
+ r
75
+ else raise StandardError, "Handler unimplemented for response #{options[:response]}"
76
+ end
77
+ end
78
+
79
+ def has_baked_response?
80
+ options.has_key?(:response)
81
+ end
82
+
83
+ def optionally_raise(response)
84
+ return unless options.has_key?(:exception)
85
+ ex_alloc = options[:exception].allocate
86
+ ex_instance = case ex_alloc
87
+ when Net::HTTPError, OpenURI::HTTPError
88
+ options[:exception].new('Exception from FakeWeb', response)
89
+ else options[:exception].new
90
+ end
91
+ raise ex_instance
92
+ end
93
+
94
+ def meta_information
95
+ options.has_key?(:status) ? options[:status] : [200, 'OK']
96
+ end
97
+
98
+ end
99
+ end