astrotrain 0.5.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/LICENSE +18 -17
- data/Rakefile +118 -103
- data/astrotrain.gemspec +87 -136
- data/lib/astrotrain.rb +47 -51
- data/lib/astrotrain/attachment.rb +55 -0
- data/lib/astrotrain/message.rb +221 -235
- data/lib/astrotrain/transports/http_post.rb +67 -0
- data/lib/astrotrain/transports/resque.rb +63 -0
- data/test/fixtures/bad_email_format.txt +15 -0
- data/test/fixtures/basic.txt +4 -1
- data/test/fixtures/iso-8859-1.txt +1 -0
- data/test/message_test.rb +146 -457
- data/test/test_helper.rb +20 -42
- data/test/transport_test.rb +98 -100
- metadata +100 -243
- data/.gitignore +0 -26
- data/README +0 -47
- data/VERSION +0 -1
- data/config/sample.rb +0 -12
- data/lib/astrotrain/api.rb +0 -52
- data/lib/astrotrain/logged_mail.rb +0 -48
- data/lib/astrotrain/mapping.rb +0 -162
- data/lib/astrotrain/mapping/http_post.rb +0 -18
- data/lib/astrotrain/mapping/jabber.rb +0 -28
- data/lib/astrotrain/mapping/transport.rb +0 -55
- data/lib/astrotrain/tmail.rb +0 -58
- data/lib/astrotrain/worker.rb +0 -65
- data/lib/vendor/rest-client/README.rdoc +0 -104
- data/lib/vendor/rest-client/Rakefile +0 -84
- data/lib/vendor/rest-client/bin/restclient +0 -65
- data/lib/vendor/rest-client/foo.diff +0 -66
- data/lib/vendor/rest-client/lib/rest_client.rb +0 -188
- data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +0 -23
- data/lib/vendor/rest-client/lib/rest_client/payload.rb +0 -185
- data/lib/vendor/rest-client/lib/rest_client/request_errors.rb +0 -75
- data/lib/vendor/rest-client/lib/rest_client/resource.rb +0 -103
- data/lib/vendor/rest-client/rest-client.gemspec +0 -18
- data/lib/vendor/rest-client/spec/base.rb +0 -5
- data/lib/vendor/rest-client/spec/master_shake.jpg +0 -0
- data/lib/vendor/rest-client/spec/payload_spec.rb +0 -71
- data/lib/vendor/rest-client/spec/request_errors_spec.rb +0 -44
- data/lib/vendor/rest-client/spec/resource_spec.rb +0 -52
- data/lib/vendor/rest-client/spec/rest_client_spec.rb +0 -219
- data/test/api_test.rb +0 -32
- data/test/logged_mail_test.rb +0 -67
- data/test/mapping_test.rb +0 -129
@@ -1,188 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'net/https'
|
3
|
-
|
4
|
-
require File.dirname(__FILE__) + '/rest_client/resource'
|
5
|
-
require File.dirname(__FILE__) + '/rest_client/request_errors'
|
6
|
-
require File.dirname(__FILE__) + '/rest_client/payload'
|
7
|
-
require File.dirname(__FILE__) + '/rest_client/net_http_ext'
|
8
|
-
|
9
|
-
# This module's static methods are the entry point for using the REST client.
|
10
|
-
#
|
11
|
-
# # GET
|
12
|
-
# xml = RestClient.get 'http://example.com/resource'
|
13
|
-
# jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
|
14
|
-
#
|
15
|
-
# # authentication and SSL
|
16
|
-
# RestClient.get 'https://user:password@example.com/private/resource'
|
17
|
-
#
|
18
|
-
# # POST or PUT with a hash sends parameters as a urlencoded form body
|
19
|
-
# RestClient.post 'http://example.com/resource', :param1 => 'one'
|
20
|
-
#
|
21
|
-
# # nest hash parameters
|
22
|
-
# RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
|
23
|
-
#
|
24
|
-
# # POST and PUT with raw payloads
|
25
|
-
# RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
|
26
|
-
# RestClient.post 'http://example.com/resource.xml', xml_doc
|
27
|
-
# RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
|
28
|
-
#
|
29
|
-
# # DELETE
|
30
|
-
# RestClient.delete 'http://example.com/resource'
|
31
|
-
#
|
32
|
-
# For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call:
|
33
|
-
#
|
34
|
-
# >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz'
|
35
|
-
# => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
|
36
|
-
#
|
37
|
-
module RestClient
|
38
|
-
def self.get(url, headers={}, &b)
|
39
|
-
Request.execute(:method => :get,
|
40
|
-
:url => url,
|
41
|
-
:headers => headers, &b)
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.post(url, payload, headers={}, &b)
|
45
|
-
Request.execute(:method => :post,
|
46
|
-
:url => url,
|
47
|
-
:payload => payload,
|
48
|
-
:headers => headers, &b)
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.put(url, payload, headers={}, &b)
|
52
|
-
Request.execute(:method => :put,
|
53
|
-
:url => url,
|
54
|
-
:payload => payload,
|
55
|
-
:headers => headers, &b)
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.delete(url, headers={}, &b)
|
59
|
-
Request.execute(:method => :delete,
|
60
|
-
:url => url,
|
61
|
-
:headers => headers, &b)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Internal class used to build and execute the request.
|
65
|
-
class Request
|
66
|
-
attr_reader :method, :url, :headers, :user, :password
|
67
|
-
|
68
|
-
def self.execute(args, &b)
|
69
|
-
new(args).execute(&b)
|
70
|
-
end
|
71
|
-
|
72
|
-
def initialize(args)
|
73
|
-
@method = args[:method] or raise ArgumentError, "must pass :method"
|
74
|
-
@url = args[:url] or raise ArgumentError, "must pass :url"
|
75
|
-
@payload = Payload.generate(args[:payload] || '')
|
76
|
-
@headers = args[:headers] || {}
|
77
|
-
@user = args[:user]
|
78
|
-
@password = args[:password]
|
79
|
-
end
|
80
|
-
|
81
|
-
def execute(&b)
|
82
|
-
execute_inner(&b)
|
83
|
-
rescue Redirect => e
|
84
|
-
@url = e.url
|
85
|
-
execute(&b)
|
86
|
-
end
|
87
|
-
|
88
|
-
def execute_inner(&b)
|
89
|
-
uri = parse_url_with_auth(url)
|
90
|
-
transmit(uri, net_http_class(method).new(uri.request_uri, make_headers(headers)), payload, &b)
|
91
|
-
end
|
92
|
-
|
93
|
-
def make_headers(user_headers)
|
94
|
-
final = {}
|
95
|
-
merged = default_headers.merge(user_headers)
|
96
|
-
merged.keys.each do |key|
|
97
|
-
final[key.to_s.gsub(/_/, '-').capitalize] = merged[key]
|
98
|
-
end
|
99
|
-
final
|
100
|
-
end
|
101
|
-
|
102
|
-
def net_http_class(method)
|
103
|
-
Net::HTTP.const_get(method.to_s.capitalize)
|
104
|
-
end
|
105
|
-
|
106
|
-
def parse_url(url)
|
107
|
-
url = "http://#{url}" unless url.match(/^http/)
|
108
|
-
URI.parse(url)
|
109
|
-
end
|
110
|
-
|
111
|
-
def parse_url_with_auth(url)
|
112
|
-
uri = parse_url(url)
|
113
|
-
@user = uri.user if uri.user
|
114
|
-
@password = uri.password if uri.password
|
115
|
-
uri
|
116
|
-
end
|
117
|
-
|
118
|
-
def process_payload(p=nil, parent_key=nil)
|
119
|
-
unless p.is_a?(Hash)
|
120
|
-
p
|
121
|
-
else
|
122
|
-
@headers[:content_type] ||= 'application/x-www-form-urlencoded'
|
123
|
-
p.keys.map do |k|
|
124
|
-
key = parent_key ? "#{parent_key}[#{k}]" : k
|
125
|
-
if p[k].is_a? Hash
|
126
|
-
process_payload(p[k], key)
|
127
|
-
else
|
128
|
-
value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
129
|
-
"#{key}=#{value}"
|
130
|
-
end
|
131
|
-
end.join("&")
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def transmit(uri, req, payload, &b)
|
136
|
-
setup_credentials(req)
|
137
|
-
|
138
|
-
net = Net::HTTP.new(uri.host, uri.port)
|
139
|
-
net.use_ssl = uri.is_a?(URI::HTTPS)
|
140
|
-
net.start do |http|
|
141
|
-
## Ok. I know this is weird but it's a hack for now
|
142
|
-
## this lets process_result determine if it should read the body
|
143
|
-
## into memory or not
|
144
|
-
process_result(http.request(req, payload || "", &b), &b)
|
145
|
-
end
|
146
|
-
rescue EOFError
|
147
|
-
raise RestClient::ServerBrokeConnection
|
148
|
-
rescue Timeout::Error
|
149
|
-
raise RestClient::RequestTimeout
|
150
|
-
ensure
|
151
|
-
payload.close
|
152
|
-
end
|
153
|
-
|
154
|
-
def setup_credentials(req)
|
155
|
-
req.basic_auth(user, password) if user
|
156
|
-
end
|
157
|
-
|
158
|
-
def process_result(res, &b)
|
159
|
-
if %w(200 201 202).include? res.code
|
160
|
-
return res.body unless b
|
161
|
-
elsif %w(301 302 303).include? res.code
|
162
|
-
url = res.header['Location']
|
163
|
-
|
164
|
-
if url !~ /^http/
|
165
|
-
uri = URI.parse(@url)
|
166
|
-
uri.path = "/#{url}".squeeze('/')
|
167
|
-
url = uri.to_s
|
168
|
-
end
|
169
|
-
|
170
|
-
raise Redirect, url
|
171
|
-
elsif res.code == "401"
|
172
|
-
raise Unauthorized
|
173
|
-
elsif res.code == "404"
|
174
|
-
raise ResourceNotFound
|
175
|
-
else
|
176
|
-
raise RequestFailed, res
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def payload
|
181
|
-
@payload
|
182
|
-
end
|
183
|
-
|
184
|
-
def default_headers
|
185
|
-
@payload.headers.merge({ :accept => 'application/xml' })
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
@@ -1,23 +0,0 @@
|
|
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
|
-
Net::HTTP.ssl_context_accessor(:tmp_dh_callback)
|
9
|
-
|
10
|
-
module Net
|
11
|
-
class HTTP
|
12
|
-
alias __request__ request
|
13
|
-
|
14
|
-
def request(req, body=nil, &block)
|
15
|
-
if body != nil && body.respond_to?(:read)
|
16
|
-
req.body_stream = body
|
17
|
-
return __request__(req, nil, &block)
|
18
|
-
else
|
19
|
-
return __request__(req, body, &block)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,185 +0,0 @@
|
|
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
|
@@ -1,75 +0,0 @@
|
|
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
|