davetron5000-rest-client 0.5.3

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/README.rdoc ADDED
@@ -0,0 +1,63 @@
1
+ = REST Client -- simple DSL for accessing REST resources
2
+
3
+ A simple REST client for Ruby, inspired by the Sinatra's microframework style
4
+ of specifying actions: get, put, post, delete.
5
+
6
+ <b>This version is a bit out of date, consider the updated one, linked below</b>
7
+
8
+ * RDoc - http://davetron5000.github.com/rest-client
9
+ * Update version you should use - http://github.com/adamwiggins/rest-client/tree/master
10
+
11
+ == Usage: Raw URL
12
+
13
+ require 'rest_client'
14
+
15
+ xml = RestClient.get 'http://example.com/resource'
16
+ jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
17
+
18
+ private_resource = RestClient.get 'https://user:password@example.com/private/resource'
19
+
20
+ RestClient.put 'http://example.com/resource', File.read('my.pdf'), :content_type => 'application/pdf'
21
+
22
+ RestClient.post 'http://example.com/resource', xml, :content_type => 'application/xml'
23
+
24
+ RestClient.delete 'http://example.com/resource'
25
+
26
+ See RestClient module docs for details.
27
+
28
+ == Usage: ActiveResource-Style
29
+
30
+ resource = RestClient::Resource.new 'http://example.com/resource'
31
+ resource.get
32
+
33
+ private_resource = RestClient::Resource.new 'http://example.com/private/resource', 'user', 'pass'
34
+ private_resource.put File.read('pic.jpg'), :content_type => 'image/jpg'
35
+
36
+ See RestClient::Resource module docs for details.
37
+
38
+ == Usage: Resource Nesting
39
+
40
+ site = RestClient::Resource.new('http://example.com')
41
+ site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
42
+
43
+ See RestClient::Resource docs for details.
44
+
45
+ == Shell
46
+
47
+ Require rest_client from within irb to access RestClient interactively, like
48
+ using curl at the command line. Better yet, require gem from within your
49
+ ~/.rush/env.rb and have instant access to it from within your rush
50
+ (http://rush.heroku.com) sessions.
51
+
52
+ == Meta
53
+
54
+ Written by Adam Wiggins (adam at heroku dot com)
55
+
56
+ Patches contributed by: Chris Anderson, Greg Borenstein, Ardekantur, Pedro Belo, and Rafael Souza
57
+
58
+ Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
59
+
60
+ http://rest-client.heroku.com
61
+
62
+ http://github.com/adamwiggins/rest-client
63
+
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ require 'fileutils'
2
+ require 'hanna/rdoctask'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/gempackagetask'
6
+ require 'rake/rdoctask'
7
+ require 'rake/testtask'
8
+ require 'spec/rake/spectask'
9
+
10
+ $: << '../grancher/lib'
11
+ require 'grancher/task'
12
+
13
+ Grancher::Task.new do |g|
14
+ g.branch = 'gh-pages'
15
+ g.push_to = 'origin'
16
+ g.directory 'html'
17
+ end
18
+
19
+ desc "Run all specs"
20
+ Spec::Rake::SpecTask.new('spec') do |t|
21
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
22
+ t.spec_files = FileList['spec/*_spec.rb']
23
+ end
24
+
25
+ desc "Print specdocs"
26
+ Spec::Rake::SpecTask.new(:doc) do |t|
27
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
28
+ t.spec_files = FileList['spec/*_spec.rb']
29
+ end
30
+
31
+ desc "Run all examples with RCov"
32
+ Spec::Rake::SpecTask.new('rcov') do |t|
33
+ t.spec_files = FileList['spec/*_spec.rb']
34
+ t.rcov = true
35
+ t.rcov_opts = ['--exclude', 'examples']
36
+ end
37
+
38
+
39
+ spec = eval(File.read('rest-client.gemspec'))
40
+
41
+ Rake::GemPackageTask.new(spec) do |pkg|
42
+ pkg.need_tar = true
43
+ end
44
+
45
+ Rake::TestTask.new do |t|
46
+ t.libs << "spec"
47
+ t.test_files = FileList['spec/*_spec.rb']
48
+ t.verbose = true
49
+ end
50
+
51
+ Rake::RDocTask.new do |t|
52
+ t.title = "rest-client, fetch RESTful resources effortlessly"
53
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
54
+ t.options << '--charset' << 'utf-8'
55
+ t.rdoc_files.include('README.rdoc')
56
+ t.rdoc_files.include('lib/*.rb')
57
+ end
58
+
59
+ CLEAN.include [ 'pkg', '*.gem', '.config', 'html' ]
60
+
61
+ task :default => :spec
62
+
63
+ task :publish_rdoc => [:rdoc,:publish]
@@ -0,0 +1,57 @@
1
+ require 'rexml/document'
2
+
3
+ module RestClient
4
+ # A redirect was encountered; caught by execute to retry with the new url.
5
+ class Redirect < RuntimeError; end
6
+
7
+ # Authorization is required to access the resource specified.
8
+ class Unauthorized < RuntimeError; end
9
+
10
+ # No resource was found at the given URL.
11
+ class ResourceNotFound < RuntimeError; end
12
+
13
+ # The server broke the connection prior to the request completing.
14
+ class ServerBrokeConnection < RuntimeError; end
15
+
16
+ # The server took too long to respond.
17
+ class RequestTimeout < RuntimeError; end
18
+
19
+ # The request failed, meaning the remote HTTP server returned a code other
20
+ # than success, unauthorized, or redirect.
21
+ #
22
+ # The exception message attempts to extract the error from the XML, using
23
+ # format returned by Rails: <errors><error>some message</error></errors>
24
+ #
25
+ # You can get the status code by e.http_code, or see anything about the
26
+ # response via e.response. For example, the entire result body (which is
27
+ # probably an HTML error page) is e.response.body.
28
+ class RequestFailed < RuntimeError
29
+ attr_accessor :response
30
+
31
+ def initialize(response=nil)
32
+ @response = response
33
+ end
34
+
35
+ def http_code
36
+ @response.code.to_i if @response
37
+ end
38
+
39
+ def message(default="Unknown error, HTTP status code #{http_code}")
40
+ return default unless @response
41
+ parse_error_xml rescue default
42
+ end
43
+
44
+ def parse_error_xml
45
+ xml_errors = REXML::Document.new(@response.body).elements.to_a("//errors/error")
46
+ xml_errors.empty? ? raise : xml_errors.map { |a| a.text }.join(" / ")
47
+ end
48
+
49
+ def to_s
50
+ message
51
+ end
52
+ end
53
+ end
54
+
55
+ # backwards compatibility
56
+ class RestClient::Request
57
+ end
data/lib/resource.rb ADDED
@@ -0,0 +1,103 @@
1
+ module RestClient
2
+ # A class that can be instantiated for access to a RESTful resource,
3
+ # including authentication.
4
+ #
5
+ # Example:
6
+ #
7
+ # resource = RestClient::Resource.new('http://some/resource')
8
+ # jpg = resource.get(:accept => 'image/jpg')
9
+ #
10
+ # With HTTP basic authentication:
11
+ #
12
+ # resource = RestClient::Resource.new('http://protected/resource', 'user', 'pass')
13
+ # resource.delete
14
+ #
15
+ # Use the [] syntax to allocate subresources:
16
+ #
17
+ # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd')
18
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
19
+ #
20
+ class Resource
21
+ attr_reader :url, :user, :password
22
+
23
+ def initialize(url, user=nil, password=nil)
24
+ @url = url
25
+ @user = user
26
+ @password = password
27
+ end
28
+
29
+ def get(headers={})
30
+ Request.execute(:method => :get,
31
+ :url => url,
32
+ :user => user,
33
+ :password => password,
34
+ :headers => headers)
35
+ end
36
+
37
+ def post(payload, headers={})
38
+ Request.execute(:method => :post,
39
+ :url => url,
40
+ :payload => payload,
41
+ :user => user,
42
+ :password => password,
43
+ :headers => headers)
44
+ end
45
+
46
+ def put(payload, headers={})
47
+ Request.execute(:method => :put,
48
+ :url => url,
49
+ :payload => payload,
50
+ :user => user,
51
+ :password => password,
52
+ :headers => headers)
53
+ end
54
+
55
+ def delete(headers={})
56
+ Request.execute(:method => :delete,
57
+ :url => url,
58
+ :user => user,
59
+ :password => password,
60
+ :headers => headers)
61
+ end
62
+
63
+ # Construct a subresource, preserving authentication.
64
+ #
65
+ # Example:
66
+ #
67
+ # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd')
68
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
69
+ #
70
+ # This is especially useful if you wish to define your site in one place and
71
+ # call it in multiple locations:
72
+ #
73
+ # def orders
74
+ # RestClient::Resource.new('http://example.com/orders', 'admin', 'mypasswd')
75
+ # end
76
+ #
77
+ # orders.get # GET http://example.com/orders
78
+ # orders['1'].get # GET http://example.com/orders/1
79
+ # orders['1/items'].delete # DELETE http://example.com/orders/1/items
80
+ #
81
+ # Nest resources as far as you want:
82
+ #
83
+ # site = RestClient::Resource.new('http://example.com')
84
+ # posts = site['posts']
85
+ # first_post = posts['1']
86
+ # comments = first_post['comments']
87
+ # comments.post 'Hello', :content_type => 'text/plain'
88
+ #
89
+ def [](suburl)
90
+ self.class.new(concat_urls(url, suburl), user, password)
91
+ end
92
+
93
+ def concat_urls(url, suburl) # :nodoc:
94
+ url = url.to_s
95
+ suburl = suburl.to_s
96
+ if url.slice(-1, 1) == '/' or suburl.slice(0, 1) == '/'
97
+ url + suburl
98
+ else
99
+ "#{url}/#{suburl}"
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,142 @@
1
+ require 'uri'
2
+ require 'net/https'
3
+
4
+ require File.dirname(__FILE__) + '/resource'
5
+ require File.dirname(__FILE__) + '/request_errors'
6
+
7
+ # This module's static methods are the entry point for using the REST client.
8
+ module RestClient
9
+ def self.get(url, headers={})
10
+ Request.execute(:method => :get,
11
+ :url => url,
12
+ :headers => headers)
13
+ end
14
+
15
+ def self.post(url, payload, headers={})
16
+ Request.execute(:method => :post,
17
+ :url => url,
18
+ :payload => payload,
19
+ :headers => headers)
20
+ end
21
+
22
+ def self.put(url, payload, headers={})
23
+ Request.execute(:method => :put,
24
+ :url => url,
25
+ :payload => payload,
26
+ :headers => headers)
27
+ end
28
+
29
+ def self.delete(url, headers={})
30
+ Request.execute(:method => :delete,
31
+ :url => url,
32
+ :headers => headers)
33
+ end
34
+
35
+ # Internal class used to build and execute the request.
36
+ class Request
37
+ class << self
38
+ attr_accessor :default_headers, :timeout_threshold
39
+ end
40
+
41
+ self.default_headers = { :accept => 'application/xml' }
42
+
43
+ # Set a custom open and read timeout for Net::HTTP instances.
44
+ self.timeout_threshold = nil
45
+
46
+ attr_reader :method, :url, :payload, :headers, :user, :password
47
+
48
+ def self.execute(args)
49
+ new(args).execute
50
+ end
51
+
52
+ def initialize(args)
53
+ @method = args[:method] or raise ArgumentError, "must pass :method"
54
+ @url = args[:url] or raise ArgumentError, "must pass :url"
55
+ @headers = args[:headers] || {}
56
+ @payload = process_payload(args[:payload])
57
+ @user = args[:user]
58
+ @password = args[:password]
59
+ end
60
+
61
+ def execute
62
+ execute_inner
63
+ rescue Redirect => e
64
+ @url = e.message
65
+ execute
66
+ end
67
+
68
+ def execute_inner
69
+ uri = parse_url_with_auth(url)
70
+ transmit uri, net_http_class(method).new(uri.request_uri, make_headers(headers)), payload
71
+ end
72
+
73
+ def make_headers(user_headers)
74
+ merged = self.class.default_headers.merge(user_headers)
75
+ merged.inject({}) do |final, (key, value)|
76
+ final.update(key.to_s.gsub(/_/, '-').capitalize => value)
77
+ end
78
+ end
79
+
80
+ def net_http_class(method)
81
+ Net::HTTP.const_get(method.to_s.capitalize)
82
+ end
83
+
84
+ def parse_url(url)
85
+ url = "http://#{url}" unless url.match(/^http/)
86
+ URI.parse(url)
87
+ end
88
+
89
+ def parse_url_with_auth(url)
90
+ uri = parse_url(url)
91
+ @user = uri.user if uri.user
92
+ @password = uri.password if uri.password
93
+ uri
94
+ end
95
+
96
+ def process_payload(p=nil)
97
+ unless p.is_a?(Hash)
98
+ p
99
+ else
100
+ @headers[:content_type] = 'application/x-www-form-urlencoded'
101
+ p.keys.map do |k|
102
+ v = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
103
+ "#{k}=#{v}"
104
+ end.join("&")
105
+ end
106
+ end
107
+
108
+ def transmit(uri, req, payload)
109
+ setup_credentials(req)
110
+
111
+ net = Net::HTTP.new(uri.host, uri.port)
112
+ net.read_timeout = net.open_timeout = self.class.timeout_threshold if self.class.timeout_threshold
113
+ net.use_ssl = uri.is_a?(URI::HTTPS)
114
+
115
+ net.start do |http|
116
+ process_result http.request(req, payload || "")
117
+ end
118
+ rescue EOFError
119
+ raise RestClient::ServerBrokeConnection
120
+ rescue Timeout::Error
121
+ raise RestClient::RequestTimeout
122
+ end
123
+
124
+ def setup_credentials(req)
125
+ req.basic_auth(user, password) if user
126
+ end
127
+
128
+ def process_result(res)
129
+ if %w(200 201 202).include? res.code
130
+ res.body
131
+ elsif %w(301 302 303).include? res.code
132
+ raise Redirect, res.header['Location']
133
+ elsif res.code == "401"
134
+ raise Unauthorized
135
+ elsif res.code == "404"
136
+ raise ResourceNotFound
137
+ else
138
+ raise RequestFailed, res
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rest-client"
3
+ s.version = "0.5.3"
4
+ s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
5
+ s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
6
+ s.author = "Adam Wiggins"
7
+ s.email = "adam@heroku.com"
8
+ s.rubyforge_project = "rest-client"
9
+ s.homepage = "http://rest-client.heroku.com/"
10
+ s.has_rdoc = true
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = %w(Rakefile README.rdoc rest-client.gemspec
13
+ lib/request_errors.rb lib/resource.rb lib/rest_client.rb
14
+ spec/base.rb spec/request_errors_spec.rb spec/resource_spec.rb spec/rest_client_spec.rb)
15
+ s.require_path = "lib"
16
+ end
data/spec/base.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require File.dirname(__FILE__) + '/../lib/rest_client'
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe RestClient::RequestFailed do
4
+ before do
5
+ @error = RestClient::RequestFailed.new
6
+ end
7
+
8
+ it "extracts the error message from xml" do
9
+ @error.response = mock('response', :code => '422', :body => '<errors><error>Error 1</error><error>Error 2</error></errors>')
10
+ @error.message.should == 'Error 1 / Error 2'
11
+ end
12
+
13
+ it "ignores responses without xml since they might contain sensitive data" do
14
+ @error.response = mock('response', :code => '500', :body => 'Syntax error in SQL query: SELECT * FROM ...')
15
+ @error.message.should == 'Unknown error, HTTP status code 500'
16
+ end
17
+
18
+ it "accepts a default error message" do
19
+ @error.response = mock('response', :code => '500', :body => 'Internal Server Error')
20
+ @error.message('Custom default message').should == 'Custom default message'
21
+ end
22
+
23
+ it "doesn't show the default error message when there's something in the xml" do
24
+ @error.response = mock('response', :code => '422', :body => '<errors><error>Specific error message</error></errors>')
25
+ @error.message('Custom default message').should == 'Specific error message'
26
+ end
27
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe RestClient::Resource do
4
+ before do
5
+ @resource = RestClient::Resource.new('http://some/resource', 'jane', 'mypass')
6
+ end
7
+
8
+ it "GET" do
9
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {}, :user => 'jane', :password => 'mypass')
10
+ @resource.get
11
+ end
12
+
13
+ it "POST" do
14
+ RestClient::Request.should_receive(:execute).with(:method => :post, :url => 'http://some/resource', :payload => 'abc', :headers => { :content_type => 'image/jpg' }, :user => 'jane', :password => 'mypass')
15
+ @resource.post 'abc', :content_type => 'image/jpg'
16
+ end
17
+
18
+ it "PUT" do
19
+ RestClient::Request.should_receive(:execute).with(:method => :put, :url => 'http://some/resource', :payload => 'abc', :headers => { :content_type => 'image/jpg' }, :user => 'jane', :password => 'mypass')
20
+ @resource.put 'abc', :content_type => 'image/jpg'
21
+ end
22
+
23
+ it "DELETE" do
24
+ RestClient::Request.should_receive(:execute).with(:method => :delete, :url => 'http://some/resource', :headers => {}, :user => 'jane', :password => 'mypass')
25
+ @resource.delete
26
+ end
27
+
28
+ it "can instantiate with no user/password" do
29
+ @resource = RestClient::Resource.new('http://some/resource')
30
+ end
31
+
32
+ it "concatinates urls, inserting a slash when it needs one" do
33
+ @resource.concat_urls('http://example.com', 'resource').should == 'http://example.com/resource'
34
+ end
35
+
36
+ it "concatinates urls, using no slash if the first url ends with a slash" do
37
+ @resource.concat_urls('http://example.com/', 'resource').should == 'http://example.com/resource'
38
+ end
39
+
40
+ it "concatinates urls, using no slash if the second url starts with a slash" do
41
+ @resource.concat_urls('http://example.com', '/resource').should == 'http://example.com/resource'
42
+ end
43
+
44
+ it "concatinates even non-string urls, :posts + 1 => 'posts/1'" do
45
+ @resource.concat_urls(:posts, 1).should == 'posts/1'
46
+ end
47
+
48
+ it "offers subresources via []" do
49
+ parent = RestClient::Resource.new('http://example.com')
50
+ parent['posts'].url.should == 'http://example.com/posts'
51
+ end
52
+ end
@@ -0,0 +1,202 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe RestClient do
4
+ context "public API" do
5
+ it "GET" do
6
+ RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {})
7
+ RestClient.get('http://some/resource')
8
+ end
9
+
10
+ it "POST" do
11
+ RestClient::Request.should_receive(:execute).with(:method => :post, :url => 'http://some/resource', :payload => 'payload', :headers => {})
12
+ RestClient.post('http://some/resource', 'payload')
13
+ end
14
+
15
+ it "PUT" do
16
+ RestClient::Request.should_receive(:execute).with(:method => :put, :url => 'http://some/resource', :payload => 'payload', :headers => {})
17
+ RestClient.put('http://some/resource', 'payload')
18
+ end
19
+
20
+ it "DELETE" do
21
+ RestClient::Request.should_receive(:execute).with(:method => :delete, :url => 'http://some/resource', :headers => {})
22
+ RestClient.delete('http://some/resource')
23
+ end
24
+ end
25
+
26
+ context RestClient::Request do
27
+ before do
28
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
29
+
30
+ @uri = mock("uri")
31
+ @uri.stub!(:request_uri).and_return('/resource')
32
+ @uri.stub!(:host).and_return('some')
33
+ @uri.stub!(:port).and_return(80)
34
+
35
+ @net = mock("net::http base")
36
+ @http = mock("net::http connection")
37
+ Net::HTTP.stub!(:new).and_return(@net)
38
+ @net.stub!(:start).and_yield(@http)
39
+ @net.stub!(:use_ssl=)
40
+ end
41
+
42
+ it "requests xml mimetype" do
43
+ RestClient::Request.default_headers[:accept].should == 'application/xml'
44
+ end
45
+
46
+ it "processes a successful result" do
47
+ res = mock("result")
48
+ res.stub!(:code).and_return("200")
49
+ res.stub!(:body).and_return('body')
50
+ @request.process_result(res).should == 'body'
51
+ end
52
+
53
+ it "parses a url into a URI object" do
54
+ URI.should_receive(:parse).with('http://example.com/resource')
55
+ @request.parse_url('http://example.com/resource')
56
+ end
57
+
58
+ it "adds http:// to the front of resources specified in the syntax example.com/resource" do
59
+ URI.should_receive(:parse).with('http://example.com/resource')
60
+ @request.parse_url('example.com/resource')
61
+ end
62
+
63
+ it "extracts the username and password when parsing http://user:password@example.com/" do
64
+ URI.stub!(:parse).and_return(mock('uri', :user => 'joe', :password => 'pass1'))
65
+ @request.parse_url_with_auth('http://joe:pass1@example.com/resource')
66
+ @request.user.should == 'joe'
67
+ @request.password.should == 'pass1'
68
+ end
69
+
70
+ it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
71
+ URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
72
+ @request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
73
+ @request.parse_url_with_auth('http://example.com/resource')
74
+ @request.user.should == 'beth'
75
+ @request.password.should == 'pass2'
76
+ end
77
+
78
+ it "determines the Net::HTTP class to instantiate by the method name" do
79
+ @request.net_http_class(:put).should == Net::HTTP::Put
80
+ end
81
+
82
+ it "merges user headers with the default headers" do
83
+ RestClient::Request.should_receive(:default_headers).and_return({ '1' => '2' })
84
+ @request.make_headers('3' => '4').should == { '1' => '2', '3' => '4' }
85
+ end
86
+
87
+ it "prefers the user header when the same header exists in the defaults" do
88
+ RestClient::Request.should_receive(:default_headers).and_return({ '1' => '2' })
89
+ @request.make_headers('1' => '3').should == { '1' => '3' }
90
+ end
91
+
92
+ it "converts header symbols from :content_type to 'Content-type'" do
93
+ RestClient::Request.should_receive(:default_headers).and_return({})
94
+ @request.make_headers(:content_type => 'abc').should == { 'Content-type' => 'abc' }
95
+ end
96
+
97
+ it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
98
+ @request.should_receive(:parse_url_with_auth).with('http://some/resource').and_return(@uri)
99
+ klass = mock("net:http class")
100
+ @request.should_receive(:net_http_class).with(:put).and_return(klass)
101
+ klass.should_receive(:new).and_return('result')
102
+ @request.should_receive(:transmit).with(@uri, 'result', 'payload')
103
+ @request.execute_inner
104
+ end
105
+
106
+ it "transmits the request with Net::HTTP" do
107
+ @http.should_receive(:request).with('req', 'payload')
108
+ @request.should_receive(:process_result)
109
+ @request.transmit(@uri, 'req', 'payload')
110
+ end
111
+
112
+ it "uses SSL when the URI refers to a https address" do
113
+ @uri.stub!(:is_a?).with(URI::HTTPS).and_return(true)
114
+ @net.should_receive(:use_ssl=).with(true)
115
+ @http.stub!(:request)
116
+ @request.stub!(:process_result)
117
+ @request.transmit(@uri, 'req', 'payload')
118
+ end
119
+
120
+ it "doesn't send nil payloads" do
121
+ @http.should_receive(:request).with('req', '')
122
+ @request.should_receive(:process_result)
123
+ @request.transmit(@uri, 'req', nil)
124
+ end
125
+
126
+ it "passes non-hash payloads straight through" do
127
+ @request.process_payload("x").should == "x"
128
+ end
129
+
130
+ it "converts a hash payload to urlencoded data" do
131
+ @request.process_payload(:a => 'b c+d').should == "a=b%20c%2Bd"
132
+ end
133
+
134
+ it "set urlencoded content_type header on hash payloads" do
135
+ @request.process_payload(:a => 1)
136
+ @request.headers[:content_type].should == 'application/x-www-form-urlencoded'
137
+ end
138
+
139
+ it "sets up the credentials prior to the request" do
140
+ @http.stub!(:request)
141
+ @request.stub!(:process_result)
142
+
143
+ @request.stub!(:user).and_return('joe')
144
+ @request.stub!(:password).and_return('mypass')
145
+ @request.should_receive(:setup_credentials).with('req')
146
+
147
+ @request.transmit(@uri, 'req', nil)
148
+ end
149
+
150
+ it "does not attempt to send any credentials if user is nil" do
151
+ @request.stub!(:user).and_return(nil)
152
+ req = mock("request")
153
+ req.should_not_receive(:basic_auth)
154
+ @request.setup_credentials(req)
155
+ end
156
+
157
+ it "setup credentials when there's a user" do
158
+ @request.stub!(:user).and_return('joe')
159
+ @request.stub!(:password).and_return('mypass')
160
+ req = mock("request")
161
+ req.should_receive(:basic_auth).with('joe', 'mypass')
162
+ @request.setup_credentials(req)
163
+ end
164
+
165
+ it "catches EOFError and shows the more informative ServerBrokeConnection" do
166
+ @http.stub!(:request).and_raise(EOFError)
167
+ lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::ServerBrokeConnection)
168
+ end
169
+
170
+ it "execute calls execute_inner" do
171
+ @request.should_receive(:execute_inner)
172
+ @request.execute
173
+ end
174
+
175
+ it "class method execute wraps constructor" do
176
+ req = mock("rest request")
177
+ RestClient::Request.should_receive(:new).with(1 => 2).and_return(req)
178
+ req.should_receive(:execute)
179
+ RestClient::Request.execute(1 => 2)
180
+ end
181
+
182
+ it "raises a Redirect with the new location when the response is in the 30x range" do
183
+ res = mock('response', :code => '301', :header => { 'Location' => 'http://new/resource' })
184
+ lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect, 'http://new/resource')
185
+ end
186
+
187
+ it "raises Unauthorized when the response is 401" do
188
+ res = mock('response', :code => '401')
189
+ lambda { @request.process_result(res) }.should raise_error(RestClient::Unauthorized)
190
+ end
191
+
192
+ it "raises ResourceNotFound when the response is 404" do
193
+ res = mock('response', :code => '404')
194
+ lambda { @request.process_result(res) }.should raise_error(RestClient::ResourceNotFound)
195
+ end
196
+
197
+ it "raises RequestFailed otherwise" do
198
+ res = mock('response', :code => '500')
199
+ lambda { @request.process_result(res) }.should raise_error(RestClient::RequestFailed)
200
+ end
201
+ end
202
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: davetron5000-rest-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.3
5
+ platform: ruby
6
+ authors:
7
+ - Adam Wiggins
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-05 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
17
+ email: adam@heroku.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - README.rdoc
27
+ - rest-client.gemspec
28
+ - lib/request_errors.rb
29
+ - lib/resource.rb
30
+ - lib/rest_client.rb
31
+ - spec/base.rb
32
+ - spec/request_errors_spec.rb
33
+ - spec/resource_spec.rb
34
+ - spec/rest_client_spec.rb
35
+ has_rdoc: true
36
+ homepage: http://rest-client.heroku.com/
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project: rest-client
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: Simple REST client for Ruby, inspired by microframework syntax for specifying actions.
61
+ test_files: []
62
+