http_monkey 0.0.1 → 0.0.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 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