sandro-fakeweb 1.2.5

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.
@@ -0,0 +1,78 @@
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 = FileList["test/**/*.rb"].exclude("test/test_helper.rb")
19
+ test.verbose = false
20
+ test.warning = true
21
+ end
22
+
23
+ desc "Generate Documentation"
24
+ RDoc::Task.new do |rdoc|
25
+ rdoc.main = "README.rdoc"
26
+ rdoc.rdoc_dir = "doc"
27
+ rdoc.rdoc_files.include("README.rdoc", "CHANGELOG", "LICENSE.txt", "lib/*.rb")
28
+ rdoc.title = "FakeWeb API Documentation"
29
+ rdoc.options << '--line-numbers' << '--charset' << 'utf-8'
30
+ end
31
+
32
+ desc %{Update ".manifest" with the latest list of project filenames. Respect\
33
+ .gitignore by excluding everything that git ignores. Update `files` and\
34
+ `test_files` arrays in "*.gemspec" file if it's present.}
35
+ task :manifest do
36
+ list = Dir['**/*'].sort
37
+ spec_file = Dir['*.gemspec'].first
38
+ list -= [spec_file] if spec_file
39
+
40
+ File.read('.gitignore').each_line do |glob|
41
+ glob = glob.chomp.sub(/^\//, '')
42
+ list -= Dir[glob]
43
+ list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
44
+ puts "excluding #{glob}"
45
+ end
46
+
47
+ if spec_file
48
+ spec = File.read spec_file
49
+ spec.gsub!(/^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx) do
50
+ assignment = $1
51
+ bunch = $2 ? list.grep(/^test\//) : list
52
+ '%s%%w(%s)' % [assignment, bunch.join(' ')]
53
+ end
54
+
55
+ File.open(spec_file, 'w') {|f| f << spec }
56
+ end
57
+ File.open('.manifest', 'w') {|f| f << list.join("\n") }
58
+ end
59
+
60
+ if RUBY_PLATFORM =~ /java/
61
+ puts "rcov support disabled (running under JRuby)."
62
+ elsif RUBY_VERSION =~ /^1\.9/
63
+ puts "rcov support disabled (running under Ruby 1.9)"
64
+ else
65
+ require 'rcov/rcovtask'
66
+ Rcov::RcovTask.new do |t|
67
+ t.test_files = FileList["test/**/*.rb"].exclude("test/test_helper.rb")
68
+ t.rcov_opts << "--sort coverage"
69
+ t.rcov_opts << "--exclude gems"
70
+ t.warning = true
71
+ end
72
+ end
73
+
74
+ spec = eval(File.read(File.join(File.dirname(__FILE__), "fakeweb.gemspec")))
75
+ Rake::GemPackageTask.new(spec) do |pkg|
76
+ pkg.need_tar_gz = true
77
+ pkg.need_zip = true
78
+ end
@@ -0,0 +1,204 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+
4
+ require 'fake_web/ext/net_http'
5
+ require 'fake_web/registry'
6
+ require 'fake_web/response'
7
+ require 'fake_web/responder'
8
+ require 'fake_web/stub_socket'
9
+ require 'fake_web/utility'
10
+ require 'fake_web/fixture'
11
+
12
+ module FakeWeb
13
+
14
+ # Resets the FakeWeb Registry. This will force all subsequent web requests to
15
+ # behave as real requests.
16
+ def self.clean_registry
17
+ Registry.instance.clean_registry
18
+ end
19
+
20
+ # Enables or disables real HTTP connections for requests that don't match
21
+ # registered URIs.
22
+ #
23
+ # If you set <tt>FakeWeb.allow_net_connect = false</tt> and subsequently try
24
+ # to make a request to a URI you haven't registered with #register_uri, a
25
+ # NetConnectNotAllowedError will be raised. This is handy when you want to
26
+ # make sure your tests are self-contained, or want to catch the scenario
27
+ # when a URI is changed in implementation code without a corresponding test
28
+ # change.
29
+ #
30
+ # When <tt>FakeWeb.allow_net_connect = true</tt> (the default), requests to
31
+ # URIs not stubbed with FakeWeb are passed through to Net::HTTP.
32
+ def self.allow_net_connect=(allowed)
33
+ @allow_net_connect = allowed
34
+ end
35
+
36
+ # Enable pass-through to Net::HTTP by default.
37
+ self.allow_net_connect = true
38
+
39
+ # Returns +true+ if requests to URIs not registered with FakeWeb are passed
40
+ # through to Net::HTTP for normal processing (the default). Returns +false+
41
+ # if an exception is raised for these requests.
42
+ def self.allow_net_connect?
43
+ @allow_net_connect
44
+ end
45
+
46
+ class << self
47
+ attr_reader :fixture_path
48
+ alias generate_fixtures? fixture_path
49
+
50
+ # Puts FakeWeb into generate_fixtures? mode wherein all responses are
51
+ # are saved into the provided directory +path+.
52
+ # You can later load the saved fixtures with +register_fixtures+
53
+ def generate_fixtures(path)
54
+ self.allow_net_connect = true
55
+ raise RuntimeError, "The path: #{path} is not a directory" unless File.directory? path
56
+ @fixture_path = path
57
+ end
58
+
59
+ # Loads the *.fixture files in the provided +path+ and registers the url
60
+ # and response objects for each fixture.
61
+ # Stops fixture generation such that FakeWeb.generate_fixtures? returns
62
+ # false
63
+ def register_fixtures(path)
64
+ stop_generating_fixtures!
65
+ Fixture.register(path)
66
+ end
67
+
68
+ # Disables fixture generation such that FakeWeb.generate_fixtures?
69
+ # returns false.
70
+ def stop_generating_fixtures!
71
+ @fixture_path = nil
72
+ end
73
+ end
74
+
75
+ # This exception is raised if you set <tt>FakeWeb.allow_net_connect =
76
+ # false</tt> and subsequently try to make a request to a URI you haven't
77
+ # stubbed.
78
+ class NetConnectNotAllowedError < StandardError; end;
79
+
80
+ # This exception is raised if a Net::HTTP request matches more than one of
81
+ # the stubs you've registered. To fix the problem, remove a duplicate
82
+ # registration or disambiguate any regular expressions by making them more
83
+ # specific.
84
+ class MultipleMatchingURIsError < StandardError; end;
85
+
86
+ # call-seq:
87
+ # FakeWeb.register_uri(method, uri, options)
88
+ #
89
+ # Register requests using the HTTP method specified by the symbol +method+
90
+ # for +uri+ to be handled according to +options+. If you specify the method
91
+ # <tt>:any</tt>, the response will be reigstered for any request for +uri+.
92
+ # +uri+ can be a +String+, +URI+, or +Regexp+ object. +options+ must be either
93
+ # a +Hash+ or an +Array+ of +Hashes+ (see below), which must contain one of
94
+ # these two keys:
95
+ #
96
+ # <tt>:body</tt>::
97
+ # A string which is used as the body of the response. If the string refers
98
+ # to a valid filesystem path, the contents of that file will be read and used
99
+ # as the body of the response instead. (This used to be two options,
100
+ # <tt>:string</tt> and <tt>:file</tt>, respectively. These are now deprecated.)
101
+ # <tt>:response</tt>::
102
+ # Either an <tt>Net::HTTPResponse</tt>, an +IO+, or a +String+ which is used
103
+ # as the full response for the request.
104
+ #
105
+ # The easier way by far is to pass the <tt>:response</tt> option to
106
+ # +register_uri+ as a +String+ or an (open for reads) +IO+ object which
107
+ # will be used as the complete HTTP response, including headers and body.
108
+ # If the string points to a readable file, this file will be used as the
109
+ # content for the request.
110
+ #
111
+ # To obtain a complete response document, you can use the +curl+ command,
112
+ # like so:
113
+ #
114
+ # curl -i http://www.example.com/ > response_for_www.example.com
115
+ #
116
+ # which can then be used in your test environment like so:
117
+ #
118
+ # FakeWeb.register_uri(:get, 'http://www.example.com/', :response => 'response_for_www.example.com')
119
+ #
120
+ # See the <tt>Net::HTTPResponse</tt>
121
+ # documentation[http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/classes/Net/HTTPResponse.html]
122
+ # for more information on creating custom response objects.
123
+ #
124
+ # +options+ may also be an +Array+ containing a list of the above-described
125
+ # +Hash+. In this case, FakeWeb will rotate through each provided response,
126
+ # you may optionally provide:
127
+ #
128
+ # <tt>:times</tt>::
129
+ # The number of times this response will be used. Decremented by one each time it's called.
130
+ # FakeWeb will use the final provided request indefinitely, regardless of its :times parameter.
131
+ #
132
+ # Two optional arguments are also accepted:
133
+ #
134
+ # <tt>:status</tt>::
135
+ # Passing <tt>:status</tt> as a two-value array will set the response code
136
+ # and message. The defaults are <tt>200</tt> and <tt>OK</tt>, respectively.
137
+ # Example:
138
+ # FakeWeb.register_uri("http://www.example.com/", :body => "Go away!", :status => [404, "Not Found"])
139
+ # <tt>:exception</tt>::
140
+ # The argument passed via <tt>:exception</tt> will be raised when the
141
+ # specified URL is requested. Any +Exception+ class is valid. Example:
142
+ # FakeWeb.register_uri('http://www.example.com/', :exception => Net::HTTPError)
143
+ #
144
+ # If you're using the <tt>:body</tt> response type, you can pass additional
145
+ # options to specify the HTTP headers to be used in the response. Example:
146
+ #
147
+ # FakeWeb.register_uri(:get, "http://example.com/index.txt", :body => "Hello", :content_type => "text/plain")
148
+ def self.register_uri(*args)
149
+ case args.length
150
+ when 3
151
+ Registry.instance.register_uri(*args)
152
+ when 2
153
+ print_missing_http_method_deprecation_warning(*args)
154
+ Registry.instance.register_uri(:any, *args)
155
+ else
156
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 3)")
157
+ end
158
+ end
159
+
160
+ # call-seq:
161
+ # FakeWeb.response_for(method, uri)
162
+ #
163
+ # Returns the faked Net::HTTPResponse object associated with +method+ and +uri+.
164
+ def self.response_for(*args, &block) #:nodoc: :yields: response
165
+ case args.length
166
+ when 2
167
+ Registry.instance.response_for(*args, &block)
168
+ when 1
169
+ print_missing_http_method_deprecation_warning(*args)
170
+ Registry.instance.response_for(:any, *args, &block)
171
+ else
172
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
173
+ end
174
+ end
175
+
176
+ # call-seq:
177
+ # FakeWeb.registered_uri?(method, uri)
178
+ #
179
+ # Returns true if a +method+ request for +uri+ is registered with FakeWeb.
180
+ # Specify a method of <tt>:any</tt> to check for against all HTTP methods.
181
+ def self.registered_uri?(*args)
182
+ case args.length
183
+ when 2
184
+ Registry.instance.registered_uri?(*args)
185
+ when 1
186
+ print_missing_http_method_deprecation_warning(*args)
187
+ Registry.instance.registered_uri?(:any, *args)
188
+ else
189
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 2)")
190
+ end
191
+ end
192
+
193
+ private
194
+
195
+ def self.print_missing_http_method_deprecation_warning(*args)
196
+ method = caller.first.match(/`(.*?)'/)[1]
197
+ new_args = args.map { |a| a.inspect }.unshift(":any")
198
+ new_args.last.gsub!(/^\{|\}$/, "").gsub!("=>", " => ") if args.last.is_a?(Hash)
199
+ $stderr.puts
200
+ $stderr.puts "Deprecation warning: FakeWeb requires an HTTP method argument (or use :any). Try this:"
201
+ $stderr.puts " FakeWeb.#{method}(#{new_args.join(', ')})"
202
+ $stderr.puts "Called at #{caller[1]}"
203
+ end
204
+ end
@@ -0,0 +1,76 @@
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
+ if FakeWeb.registered_uri?(method, uri)
54
+ @socket = Net::HTTP.socket_type.new
55
+ FakeWeb.response_for(method, uri, &block)
56
+ elsif FakeWeb.allow_net_connect?
57
+ connect_without_fakeweb
58
+ response = request_without_fakeweb(request, body, &block)
59
+ if path = FakeWeb.generate_fixtures?
60
+ fixture_response = block_given? ? request_without_fakeweb(request, body) : response
61
+ FakeWeb::Fixture.new(path, method, uri, fixture_response).save
62
+ end
63
+ response
64
+ else
65
+ uri = FakeWeb::Utility.strip_default_port_from_uri(uri)
66
+ raise FakeWeb::NetConnectNotAllowedError,
67
+ "Real HTTP connections are disabled. Unregistered request: #{request.method} #{uri}"
68
+ end
69
+ end
70
+
71
+ alias connect_without_fakeweb connect
72
+ def connect
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,60 @@
1
+ module FakeWeb
2
+ class Fixture
3
+ attr_reader :path, :method, :uri, :response
4
+
5
+ def self.register(path)
6
+ Dir.glob("#{path}/*.fixture") do |name|
7
+ fixture = YAML.load_file name
8
+ fixture.register
9
+ end
10
+ end
11
+
12
+ def initialize(path, method, uri, response)
13
+ @path = path
14
+ @method = method
15
+ @uri = uri
16
+ @response = response
17
+ end
18
+
19
+ def file_name
20
+ @file_name ||= generate_file_name
21
+ end
22
+
23
+ def register
24
+ FakeWeb.register_uri(method, uri, :response => response)
25
+ end
26
+
27
+ def save
28
+ Dir.chdir path do
29
+ File.open(file_name, 'w') do |f|
30
+ f.write self.to_yaml
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def base_file_name
38
+ u = URI.parse(uri)
39
+ path = u.path.gsub(/^\/$/, '').gsub(/\/$/, '').gsub('/', '-')
40
+ "#{method.to_s.upcase}_#{u.host}#{path}"
41
+ end
42
+
43
+ def generate_file_name
44
+ name = base_file_name + ".fixture"
45
+ name = next_unique_file_name(name) if File.exists?(File.join(path, name))
46
+ name
47
+ end
48
+
49
+ def next_unique_file_name(name)
50
+ path = "."
51
+ count = 2
52
+ ext = File.extname(name)
53
+ while File.exists?(File.join(path, name)) do
54
+ name = "#{File.basename(name, ext).gsub(/_\d$/, '')}_#{count}#{ext}"
55
+ count += 1
56
+ end
57
+ name
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,127 @@
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
+ 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
+ !responses_for(method, uri).empty?
23
+ end
24
+
25
+ def response_for(method, uri, &block)
26
+ responses = responses_for(method, uri)
27
+ return nil if responses.empty?
28
+
29
+ next_response = responses.last
30
+ responses.each do |response|
31
+ if response.times and response.times > 0
32
+ response.times -= 1
33
+ next_response = response
34
+ break
35
+ end
36
+ end
37
+
38
+ next_response.response(&block)
39
+ end
40
+
41
+
42
+ private
43
+
44
+ def responses_for(method, uri)
45
+ uri = normalize_uri(uri)
46
+
47
+ uri_map_matches(method, uri, URI) ||
48
+ uri_map_matches(:any, uri, URI) ||
49
+ uri_map_matches(method, uri, Regexp) ||
50
+ uri_map_matches(:any, uri, Regexp) ||
51
+ []
52
+ end
53
+
54
+ def uri_map_matches(method, uri, type_to_check = URI)
55
+ uris_to_check = variations_of_uri_as_strings(uri)
56
+
57
+ matches = uri_map.select { |registered_uri, method_hash|
58
+ registered_uri.is_a?(type_to_check) && method_hash.has_key?(method)
59
+ }.select { |registered_uri, method_hash|
60
+ if type_to_check == URI
61
+ uris_to_check.include?(registered_uri.to_s)
62
+ elsif type_to_check == Regexp
63
+ uris_to_check.any? { |u| u.match(registered_uri) }
64
+ end
65
+ }
66
+
67
+ if matches.size > 1
68
+ raise MultipleMatchingURIsError,
69
+ "More than one registered URI matched this request: #{method.to_s.upcase} #{uri}"
70
+ end
71
+
72
+ matches.map { |_, method_hash| method_hash[method] }.first
73
+ end
74
+
75
+
76
+ def variations_of_uri_as_strings(uri_object)
77
+ uris = []
78
+ normalized_uri = normalize_uri(uri_object)
79
+
80
+ # all orderings of query parameters
81
+ query = normalized_uri.query
82
+ if query.nil? || query.empty?
83
+ uris << normalized_uri
84
+ else
85
+ FakeWeb::Utility.simple_array_permutation(query.split('&')) do |p|
86
+ current_permutation = normalized_uri.dup
87
+ current_permutation.query = p.join('&')
88
+ uris << current_permutation
89
+ end
90
+ end
91
+
92
+ uri_strings = uris.map { |uri| uri.to_s }
93
+
94
+ # including and omitting the default port
95
+ if normalized_uri.default_port == normalized_uri.port
96
+ uri_strings += uris.map { |uri|
97
+ uri.to_s.sub(/#{Regexp.escape(normalized_uri.request_uri)}$/,
98
+ ":#{normalized_uri.port}#{normalized_uri.request_uri}")
99
+ }
100
+ end
101
+
102
+ uri_strings
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