rest-client 0.1
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.
Potentially problematic release.
This version of rest-client might be problematic. Click here for more details.
- data/Rakefile +82 -0
- data/lib/rest_client.rb +98 -0
- data/spec/base.rb +4 -0
- data/spec/rest_client_spec.rb +114 -0
- metadata +56 -0
data/Rakefile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc "Run all specs"
|
5
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
6
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Print specdocs"
|
10
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
11
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
12
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run all examples with RCov"
|
16
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
17
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
18
|
+
t.rcov = true
|
19
|
+
t.rcov_opts = ['--exclude', 'examples']
|
20
|
+
end
|
21
|
+
|
22
|
+
task :default => :spec
|
23
|
+
|
24
|
+
######################################################
|
25
|
+
|
26
|
+
require 'rake'
|
27
|
+
require 'rake/testtask'
|
28
|
+
require 'rake/clean'
|
29
|
+
require 'rake/gempackagetask'
|
30
|
+
require 'rake/rdoctask'
|
31
|
+
require 'fileutils'
|
32
|
+
|
33
|
+
version = "0.1"
|
34
|
+
name = "rest-client"
|
35
|
+
|
36
|
+
spec = Gem::Specification.new do |s|
|
37
|
+
s.name = name
|
38
|
+
s.version = version
|
39
|
+
s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
|
40
|
+
s.description = "A simple REST client for Ruby, inspired by the microframework (Camping, Sinatra...) style of specifying actions: get, put, post, delete."
|
41
|
+
s.author = "Adam Wiggins"
|
42
|
+
s.email = "adam@heroku.com"
|
43
|
+
s.homepage = "http://rest-client.heroku.com/"
|
44
|
+
s.rubyforge_project = "rest-client"
|
45
|
+
|
46
|
+
s.platform = Gem::Platform::RUBY
|
47
|
+
s.has_rdoc = true
|
48
|
+
|
49
|
+
s.files = %w(Rakefile) + Dir.glob("{lib,spec}/**/*")
|
50
|
+
|
51
|
+
s.require_path = "lib"
|
52
|
+
end
|
53
|
+
|
54
|
+
Rake::GemPackageTask.new(spec) do |p|
|
55
|
+
p.need_tar = true if RUBY_PLATFORM !~ /mswin/
|
56
|
+
end
|
57
|
+
|
58
|
+
task :install => [ :package ] do
|
59
|
+
sh %{sudo gem install pkg/#{name}-#{version}.gem}
|
60
|
+
end
|
61
|
+
|
62
|
+
task :uninstall => [ :clean ] do
|
63
|
+
sh %{sudo gem uninstall #{name}}
|
64
|
+
end
|
65
|
+
|
66
|
+
Rake::TestTask.new do |t|
|
67
|
+
t.libs << "spec"
|
68
|
+
t.test_files = FileList['spec/*_spec.rb']
|
69
|
+
t.verbose = true
|
70
|
+
end
|
71
|
+
|
72
|
+
Rake::RDocTask.new do |t|
|
73
|
+
t.rdoc_dir = 'rdoc'
|
74
|
+
t.title = "rest-client, fetch RESTful resources effortlessly"
|
75
|
+
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
76
|
+
t.options << '--charset' << 'utf-8'
|
77
|
+
t.rdoc_files.include('README')
|
78
|
+
t.rdoc_files.include('lib/rest_client.rb')
|
79
|
+
end
|
80
|
+
|
81
|
+
CLEAN.include [ 'pkg', '*.gem', '.config' ]
|
82
|
+
|
data/lib/rest_client.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
# This module's static methods are the entry point for using the REST client.
|
5
|
+
module RestClient
|
6
|
+
def self.get(url, headers={})
|
7
|
+
Request.new(:get, url, nil, headers).execute
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.post(url, payload=nil, headers={})
|
11
|
+
Request.new(:post, url, payload, headers).execute
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.put(url, payload=nil, headers={})
|
15
|
+
Request.new(:put, url, payload, headers).execute
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.delete(url, headers={})
|
19
|
+
Request.new(:delete, url, nil, headers).execute
|
20
|
+
end
|
21
|
+
|
22
|
+
# Internal class used to build and execute the request.
|
23
|
+
class Request
|
24
|
+
attr_reader :method, :url, :payload, :headers
|
25
|
+
|
26
|
+
def initialize(method, url, payload, headers)
|
27
|
+
@method = method
|
28
|
+
@url = url
|
29
|
+
@payload = payload
|
30
|
+
@headers = headers
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute
|
34
|
+
execute_inner
|
35
|
+
rescue Redirect => e
|
36
|
+
@url = e.message
|
37
|
+
execute
|
38
|
+
end
|
39
|
+
|
40
|
+
def execute_inner
|
41
|
+
uri = parse_url(url)
|
42
|
+
transmit uri, net_http_class(method).new(uri.path, make_headers(headers)), payload
|
43
|
+
end
|
44
|
+
|
45
|
+
def make_headers(user_headers)
|
46
|
+
final = {}
|
47
|
+
merged = default_headers.merge(user_headers)
|
48
|
+
merged.keys.each do |key|
|
49
|
+
final[key.to_s.gsub(/_/, '-').capitalize] = merged[key]
|
50
|
+
end
|
51
|
+
final
|
52
|
+
end
|
53
|
+
|
54
|
+
def net_http_class(method)
|
55
|
+
Object.module_eval "Net::HTTP::#{method.to_s.capitalize}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_url(url)
|
59
|
+
url = "http://#{url}" unless url.match(/^http/)
|
60
|
+
URI.parse(url)
|
61
|
+
end
|
62
|
+
|
63
|
+
# A redirect was encountered; caught by execute to retry with the new url.
|
64
|
+
class Redirect < Exception; end
|
65
|
+
|
66
|
+
# Request failed with an unhandled http error code.
|
67
|
+
class RequestFailed < Exception; end
|
68
|
+
|
69
|
+
# Authorization is required to access the resource specified.
|
70
|
+
class Unauthorized < Exception; end
|
71
|
+
|
72
|
+
def transmit(uri, req, payload)
|
73
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
74
|
+
process_result http.request(req, payload || "")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def process_result(res)
|
79
|
+
if %w(200 201 202).include? res.code
|
80
|
+
res.body
|
81
|
+
elsif %w(301 302 303).include? res.code
|
82
|
+
raise Redirect, res.header['Location']
|
83
|
+
elsif res.code == "401"
|
84
|
+
raise Unauthorized
|
85
|
+
else
|
86
|
+
raise RequestFailed, error_message(res)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def error_message(res)
|
91
|
+
"HTTP code #{res.code}: #{res.body}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def default_headers
|
95
|
+
{ :accept => 'application/xml' }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/spec/base.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe RestClient do
|
4
|
+
context "public API" do
|
5
|
+
before do
|
6
|
+
@request = mock("restclient request")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "GET" do
|
10
|
+
RestClient::Request.should_receive(:new).with(:get, 'http://some/resource', nil, {}).and_return(@request)
|
11
|
+
@request.should_receive(:execute)
|
12
|
+
RestClient.get('http://some/resource')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "POST" do
|
16
|
+
RestClient::Request.should_receive(:new).with(:post, 'http://some/resource', 'payload', {}).and_return(@request)
|
17
|
+
@request.should_receive(:execute)
|
18
|
+
RestClient.post('http://some/resource', 'payload')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "PUT" do
|
22
|
+
RestClient::Request.should_receive(:new).with(:put, 'http://some/resource', 'payload', {}).and_return(@request)
|
23
|
+
@request.should_receive(:execute)
|
24
|
+
RestClient.put('http://some/resource', 'payload')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "DELETE" do
|
28
|
+
RestClient::Request.should_receive(:new).with(:delete, 'http://some/resource', nil, {}).and_return(@request)
|
29
|
+
@request.should_receive(:execute)
|
30
|
+
RestClient.delete('http://some/resource')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context RestClient::Request do
|
35
|
+
before do
|
36
|
+
@request = RestClient::Request.new(:put, 'http://some/resource', 'payload', {})
|
37
|
+
|
38
|
+
@uri = mock("uri")
|
39
|
+
@uri.stub!(:path).and_return('/resource')
|
40
|
+
@uri.stub!(:host).and_return('some')
|
41
|
+
@uri.stub!(:port).and_return(80)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "requests xml mimetype" do
|
45
|
+
@request.default_headers[:accept].should == 'application/xml'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "processes a successful result" do
|
49
|
+
res = mock("result")
|
50
|
+
res.stub!(:code).and_return("200")
|
51
|
+
res.stub!(:body).and_return('body')
|
52
|
+
@request.process_result(res).should == 'body'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "parses a url into a URI object" do
|
56
|
+
URI.should_receive(:parse).with('http://example.com/resource')
|
57
|
+
@request.parse_url('http://example.com/resource')
|
58
|
+
end
|
59
|
+
|
60
|
+
it "adds http:// to the front of resources specified in the syntax example.com/resource" do
|
61
|
+
URI.should_receive(:parse).with('http://example.com/resource')
|
62
|
+
@request.parse_url('example.com/resource')
|
63
|
+
end
|
64
|
+
|
65
|
+
it "determines the Net::HTTP class to instantiate by the method name" do
|
66
|
+
@request.net_http_class(:put).should == Net::HTTP::Put
|
67
|
+
end
|
68
|
+
|
69
|
+
it "merges user headers with the default headers" do
|
70
|
+
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
71
|
+
@request.make_headers('3' => '4').should == { '1' => '2', '3' => '4' }
|
72
|
+
end
|
73
|
+
|
74
|
+
it "prefers the user header when the same header exists in the defaults" do
|
75
|
+
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
76
|
+
@request.make_headers('1' => '3').should == { '1' => '3' }
|
77
|
+
end
|
78
|
+
|
79
|
+
it "converts header symbols from :content_type to 'Content-type'" do
|
80
|
+
@request.should_receive(:default_headers).and_return({})
|
81
|
+
@request.make_headers(:content_type => 'abc').should == { 'Content-type' => 'abc' }
|
82
|
+
end
|
83
|
+
|
84
|
+
it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
|
85
|
+
@request.should_receive(:parse_url).with('http://some/resource').and_return(@uri)
|
86
|
+
klass = mock("net:http class")
|
87
|
+
@request.should_receive(:net_http_class).with(:put).and_return(klass)
|
88
|
+
klass.should_receive(:new).and_return('result')
|
89
|
+
@request.should_receive(:transmit).with(@uri, 'result', 'payload')
|
90
|
+
@request.execute_inner
|
91
|
+
end
|
92
|
+
|
93
|
+
it "transmits the request with Net::HTTP" do
|
94
|
+
http = mock("net::http connection")
|
95
|
+
Net::HTTP.should_receive(:start).and_yield(http)
|
96
|
+
http.should_receive(:request).with('req', 'payload')
|
97
|
+
@request.should_receive(:process_result)
|
98
|
+
@request.transmit(@uri, 'req', 'payload')
|
99
|
+
end
|
100
|
+
|
101
|
+
it "doesn't send nil payloads" do
|
102
|
+
http = mock("net::http connection")
|
103
|
+
Net::HTTP.should_receive(:start).and_yield(http)
|
104
|
+
http.should_receive(:request).with('req', '')
|
105
|
+
@request.should_receive(:process_result)
|
106
|
+
@request.transmit(@uri, 'req', nil)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "execute calls execute_inner" do
|
110
|
+
@request.should_receive(:execute_inner)
|
111
|
+
@request.execute
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rest-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Wiggins
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-03-08 23:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: "A simple REST client for Ruby, inspired by the microframework (Camping, Sinatra...) 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
|
+
- lib/rest_client.rb
|
27
|
+
- spec/base.rb
|
28
|
+
- spec/rest_client_spec.rb
|
29
|
+
has_rdoc: true
|
30
|
+
homepage: http://rest-client.heroku.com/
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: "0"
|
41
|
+
version:
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
requirements: []
|
49
|
+
|
50
|
+
rubyforge_project: rest-client
|
51
|
+
rubygems_version: 1.0.1
|
52
|
+
signing_key:
|
53
|
+
specification_version: 2
|
54
|
+
summary: Simple REST client for Ruby, inspired by microframework syntax for specifying actions.
|
55
|
+
test_files: []
|
56
|
+
|