myronmarston-rack-client 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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