rubyist-fakeweb 1.2.2

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/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 = false
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,154 @@
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/url_encoded_pair_parser'
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
+ # call-seq:
50
+ # FakeWeb.register_uri(method, uri, options)
51
+ # FakeWeb.register_uri(uri, options)
52
+ #
53
+ # Register requests using the HTTP method specified by the symbol +method+ for
54
+ # +uri+ to be handled according to +options+. If no +method+ is specified, or
55
+ # you explicitly specify <tt>:any</tt>, the response will be reigstered for
56
+ # any request for +uri+. +uri+ can be a +String+ or a +URI+ object. +options+
57
+ # must be either a +Hash+ or an +Array+ of +Hashes+ (see below) that must
58
+ # contain any one of the following keys:
59
+ #
60
+ # <tt>:string</tt>::
61
+ # Takes a +String+ argument that is returned as the body of the response.
62
+ # FakeWeb.register_uri(:get, 'http://example.com/', :string => "Hello World!")
63
+ # <tt>:file</tt>::
64
+ # Takes a valid filesystem path to a file that is slurped and returned as
65
+ # the body of the response.
66
+ # FakeWeb.register_uri(:post, 'http://example.com/', :file => "/tmp/my_response_body.txt")
67
+ # <tt>:response</tt>::
68
+ # Either an <tt>Net::HTTPResponse</tt>, an +IO+ or a +String+.
69
+ #
70
+ # The easier way by far is to pass the <tt>:response</tt> option to
71
+ # +register_uri+ as a +String+ or an (open for reads) +IO+ object which
72
+ # will be used as the complete HTTP response, including headers and body.
73
+ # If the string points to a readable file, this file will be used as the
74
+ # content for the request.
75
+ #
76
+ # To obtain a complete response document, you can use the +curl+ command,
77
+ # like so:
78
+ #
79
+ # curl -i http://www.example.com/ > response_for_www.example.com
80
+ #
81
+ # which can then be used in your test environment like so:
82
+ #
83
+ # FakeWeb.register_uri(:get, 'http://www.example.com/', :response => 'response_for_www.example.com')
84
+ #
85
+ # See the <tt>Net::HTTPResponse</tt>
86
+ # documentation[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html]
87
+ # for more information on creating custom response objects.
88
+ #
89
+ # +options+ may also be an +Array+ containing a list of the above-described +Hash+.
90
+ # In this case, FakeWeb will rotate through each provided response, you may optionally
91
+ # provide:
92
+ #
93
+ # <tt>:times</tt>::
94
+ # The number of times this response will be used. Decremented by one each time it's called.
95
+ # FakeWeb will use the final provided request indefinitely, regardless of its :times parameter.
96
+ #
97
+ # Two optional arguments are also accepted:
98
+ #
99
+ # <tt>:status</tt>::
100
+ # Passing <tt>:status</tt> as a two-value array will set the response code
101
+ # and message. The defaults are <tt>200</tt> and <tt>OK</tt>, respectively.
102
+ # Example:
103
+ # FakeWeb.register_uri('http://www.example.com/', :response => "Go away!", :status => [ 404, "Not Found" ])
104
+ # <tt>:exception</tt>::
105
+ # The argument passed via <tt>:exception</tt> will be raised when the
106
+ # specified URL is requested. Any +Exception+ class is valid. Example:
107
+ # FakeWeb.register_uri('http://www.example.com/', :exception => Net::HTTPError)
108
+ #
109
+ def self.register_uri(*args, &block)
110
+ method = :any
111
+ case args.length
112
+ when 3 then method, uri, options = *args
113
+ when 2 then uri, options = *args
114
+ else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri, options)")
115
+ end
116
+
117
+ Registry.instance.register_uri(method, uri, options, &block)
118
+ end
119
+
120
+ # call-seq:
121
+ # FakeWeb.response_for(method, uri)
122
+ # FakeWeb.response_for(uri)
123
+ #
124
+ # Returns the faked Net::HTTPResponse object associated with +uri+.
125
+ def self.response_for(request, *args, &block) #:nodoc: :yields: response
126
+ method = :any
127
+ case args.length
128
+ when 2 then method, uri = args
129
+ when 1 then uri = args.first
130
+ else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri)")
131
+ end
132
+
133
+ Registry.instance.response_for(request, method, uri, &block)
134
+ end
135
+
136
+ # call-seq:
137
+ # FakeWeb.registered_uri?(method, uri)
138
+ # FakeWeb.registered_uri?(uri)
139
+ #
140
+ # Returns true if +uri+ is registered with FakeWeb. You can optionally
141
+ # specify +method+ to limit the search to a certain HTTP method (or use
142
+ # <tt>:any</tt> to explicitly check against any method).
143
+ def self.registered_uri?(*args)
144
+ method = :any
145
+ case args.length
146
+ when 2 then method, uri = args
147
+ when 1 then uri = args.first
148
+ else raise ArgumentError.new("wrong number of arguments (#{args.length} for method = :any, uri)")
149
+ end
150
+
151
+ Registry.instance.registered_uri?(method, uri)
152
+ end
153
+
154
+ 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)
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 = request["authorization"].sub(/^Basic /, "").unpack("m").first + "@"
45
+ else
46
+ userinfo = ""
47
+ end
48
+
49
+ uri = "#{protocol}://#{userinfo}#{self.address}:#{self.port}#{path}"
50
+ method = request.method.downcase.to_sym
51
+
52
+ if FakeWeb.registered_uri?(method, uri)
53
+ @socket = Net::HTTP.socket_type.new
54
+ if method == :put
55
+ request.body = body
56
+ end
57
+ FakeWeb.response_for(request, method, uri, &block)
58
+ elsif FakeWeb.allow_net_connect?
59
+ connect_without_fakeweb
60
+ request_without_fakeweb(request, body, &block)
61
+ else
62
+ raise FakeWeb::NetConnectNotAllowedError,
63
+ "Real HTTP connections are disabled. Unregistered request: #{request.method} #{uri}"
64
+ end
65
+ end
66
+
67
+ alias connect_without_fakeweb connect
68
+ def connect
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,79 @@
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, &block)
18
+ uri_map[normalize_uri(uri)][method] = [*[options]].flatten.collect do |option|
19
+ FakeWeb::Responder.new(method, uri, option, option[:times], &block)
20
+ end
21
+ end
22
+
23
+ def registered_uri?(method, uri)
24
+ normalized_uri = normalize_uri(uri)
25
+ uri_map[normalized_uri].has_key?(method) || uri_map[normalized_uri].has_key?(:any)
26
+ end
27
+
28
+ def registered_uri(method, uri)
29
+ uri = normalize_uri(uri)
30
+ registered = registered_uri?(method, uri)
31
+ if registered && uri_map[uri].has_key?(method)
32
+ uri_map[uri][method]
33
+ elsif registered
34
+ uri_map[uri][:any]
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ def response_for(request, method, uri, &block)
41
+ responses = registered_uri(method, uri)
42
+ return nil if responses.nil?
43
+
44
+ next_response = responses.last
45
+ responses.each do |response|
46
+ if response.times and response.times > 0
47
+ response.times -= 1
48
+ next_response = response
49
+ break
50
+ end
51
+ end
52
+
53
+ next_response.response(request, &block)
54
+ end
55
+
56
+ private
57
+
58
+ def normalize_uri(uri)
59
+ normalized_uri =
60
+ case uri
61
+ when URI then uri
62
+ else
63
+ uri = 'http://' + uri unless uri.match('^https?://')
64
+ URI.parse(uri)
65
+ end
66
+ normalized_uri.query = sort_query_params(normalized_uri.query)
67
+ normalized_uri.normalize
68
+ end
69
+
70
+ def sort_query_params(query)
71
+ if query.nil? || query.empty?
72
+ nil
73
+ else
74
+ query.split('&').sort.join('&')
75
+ end
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,122 @@
1
+ module FakeWeb
2
+ class Responder #:nodoc:
3
+
4
+ attr_accessor :method, :uri, :options, :times, :response_block
5
+
6
+ def initialize(method, uri, options, times, &block)
7
+ self.method = method
8
+ self.uri = uri
9
+ self.options = options
10
+ self.times = times ? times : 1
11
+ self.response_block = block
12
+ end
13
+
14
+ def response(request, &block)
15
+ if has_baked_response?
16
+ response = baked_response
17
+ else
18
+ if self.response_block
19
+ code, msg = meta_information
20
+ response = Net::HTTPResponse.send(:response_class, code.to_s).new(uri, code.to_s, msg)
21
+ response.instance_variable_set(:@body, self.response_block.call(params_for(request)))
22
+ else
23
+ code, msg = meta_information
24
+ response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
25
+ response.instance_variable_set(:@body, content)
26
+ end
27
+ end
28
+
29
+ response.instance_variable_set(:@read, true)
30
+ response.extend FakeWeb::Response
31
+
32
+ optionally_raise(response)
33
+
34
+ yield response if block_given?
35
+
36
+ response
37
+ end
38
+
39
+ private
40
+
41
+ def content
42
+ [ :file, :string ].each do |map_option|
43
+ next unless options.has_key?(map_option)
44
+ return self.send("#{map_option}_response", options[map_option])
45
+ end
46
+
47
+ return ''
48
+ end
49
+
50
+ def file_response(path)
51
+ IO.read(path)
52
+ end
53
+
54
+ def string_response(string)
55
+ string
56
+ end
57
+
58
+ def baked_response
59
+ resp = case options[:response]
60
+ when Net::HTTPResponse then options[:response]
61
+ when String
62
+ socket = Net::BufferedIO.new(options[:response])
63
+ r = Net::HTTPResponse.read_new(socket)
64
+
65
+ # Store the oiriginal transfer-encoding
66
+ saved_transfer_encoding = r.instance_eval {
67
+ @header['transfer-encoding'] if @header.key?('transfer-encoding')
68
+ }
69
+
70
+ # read the body of response.
71
+ r.instance_eval { @header['transfer-encoding'] = nil }
72
+ r.reading_body(socket, true) {}
73
+
74
+ # Delete the transfer-encoding key from r.@header if there wasn't one,
75
+ # else restore the saved_transfer_encoding.
76
+ if saved_transfer_encoding.nil?
77
+ r.instance_eval { @header.delete('transfer-encoding') }
78
+ else
79
+ r.instance_eval { @header['transfer-encoding'] = saved_transfer_encoding }
80
+ end
81
+ r
82
+ else raise StandardError, "Handler unimplemented for response #{options[:response]}"
83
+ end
84
+ end
85
+
86
+ def has_baked_response?
87
+ options.has_key?(:response)
88
+ end
89
+
90
+ def optionally_raise(response)
91
+ return unless options.has_key?(:exception)
92
+
93
+ case options[:exception].to_s
94
+ when "Net::HTTPError", "OpenURI::HTTPError"
95
+ raise options[:exception].new('Exception from FakeWeb', response)
96
+ else
97
+ raise options[:exception].new('Exception from FakeWeb')
98
+ end
99
+ end
100
+
101
+ def meta_information
102
+ options.has_key?(:status) ? options[:status] : [200, 'OK']
103
+ end
104
+
105
+ def params_for(request)
106
+ if request.method == 'GET'
107
+ query = URI.parse(request.path).query
108
+ decode_params(query)
109
+ else
110
+ decode_params(request.body)
111
+ end
112
+ end
113
+
114
+ def decode_params(encoded_params)
115
+ if encoded_params
116
+ UrlEncodedPairParser.new(URI.decode(encoded_params).split('&').map {|v| v.split('=')}).result
117
+ else
118
+ {}
119
+ end
120
+ end
121
+ end
122
+ end