hugs 2.6.0 → 2.7.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/README.md +0 -6
- data/hugs.gemspec +2 -3
- data/lib/hugs/client.rb +77 -54
- data/lib/hugs/errors.rb +2 -2
- data/lib/hugs/version.rb +1 -1
- data/test/lib/hugs/client_test.rb +46 -17
- metadata +10 -19
data/README.md
CHANGED
data/hugs.gemspec
CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "hugs"
|
7
7
|
s.version = Hugs::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ["John Dewey"]
|
10
|
-
s.email = ["john@dewey.ws"]
|
9
|
+
s.authors = ["John Dewey", "Josh Kleinpeter"]
|
10
|
+
s.email = ["john@dewey.ws", "josh@kleinpeter.org"]
|
11
11
|
s.homepage = %q{http://github.com/retr0h/hugs}
|
12
12
|
s.summary = %q{Hugs net-http-persistent with convenient get, delete, post, and put methods.}
|
13
13
|
|
@@ -21,7 +21,6 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency "yajl-ruby", "~> 0.7.9"
|
22
22
|
s.add_dependency "nokogiri", "~> 1.4.4"
|
23
23
|
s.add_dependency "net-http-persistent", "~> 1.4.1"
|
24
|
-
s.add_dependency "multipart-post", "~> 1.0.1"
|
25
24
|
|
26
25
|
s.add_development_dependency "rake"
|
27
26
|
s.add_development_dependency "webmock"
|
data/lib/hugs/client.rb
CHANGED
@@ -11,6 +11,7 @@ module Hugs
|
|
11
11
|
:binary => "application/octet-stream",
|
12
12
|
:json => "application/json",
|
13
13
|
:xml => "application/xml",
|
14
|
+
:none => "text/plain"
|
14
15
|
}.freeze
|
15
16
|
|
16
17
|
CLASSES = [
|
@@ -25,24 +26,22 @@ module Hugs
|
|
25
26
|
# Required options:
|
26
27
|
# +host+: A String with the host to connect.
|
27
28
|
# Optional:
|
28
|
-
# +user+:
|
29
|
-
# +pass+:
|
30
|
-
# +port+:
|
31
|
-
# +scheme+:
|
32
|
-
# +type+:
|
33
|
-
#
|
34
|
-
# +
|
35
|
-
# +raises+: A boolean if HTTP 4xx and 5xx status codes should be raised.
|
29
|
+
# +user+: A String containing the username for use in HTTP Basic Authentication.
|
30
|
+
# +pass+: A String containing the password for use in HTTP Basic Authentication.
|
31
|
+
# +port+: An Integer containing the port to connect.
|
32
|
+
# +scheme+: A String containing the HTTP scheme.
|
33
|
+
# +type+: A Symbol containing (:json or :xml) for automatic content-type parsing
|
34
|
+
# and encoding.
|
35
|
+
# +raises+: A boolean if HTTP 4xx and 5xx status codes should be raised.
|
36
36
|
|
37
37
|
def initialize options
|
38
38
|
@user = options[:user]
|
39
39
|
@pass = options[:password]
|
40
40
|
host = options[:host]
|
41
41
|
raises = options[:raise_errors]
|
42
|
-
port = options[:port]
|
43
|
-
scheme = options[:scheme]
|
44
|
-
@type = options[:type]
|
45
|
-
@headers = options[:headers] || {}
|
42
|
+
port = options[:port] || 80
|
43
|
+
scheme = options[:scheme] || "http"
|
44
|
+
@type = options[:type] || :json
|
46
45
|
|
47
46
|
@http = Net::HTTP::Persistent.new
|
48
47
|
@errors = Errors::HTTP.new :raise_errors => raises
|
@@ -53,14 +52,6 @@ module Hugs
|
|
53
52
|
|
54
53
|
##
|
55
54
|
# Perform an HTTP Delete, Head, Get, Post, or Put.
|
56
|
-
#
|
57
|
-
# +path+: A String with the path to the HTTP resource.
|
58
|
-
# +params+: A Hash with the following keys:
|
59
|
-
# +:query+: A String with the format "foo=bar".
|
60
|
-
# +:body+: A String containing the message body for Put and Post requests.
|
61
|
-
# +:upload+: A Hash with the following keys:
|
62
|
-
# +file+: The file to be HTTP chunked uploaded.
|
63
|
-
# +headers+: A Hash containing additional HTTP headers.
|
64
55
|
|
65
56
|
CLASSES.each do |clazz|
|
66
57
|
verb = clazz.to_s.split("::").last.downcase
|
@@ -92,74 +83,102 @@ module Hugs
|
|
92
83
|
##
|
93
84
|
# Worker method to be called by #delete, #get, #head, #post, or #put.
|
94
85
|
#
|
95
|
-
#
|
86
|
+
# +clazz+: A Net::HTTP request Object.
|
87
|
+
# +path+: A String with the path to the HTTP resource.
|
88
|
+
# +params+: A Hash containing the following keys:
|
89
|
+
# +:query+: A String with the format "foo=bar".
|
90
|
+
# +:type+: A Symbol with the mime_type.
|
91
|
+
# +:body+: A String containing the message body for Put and Post requests.
|
92
|
+
# +:upload+: A sub-Hash with the following keys:
|
93
|
+
# +:file+: The file to be HTTP chunked uploaded.
|
94
|
+
# +:headers+: A Hash containing additional HTTP headers.
|
96
95
|
|
97
96
|
def response_for clazz, path, params
|
98
97
|
request = clazz.new path_with_query path, params[:query]
|
99
|
-
request.body = encode params[:body]
|
98
|
+
request.body = encode params[:body], params[:type]
|
100
99
|
|
101
|
-
handle_request request, params[:upload]
|
100
|
+
handle_request request, params[:upload], params[:type], params[:headers] || {}
|
102
101
|
end
|
103
102
|
|
104
103
|
##
|
105
104
|
# Handles setting headers, performing the HTTP connection, parsing the
|
106
105
|
# response, and checking for any response errors (if configured).
|
107
106
|
#
|
108
|
-
# +request+: A
|
107
|
+
# +request+: A Net::HTTP request Object.
|
108
|
+
# +:upload+: A sub-Hash with the following keys:
|
109
|
+
# +:file+: The file to be HTTP chunked uploaded.
|
110
|
+
# +:headers+: A Hash containing additional HTTP headers.
|
111
|
+
# +:type+: A Symbol with the mime_type.
|
112
|
+
# +:headers+: A Hash containing additional HTTP headers.
|
109
113
|
|
110
|
-
def handle_request request, upload
|
111
|
-
|
112
|
-
|
114
|
+
def handle_request request, upload, type, headers
|
115
|
+
handle_uploads request, upload, type
|
116
|
+
handle_headers request, upload, type, headers
|
113
117
|
|
114
118
|
response = @http.request @uri, request
|
115
|
-
response.body = parse response.body
|
119
|
+
response.body = parse response.body, type
|
116
120
|
|
117
|
-
|
121
|
+
finish_uploads request
|
118
122
|
|
119
|
-
@errors.
|
123
|
+
@errors.on response
|
120
124
|
end
|
121
125
|
|
122
126
|
##
|
123
127
|
# Handle chunked uploads.
|
124
128
|
#
|
125
|
-
# +request+: A
|
126
|
-
# +upload+:
|
129
|
+
# +request+: A Net::HTTP request Object.
|
130
|
+
# +upload+: A Hash with the following keys:
|
131
|
+
# +:file+: The file to be HTTP chunked uploaded.
|
132
|
+
# +:headers+: A Hash containing additional HTTP headers.
|
133
|
+
# +:type+: A Symbol with the mime_type.
|
127
134
|
|
128
|
-
def
|
135
|
+
def handle_uploads request, upload, type
|
129
136
|
return unless upload
|
130
137
|
|
131
|
-
@headers.merge! chunked_headers upload if upload
|
132
|
-
|
133
138
|
request.body_stream = File.open upload[:file]
|
134
139
|
end
|
135
140
|
|
141
|
+
##
|
142
|
+
# Close filehandles used for chunked uploads.
|
143
|
+
#
|
144
|
+
# +request+: A Net::HTTP request Object.
|
145
|
+
|
146
|
+
def finish_uploads request
|
147
|
+
request.body_stream.close if request.body_stream
|
148
|
+
end
|
149
|
+
|
136
150
|
##
|
137
151
|
# Handles the setting of various default and custom headers.
|
138
152
|
# Headers set in initialize can override all others.
|
139
153
|
#
|
140
|
-
# +request+: A
|
154
|
+
# +request+: A Net::HTTP request Object.
|
155
|
+
# +upload+: A Hash with the following keys:
|
156
|
+
# +:type+: A Symbol with the mime_type.
|
157
|
+
# +:headers+: A Hash containing additional HTTP headers.
|
141
158
|
|
142
|
-
def
|
159
|
+
def handle_headers request, upload, type, headers
|
143
160
|
request.basic_auth(@user, @pass) if requires_authentication?
|
144
161
|
|
145
|
-
request.add_field "Accept",
|
146
|
-
request.add_field "Content-Type",
|
162
|
+
request.add_field "Accept", mime_type(type)
|
163
|
+
request.add_field "Content-Type", mime_type(type) if requires_content_type? request
|
147
164
|
|
148
|
-
|
165
|
+
headers.merge! chunked_headers upload
|
166
|
+
headers.each do |header, value|
|
149
167
|
request[header] = value
|
150
|
-
end
|
168
|
+
end
|
151
169
|
end
|
152
170
|
|
153
171
|
##
|
154
172
|
# Setting of chunked upload headers.
|
155
173
|
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
# +headers+: A Hash containing additional HTTP headers.
|
174
|
+
# +upload+: A Hash with the following keys:
|
175
|
+
# +:file+: The file to be HTTP chunked uploaded.
|
159
176
|
|
160
177
|
def chunked_headers upload
|
178
|
+
return {} unless upload
|
179
|
+
|
161
180
|
chunked_headers = {
|
162
|
-
"Content-Type" =>
|
181
|
+
"Content-Type" => mime_type(:binary),
|
163
182
|
"Content-Length" => File.size(upload[:file]),
|
164
183
|
"Transfer-Encoding" => "chunked"
|
165
184
|
}.merge upload[:headers] || {}
|
@@ -169,22 +188,22 @@ module Hugs
|
|
169
188
|
[path, query].compact.join "?"
|
170
189
|
end
|
171
190
|
|
172
|
-
def parse data
|
191
|
+
def parse data, type
|
173
192
|
return unless data
|
174
193
|
|
175
|
-
if is_json?
|
194
|
+
if is_json? type
|
176
195
|
Yajl::Parser.parse data
|
177
|
-
elsif is_xml?
|
196
|
+
elsif is_xml? type
|
178
197
|
Nokogiri::XML.parse data
|
179
198
|
else
|
180
199
|
data
|
181
200
|
end
|
182
201
|
end
|
183
202
|
|
184
|
-
def encode body
|
203
|
+
def encode body, type
|
185
204
|
return unless body
|
186
205
|
|
187
|
-
is_json? ? (Yajl::Encoder.encode body) : body
|
206
|
+
(is_json? type) ? (Yajl::Encoder.encode body) : body
|
188
207
|
end
|
189
208
|
|
190
209
|
def requires_authentication?
|
@@ -195,12 +214,16 @@ module Hugs
|
|
195
214
|
[Net::HTTP::Post, Net::HTTP::Put].include? request.class
|
196
215
|
end
|
197
216
|
|
198
|
-
def is_xml?
|
199
|
-
|
217
|
+
def is_xml? type
|
218
|
+
(mime_type type) =~ %r{/xml$}
|
219
|
+
end
|
220
|
+
|
221
|
+
def is_json? type
|
222
|
+
(mime_type type) =~ %r{/json$}
|
200
223
|
end
|
201
224
|
|
202
|
-
def
|
203
|
-
|
225
|
+
def mime_type type
|
226
|
+
MIME_TYPES[type] || MIME_TYPES[@type]
|
204
227
|
end
|
205
228
|
end
|
206
229
|
end
|
data/lib/hugs/errors.rb
CHANGED
@@ -42,11 +42,11 @@ module Hugs
|
|
42
42
|
class HTTP
|
43
43
|
def initialize options
|
44
44
|
@raise_errors = options[:raise_errors]
|
45
|
-
|
45
|
+
|
46
46
|
initialize_errors
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def on response
|
50
50
|
case response.code
|
51
51
|
when @raise_errors && %r{^[45]{1}[0-9]{2}$} ; raise_for(response)
|
52
52
|
end
|
data/lib/hugs/version.rb
CHANGED
@@ -69,29 +69,44 @@ describe Hugs::Client do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
describe "
|
72
|
+
describe "override :headers per request" do
|
73
73
|
before do
|
74
74
|
@client = Hugs::Client.new(
|
75
|
-
:host
|
76
|
-
:headers => { "foo" => "bar", "baz" => "xyzzy" }
|
75
|
+
:host => @uri.host
|
77
76
|
)
|
78
77
|
end
|
79
78
|
|
80
79
|
it "adds headers" do
|
81
|
-
must_have_headers_for :get, {
|
80
|
+
must_have_headers_for :get, { :headers => { "foo" => "bar" } }, "foo" => "bar"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "overrides default headers" do
|
84
|
+
must_have_headers_for :get, { :headers => { "Accept" => "foo bar" } }, "Accept" => "foo bar"
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
85
|
-
describe "
|
88
|
+
describe "override :type per request" do
|
86
89
|
before do
|
87
90
|
@client = Hugs::Client.new(
|
88
|
-
:host
|
89
|
-
:headers => { "Accept" => "foo bar" }
|
91
|
+
:host => @uri.host
|
90
92
|
)
|
91
93
|
end
|
92
94
|
|
93
|
-
it "
|
94
|
-
|
95
|
+
it "adds Accept header" do
|
96
|
+
must_have_headers_for_get :xml, :type => :xml
|
97
|
+
end
|
98
|
+
|
99
|
+
it "adds Content-Type header" do
|
100
|
+
must_have_headers_for_put :xml, :type => :xml
|
101
|
+
must_have_headers_for_post :xml, :type => :xml
|
102
|
+
end
|
103
|
+
|
104
|
+
it "doesn't parse body" do
|
105
|
+
stub_request(:get, @uri.to_s).to_return :body => "raw body"
|
106
|
+
|
107
|
+
response = @client.get @uri.path, :type => :none
|
108
|
+
|
109
|
+
response.body.must_equal "raw body"
|
95
110
|
end
|
96
111
|
end
|
97
112
|
end
|
@@ -139,6 +154,20 @@ describe Hugs::Client do
|
|
139
154
|
response.body.xpath('foo').text.must_equal "bar"
|
140
155
|
end
|
141
156
|
end
|
157
|
+
|
158
|
+
describe "none" do
|
159
|
+
before do
|
160
|
+
@client = Hugs::Client.new :host => @uri.host, :type => :none
|
161
|
+
end
|
162
|
+
|
163
|
+
it "doesn't parse body" do
|
164
|
+
stub_request(:get, @uri.to_s).to_return :body => "raw body"
|
165
|
+
|
166
|
+
response = @client.get @uri.path, :type => :none
|
167
|
+
|
168
|
+
response.body.must_equal "raw body"
|
169
|
+
end
|
170
|
+
end
|
142
171
|
end
|
143
172
|
|
144
173
|
describe "encode body" do
|
@@ -182,7 +211,7 @@ describe Hugs::Client do
|
|
182
211
|
}
|
183
212
|
end
|
184
213
|
|
185
|
-
it "has
|
214
|
+
it "has headers" do
|
186
215
|
must_have_headers_for :post, @upload, {
|
187
216
|
"Content-Type" => "application/octet-stream",
|
188
217
|
###"Content-Length" => "0",
|
@@ -190,7 +219,7 @@ describe Hugs::Client do
|
|
190
219
|
}
|
191
220
|
end
|
192
221
|
|
193
|
-
it "
|
222
|
+
it "overrides default headers" do
|
194
223
|
must_have_headers_for :post, @upload_with_headers, "Accept" => "foo bar"
|
195
224
|
end
|
196
225
|
|
@@ -257,16 +286,16 @@ describe Hugs::Client do
|
|
257
286
|
assert_requested verb, @uri.to_s
|
258
287
|
end
|
259
288
|
|
260
|
-
def must_have_headers_for_get type
|
261
|
-
must_have_headers_for :get,
|
289
|
+
def must_have_headers_for_get type, options = {}
|
290
|
+
must_have_headers_for :get, options, "Accept" => ["*/*", "application/#{type}"]
|
262
291
|
end
|
263
292
|
|
264
|
-
def must_have_headers_for_put type
|
265
|
-
must_have_headers_for :put,
|
293
|
+
def must_have_headers_for_put type, options = {}
|
294
|
+
must_have_headers_for :put, options, "Content-Type" => "application/#{type}"
|
266
295
|
end
|
267
296
|
|
268
|
-
def must_have_headers_for_post type
|
269
|
-
must_have_headers_for :post,
|
297
|
+
def must_have_headers_for_post type, options = {}
|
298
|
+
must_have_headers_for :post, options, "Content-Type" => "application/#{type}"
|
270
299
|
end
|
271
300
|
|
272
301
|
def must_have_headers_for verb, options, headers
|
metadata
CHANGED
@@ -2,15 +2,16 @@
|
|
2
2
|
name: hugs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 2.
|
5
|
+
version: 2.7.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- John Dewey
|
9
|
+
- Josh Kleinpeter
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
13
|
|
13
|
-
date: 2011-
|
14
|
+
date: 2011-06-07 00:00:00 -07:00
|
14
15
|
default_executable:
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
@@ -46,53 +47,43 @@ dependencies:
|
|
46
47
|
version: 1.4.1
|
47
48
|
type: :runtime
|
48
49
|
version_requirements: *id003
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
|
-
name: multipart-post
|
51
|
-
prerelease: false
|
52
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
-
none: false
|
54
|
-
requirements:
|
55
|
-
- - ~>
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: 1.0.1
|
58
|
-
type: :runtime
|
59
|
-
version_requirements: *id004
|
60
50
|
- !ruby/object:Gem::Dependency
|
61
51
|
name: rake
|
62
52
|
prerelease: false
|
63
|
-
requirement: &
|
53
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
54
|
none: false
|
65
55
|
requirements:
|
66
56
|
- - ">="
|
67
57
|
- !ruby/object:Gem::Version
|
68
58
|
version: "0"
|
69
59
|
type: :development
|
70
|
-
version_requirements: *
|
60
|
+
version_requirements: *id004
|
71
61
|
- !ruby/object:Gem::Dependency
|
72
62
|
name: webmock
|
73
63
|
prerelease: false
|
74
|
-
requirement: &
|
64
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
75
65
|
none: false
|
76
66
|
requirements:
|
77
67
|
- - ">="
|
78
68
|
- !ruby/object:Gem::Version
|
79
69
|
version: "0"
|
80
70
|
type: :development
|
81
|
-
version_requirements: *
|
71
|
+
version_requirements: *id005
|
82
72
|
- !ruby/object:Gem::Dependency
|
83
73
|
name: minitest
|
84
74
|
prerelease: false
|
85
|
-
requirement: &
|
75
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
86
76
|
none: false
|
87
77
|
requirements:
|
88
78
|
- - ">="
|
89
79
|
- !ruby/object:Gem::Version
|
90
80
|
version: "0"
|
91
81
|
type: :development
|
92
|
-
version_requirements: *
|
82
|
+
version_requirements: *id006
|
93
83
|
description:
|
94
84
|
email:
|
95
85
|
- john@dewey.ws
|
86
|
+
- josh@kleinpeter.org
|
96
87
|
executables: []
|
97
88
|
|
98
89
|
extensions: []
|