http_monkey 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ ENV["RUBYOPT"] = "rubygems" if ENV["RUBYOPT"].nil?
5
5
 
6
6
  Rake::TestTask.new do |t|
7
7
  t.libs << "test"
8
- t.test_files = FileList['test/http_monkey/*_test.rb']
8
+ t.test_files = FileList['test/http_monkey/**/*_test.rb']
9
9
  end
10
10
 
11
11
  Rake::TestTask.new("test:integration") do |t|
data/Readme.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  A fluent interface to do HTTP calls, free of fat dependencies and at same time, powered by middlewares rack.
4
4
 
5
- It's an awesome client with an awful name.
6
-
7
5
  ## Light and powerful
8
6
 
9
7
  ``` ruby
@@ -44,9 +42,19 @@ It's an awesome client with an awful name.
44
42
  req.auth.ssl.ssl_version = :TLSv1 # or one of [:SSLv2, :SSLv3]
45
43
  end.get
46
44
 
47
- # Default HTTP Headers (to all requests)
45
+ # HttpMonkey "built-in" middlewares
48
46
  HttpMonkey.configure do
49
- middlewares.use HttpMonkey::Middlewares::DefaultHeaders, {"Content-Type" => "application/json"}
47
+ # Default HTTP Headers (to all requests)
48
+ middlewares.use HttpMonkey::M::DefaultHeaders, {"Content-Type" => "application/json"}
49
+
50
+ # Filter ALL requests (access to env and request objects)
51
+ middlewares.use HttpMonkey::M::RequestFilter do |env, request|
52
+ # HTTPI::Request, you can set proxy, timeouts, authentication etc.
53
+ # req.proxy = "http://proxy.com"
54
+ end
55
+
56
+ # Enable automatic follow redirect
57
+ middlewares.use HttpMonkey::M::FollowRedirect, :max_tries => 3
50
58
  end
51
59
  ```
52
60
 
@@ -156,12 +164,14 @@ Easy to extend, using the power of Rack middleware interface.
156
164
  end
157
165
  ```
158
166
 
159
- Some ideas:
167
+ Some ideas to future:
168
+
169
+ * Cache? Finish [cachebag](https://github.com/abril/cachebag)
170
+ * Support Cookies with http_monkey-cookies (to do).
171
+ * Logger? Maybe a built-in middleware? Apache/Custom format.
172
+ * Cool ways to support asynchronous calls.
160
173
 
161
- * Cache? [rack-cache](https://github.com/rtomayko/rack-cache)
162
- * Logger? [http://rack.rubyforge.org/doc/Rack/Logger.html]
163
- * Profile?
164
- * Support to specific Media Type?
174
+ You can see my [presentation](http://www.slideshare.net/rogerleite14/http-monkey) in *pt-br* at Editora Abril.
165
175
 
166
176
  ## Easy to contribute
167
177
 
@@ -24,4 +24,5 @@ Gem::Specification.new do |gem|
24
24
  gem.add_development_dependency "minitest", "~> 3"
25
25
  gem.add_development_dependency "minitest-reporters", "~> 0.7.0"
26
26
  gem.add_development_dependency "mocha"
27
+ gem.add_development_dependency "minion_server"
27
28
  end
@@ -33,15 +33,6 @@ module HttpMonkey
33
33
  @@default_client ||= build do
34
34
  net_adapter :net_http
35
35
  behaviours do
36
- # Follow redirects
37
- on([301, 302, 303, 307]) do |client, request, response|
38
- if (location = response.headers["location"])
39
- request.url = location
40
- client.http_request(:get, request)
41
- else
42
- raise "HTTP status #{response.code} not supported (or location not found)"
43
- end
44
- end
45
36
  # By default, always return response
46
37
  on_unknown do |client, request, response|
47
38
  response
@@ -1,23 +1,27 @@
1
1
  module HttpMonkey
2
2
 
3
- # Rack environment with helpers.
4
- class Client::Environment
3
+ # Rack environment on steroids! -_-
4
+ # This Hash with super powers is passed to Middlewares.
5
+ #
6
+ # "Snaky" middlewares always remember! "With great power comes great responsibility"
7
+ class Client::Environment < ::Hash
5
8
 
6
- def initialize(env)
7
- @env = env
9
+ def initialize(obj = nil, &block)
10
+ super
11
+ self.merge!(obj) unless obj.nil? # better idea? pull please.
12
+ self.default = nil
8
13
  end
9
14
 
10
- def [](key)
11
- @env[key]
12
- end
13
-
14
- def []=(key, value)
15
- @env[key] = value
16
- end
17
-
18
- # From {"HTTP_CONTENT_TYPE" => "text/html"} to {"Content-Type" => "text/html"}
15
+ # Extracts HTTP_ headers from rack environment.
16
+ #
17
+ # Example
18
+ #
19
+ # env = Client::Environment.new({"HTTP_X_CUSTOM" => "custom"})
20
+ # env.http_headers # => {"X-Custom" => "custom"}
21
+ #
22
+ # Returns Hash with normalized http headers.
19
23
  def http_headers
20
- req_headers = @env.reject {|k,v| !k.start_with? "HTTP_" }
24
+ req_headers = self.reject {|k,v| !k.start_with? "HTTP_" }
21
25
  normalized = req_headers.map do |key, value|
22
26
  new_key = key.sub("HTTP_",'').split('_').map(&:capitalize).join('-')
23
27
  [new_key, value]
@@ -25,6 +29,70 @@ module HttpMonkey
25
29
  Hash[normalized]
26
30
  end
27
31
 
32
+ # Sets HTTP header in rack standard way of life.
33
+ #
34
+ # Example
35
+ #
36
+ # env = Client::Environment.new
37
+ # env.add_http_header("Content-Type" => "text/html")
38
+ # env.inspect # => {"HTTP_CONTENT_TYPE" => "text/html"}
39
+ #
40
+ # Returns nothing important.
41
+ def add_http_header(headers = {})
42
+ headers.each do |key, value|
43
+ self["HTTP_#{key.to_s.upcase.gsub("-", "_")}"] = value
44
+ end
45
+ end
46
+
47
+ # Returns HTTPI::Request instance or nil.
48
+ def monkey_request
49
+ if (data = self['http_monkey.request'])
50
+ data[1]
51
+ end
52
+ end
53
+
54
+ # Returns HttpMonkey::Client instance or nil.
55
+ def monkey_client
56
+ if (data = self['http_monkey.request'])
57
+ data[2]
58
+ end
59
+ end
60
+
61
+ # Sets uri as Rack wants.
62
+ def uri=(uri)
63
+ self['SERVER_NAME'] = uri.host
64
+ self['SERVER_PORT'] = uri.port.to_s
65
+ self['QUERY_STRING'] = (uri.query || "")
66
+ self['PATH_INFO'] = (!uri.path || uri.path.empty?) ? "/" : uri.path
67
+ self['rack.url_scheme'] = uri.scheme
68
+ self['HTTPS'] = (uri.scheme == "https" ? "on" : "off")
69
+ self['REQUEST_URI'] = uri.request_uri
70
+ self['HTTP_HOST'] = uri.host
71
+ end
72
+
73
+ # Returns uri from Rack environment.
74
+ # Throws ArgumentError for invalid uri.
75
+ def uri
76
+ uri = %Q{#{self['rack.url_scheme']}://#{self['SERVER_NAME']}:#{self['SERVER_PORT']}#{self['REQUEST_URI']}}
77
+ begin
78
+ URI.parse(uri)
79
+ rescue StandardError => e
80
+ raise ArgumentError, "Invalid #{uri}", e.backtrace
81
+ end
82
+ end
83
+
84
+ # Returns normalized request method.
85
+ #
86
+ # Example
87
+ #
88
+ # env = Client::Environment.new('REQUEST_METHOD' => 'GET')
89
+ # env.request_method # => :get
90
+ #
91
+ def request_method
92
+ method = self['REQUEST_METHOD'].to_s
93
+ (method.empty? ? nil : method.downcase.to_sym)
94
+ end
95
+
28
96
  end
29
97
 
30
98
  end
@@ -11,6 +11,7 @@ module HttpMonkey
11
11
  "rack.multithread" => true,
12
12
  "rack.multiprocess" => false,
13
13
  "rack.run_once" => false,
14
+ 'SCRIPT_NAME' => "" # call me Suzy
14
15
  }
15
16
 
16
17
  def initialize(client, method, request)
@@ -19,41 +20,29 @@ module HttpMonkey
19
20
  @request = request
20
21
  end
21
22
 
23
+ # Returns a instance of HttpMonkey::Client::Environment with
24
+ # rack like headers.
22
25
  def to_env
23
26
  uri = @request.url
24
27
  rack_input = normalize_body(@request.body)
25
28
 
26
- env = DEFAULT_ENV.dup
27
- env = env.merge({
29
+ env = HttpMonkey::Client::Environment.new(DEFAULT_ENV)
30
+ env.uri = @request.url
31
+
32
+ env.update({
28
33
  # request info
29
34
  'REQUEST_METHOD' => @method.to_s.upcase,
30
- 'SERVER_NAME' => uri.host,
31
- 'SERVER_PORT' => (uri.port || uri.inferred_port).to_s,
32
- 'QUERY_STRING' => uri.query || "",
33
- 'PATH_INFO' => (!uri.path || uri.path.empty?) ? "/" : uri.path,
34
- 'rack.url_scheme' => uri.scheme,
35
- 'HTTPS' => (uri.scheme == "https" ? "on" : "off"),
36
- 'SCRIPT_NAME' => "", # call me Suzy
37
35
 
38
- 'REQUEST_URI' => uri.request_uri,
39
- 'HTTP_HOST' => uri.host,
40
36
  'rack.input' => rack_input,
41
37
  'CONTENT_LENGTH' => rack_input.length.to_s,
42
38
 
43
39
  # custom info
44
- 'http_monkey.request' => [@method, @request, @client.net_adapter]
45
- }).update(http_headers)
40
+ 'http_monkey.request' => [@method, @request, @client]
41
+ })
42
+ env.add_http_header(@request.headers)
46
43
  env
47
44
  end
48
45
 
49
- # From {"Content-Type" => "text/html"} to {"HTTP_CONTENT_TYPE" => "text/html"}
50
- def http_headers
51
- env_headers = @request.headers.map do |key, value|
52
- ["HTTP_#{key.to_s.upcase.gsub("-", "_")}", value]
53
- end
54
- Hash[env_headers]
55
- end
56
-
57
46
  def normalize_body(body)
58
47
  return "" if body.nil?
59
48
  input = body.dup
@@ -5,13 +5,15 @@ module HttpMonkey
5
5
  class Client::HttpRequest
6
6
 
7
7
  def self.call(env)
8
- env = Client::Environment.new(env)
9
- method, request, net_adapter = env['http_monkey.request']
8
+ env = Client::Environment.new(env) unless env.is_a?(Client::Environment)
9
+ _, request, client = env['http_monkey.request']
10
10
 
11
+ method = env.request_method
12
+ request.url = env.uri
11
13
  request.headers = env.http_headers
12
14
  request.body = env['rack.input']
13
15
 
14
- response = HTTPI.request(method, request, net_adapter)
16
+ response = HTTPI.request(method, request, client.net_adapter)
15
17
  [response.code, response.headers, response.body]
16
18
  end
17
19
 
@@ -1,5 +1,8 @@
1
1
  module HttpMonkey
2
2
  module Middlewares
3
3
  autoload :DefaultHeaders, "http_monkey/middlewares/default_headers"
4
+ autoload :RequestFilter, "http_monkey/middlewares/request_filter"
5
+ autoload :FollowRedirect, "http_monkey/middlewares/follow_redirect"
4
6
  end
7
+ M = Middlewares
5
8
  end
@@ -0,0 +1,41 @@
1
+ module HttpMonkey::Middlewares
2
+
3
+ # Follow Redirects
4
+ # For response codes [301, 302, 303, 307], follow Location header.
5
+ # If header not found or tries is bigger than +max_tries+, throw RuntimeError.
6
+ #
7
+ # Example
8
+ #
9
+ # HttpMonkey.configure do
10
+ # middlewares.use HttpMonkey::M::FollowRedirect, :max_tries => 5 # default: 3
11
+ # end
12
+ #
13
+ class FollowRedirect
14
+
15
+ InfiniteRedirectError = Class.new(StandardError)
16
+
17
+ def initialize(app, options = {})
18
+ @app = app
19
+ @max_tries = options.fetch(:max_tries, 3)
20
+ @follow_headers = options.fetch(:headers, [301, 302, 303, 307])
21
+ end
22
+
23
+ def call(env)
24
+ current_try = 0
25
+ begin
26
+ code, headers, body = @app.call(env)
27
+ break unless @follow_headers.include?(code)
28
+
29
+ location = (headers["Location"] || headers["location"])
30
+ raise RuntimeError, "HTTP status #{code}. No Location header." unless location
31
+
32
+ env.uri = URI.parse(location) # change uri and submit again
33
+ current_try += 1
34
+ raise RuntimeError, "Reached the maximum number of attempts in following url." if current_try > @max_tries
35
+ end while true
36
+ [code, headers, body]
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,33 @@
1
+ module HttpMonkey::Middlewares
2
+
3
+ # Intercept all requests
4
+ #
5
+ # Example
6
+ #
7
+ # HttpMonkey.configure do
8
+ # middlewares.use HttpMonkey::Middlewares::RequestFilter do |env, request|
9
+ # # HttpMonkey::Client::Environment, hash rack on steroids
10
+ # # You can use "snaky" methods like:
11
+ # # env.http_headers # => {"Content-Type" => "text/html"}
12
+ # # env.add_http_header("X-Custom" => "custom")
13
+ #
14
+ # # HTTPI::Request, you can set proxy, timeouts, authentication etc.
15
+ # # req.proxy = "http://example.com"
16
+ # end
17
+ # end
18
+ #
19
+ class RequestFilter
20
+
21
+ def initialize(app, &block)
22
+ @app = app
23
+ @block = (block || lambda {|env, req| })
24
+ end
25
+
26
+ def call(env)
27
+ @block.call(env, env.monkey_request)
28
+ @app.call(env)
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -1,3 +1,3 @@
1
1
  module HttpMonkey
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -4,25 +4,125 @@ describe HttpMonkey::Client::Environment do
4
4
 
5
5
  subject { HttpMonkey::Client::Environment }
6
6
 
7
- describe "#accessor" do
8
- it "gets like Hash" do
9
- env = subject.new(:test => "value")
10
- env[:test].must_equal("value")
7
+ it "must be kind of Hash" do
8
+ subject.new.must_be_kind_of(Hash)
9
+ end
10
+
11
+ describe "hash default" do
12
+ it "default non key be nil" do
13
+ env = subject.new("HTTP_CONTENT_TYPE" => "text/html",
14
+ "HTTP_X_CUSTOM" => "custom")
15
+ env["HTTP_X_CUSTOM"].must_equal("custom")
16
+ env["NON_EXIST"].must_be_nil
17
+ end
18
+ end
19
+
20
+ describe "#http_headers" do
21
+ it "empty env must return empty Hash" do
22
+ http_headers = subject.new.http_headers
23
+ http_headers.must_be_empty
24
+ end
25
+ it "must list HTTP Headers" do
26
+ env = subject.new("HTTP_CONTENT_TYPE" => "text/html",
27
+ "HTTP_X_CUSTOM" => "custom")
28
+
29
+ http_headers = env.http_headers
30
+ http_headers["Content-Type"].must_equal("text/html")
31
+ http_headers["X-Custom"].must_equal("custom")
32
+ end
33
+ end
34
+
35
+ it "#add_http_header" do
36
+ env = subject.new("HTTP_CONTENT_TYPE" => "text/html")
37
+
38
+ env.add_http_header("Content-Type" => "application/json",
39
+ "X-Custom" => "custom")
40
+ env["HTTP_CONTENT_TYPE"].must_equal("application/json")
41
+ env["HTTP_X_CUSTOM"].must_equal("custom")
42
+ end
43
+
44
+ describe "#monkey_request" do
45
+ it "with object" do
46
+ stub_req = stub("request")
47
+ env = subject.new("http_monkey.request" => [nil, stub_req, nil])
48
+ env.monkey_request.must_be_same_as(stub_req)
11
49
  end
12
- it "sets like Hash" do
13
- env = subject.new(:test => "value")
14
- env[:test] = "owned"
15
- env[:test].must_equal("owned")
50
+ it "without object" do
51
+ env = subject.new
52
+ env.monkey_request.must_be_nil
16
53
  end
17
54
  end
18
55
 
19
- it "#http_headers" do
20
- env = subject.new("HTTP_CONTENT_TYPE" => "text/html",
21
- "HTTP_X_CUSTOM" => "custom")
56
+ describe "#monkey_client" do
57
+ it "with object" do
58
+ stub_client = stub("request")
59
+ env = subject.new("http_monkey.request" => [nil, nil, stub_client])
60
+ env.monkey_client.must_be_same_as(stub_client)
61
+ end
62
+ it "without object" do
63
+ env = subject.new
64
+ env.monkey_client.must_be_nil
65
+ end
66
+ end
22
67
 
23
- env.http_headers.must_be_instance_of(Hash)
24
- env.http_headers["Content-Type"].must_equal("text/html")
25
- env.http_headers["X-Custom"].must_equal("custom")
68
+ describe "#uri=" do
69
+ it "complete url" do
70
+ env = subject.new
71
+ env.uri = URI.parse("http://localhost:3000/path?q=query")
72
+
73
+ env['SERVER_NAME'].must_equal("localhost")
74
+ env['SERVER_PORT'].must_equal("3000")
75
+ env['QUERY_STRING'].must_equal("q=query")
76
+ env['PATH_INFO'].must_equal("/path")
77
+ env['rack.url_scheme'].must_equal("http")
78
+ env['HTTPS'].must_equal("off")
79
+ env['REQUEST_URI'].must_equal("/path?q=query")
80
+ env['HTTP_HOST'].must_equal("localhost")
81
+ end
82
+ it "simple url" do
83
+ env = subject.new
84
+ env.uri = URI.parse("http://localhost")
85
+
86
+ env['SERVER_PORT'].must_equal("80")
87
+ env['QUERY_STRING'].must_equal("")
88
+ env['PATH_INFO'].must_equal("/")
89
+ end
90
+ it "https url" do
91
+ env = subject.new
92
+ env.uri = URI.parse("https://localhost:3000")
93
+
94
+ env['HTTPS'].must_equal("on")
95
+ end
96
+ end
97
+
98
+ describe "#uri" do
99
+ it "valid url" do
100
+ env = subject.new
101
+ env['SERVER_NAME'] = "localhost"
102
+ env['SERVER_PORT'] = "3000"
103
+ env['rack.url_scheme'] = "http"
104
+ env['REQUEST_URI'] = "/path?q=query"
105
+
106
+ uri = env.uri
107
+ uri.to_s.must_equal("http://localhost:3000/path?q=query")
108
+ end
109
+ it "invalid url" do
110
+ env = subject.new
111
+ lambda do
112
+ env.uri
113
+ end.must_raise(ArgumentError)
114
+ end
115
+ end
116
+
117
+ describe "#request_method" do
118
+ it "with value" do
119
+ env = subject.new('REQUEST_METHOD' => "GET")
120
+ env.request_method.must_equal(:get)
121
+ end
122
+ it "empty" do
123
+ env = subject.new
124
+ env.request_method.must_be_nil
125
+ end
26
126
  end
27
127
 
28
128
  end
@@ -7,7 +7,7 @@ describe HttpMonkey::Middlewares::DefaultHeaders do
7
7
  end
8
8
 
9
9
  let(:fake_env) { Hash.new }
10
- subject { HttpMonkey::Middlewares::DefaultHeaders }
10
+ subject { HttpMonkey::M::DefaultHeaders }
11
11
 
12
12
  it "always call app" do
13
13
  @mock_app.expects(:call).with(fake_env)
@@ -0,0 +1,7 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Middlewares::FollowRedirect do
4
+
5
+ it "tests on integration only"
6
+
7
+ end
@@ -0,0 +1,31 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Middlewares::RequestFilter do
4
+
5
+ include HttpMonkey::Support::FakeEnvironment
6
+
7
+ before do
8
+ @mock_app = stub("app", :call => "stubbed")
9
+ end
10
+
11
+ subject { HttpMonkey::M::RequestFilter }
12
+
13
+ it "always call app" do
14
+ @mock_app.expects(:call).with(fake_env)
15
+ subject.new(@mock_app).call(fake_env)
16
+ end
17
+
18
+ it "executes block" do
19
+ flag = "out block"
20
+
21
+ middle = subject.new(@mock_app) do |env, req|
22
+ env.must_be_same_as(fake_env)
23
+ req.must_be_same_as(fake_request)
24
+ flag = "inside block"
25
+ end
26
+ middle.call(fake_env)
27
+
28
+ flag.must_equal("inside block")
29
+ end
30
+
31
+ end
@@ -0,0 +1,7 @@
1
+ require "test_helper"
2
+
3
+ describe HttpMonkey::Middlewares do
4
+ it "alias to M" do
5
+ HttpMonkey::Middlewares.must_be_same_as(HttpMonkey::M)
6
+ end
7
+ end
@@ -0,0 +1,89 @@
1
+ require "test_helper"
2
+
3
+ class Counter
4
+ @@counter = 0
5
+ def self.value
6
+ @@counter
7
+ end
8
+ def self.increment
9
+ @@counter += 1
10
+ end
11
+ end
12
+
13
+ RedirectApp = Rack::Builder.new do
14
+ map "/" do
15
+ run lambda { |env|
16
+ [301, {"Content-Type" => "text/plain", "Location" => "http://localhost:4000/home"}, ["Redirecting ..."]]
17
+ }
18
+ end
19
+
20
+ map "/home" do
21
+ run lambda { |env|
22
+ [200, {"Content-Type" => "text/plain"}, ["home sweet home"]]
23
+ }
24
+ end
25
+
26
+ map "/redirect3times" do
27
+ run lambda { |e| [301, {"Location" => "http://localhost:4000/redirect2times"}, ["Should Redirect"]]}
28
+ end
29
+ map "/redirect2times" do
30
+ run lambda { |e| [301, {"Location" => "http://localhost:4000/redirect1times"}, ["Should Redirect"]]}
31
+ end
32
+ map "/redirect1times" do
33
+ run lambda { |e| [301, {"Location" => "http://localhost:4000/home"}, ["Should Redirect"]]}
34
+ end
35
+
36
+ map "/infinite-redirect" do
37
+ run lambda { |env|
38
+ Counter.increment
39
+ if Counter.value > 10
40
+ [200, {}, ["Give up, i can't blow yu machine"]]
41
+ else
42
+ [301, {"Location" => "http://localhost:4000/infinite-redirect"}, ["Stop with you can"]]
43
+ end
44
+ }
45
+ end
46
+
47
+ map "/no-location" do
48
+ run lambda { |env| [301, {}, ["no location header"]] }
49
+ end
50
+ end
51
+
52
+ describe "Integration Specs - Middleware Follow Redirects" do
53
+
54
+ def self.before_suite
55
+ @server = MinionServer.new(RedirectApp).start
56
+ end
57
+
58
+ def self.after_suite
59
+ @server.shutdown
60
+ end
61
+
62
+ let(:client) do
63
+ HttpMonkey.build do
64
+ middlewares.use HttpMonkey::M::FollowRedirect, :max_tries => 3
65
+ end
66
+ end
67
+
68
+ it "follow redirects" do
69
+ response = client.at("http://localhost:4000").get
70
+ response.body.must_equal("home sweet home")
71
+ end
72
+
73
+ it "follow redirects in chain" do
74
+ response = client.at("http://localhost:4000/redirect3times").get
75
+ response.body.must_equal("home sweet home")
76
+ end
77
+
78
+ it "respect max_tries" do
79
+ lambda {
80
+ response = client.at("http://localhost:4000/infinite-redirect").get
81
+ }.must_raise(RuntimeError)
82
+ end
83
+
84
+ it "raises exception in lack of Location" do
85
+ lambda {
86
+ response = client.at("http://localhost:4000/no-location").get
87
+ }.must_raise(RuntimeError)
88
+ end
89
+ end
@@ -1,7 +1,6 @@
1
- require "integration/server"
1
+ require "test_helper"
2
2
 
3
3
  VerbsApp = Rack::Builder.new do
4
- #use IntegrationServer::InspectEnv # i can see clear now the rain is gone ...
5
4
  map "/" do
6
5
  run lambda { |env|
7
6
  env['rack.input'] = env['rack.input'].read if env['rack.input'].respond_to?(:read)
@@ -11,25 +10,29 @@ VerbsApp = Rack::Builder.new do
11
10
  end
12
11
  end
13
12
 
14
- IntegrationServer.new(VerbsApp).start
15
-
16
13
  describe "Integration Specs - Verbs" do
17
14
 
18
- describe "#get" do
19
- it "no parameter" do
20
- response = HttpMonkey.at("http://localhost:4000").get
21
- server_env = YAML.load(response.body)
22
-
23
- server_env["REQUEST_METHOD"].must_equal("GET")
24
- server_env["QUERY_STRING"].must_be_empty
25
- end
26
- it "with parameters" do
27
- response = HttpMonkey.at("http://localhost:4000").get(:q => "query")
28
- server_env = YAML.load(response.body)
29
-
30
- server_env["REQUEST_METHOD"].must_equal("GET")
31
- server_env["QUERY_STRING"].must_equal("q=query")
32
- end
15
+ def self.before_suite
16
+ @@server = MinionServer.new(VerbsApp).start
17
+ end
18
+
19
+ def self.after_suite
20
+ @@server.shutdown
21
+ end
22
+
23
+ it "#get - no parameter" do
24
+ response = HttpMonkey.at("http://localhost:4000").get
25
+ server_env = YAML.load(response.body)
26
+
27
+ server_env["REQUEST_METHOD"].must_equal("GET")
28
+ server_env["QUERY_STRING"].must_be_empty
29
+ end
30
+ it "#get - with parameters" do
31
+ response = HttpMonkey.at("http://localhost:4000").get(:q => "query")
32
+ server_env = YAML.load(response.body)
33
+
34
+ server_env["REQUEST_METHOD"].must_equal("GET")
35
+ server_env["QUERY_STRING"].must_equal("q=query")
33
36
  end
34
37
 
35
38
  it "#post" do
@@ -0,0 +1,42 @@
1
+ module HttpMonkey::Support
2
+
3
+ # Add *before_suite* and *after_suite* on tests.
4
+ #
5
+ # Examples
6
+ #
7
+ # describe "Sample spec" do
8
+ #
9
+ # def self.before_suite
10
+ # puts "before all"
11
+ # end
12
+ # def self.after_suite
13
+ # puts "after all"
14
+ # end
15
+ #
16
+ # it "should test something"
17
+ # end
18
+ class CaptainHook
19
+
20
+ def before_suites(suites, type); end
21
+ def after_suites(suites, type); end
22
+
23
+ def before_suite(suite)
24
+ suite.before_suite if suite.respond_to?(:before_suite)
25
+ end
26
+ def after_suite(suite)
27
+ suite.after_suite if suite.respond_to?(:after_suite)
28
+ end
29
+
30
+ def before_test(suite, test); end
31
+ # methods "pass, skip, failure, error" should call this
32
+ #def after_test(suite, test, test_runner)
33
+ #end
34
+
35
+ def pass(suite, test, test_runner); end
36
+ def skip(suite, test, test_runner); end
37
+ def failure(suite, test, test_runner); end
38
+ def error(suite, test, test_runner); end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,22 @@
1
+ module HttpMonkey::Support
2
+
3
+ module FakeEnvironment
4
+
5
+ def fake_request
6
+ @fake_request ||= stub("request")
7
+ end
8
+
9
+ def fake_client
10
+ @fake_client ||= stub("client")
11
+ end
12
+
13
+ def fake_env
14
+ @fake_env ||= begin
15
+ env = {'http_monkey.request' => [nil, fake_request, fake_client]}
16
+ HttpMonkey::Client::Environment.new(env)
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -5,5 +5,11 @@ require "minitest/reporters"
5
5
 
6
6
  require "mocha"
7
7
 
8
+ require "support/fake_environment"
9
+ require "support/captain_hook"
10
+
8
11
  MiniTest::Unit.runner = MiniTest::SuiteRunner.new
9
12
  MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
13
+ MiniTest::Unit.runner.reporters << HttpMonkey::Support::CaptainHook.new
14
+
15
+ require "minion_server"
metadata CHANGED
@@ -1,119 +1,101 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: http_monkey
3
- version: !ruby/object:Gem::Version
4
- hash: 29
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 1
10
- version: 0.0.1
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Roger Leite
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-11-12 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-11-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: rack
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70184962553820 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 3
29
- segments:
30
- - 0
31
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
32
22
  type: :runtime
33
- version_requirements: *id001
34
- - !ruby/object:Gem::Dependency
35
- name: httpi
36
23
  prerelease: false
37
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70184962553820
25
+ - !ruby/object:Gem::Dependency
26
+ name: httpi
27
+ requirement: &70184962553260 !ruby/object:Gem::Requirement
38
28
  none: false
39
- requirements:
29
+ requirements:
40
30
  - - ~>
41
- - !ruby/object:Gem::Version
42
- hash: 13
43
- segments:
44
- - 1
45
- - 1
46
- version: "1.1"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.1'
47
33
  type: :runtime
48
- version_requirements: *id002
49
- - !ruby/object:Gem::Dependency
50
- name: rake
51
34
  prerelease: false
52
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *70184962553260
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70184962552740 !ruby/object:Gem::Requirement
53
39
  none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- hash: 3
58
- segments:
59
- - 0
60
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
61
44
  type: :development
62
- version_requirements: *id003
63
- - !ruby/object:Gem::Dependency
64
- name: minitest
65
45
  prerelease: false
66
- requirement: &id004 !ruby/object:Gem::Requirement
46
+ version_requirements: *70184962552740
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: &70184962552180 !ruby/object:Gem::Requirement
67
50
  none: false
68
- requirements:
51
+ requirements:
69
52
  - - ~>
70
- - !ruby/object:Gem::Version
71
- hash: 5
72
- segments:
73
- - 3
74
- version: "3"
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
75
55
  type: :development
76
- version_requirements: *id004
77
- - !ruby/object:Gem::Dependency
78
- name: minitest-reporters
79
56
  prerelease: false
80
- requirement: &id005 !ruby/object:Gem::Requirement
57
+ version_requirements: *70184962552180
58
+ - !ruby/object:Gem::Dependency
59
+ name: minitest-reporters
60
+ requirement: &70184962551620 !ruby/object:Gem::Requirement
81
61
  none: false
82
- requirements:
62
+ requirements:
83
63
  - - ~>
84
- - !ruby/object:Gem::Version
85
- hash: 3
86
- segments:
87
- - 0
88
- - 7
89
- - 0
64
+ - !ruby/object:Gem::Version
90
65
  version: 0.7.0
91
66
  type: :development
92
- version_requirements: *id005
93
- - !ruby/object:Gem::Dependency
67
+ prerelease: false
68
+ version_requirements: *70184962551620
69
+ - !ruby/object:Gem::Dependency
94
70
  name: mocha
71
+ requirement: &70184962551220 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
95
78
  prerelease: false
96
- requirement: &id006 !ruby/object:Gem::Requirement
79
+ version_requirements: *70184962551220
80
+ - !ruby/object:Gem::Dependency
81
+ name: minion_server
82
+ requirement: &70184962550760 !ruby/object:Gem::Requirement
97
83
  none: false
98
- requirements:
99
- - - ">="
100
- - !ruby/object:Gem::Version
101
- hash: 3
102
- segments:
103
- - 0
104
- version: "0"
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
105
88
  type: :development
106
- version_requirements: *id006
107
- description: A fluent interface to do HTTP calls, free of fat dependencies and at same time, powered by middlewares rack.
108
- email:
89
+ prerelease: false
90
+ version_requirements: *70184962550760
91
+ description: A fluent interface to do HTTP calls, free of fat dependencies and at
92
+ same time, powered by middlewares rack.
93
+ email:
109
94
  - roger.barreto@gmail.com
110
95
  executables: []
111
-
112
96
  extensions: []
113
-
114
97
  extra_rdoc_files: []
115
-
116
- files:
98
+ files:
117
99
  - .gitignore
118
100
  - .travis.yml
119
101
  - Gemfile
@@ -132,6 +114,8 @@ files:
132
114
  - lib/http_monkey/entry_point.rb
133
115
  - lib/http_monkey/middlewares.rb
134
116
  - lib/http_monkey/middlewares/default_headers.rb
117
+ - lib/http_monkey/middlewares/follow_redirect.rb
118
+ - lib/http_monkey/middlewares/request_filter.rb
135
119
  - lib/http_monkey/version.rb
136
120
  - test/http_monkey/client/environment_test.rb
137
121
  - test/http_monkey/client_test.rb
@@ -141,43 +125,40 @@ files:
141
125
  - test/http_monkey/entry_point_test.rb
142
126
  - test/http_monkey/http_monkey_test.rb
143
127
  - test/http_monkey/middlewares/default_headers_test.rb
144
- - test/integration/server.rb
128
+ - test/http_monkey/middlewares/follow_redirect_test.rb
129
+ - test/http_monkey/middlewares/request_filter_test.rb
130
+ - test/http_monkey/middlewares_test.rb
131
+ - test/integration/follow_redirect_test.rb
145
132
  - test/integration/verbs_test.rb
133
+ - test/support/captain_hook.rb
134
+ - test/support/fake_environment.rb
146
135
  - test/test_helper.rb
147
136
  homepage: https://github.com/rogerleite/http_monkey
148
137
  licenses: []
149
-
150
138
  post_install_message:
151
139
  rdoc_options: []
152
-
153
- require_paths:
140
+ require_paths:
154
141
  - lib
155
- required_ruby_version: !ruby/object:Gem::Requirement
142
+ required_ruby_version: !ruby/object:Gem::Requirement
156
143
  none: false
157
- requirements:
158
- - - ">="
159
- - !ruby/object:Gem::Version
160
- hash: 3
161
- segments:
162
- - 0
163
- version: "0"
164
- required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
149
  none: false
166
- requirements:
167
- - - ">="
168
- - !ruby/object:Gem::Version
169
- hash: 3
170
- segments:
171
- - 0
172
- version: "0"
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
173
154
  requirements: []
174
-
175
155
  rubyforge_project:
176
156
  rubygems_version: 1.8.15
177
157
  signing_key:
178
158
  specification_version: 3
179
- summary: A fluent interface to do HTTP calls, free of fat dependencies and at same time, powered by middlewares rack.
180
- test_files:
159
+ summary: A fluent interface to do HTTP calls, free of fat dependencies and at same
160
+ time, powered by middlewares rack.
161
+ test_files:
181
162
  - test/http_monkey/client/environment_test.rb
182
163
  - test/http_monkey/client_test.rb
183
164
  - test/http_monkey/configuration/behaviours_test.rb
@@ -186,6 +167,11 @@ test_files:
186
167
  - test/http_monkey/entry_point_test.rb
187
168
  - test/http_monkey/http_monkey_test.rb
188
169
  - test/http_monkey/middlewares/default_headers_test.rb
189
- - test/integration/server.rb
170
+ - test/http_monkey/middlewares/follow_redirect_test.rb
171
+ - test/http_monkey/middlewares/request_filter_test.rb
172
+ - test/http_monkey/middlewares_test.rb
173
+ - test/integration/follow_redirect_test.rb
190
174
  - test/integration/verbs_test.rb
175
+ - test/support/captain_hook.rb
176
+ - test/support/fake_environment.rb
191
177
  - test/test_helper.rb
@@ -1,73 +0,0 @@
1
- require "test_helper"
2
-
3
- # Inspiration from https://github.com/djanowski/mock-server
4
- class IntegrationServer
5
-
6
- def initialize(app)
7
- @app = app
8
- end
9
-
10
- def start(host = "localhost", port = 4000)
11
- Thread.new do
12
- silence_output do # comment this if you want information
13
- Rack::Server.start(
14
- :app => @app,
15
- :server => 'webrick',
16
- :environment => :none,
17
- :daemonize => false,
18
- :Host => host,
19
- :Port => port
20
- )
21
- end
22
- end
23
- wait_for_service(host, port)
24
- true
25
- end
26
-
27
- protected
28
-
29
- # quick and dirty
30
- def silence_output
31
- $stdout = File.new('/dev/null', 'w')
32
- $stderr = File.new('/dev/null', 'w')
33
- yield
34
- ensure
35
- $stdout = STDOUT
36
- $stderr = STDERR
37
- end
38
-
39
- def listening?(host, port)
40
- begin
41
- socket = TCPSocket.new(host, port)
42
- socket.close unless socket.nil?
43
- true
44
- rescue Errno::ECONNREFUSED,
45
- Errno::EBADF, # Windows
46
- Errno::EADDRNOTAVAIL # Windows
47
- false
48
- end
49
- end
50
-
51
- def wait_for_service(host, port, timeout = 5)
52
- start_time = Time.now
53
-
54
- until listening?(host, port)
55
- if timeout && (Time.now > (start_time + timeout))
56
- raise SocketError.new("Socket did not open within #{timeout} seconds")
57
- end
58
- end
59
-
60
- true
61
- end
62
-
63
- end
64
-
65
- class IntegrationServer::InspectEnv
66
- def initialize(app)
67
- @app = app
68
- end
69
- def call(env)
70
- puts "-> #{env.inspect}"
71
- @app.call(env)
72
- end
73
- end