entp-astrotrain 0.2.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/.gitignore +26 -0
- data/LICENSE +20 -0
- data/README +47 -0
- data/Rakefile +145 -0
- data/VERSION +1 -0
- data/astrotrain.gemspec +96 -0
- data/config/sample.rb +12 -0
- data/lib/astrotrain/api.rb +53 -0
- data/lib/astrotrain/logged_mail.rb +41 -0
- data/lib/astrotrain/mapping/http_post.rb +18 -0
- data/lib/astrotrain/mapping/jabber.rb +23 -0
- data/lib/astrotrain/mapping/transport.rb +55 -0
- data/lib/astrotrain/mapping.rb +157 -0
- data/lib/astrotrain/message.rb +313 -0
- data/lib/astrotrain/tmail.rb +48 -0
- data/lib/astrotrain.rb +55 -0
- data/lib/vendor/rest-client/README.rdoc +104 -0
- data/lib/vendor/rest-client/Rakefile +84 -0
- data/lib/vendor/rest-client/bin/restclient +65 -0
- data/lib/vendor/rest-client/foo.diff +66 -0
- data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +21 -0
- data/lib/vendor/rest-client/lib/rest_client/payload.rb +185 -0
- data/lib/vendor/rest-client/lib/rest_client/request_errors.rb +75 -0
- data/lib/vendor/rest-client/lib/rest_client/resource.rb +103 -0
- data/lib/vendor/rest-client/lib/rest_client.rb +189 -0
- data/lib/vendor/rest-client/rest-client.gemspec +18 -0
- data/lib/vendor/rest-client/spec/base.rb +5 -0
- data/lib/vendor/rest-client/spec/master_shake.jpg +0 -0
- data/lib/vendor/rest-client/spec/payload_spec.rb +71 -0
- data/lib/vendor/rest-client/spec/request_errors_spec.rb +44 -0
- data/lib/vendor/rest-client/spec/resource_spec.rb +52 -0
- data/lib/vendor/rest-client/spec/rest_client_spec.rb +219 -0
- data/tasks/doc.thor +149 -0
- data/tasks/merb.thor +2020 -0
- data/test/api_test.rb +28 -0
- data/test/fixtures/apple_multipart.txt +100 -0
- data/test/fixtures/basic.txt +14 -0
- data/test/fixtures/custom.txt +15 -0
- data/test/fixtures/fwd.txt +0 -0
- data/test/fixtures/gb2312_encoding.txt +16 -0
- data/test/fixtures/gb2312_encoding_invalid.txt +15 -0
- data/test/fixtures/html.txt +16 -0
- data/test/fixtures/iso-8859-1.txt +13 -0
- data/test/fixtures/mapped.txt +13 -0
- data/test/fixtures/multipart.txt +213 -0
- data/test/fixtures/multipart2.txt +213 -0
- data/test/fixtures/multiple.txt +13 -0
- data/test/fixtures/multiple_delivered_to.txt +14 -0
- data/test/fixtures/multiple_with_body_recipients.txt +15 -0
- data/test/fixtures/reply.txt +16 -0
- data/test/fixtures/utf-8.txt +13 -0
- data/test/logged_mail_test.rb +63 -0
- data/test/mapping_test.rb +129 -0
- data/test/message_test.rb +424 -0
- data/test/test_helper.rb +54 -0
- data/test/transport_test.rb +111 -0
- metadata +115 -0
@@ -0,0 +1,84 @@
|
|
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_opts = ['--colour --format progress --loadby mtime --reverse']
|
7
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Print specdocs"
|
11
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
12
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
13
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Run all examples with RCov"
|
17
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
18
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
19
|
+
t.rcov = true
|
20
|
+
t.rcov_opts = ['--exclude', 'examples']
|
21
|
+
end
|
22
|
+
|
23
|
+
task :default => :spec
|
24
|
+
|
25
|
+
######################################################
|
26
|
+
|
27
|
+
require 'rake'
|
28
|
+
require 'rake/testtask'
|
29
|
+
require 'rake/clean'
|
30
|
+
require 'rake/gempackagetask'
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
require 'fileutils'
|
33
|
+
|
34
|
+
version = "0.6.2"
|
35
|
+
name = "rest-client"
|
36
|
+
|
37
|
+
spec = Gem::Specification.new do |s|
|
38
|
+
s.name = name
|
39
|
+
s.version = version
|
40
|
+
s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
|
41
|
+
s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
42
|
+
s.author = "Adam Wiggins"
|
43
|
+
s.email = "adam@heroku.com"
|
44
|
+
s.homepage = "http://rest-client.heroku.com/"
|
45
|
+
s.rubyforge_project = "rest-client"
|
46
|
+
|
47
|
+
s.platform = Gem::Platform::RUBY
|
48
|
+
s.has_rdoc = true
|
49
|
+
|
50
|
+
s.files = %w(Rakefile) + Dir.glob("{lib,spec}/**/*")
|
51
|
+
s.executables = ['restclient']
|
52
|
+
|
53
|
+
s.require_path = "lib"
|
54
|
+
end
|
55
|
+
|
56
|
+
Rake::GemPackageTask.new(spec) do |p|
|
57
|
+
p.need_tar = true if RUBY_PLATFORM !~ /mswin/
|
58
|
+
end
|
59
|
+
|
60
|
+
task :install => [ :package ] do
|
61
|
+
sh %{sudo gem install pkg/#{name}-#{version}.gem}
|
62
|
+
end
|
63
|
+
|
64
|
+
task :uninstall => [ :clean ] do
|
65
|
+
sh %{sudo gem uninstall #{name}}
|
66
|
+
end
|
67
|
+
|
68
|
+
Rake::TestTask.new do |t|
|
69
|
+
t.libs << "spec"
|
70
|
+
t.test_files = FileList['spec/*_spec.rb']
|
71
|
+
t.verbose = true
|
72
|
+
end
|
73
|
+
|
74
|
+
Rake::RDocTask.new do |t|
|
75
|
+
t.rdoc_dir = 'rdoc'
|
76
|
+
t.title = "rest-client, fetch RESTful resources effortlessly"
|
77
|
+
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
78
|
+
t.options << '--charset' << 'utf-8'
|
79
|
+
t.rdoc_files.include('README.rdoc')
|
80
|
+
t.rdoc_files.include('lib/*.rb')
|
81
|
+
end
|
82
|
+
|
83
|
+
CLEAN.include [ 'pkg', '*.gem', '.config' ]
|
84
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
4
|
+
require 'rest_client'
|
5
|
+
|
6
|
+
require "yaml"
|
7
|
+
|
8
|
+
def usage(why = nil)
|
9
|
+
puts "failed for reason: #{why}" if why
|
10
|
+
puts "usage: restclient url|name [username] [password]"
|
11
|
+
exit(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
@url = ARGV.shift || 'http://localhost:4567'
|
15
|
+
|
16
|
+
config = YAML.load(File.read(ENV['HOME'] + "/.restclient")) rescue {}
|
17
|
+
|
18
|
+
@url, @username, @password = if c = config[@url]
|
19
|
+
[c['url'], c['username'], c['password']]
|
20
|
+
else
|
21
|
+
[@url, *ARGV]
|
22
|
+
end
|
23
|
+
|
24
|
+
usage("invalid url '#{@url}") unless @url =~ /^https?/
|
25
|
+
usage("to few args") unless ARGV.size < 3
|
26
|
+
|
27
|
+
def r
|
28
|
+
@r ||= RestClient::Resource.new(@url, @username, @password)
|
29
|
+
end
|
30
|
+
|
31
|
+
r # force rc to load
|
32
|
+
|
33
|
+
%w(get post put delete).each do |m|
|
34
|
+
eval <<-end_eval
|
35
|
+
def #{m}(path, *args, &b)
|
36
|
+
r[path].#{m}(*args, &b)
|
37
|
+
end
|
38
|
+
end_eval
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(s, *args, &b)
|
42
|
+
super unless r.respond_to?(s)
|
43
|
+
begin
|
44
|
+
r.send(s, *args, &b)
|
45
|
+
rescue RestClient::RequestFailed => e
|
46
|
+
puts e.response.body
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
require 'irb'
|
52
|
+
require 'irb/completion'
|
53
|
+
|
54
|
+
if File.exists? ".irbrc"
|
55
|
+
ENV['IRBRC'] = ".irbrc"
|
56
|
+
end
|
57
|
+
|
58
|
+
if File.exists?(rcfile = "~/.restclientrc")
|
59
|
+
load(rcfile)
|
60
|
+
end
|
61
|
+
|
62
|
+
ARGV.clear
|
63
|
+
|
64
|
+
IRB.start
|
65
|
+
exit!
|
@@ -0,0 +1,66 @@
|
|
1
|
+
commit 1a1a16d6abda6b40091b49d3ad082400018e665b
|
2
|
+
Author: rick <technoweenie@gmail.com>
|
3
|
+
Date: Wed Dec 10 20:01:02 2008 -0800
|
4
|
+
|
5
|
+
fix spacing issues with multipart bodies:
|
6
|
+
* the final boundary should be on a line below the final param's content.
|
7
|
+
* the ending double dash should be on the same line as the final boundary
|
8
|
+
|
9
|
+
diff --git a/lib/rest_client/payload.rb b/lib/rest_client/payload.rb
|
10
|
+
index dfc2921..e896be0 100644
|
11
|
+
--- a/lib/rest_client/payload.rb
|
12
|
+
+++ b/lib/rest_client/payload.rb
|
13
|
+
@@ -76,9 +76,10 @@ module RestClient
|
14
|
+
else
|
15
|
+
create_regular_field(@stream, k,v)
|
16
|
+
end
|
17
|
+
- @stream.write(b + EOL)
|
18
|
+
+ @stream.write(EOL + b)
|
19
|
+
end
|
20
|
+
@stream.write('--')
|
21
|
+
+ @stream.write(EOL)
|
22
|
+
@stream.seek(0)
|
23
|
+
end
|
24
|
+
|
25
|
+
diff --git a/spec/payload_spec.rb b/spec/payload_spec.rb
|
26
|
+
index 22b5a5a..35564ee 100644
|
27
|
+
--- a/spec/payload_spec.rb
|
28
|
+
+++ b/spec/payload_spec.rb
|
29
|
+
@@ -20,29 +20,27 @@ describe RestClient::Payload do
|
30
|
+
m.headers['Content-Type'].should == 'multipart/form-data; boundary="123"'
|
31
|
+
end
|
32
|
+
|
33
|
+
- xit "should form properly seperated multipart data" do
|
34
|
+
+ it "should form properly seperated multipart data" do
|
35
|
+
m = RestClient::Payload::Multipart.new({:foo => "bar"})
|
36
|
+
- m.stub!(:boundary).and_return("123")
|
37
|
+
m.to_s.should == <<-EOS
|
38
|
+
---123\r
|
39
|
+
+--#{m.boundary}\r
|
40
|
+
Content-Disposition: multipart/form-data; name="foo"\r
|
41
|
+
\r
|
42
|
+
bar\r
|
43
|
+
---123--\r
|
44
|
+
+--#{m.boundary}--\r
|
45
|
+
EOS
|
46
|
+
end
|
47
|
+
|
48
|
+
- xit "should form properly seperated multipart data" do
|
49
|
+
+ it "should form properly seperated multipart data" do
|
50
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
51
|
+
m = RestClient::Payload::Multipart.new({:foo => f})
|
52
|
+
- m.stub!(:boundary).and_return("123")
|
53
|
+
m.to_s.should == <<-EOS
|
54
|
+
---123\r
|
55
|
+
-Content-Disposition: multipart/form-data; name="foo"; filename="master_shake.jpg"\r
|
56
|
+
+--#{m.boundary}\r
|
57
|
+
+Content-Disposition: multipart/form-data; name="foo"; filename="./spec/master_shake.jpg"\r
|
58
|
+
Content-Type: image/jpeg\r
|
59
|
+
\r
|
60
|
+
-datadatadata\r
|
61
|
+
---123--\r
|
62
|
+
+#{IO.read(f.path)}\r
|
63
|
+
+--#{m.boundary}--\r
|
64
|
+
EOS
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# Replace the request method in Net::HTTP to sniff the body type
|
3
|
+
# and set the stream if appropriate
|
4
|
+
#
|
5
|
+
# Taken from:
|
6
|
+
# http://www.missiondata.com/blog/ruby/29/streaming-data-to-s3-with-ruby/
|
7
|
+
|
8
|
+
module Net
|
9
|
+
class HTTP
|
10
|
+
alias __request__ request
|
11
|
+
|
12
|
+
def request(req, body=nil, &block)
|
13
|
+
if body != nil && body.respond_to?(:read)
|
14
|
+
req.body_stream = body
|
15
|
+
return __request__(req, nil, &block)
|
16
|
+
else
|
17
|
+
return __request__(req, body, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require "tempfile"
|
2
|
+
require "stringio"
|
3
|
+
|
4
|
+
module RestClient
|
5
|
+
module Payload
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def generate(params)
|
9
|
+
if params.is_a?(String)
|
10
|
+
Base.new(params)
|
11
|
+
elsif params.delete(:multipart) == true ||
|
12
|
+
params.any? { |_,v| v.respond_to?(:path) && v.respond_to?(:read) }
|
13
|
+
Multipart.new(params)
|
14
|
+
else
|
15
|
+
UrlEncoded.new(params)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Base
|
20
|
+
def initialize(params)
|
21
|
+
build_stream(params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_stream(params)
|
25
|
+
@stream = StringIO.new(params)
|
26
|
+
@stream.seek(0)
|
27
|
+
end
|
28
|
+
|
29
|
+
def read(bytes=nil)
|
30
|
+
@stream.read(bytes)
|
31
|
+
end
|
32
|
+
alias :to_s :read
|
33
|
+
|
34
|
+
def escape(v)
|
35
|
+
URI.escape(v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
36
|
+
end
|
37
|
+
|
38
|
+
def headers
|
39
|
+
{ 'Content-Length' => size.to_s }
|
40
|
+
end
|
41
|
+
|
42
|
+
def size
|
43
|
+
@stream.size
|
44
|
+
end
|
45
|
+
alias :length :size
|
46
|
+
|
47
|
+
def close
|
48
|
+
@stream.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class UrlEncoded < Base
|
53
|
+
def build_stream(params)
|
54
|
+
@stream = StringIO.new(params.map do |k,v|
|
55
|
+
"#{escape(k)}=#{escape(v)}"
|
56
|
+
end.join("&"))
|
57
|
+
@stream.seek(0)
|
58
|
+
end
|
59
|
+
|
60
|
+
def headers
|
61
|
+
super.merge({ 'Content-Type' => 'application/x-www-form-urlencoded' })
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Multipart < Base
|
66
|
+
EOL = "\r\n"
|
67
|
+
|
68
|
+
def build_stream(params)
|
69
|
+
b = "--#{boundary}"
|
70
|
+
|
71
|
+
@stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
|
72
|
+
@stream.write(b)
|
73
|
+
params.each do |k,v|
|
74
|
+
@stream.write(EOL)
|
75
|
+
if v.respond_to?(:read) && v.respond_to?(:path)
|
76
|
+
create_file_field(@stream, k,v)
|
77
|
+
else
|
78
|
+
create_regular_field(@stream, k,v)
|
79
|
+
end
|
80
|
+
@stream.write(EOL + b)
|
81
|
+
end
|
82
|
+
@stream.write('--')
|
83
|
+
@stream.write(EOL)
|
84
|
+
@stream.seek(0)
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_regular_field(s, k, v)
|
88
|
+
s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"")
|
89
|
+
s.write(EOL)
|
90
|
+
s.write(EOL)
|
91
|
+
s.write(v)
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_file_field(s, k, v)
|
95
|
+
begin
|
96
|
+
s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"; filename=\"#{v.path}\"#{EOL}")
|
97
|
+
s.write("Content-Type: #{mime_for(v.path)}#{EOL}")
|
98
|
+
s.write(EOL)
|
99
|
+
while data = v.read(8124)
|
100
|
+
s.write(data)
|
101
|
+
end
|
102
|
+
ensure
|
103
|
+
v.close
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def mime_for(path)
|
108
|
+
ext = File.extname(path)[1..-1]
|
109
|
+
MIME_TYPES[ext] || 'text/plain'
|
110
|
+
end
|
111
|
+
|
112
|
+
def boundary
|
113
|
+
@boundary ||= rand(1_000_000).to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
def headers
|
117
|
+
super.merge({'Content-Type' => %Q{multipart/form-data; boundary="#{boundary}"}})
|
118
|
+
end
|
119
|
+
|
120
|
+
def close
|
121
|
+
@stream.close
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# :stopdoc:
|
126
|
+
# From WEBrick.
|
127
|
+
MIME_TYPES = {
|
128
|
+
"ai" => "application/postscript",
|
129
|
+
"asc" => "text/plain",
|
130
|
+
"avi" => "video/x-msvideo",
|
131
|
+
"bin" => "application/octet-stream",
|
132
|
+
"bmp" => "image/bmp",
|
133
|
+
"class" => "application/octet-stream",
|
134
|
+
"cer" => "application/pkix-cert",
|
135
|
+
"crl" => "application/pkix-crl",
|
136
|
+
"crt" => "application/x-x509-ca-cert",
|
137
|
+
"css" => "text/css",
|
138
|
+
"dms" => "application/octet-stream",
|
139
|
+
"doc" => "application/msword",
|
140
|
+
"dvi" => "application/x-dvi",
|
141
|
+
"eps" => "application/postscript",
|
142
|
+
"etx" => "text/x-setext",
|
143
|
+
"exe" => "application/octet-stream",
|
144
|
+
"gif" => "image/gif",
|
145
|
+
"gz" => "application/x-gzip",
|
146
|
+
"htm" => "text/html",
|
147
|
+
"html" => "text/html",
|
148
|
+
"jpe" => "image/jpeg",
|
149
|
+
"jpeg" => "image/jpeg",
|
150
|
+
"jpg" => "image/jpeg",
|
151
|
+
"js" => "text/javascript",
|
152
|
+
"lha" => "application/octet-stream",
|
153
|
+
"lzh" => "application/octet-stream",
|
154
|
+
"mov" => "video/quicktime",
|
155
|
+
"mpe" => "video/mpeg",
|
156
|
+
"mpeg" => "video/mpeg",
|
157
|
+
"mpg" => "video/mpeg",
|
158
|
+
"pbm" => "image/x-portable-bitmap",
|
159
|
+
"pdf" => "application/pdf",
|
160
|
+
"pgm" => "image/x-portable-graymap",
|
161
|
+
"png" => "image/png",
|
162
|
+
"pnm" => "image/x-portable-anymap",
|
163
|
+
"ppm" => "image/x-portable-pixmap",
|
164
|
+
"ppt" => "application/vnd.ms-powerpoint",
|
165
|
+
"ps" => "application/postscript",
|
166
|
+
"qt" => "video/quicktime",
|
167
|
+
"ras" => "image/x-cmu-raster",
|
168
|
+
"rb" => "text/plain",
|
169
|
+
"rd" => "text/plain",
|
170
|
+
"rtf" => "application/rtf",
|
171
|
+
"sgm" => "text/sgml",
|
172
|
+
"sgml" => "text/sgml",
|
173
|
+
"tif" => "image/tiff",
|
174
|
+
"tiff" => "image/tiff",
|
175
|
+
"txt" => "text/plain",
|
176
|
+
"xbm" => "image/x-xbitmap",
|
177
|
+
"xls" => "application/vnd.ms-excel",
|
178
|
+
"xml" => "text/xml",
|
179
|
+
"xpm" => "image/x-xpixmap",
|
180
|
+
"xwd" => "image/x-xwindowdump",
|
181
|
+
"zip" => "application/zip",
|
182
|
+
}
|
183
|
+
# :startdoc:
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module RestClient
|
2
|
+
# This is the base RestClient exception class. Rescue it if you want to
|
3
|
+
# catch any exception that your request might raise
|
4
|
+
class Exception < RuntimeError
|
5
|
+
def message(default=nil)
|
6
|
+
self.class::ErrorMessage
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# A redirect was encountered; caught by execute to retry with the new url.
|
11
|
+
class Redirect < Exception
|
12
|
+
ErrorMessage = "Redirect"
|
13
|
+
|
14
|
+
attr_accessor :url
|
15
|
+
def initialize(url)
|
16
|
+
@url = url
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Authorization is required to access the resource specified.
|
21
|
+
class Unauthorized < Exception
|
22
|
+
ErrorMessage = 'Unauthorized'
|
23
|
+
end
|
24
|
+
|
25
|
+
# No resource was found at the given URL.
|
26
|
+
class ResourceNotFound < Exception
|
27
|
+
ErrorMessage = 'Resource not found'
|
28
|
+
end
|
29
|
+
|
30
|
+
# The server broke the connection prior to the request completing.
|
31
|
+
class ServerBrokeConnection < Exception
|
32
|
+
ErrorMessage = 'Server broke connection'
|
33
|
+
end
|
34
|
+
|
35
|
+
# The server took too long to respond.
|
36
|
+
class RequestTimeout < Exception
|
37
|
+
ErrorMessage = 'Request timed out'
|
38
|
+
end
|
39
|
+
|
40
|
+
# The request failed, meaning the remote HTTP server returned a code other
|
41
|
+
# than success, unauthorized, or redirect.
|
42
|
+
#
|
43
|
+
# The exception message attempts to extract the error from the XML, using
|
44
|
+
# format returned by Rails: <errors><error>some message</error></errors>
|
45
|
+
#
|
46
|
+
# You can get the status code by e.http_code, or see anything about the
|
47
|
+
# response via e.response. For example, the entire result body (which is
|
48
|
+
# probably an HTML error page) is e.response.body.
|
49
|
+
class RequestFailed < Exception
|
50
|
+
attr_accessor :response
|
51
|
+
|
52
|
+
def initialize(response=nil)
|
53
|
+
@response = response
|
54
|
+
end
|
55
|
+
|
56
|
+
def http_code
|
57
|
+
@response.code.to_i if @response
|
58
|
+
end
|
59
|
+
|
60
|
+
def message
|
61
|
+
"HTTP status code #{http_code}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# backwards compatibility
|
71
|
+
class RestClient::Request
|
72
|
+
Redirect = RestClient::Redirect
|
73
|
+
Unauthorized = RestClient::Unauthorized
|
74
|
+
RequestFailed = RestClient::RequestFailed
|
75
|
+
end
|
@@ -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={}, &b)
|
30
|
+
Request.execute(:method => :get,
|
31
|
+
:url => url,
|
32
|
+
:user => user,
|
33
|
+
:password => password,
|
34
|
+
:headers => headers, &b)
|
35
|
+
end
|
36
|
+
|
37
|
+
def post(payload, headers={}, &b)
|
38
|
+
Request.execute(:method => :post,
|
39
|
+
:url => url,
|
40
|
+
:payload => payload,
|
41
|
+
:user => user,
|
42
|
+
:password => password,
|
43
|
+
:headers => headers, &b)
|
44
|
+
end
|
45
|
+
|
46
|
+
def put(payload, headers={}, &b)
|
47
|
+
Request.execute(:method => :put,
|
48
|
+
:url => url,
|
49
|
+
:payload => payload,
|
50
|
+
:user => user,
|
51
|
+
:password => password,
|
52
|
+
:headers => headers, &b)
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete(headers={}, &b)
|
56
|
+
Request.execute(:method => :delete,
|
57
|
+
:url => url,
|
58
|
+
:user => user,
|
59
|
+
:password => password,
|
60
|
+
:headers => headers, &b)
|
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
|