rack-client 0.1.0
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/History.txt +5 -0
 - data/LICENSE +19 -0
 - data/README.textile +63 -0
 - data/Rakefile +34 -0
 - data/demo/client.rb +26 -0
 - data/demo/config.ru +3 -0
 - data/demo/demo.rb +31 -0
 - data/demo/demo_spec.rb +54 -0
 - data/lib/rack/client.rb +37 -0
 - data/lib/rack/client/auth.rb +13 -0
 - data/lib/rack/client/follow_redirects.rb +27 -0
 - data/lib/rack/client/http.rb +77 -0
 - data/spec/apps/example.org.ru +84 -0
 - data/spec/auth_spec.rb +22 -0
 - data/spec/core_spec.rb +123 -0
 - data/spec/endpoint_spec.rb +39 -0
 - data/spec/middleware_spec.rb +10 -0
 - data/spec/redirect_spec.rb +12 -0
 - data/spec/spec_helper.rb +6 -0
 - metadata +73 -0
 
    
        data/History.txt
    ADDED
    
    
    
        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.
         
     | 
    
        data/README.textile
    ADDED
    
    | 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            h1. What's this?
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Rack::Client is an HTTP client that aims to be a good Rack
         
     | 
| 
      
 4 
     | 
    
         
            +
            citizen.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            h2. Rack responses
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            Rack::Client can be used to make HTTP requests to any type of server, not
         
     | 
| 
      
 9 
     | 
    
         
            +
            just ones using Rack. However, when a request is made then a proper
         
     | 
| 
      
 10 
     | 
    
         
            +
            Rack response (specifically a Rack::MockResponse) object is returned. 
         
     | 
| 
      
 11 
     | 
    
         
            +
            For Rubyists, this means you don't need to learn yet another interface 
         
     | 
| 
      
 12 
     | 
    
         
            +
            and can just stick with Rack both on the server, test, and client side of
         
     | 
| 
      
 13 
     | 
    
         
            +
            things.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            <pre>
         
     | 
| 
      
 16 
     | 
    
         
            +
            response = Rack::Client.get("http://some-website.com/blah.txt")
         
     | 
| 
      
 17 
     | 
    
         
            +
            response.code #=> 200
         
     | 
| 
      
 18 
     | 
    
         
            +
            response.body #=> "some body"
         
     | 
| 
      
 19 
     | 
    
         
            +
            </pre>
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            h2. Middleware
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            Rack::Client is actually a subclass of Rack::Builder. This means that
         
     | 
| 
      
 24 
     | 
    
         
            +
            Rack::Client objects yield actual Rack apps. More importantly, this
         
     | 
| 
      
 25 
     | 
    
         
            +
            means you can reuse existing Rack middleware on the client side too
         
     | 
| 
      
 26 
     | 
    
         
            +
            (but also feel free to make new middleware that only makes sense on
         
     | 
| 
      
 27 
     | 
    
         
            +
            the client side under the Rack::Client namespace). Note that by default
         
     | 
| 
      
 28 
     | 
    
         
            +
            Rack::Client will "run" Rack::Client::HTTP as an endpoint, but this
         
     | 
| 
      
 29 
     | 
    
         
            +
            will not be performed if you specify your own "run" endpoint.
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            <pre>
         
     | 
| 
      
 32 
     | 
    
         
            +
            client = Rack::Client.new { use Rack::ETag }
         
     | 
| 
      
 33 
     | 
    
         
            +
            response = client.get("http://localhost:9292/no-etag")
         
     | 
| 
      
 34 
     | 
    
         
            +
            </pre>
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            h2. Rack::Test compatibility
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            Rack::Client reuses a lot of Rack::Test to provide users with a
         
     | 
| 
      
 39 
     | 
    
         
            +
            familiar interface. What's even cooler is that you can use a
         
     | 
| 
      
 40 
     | 
    
         
            +
            Rack::Client object as your "app" in Rack::Test. This means that you
         
     | 
| 
      
 41 
     | 
    
         
            +
            can test-drive an application with Rack::Test, then when ready
         
     | 
| 
      
 42 
     | 
    
         
            +
            actually run your Rack app, switch your Rack::Test "app" to a
         
     | 
| 
      
 43 
     | 
    
         
            +
            Rack::Client, and get free full-blown integration testing! Note that
         
     | 
| 
      
 44 
     | 
    
         
            +
            the integration-tested server does not need to be all-Rack, so you can
         
     | 
| 
      
 45 
     | 
    
         
            +
            develop quickly with middleware like Rack::Cache but then remove it
         
     | 
| 
      
 46 
     | 
    
         
            +
            and integration test with a dedicated cache server like Varnish.
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            <pre>
         
     | 
| 
      
 49 
     | 
    
         
            +
            # NOTE: For a complete example, look in the "demo" directory
         
     | 
| 
      
 50 
     | 
    
         
            +
            describe Demo, "/store resource" do
         
     | 
| 
      
 51 
     | 
    
         
            +
              include Rack::Test::Methods
         
     | 
| 
      
 52 
     | 
    
         
            +
              def app
         
     | 
| 
      
 53 
     | 
    
         
            +
                # replace this with Rack::Client.new
         
     | 
| 
      
 54 
     | 
    
         
            +
                # for integration testing
         
     | 
| 
      
 55 
     | 
    
         
            +
                Demo::App.new
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
              # ... etc
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     | 
| 
      
 59 
     | 
    
         
            +
            </pre>
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            h1. Contributors
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            halorgium, larrytheliquid
         
     | 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 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              = "rack-client"
         
     | 
| 
      
 16 
     | 
    
         
            +
              s.version           = Rack::Client::VERSION
         
     | 
| 
      
 17 
     | 
    
         
            +
              s.author            = "Tim Carey-Smith"
         
     | 
| 
      
 18 
     | 
    
         
            +
              s.email             = "time" + "@" + "spork.in"
         
     | 
| 
      
 19 
     | 
    
         
            +
              s.homepage          = "http://github.com/halorgium/rack-client"
         
     | 
| 
      
 20 
     | 
    
         
            +
              s.summary           = "A client wrapper around a Rack app or HTTP"
         
     | 
| 
      
 21 
     | 
    
         
            +
              s.description       = s.summary
         
     | 
| 
      
 22 
     | 
    
         
            +
              s.files             = %w[History.txt LICENSE README.textile Rakefile] + Dir["lib/**/*"] + Dir["demo/**/*"]
         
     | 
| 
      
 23 
     | 
    
         
            +
              s.test_files        = Dir["spec/**/*"]
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            Rake::GemPackageTask.new(spec) do |package|
         
     | 
| 
      
 27 
     | 
    
         
            +
              package.gem_spec = spec
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            desc 'Install the package as a gem.'
         
     | 
| 
      
 31 
     | 
    
         
            +
            task :install => [:clean, :package] do
         
     | 
| 
      
 32 
     | 
    
         
            +
              gem = Dir['pkg/*.gem'].first
         
     | 
| 
      
 33 
     | 
    
         
            +
              sh "sudo gem install --no-rdoc --no-ri --local #{gem}"
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
    
        data/demo/client.rb
    ADDED
    
    | 
         @@ -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")
         
     | 
    
        data/demo/config.ru
    ADDED
    
    
    
        data/demo/demo.rb
    ADDED
    
    | 
         @@ -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
         
     | 
    
        data/demo/demo_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "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
         
     | 
    
        data/lib/rack/client.rb
    ADDED
    
    | 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__) + "/.."))
         
     | 
| 
      
 2 
     | 
    
         
            +
              $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/.."))
         
     | 
| 
      
 3 
     | 
    
         
            +
            end
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'rack'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'rack/test'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Rack
         
     | 
| 
      
 10 
     | 
    
         
            +
              class Client < Rack::Builder
         
     | 
| 
      
 11 
     | 
    
         
            +
                VERSION = "0.1.0"
         
     | 
| 
      
 12 
     | 
    
         
            +
                include Rack::Test::Methods
         
     | 
| 
      
 13 
     | 
    
         
            +
                HTTP_METHODS = [:head, :get, :put, :post, :delete]
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 16 
     | 
    
         
            +
                  extend Forwardable
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def_delegators :new, *HTTP_METHODS
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def run(*args, &block)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @ran = true
         
     | 
| 
      
 22 
     | 
    
         
            +
                  super(*args, &block)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def to_app(*args, &block)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  run Rack::Client::HTTP unless @ran
         
     | 
| 
      
 27 
     | 
    
         
            +
                  super(*args, &block)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
                alias app to_app
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            $:.unshift File.dirname(__FILE__)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            require 'client/http'
         
     | 
| 
      
 36 
     | 
    
         
            +
            require 'client/auth'
         
     | 
| 
      
 37 
     | 
    
         
            +
            require 'client/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/http'
         
     | 
| 
      
 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 
     | 
    
         
            +
                case request.request_method
         
     | 
| 
      
 14 
     | 
    
         
            +
                when "HEAD"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  head = Net::HTTP::Head.new(request.path, request_headers)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  http.request(head) do |response|
         
     | 
| 
      
 17 
     | 
    
         
            +
                    return parse(response)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end      
         
     | 
| 
      
 19 
     | 
    
         
            +
                when "GET"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  get = Net::HTTP::Get.new(request.path, request_headers)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  http.request(get) do |response|
         
     | 
| 
      
 22 
     | 
    
         
            +
                    return parse(response)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                when "POST"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  post = Net::HTTP::Post.new(request.path, request_headers)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  post.body = @env["rack.input"].read
         
     | 
| 
      
 27 
     | 
    
         
            +
                  http.request(post) do |response|
         
     | 
| 
      
 28 
     | 
    
         
            +
                    return parse(response)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                when "PUT"
         
     | 
| 
      
 31 
     | 
    
         
            +
                  put = Net::HTTP::Put.new(request.path, request_headers)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  put.body = @env["rack.input"].read
         
     | 
| 
      
 33 
     | 
    
         
            +
                  http.request(put) do |response|
         
     | 
| 
      
 34 
     | 
    
         
            +
                    return parse(response)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                when "DELETE"
         
     | 
| 
      
 37 
     | 
    
         
            +
                  delete = Net::HTTP::Delete.new(request.path, request_headers)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  http.request(delete) do |response|
         
     | 
| 
      
 39 
     | 
    
         
            +
                    return parse(response)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end           
         
     | 
| 
      
 41 
     | 
    
         
            +
                else
         
     | 
| 
      
 42 
     | 
    
         
            +
                  raise "Unsupported method: #{request.request_method.inspect}"
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              def http
         
     | 
| 
      
 47 
     | 
    
         
            +
                Net::HTTP.new(request.host, request.port)
         
     | 
| 
      
 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
         
     | 
    
        data/spec/auth_spec.rb
    ADDED
    
    | 
         @@ -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")
         
     | 
| 
      
 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
         
     | 
    
        data/spec/core_spec.rb
    ADDED
    
    | 
         @@ -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 == "/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 == "/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 == "/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 == "/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 == "/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 == "/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,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,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
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,73 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: rack-client
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Tim Carey-Smith
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2009-06-14 00:00:00 -07:00
         
     | 
| 
      
 13 
     | 
    
         
            +
            default_executable: 
         
     | 
| 
      
 14 
     | 
    
         
            +
            dependencies: []
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            description: A client wrapper around a Rack app or HTTP
         
     | 
| 
      
 17 
     | 
    
         
            +
            email: time@spork.in
         
     | 
| 
      
 18 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 25 
     | 
    
         
            +
            - History.txt
         
     | 
| 
      
 26 
     | 
    
         
            +
            - LICENSE
         
     | 
| 
      
 27 
     | 
    
         
            +
            - README.textile
         
     | 
| 
      
 28 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 29 
     | 
    
         
            +
            - lib/rack
         
     | 
| 
      
 30 
     | 
    
         
            +
            - lib/rack/client
         
     | 
| 
      
 31 
     | 
    
         
            +
            - lib/rack/client/auth.rb
         
     | 
| 
      
 32 
     | 
    
         
            +
            - lib/rack/client/http.rb
         
     | 
| 
      
 33 
     | 
    
         
            +
            - lib/rack/client/follow_redirects.rb
         
     | 
| 
      
 34 
     | 
    
         
            +
            - lib/rack/client.rb
         
     | 
| 
      
 35 
     | 
    
         
            +
            - demo/config.ru
         
     | 
| 
      
 36 
     | 
    
         
            +
            - demo/demo_spec.rb
         
     | 
| 
      
 37 
     | 
    
         
            +
            - demo/client.rb
         
     | 
| 
      
 38 
     | 
    
         
            +
            - demo/demo.rb
         
     | 
| 
      
 39 
     | 
    
         
            +
            has_rdoc: false
         
     | 
| 
      
 40 
     | 
    
         
            +
            homepage: http://github.com/halorgium/rack-client
         
     | 
| 
      
 41 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 42 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 45 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 46 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 47 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 48 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 49 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 50 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 51 
     | 
    
         
            +
              version: 
         
     | 
| 
      
 52 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 53 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 54 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 55 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 56 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 57 
     | 
    
         
            +
              version: 
         
     | 
| 
      
 58 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 61 
     | 
    
         
            +
            rubygems_version: 1.3.1
         
     | 
| 
      
 62 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 63 
     | 
    
         
            +
            specification_version: 2
         
     | 
| 
      
 64 
     | 
    
         
            +
            summary: A client wrapper around a Rack app or HTTP
         
     | 
| 
      
 65 
     | 
    
         
            +
            test_files: 
         
     | 
| 
      
 66 
     | 
    
         
            +
            - spec/redirect_spec.rb
         
     | 
| 
      
 67 
     | 
    
         
            +
            - spec/auth_spec.rb
         
     | 
| 
      
 68 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 69 
     | 
    
         
            +
            - spec/apps
         
     | 
| 
      
 70 
     | 
    
         
            +
            - spec/apps/example.org.ru
         
     | 
| 
      
 71 
     | 
    
         
            +
            - spec/endpoint_spec.rb
         
     | 
| 
      
 72 
     | 
    
         
            +
            - spec/core_spec.rb
         
     | 
| 
      
 73 
     | 
    
         
            +
            - spec/middleware_spec.rb
         
     |