myronmarston-rack-client 0.2.4

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,5 @@
1
+ == 0.1.0 / 2009-06-14
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Tim Carey-Smith
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,70 @@
1
+ h1. What's this?
2
+
3
+ Rack::Client is an HTTP client that aims to be a good Rack
4
+ citizen.
5
+
6
+ h1. Install
7
+
8
+ To install the latest release as a gem:
9
+ <pre>sudo gem install rack-client</pre>
10
+ Then in Ruby:
11
+ <pre>require "rubygems"; require "rack/client" # and you're off!</pre>
12
+
13
+ h2. Rack responses
14
+
15
+ Rack::Client can be used to make HTTP requests to any type of server, not
16
+ just ones using Rack. However, when a request is made then a proper
17
+ Rack response (specifically a Rack::MockResponse) object is returned.
18
+ For Rubyists, this means you don't need to learn yet another interface
19
+ and can just stick with Rack both on the server, test, and client side of
20
+ things.
21
+
22
+ <pre>
23
+ response = Rack::Client.get("http://some-website.com/blah.txt")
24
+ response.code #=> 200
25
+ response.body #=> "some body"
26
+ </pre>
27
+
28
+ h2. Middleware
29
+
30
+ Rack::Client is actually a subclass of Rack::Builder. This means that
31
+ Rack::Client objects yield actual Rack apps. More importantly, this
32
+ means you can reuse existing Rack middleware on the client side too
33
+ (but also feel free to make new middleware that only makes sense on
34
+ the client side under the Rack::Client namespace). Note that by default
35
+ Rack::Client will "run" Rack::Client::HTTP as an endpoint, but this
36
+ will not be performed if you specify your own "run" endpoint.
37
+
38
+ <pre>
39
+ client = Rack::Client.new { use Rack::ETag }
40
+ response = client.get("http://localhost:9292/no-etag")
41
+ </pre>
42
+
43
+ h2. Rack::Test compatibility
44
+
45
+ Rack::Client reuses a lot of Rack::Test to provide users with a
46
+ familiar interface. What's even cooler is that you can use a
47
+ Rack::Client object as your "app" in Rack::Test. This means that you
48
+ can test-drive an application with Rack::Test, then when ready
49
+ actually run your Rack app, switch your Rack::Test "app" to a
50
+ Rack::Client, and get free full-blown integration testing! Note that
51
+ the integration-tested server does not need to be all-Rack, so you can
52
+ develop quickly with middleware like Rack::Cache but then remove it
53
+ and integration test with a dedicated cache server like Varnish.
54
+
55
+ <pre>
56
+ # NOTE: For a complete example, look in the "demo" directory
57
+ describe Demo, "/store resource" do
58
+ include Rack::Test::Methods
59
+ def app
60
+ # replace this with Rack::Client.new
61
+ # for integration testing
62
+ Demo::App.new
63
+ end
64
+ # ... etc
65
+ end
66
+ </pre>
67
+
68
+ h1. Contributors
69
+
70
+ halorgium, larrytheliquid, benburkert
@@ -0,0 +1,41 @@
1
+ require 'rake'
2
+ require "rake/gempackagetask"
3
+ require "rake/clean"
4
+ require "spec/rake/spectask"
5
+ require File.expand_path("./lib/rack/client")
6
+
7
+ Spec::Rake::SpecTask.new(:spec) do |t|
8
+ t.spec_files = FileList['spec/*_spec.rb']
9
+ t.spec_opts = ['-c']
10
+ end
11
+
12
+ task :default => :spec
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = "myronmarston-rack-client"
16
+ s.rubyforge_project = s.name
17
+ s.version = Rack::Client::VERSION
18
+ s.author = "Tim Carey-Smith"
19
+ s.email = "tim" + "@" + "spork.in"
20
+ s.homepage = "http://github.com/halorgium/rack-client"
21
+ s.summary = "A client wrapper around a Rack app or HTTP"
22
+ s.description = s.summary
23
+ s.files = %w[History.txt LICENSE README.textile Rakefile] + Dir["lib/**/*"] + Dir["demo/**/*"]
24
+ s.test_files = Dir["spec/**/*"]
25
+
26
+ require 'bundler'
27
+ manifest = Bundler.setup
28
+ manifest.dependencies_for(:default).each do |d|
29
+ s.add_dependency(d.name, d.version_requirement)
30
+ end
31
+ end
32
+
33
+ Rake::GemPackageTask.new(spec) do |package|
34
+ package.gem_spec = spec
35
+ end
36
+
37
+ desc 'Install the package as a gem.'
38
+ task :install => [:clean, :package] do
39
+ gem = Dir['pkg/*.gem'].first
40
+ sh "sudo gem install --no-rdoc --no-ri --local #{gem}"
41
+ end
@@ -0,0 +1,26 @@
1
+ require "rubygems"
2
+ require "rack/client"
3
+ require "rack/contrib"
4
+
5
+ puts "PUT'ing /store/fruit (with strawberry)"
6
+ puts
7
+ Rack::Client.put "http://localhost:9292/store/fruit", "strawberry"
8
+
9
+ puts "GET'ing /store/fruit"
10
+ response = Rack::Client.get "http://localhost:9292/store/fruit"
11
+ puts ">> status: #{response.status}"
12
+ puts ">> body: #{response.body.inspect}"
13
+ puts ">> etag: #{response.headers["ETag"].inspect}"
14
+ puts
15
+
16
+ puts "GET'ing /store/fruit (with ETag middleware)"
17
+ response = Rack::Client.new do
18
+ use Rack::ETag
19
+ end.get "http://localhost:9292/store/fruit"
20
+ puts ">> status: #{response.status}"
21
+ puts ">> body: #{response.body.inspect}"
22
+ puts ">> etag: #{response.headers["ETag"].inspect}"
23
+ puts
24
+
25
+ puts "DELETE'ing /store"
26
+ Rack::Client.delete("http://localhost:9292/store")
@@ -0,0 +1,3 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/demo")
2
+
3
+ run Demo::App
@@ -0,0 +1,31 @@
1
+ require "rubygems"
2
+ require "rack"
3
+ require "sinatra/base"
4
+
5
+ module Demo
6
+ Store = Hash.new
7
+
8
+ class App < Sinatra::Base
9
+ get "/store/:id" do
10
+ if item = Store[ params[:id] ]
11
+ item
12
+ else
13
+ status 404
14
+ ""
15
+ end
16
+ end
17
+
18
+ put "/store/:id" do
19
+ Store[ params[:id] ] = request.body.read
20
+ end
21
+
22
+ delete "/store" do
23
+ Store.clear
24
+ ""
25
+ end
26
+
27
+ delete "/store/:id" do
28
+ Store.delete params[:id]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/demo")
2
+ require "rubygems"
3
+ require "spec"
4
+ require "rack"
5
+ require "rack/test"
6
+ require "rack/client"
7
+
8
+ describe Demo, "/store resource" do
9
+ include Rack::Test::Methods
10
+ def app
11
+ # Be sure to run "rackup" on the config.ru in this demo directory
12
+ Rack::Client.new
13
+ # Demo::App.new
14
+ end
15
+ before(:all) { delete "http://localhost:9292/store" }
16
+ after { delete "http://localhost:9292/store" }
17
+
18
+ it "should return a 404 if a resource does not exist" do
19
+ get "http://localhost:9292/store/does-not-exist"
20
+ last_response.status.should == 404
21
+ last_response.body.should be_empty
22
+ end
23
+
24
+ it "should be able to store and retrieve invididual items" do
25
+ put "http://localhost:9292/store/fruit", "strawberry"
26
+ put "http://localhost:9292/store/car", "lotus"
27
+ get "http://localhost:9292/store/fruit"
28
+ last_response.status.should == 200
29
+ last_response.body.should == "strawberry"
30
+ get "http://localhost:9292/store/car"
31
+ last_response.status.should == 200
32
+ last_response.body.should == "lotus"
33
+ end
34
+
35
+ it "should be able to clear the store of all items" do
36
+ put "http://localhost:9292/store/fruit", "strawberry"
37
+ put "http://localhost:9292/store/car", "lotus"
38
+ delete "http://localhost:9292/store"
39
+ get "http://localhost:9292/store/fruit"
40
+ last_response.status.should == 404
41
+ last_response.body.should be_empty
42
+ get "http://localhost:9292/store/car"
43
+ last_response.status.should == 404
44
+ last_response.body.should be_empty
45
+ end
46
+
47
+ it "should be able to clear the store of an invididual item" do
48
+ put "http://localhost:9292/store/fruit", "strawberry"
49
+ delete "http://localhost:9292/store/fruit"
50
+ get "http://localhost:9292/store/fruit"
51
+ last_response.status.should == 404
52
+ last_response.body.should be_empty
53
+ end
54
+ end
@@ -0,0 +1,34 @@
1
+ require 'rack'
2
+ require 'rack/test'
3
+ require 'forwardable'
4
+
5
+ module Rack
6
+ class Client < Rack::Builder
7
+ VERSION = "0.2.4"
8
+
9
+ include Rack::Test::Methods
10
+ HTTP_METHODS = [:head, :get, :put, :post, :delete]
11
+
12
+ class << self
13
+ extend Forwardable
14
+ def_delegators :new, *HTTP_METHODS
15
+ end
16
+
17
+ def run(*args, &block)
18
+ @ran = true
19
+ super(*args, &block)
20
+ end
21
+
22
+ def to_app(*args, &block)
23
+ run Rack::Client::HTTP unless @ran
24
+ super(*args, &block)
25
+ end
26
+ alias app to_app
27
+ end
28
+ end
29
+
30
+ current_dir = File.expand_path(File.dirname(__FILE__) + '/client')
31
+
32
+ require current_dir + '/http'
33
+ require current_dir + '/auth'
34
+ require current_dir + '/follow_redirects'
@@ -0,0 +1,13 @@
1
+ module Rack::Client::Auth
2
+ class Basic
3
+ def initialize(app, username, password)
4
+ @app, @username, @password = app, username, password
5
+ end
6
+
7
+ def call(env)
8
+ encoded_login = ["#{@username}:#{@password}"].pack("m*")
9
+ env['HTTP_AUTHORIZATION'] = "Basic #{encoded_login}"
10
+ @app.call(env)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ class Rack::Client::FollowRedirects
2
+ include Rack::Test::Methods
3
+
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ status, headers, body = @app.call(env)
10
+ response = Rack::Response.new(body, status, headers)
11
+ if response.redirect?
12
+ uri = URI.parse(response["Location"])
13
+ new_env = {}
14
+ env.each do |k,v|
15
+ if %w| HTTP_HOST SERVER_NAME SERVER_PORT |.include?(k)
16
+ new_env[k] = v
17
+ end
18
+ end
19
+ new_env["REQUEST_METHOD"] = "GET"
20
+ session = Rack::Test::Session.new(@app)
21
+ env = session.send(:env_for, uri.to_s, new_env)
22
+ call(env)
23
+ else
24
+ [status, headers, body]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,77 @@
1
+ require 'net/https'
2
+
3
+ class Rack::Client::HTTP
4
+ def self.call(env)
5
+ new(env).run
6
+ end
7
+
8
+ def initialize(env)
9
+ @env = env
10
+ end
11
+
12
+ def run
13
+ request_klass = case request.request_method
14
+ when "HEAD"
15
+ Net::HTTP::Head
16
+ when "GET"
17
+ Net::HTTP::Get
18
+ when "POST"
19
+ Net::HTTP::Post
20
+ when "PUT"
21
+ Net::HTTP::Put
22
+ when "DELETE"
23
+ Net::HTTP::Delete
24
+ else
25
+ raise "Unsupported method: #{request.request_method.inspect}"
26
+ end
27
+
28
+ request_object = request_klass.new(request.path, request_headers)
29
+
30
+ if %w( POST PUT ).include?(request.request_method)
31
+ request_object.body = @env["rack.input"].read
32
+ end
33
+
34
+ parse(http.request(request_object))
35
+ end
36
+
37
+ def https?
38
+ request.scheme == 'https'
39
+ end
40
+
41
+ def http
42
+ http = Net::HTTP.new(request.host, request.port)
43
+ if https?
44
+ http.use_ssl = true
45
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
46
+ end
47
+ http
48
+ end
49
+
50
+ def parse(response)
51
+ status = response.code.to_i
52
+ headers = {}
53
+ response.each do |key,value|
54
+ key = key.gsub(/(\w+)/) do |matches|
55
+ matches.sub(/^./) do |char|
56
+ char.upcase
57
+ end
58
+ end
59
+ headers[key] = value
60
+ end
61
+ [status, headers, response.body.to_s]
62
+ end
63
+
64
+ def request
65
+ @request ||= Rack::Request.new(@env)
66
+ end
67
+
68
+ def request_headers
69
+ headers = {}
70
+ @env.each do |k,v|
71
+ if k =~ /^HTTP_(.*)$/
72
+ headers[$1] = v
73
+ end
74
+ end
75
+ headers
76
+ end
77
+ end
@@ -0,0 +1,84 @@
1
+ require 'sinatra/base'
2
+
3
+ class ExampleOrg < Sinatra::Base
4
+ get "/empty" do
5
+ ""
6
+ end
7
+
8
+ get "/ping" do
9
+ "pong"
10
+ end
11
+
12
+ get "/redirect" do
13
+ redirect request.script_name + "/after-redirect"
14
+ end
15
+
16
+ get "/after-redirect" do
17
+ "after redirect"
18
+ end
19
+
20
+ head "/shelf" do
21
+ response["ETag"] = "828ef3fdfa96f00ad9f27c383fc9ac7f"
22
+ ""
23
+ end
24
+
25
+ put "/shelf/:book" do
26
+ response["Location"] = "/shelf/#{params[:book]}"
27
+ ""
28
+ end
29
+
30
+ delete "/shelf/:book" do
31
+ status 204
32
+ ""
33
+ end
34
+
35
+ post "/posted" do
36
+ if request.body.read == "some data"
37
+ status 201
38
+ response["Created"] = "awesome"
39
+ ""
40
+ else
41
+ raise "Not valid"
42
+ end
43
+ end
44
+
45
+ get "/no-etag" do
46
+ ""
47
+ end
48
+ end
49
+
50
+ require 'pp'
51
+ class Debug
52
+ def initialize(app)
53
+ @app = app
54
+ end
55
+
56
+ def call(env)
57
+ puts "*" * 80
58
+ puts "env is:"
59
+ pp env
60
+ res = @app.call(env)
61
+ puts "response is:"
62
+ pp res
63
+ puts "*" * 80
64
+ res
65
+ end
66
+ end
67
+
68
+ use Rack::CommonLogger
69
+ use Debug if ENV["DEBUG"]
70
+
71
+ map "http://localhost/" do
72
+ map "/auth/" do
73
+ use Rack::Auth::Basic do |username,password|
74
+ username == "username" && password == "password"
75
+ end
76
+ run ExampleOrg
77
+ end
78
+
79
+ map "/" do
80
+ run ExampleOrg
81
+ end
82
+ end
83
+
84
+ # vim:filetype=ruby
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client, "with an Auth::Basic middleware" do
4
+ it "succeeds with authorization" do
5
+ client = Rack::Client.new do
6
+ use Rack::Client::Auth::Basic, "username", "password"
7
+ end
8
+ response = client.get("http://localhost:9292/auth/ping") #, :username => "username")
9
+ response.status.should == 200
10
+ response.headers["Content-Type"].should == "text/html"
11
+ response.body.should == "pong"
12
+ end
13
+
14
+ it "fails with authorization" do
15
+ client = Rack::Client.new do
16
+ use Rack::Client::Auth::Basic, "username", "fail"
17
+ end
18
+ response = client.get("http://localhost:9292/auth/ping")
19
+ response.status.should == 401
20
+ response.body.should == ""
21
+ end
22
+ end
@@ -0,0 +1,123 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client, "without middleware" do
4
+ context "at the instance level" do
5
+ it "returns an empty body" do
6
+ response = Rack::Client.new.get("http://localhost:9292/empty")
7
+ response.status.should == 200
8
+ response.headers["Content-Type"].should == "text/html"
9
+ response.headers["Content-Length"].should == "0"
10
+ response.body.should == ""
11
+ end
12
+
13
+ it "returns a 302" do
14
+ response = Rack::Client.new.get("http://localhost:9292/redirect")
15
+ response.status.should == 302
16
+ response["Location"].should == "http://localhost:9292/after-redirect"
17
+ end
18
+
19
+ it "heads data" do
20
+ response = Rack::Client.new.head "http://localhost:9292/shelf"
21
+ response.status.should == 200
22
+ response["ETag"].should == "828ef3fdfa96f00ad9f27c383fc9ac7f"
23
+ end
24
+
25
+ it "puts data" do
26
+ response = Rack::Client.new.put "http://localhost:9292/shelf/ctm", "some data"
27
+ response.status.should == 200
28
+ response["Location"].should == "http://localhost:9292/shelf/ctm"
29
+ end
30
+
31
+ it "deletes data" do
32
+ response = Rack::Client.new.delete "http://localhost:9292/shelf/ctm"
33
+ response.status.should == 204
34
+ end
35
+
36
+ it "posts data" do
37
+ response = Rack::Client.new.post("http://localhost:9292/posted", "some data")
38
+ response.status.should == 201
39
+ response["Created"].should == "awesome"
40
+ end
41
+ end
42
+
43
+ context "at the class level" do
44
+ it "returns an empty body" do
45
+ response = Rack::Client.get("http://localhost:9292/empty")
46
+ response.status.should == 200
47
+ response.headers["Content-Type"].should == "text/html"
48
+ response.headers["Content-Length"].should == "0"
49
+ response.body.should == ""
50
+ end
51
+
52
+ it "returns a 302" do
53
+ response = Rack::Client.get("http://localhost:9292/redirect")
54
+ response.status.should == 302
55
+ response["Location"].should == "http://localhost:9292/after-redirect"
56
+ end
57
+
58
+ it "heads data" do
59
+ response = Rack::Client.head "http://localhost:9292/shelf"
60
+ response.status.should == 200
61
+ response["ETag"].should == "828ef3fdfa96f00ad9f27c383fc9ac7f"
62
+ end
63
+
64
+ it "puts data" do
65
+ response = Rack::Client.put "http://localhost:9292/shelf/ctm", "some data"
66
+ response.status.should == 200
67
+ response["Location"].should == "http://localhost:9292/shelf/ctm"
68
+ end
69
+
70
+ it "deletes data" do
71
+ response = Rack::Client.delete "http://localhost:9292/shelf/ctm"
72
+ response.status.should == 204
73
+ end
74
+
75
+ it "posts data" do
76
+ response = Rack::Client.post("http://localhost:9292/posted", "some data")
77
+ response.status.should == 201
78
+ response["Created"].should == "awesome"
79
+ end
80
+ end
81
+
82
+ context "at the rack-test level" do
83
+ include Rack::Test::Methods
84
+ def app() Rack::Client.new end
85
+
86
+ it "returns an empty body" do
87
+ get "http://localhost:9292/empty"
88
+ last_response.status.should == 200
89
+ last_response.headers["Content-Type"].should == "text/html"
90
+ last_response.headers["Content-Length"].should == "0"
91
+ last_response.body.should == ""
92
+ end
93
+
94
+ it "returns a 302" do
95
+ get "http://localhost:9292/redirect"
96
+ last_response.status.should == 302
97
+ last_response["Location"].should == "http://localhost:9292/after-redirect"
98
+ end
99
+
100
+ it "heads data" do
101
+ head "http://localhost:9292/shelf"
102
+ last_response.status.should == 200
103
+ last_response["ETag"].should == "828ef3fdfa96f00ad9f27c383fc9ac7f"
104
+ end
105
+
106
+ it "puts data" do
107
+ put "http://localhost:9292/shelf/ctm", "some data"
108
+ last_response.status.should == 200
109
+ last_response["Location"].should == "http://localhost:9292/shelf/ctm"
110
+ end
111
+
112
+ it "deletes data" do
113
+ delete "http://localhost:9292/shelf/ctm"
114
+ last_response.status.should == 204
115
+ end
116
+
117
+ it "posts data" do
118
+ post "http://localhost:9292/posted", "some data"
119
+ last_response.status.should == 201
120
+ last_response["Created"].should == "awesome"
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require 'sinatra/base'
4
+
5
+ class MyApp < Sinatra::Base
6
+ get "/awesome" do
7
+ "test"
8
+ end
9
+ end
10
+
11
+ describe Rack::Client, "with an Rack app endpoint" do
12
+ it "returns the body" do
13
+ client = Rack::Client.new do
14
+ run Rack::URLMap.new("http://example.org/" => MyApp)
15
+ end
16
+ response = client.get("http://example.org/awesome")
17
+ response.status.should == 200
18
+ response.body.should == "test"
19
+ end
20
+
21
+ describe "with a custom domain" do
22
+ it "returns the body" do
23
+ client = Rack::Client.new do
24
+ run Rack::URLMap.new("http://google.com/" => MyApp)
25
+ end
26
+ response = client.get("http://google.com/awesome")
27
+ response.status.should == 200
28
+ response.body.should == "test"
29
+ end
30
+
31
+ it "only functions for that domain" do
32
+ client = Rack::Client.new do
33
+ run Rack::URLMap.new("http://google.com/" => MyApp)
34
+ end
35
+ response = client.get("http://example.org/")
36
+ response.status.should == 404
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client, "requesting https" do
4
+ context "from fortify.net" do
5
+ it "returns the cipher" do
6
+ begin
7
+ response = Rack::Client.new.get("https://www.fortify.net/cgi/ssl_3.pl")
8
+ response.should have_selector("li.cipheronstrong", :content => 'AES cipher, 256-bit key')
9
+ rescue SocketError => e
10
+ pending do
11
+ raise ArgumentError, "Looks like you're offline"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client, "with a standard piece of Rack middleware" do
4
+ it "successfully uses that middleware" do
5
+ client = Rack::Client.new { use Rack::ETag }
6
+ response = client.get("http://localhost:9292/no-etag")
7
+ response.status.should == 200
8
+ response.headers["ETag"].should_not be_empty
9
+ end
10
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "The library itself" do
4
+
5
+ def check_for_tab_characters(filename)
6
+ failing_lines = []
7
+ File.readlines(filename).each_with_index do |line,number|
8
+ failing_lines << number + 1 if line =~ /\t/
9
+ end
10
+
11
+ unless failing_lines.empty?
12
+ "#{filename} has tab characters on lines #{failing_lines.join(', ')}"
13
+ end
14
+ end
15
+
16
+ def check_for_extra_spaces(filename)
17
+ failing_lines = []
18
+ File.readlines(filename).each_with_index do |line,number|
19
+ next if line =~ /^\s+#.*\s+\n$/
20
+ failing_lines << number + 1 if line =~ /\s+\n$/
21
+ end
22
+
23
+ unless failing_lines.empty?
24
+ "#{filename} has spaces on the EOL on lines #{failing_lines.join(', ')}"
25
+ end
26
+ end
27
+
28
+ def be_well_formed
29
+ simple_matcher("be well formed") do |given, matcher|
30
+ matcher.failure_message = given.join("\n")
31
+ given.empty?
32
+ end
33
+ end
34
+
35
+ it "has no malformed whitespace" do
36
+ error_messages = []
37
+ Dir.chdir(File.dirname(__FILE__) + '/..') do
38
+ `git ls-files`.split("\n").each do |filename|
39
+ next if filename =~ /\.gitmodules|fixtures/
40
+ error_messages << check_for_tab_characters(filename)
41
+ error_messages << check_for_extra_spaces(filename)
42
+ end
43
+ end
44
+ error_messages.compact.should be_well_formed
45
+ end
46
+ end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Rack::Client, "with a FollowRedirects middleware" do
4
+ it "follows redirects" do
5
+ client = Rack::Client.new do
6
+ use Rack::Client::FollowRedirects
7
+ end
8
+ response = client.get("http://localhost:9292/redirect")
9
+ response.status.should == 200
10
+ response.body.should == "after redirect"
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ Bundler.require(:test)
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/rack/client")
4
+
5
+ Spec::Runner.configure do |config|
6
+ config.include(Webrat::Matchers)
7
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: myronmarston-rack-client
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 4
9
+ version: 0.2.4
10
+ platform: ruby
11
+ authors:
12
+ - Tim Carey-Smith
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-19 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: rack-test
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :runtime
43
+ version_requirements: *id002
44
+ description: A client wrapper around a Rack app or HTTP
45
+ email: tim@spork.in
46
+ executables: []
47
+
48
+ extensions: []
49
+
50
+ extra_rdoc_files: []
51
+
52
+ files:
53
+ - History.txt
54
+ - LICENSE
55
+ - README.textile
56
+ - Rakefile
57
+ - lib/rack/client/auth.rb
58
+ - lib/rack/client/follow_redirects.rb
59
+ - lib/rack/client/http.rb
60
+ - lib/rack/client.rb
61
+ - demo/client.rb
62
+ - demo/config.ru
63
+ - demo/demo.rb
64
+ - demo/demo_spec.rb
65
+ has_rdoc: true
66
+ homepage: http://github.com/halorgium/rack-client
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options: []
71
+
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ requirements: []
89
+
90
+ rubyforge_project: myronmarston-rack-client
91
+ rubygems_version: 1.3.6
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: A client wrapper around a Rack app or HTTP
95
+ test_files:
96
+ - spec/apps/example.org.ru
97
+ - spec/auth_spec.rb
98
+ - spec/core_spec.rb
99
+ - spec/endpoint_spec.rb
100
+ - spec/https_spec.rb
101
+ - spec/middleware_spec.rb
102
+ - spec/quality_spec.rb
103
+ - spec/redirect_spec.rb
104
+ - spec/spec_helper.rb