rubyist-fakeweb 1.2.2

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 = 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