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 CHANGED
@@ -41,9 +41,3 @@ ruby 1.9.2
41
41
  Tests can run offline thanks to [webmock](https://github.com/bblimke/webmock).
42
42
 
43
43
  $ bundle exec rake
44
-
45
- ## TODOs
46
-
47
- * avoid the parse (eg client.raw)
48
- * override type per request
49
- * support hased query params
@@ -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"
@@ -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+: A String containing the username for use in HTTP Basic Authentication.
29
- # +pass+: A String containing the password for use in HTTP Basic Authentication.
30
- # +port+: An Integer containing the port to connect.
31
- # +scheme+: A String containing the HTTP scheme.
32
- # +type+: A Symbol containing (:json or :xml) for automatic content-type parsing
33
- # and encoding.
34
- # +headers+: A Hash containing HTTP headers.
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] || 80
43
- scheme = options[:scheme] || "http"
44
- @type = options[:type] || :json
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
- # Method arguments have been documented in the callers.
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 net/http request Object.
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
- uploads request, upload
112
- headers request, upload
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
- request.body_stream.close if request.body_stream
121
+ finish_uploads request
118
122
 
119
- @errors.errors response
123
+ @errors.on response
120
124
  end
121
125
 
122
126
  ##
123
127
  # Handle chunked uploads.
124
128
  #
125
- # +request+: A net/http request Object.
126
- # +upload+: A Hash containing :file and :headers for uploading.
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 uploads request, upload
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 net/http request Object.
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 headers request, upload
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", MIME_TYPES[@type]
146
- request.add_field "Content-Type", MIME_TYPES[@type] if requires_content_type? request
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
- @headers.each do |header, value|
165
+ headers.merge! chunked_headers upload
166
+ headers.each do |header, value|
149
167
  request[header] = value
150
- end if @headers
168
+ end
151
169
  end
152
170
 
153
171
  ##
154
172
  # Setting of chunked upload headers.
155
173
  #
156
- # +:upload+: A Hash with the following keys:
157
- # +file+: The file to be HTTP chunked uploaded.
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" => MIME_TYPES[:binary],
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
- @type == :xml
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 is_json?
203
- @type == :json
225
+ def mime_type type
226
+ MIME_TYPES[type] || MIME_TYPES[@type]
204
227
  end
205
228
  end
206
229
  end
@@ -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 errors response
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
@@ -1,3 +1,3 @@
1
1
  module Hugs
2
- VERSION = "2.6.0"
2
+ VERSION = "2.7.0"
3
3
  end
@@ -69,29 +69,44 @@ describe Hugs::Client do
69
69
  end
70
70
  end
71
71
 
72
- describe "custom" do
72
+ describe "override :headers per request" do
73
73
  before do
74
74
  @client = Hugs::Client.new(
75
- :host => @uri.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, {}, "foo" => "bar", "baz" => "xyzzy"
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 "custom override defaults" do
88
+ describe "override :type per request" do
86
89
  before do
87
90
  @client = Hugs::Client.new(
88
- :host => @uri.host,
89
- :headers => { "Accept" => "foo bar" }
91
+ :host => @uri.host
90
92
  )
91
93
  end
92
94
 
93
- it "replaces headers" do
94
- must_have_headers_for :get, {}, "Accept" => "foo bar"
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 chunked headers" do
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 "upload headers override default headers" do
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, {}, "Accept" => ["*/*", "application/#{type}"]
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, {}, "Content-Type" => "application/#{type}"
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, {}, "Content-Type" => "application/#{type}"
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.6.0
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-05-17 00:00:00 -07:00
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: &id005 !ruby/object:Gem::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: *id005
60
+ version_requirements: *id004
71
61
  - !ruby/object:Gem::Dependency
72
62
  name: webmock
73
63
  prerelease: false
74
- requirement: &id006 !ruby/object:Gem::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: *id006
71
+ version_requirements: *id005
82
72
  - !ruby/object:Gem::Dependency
83
73
  name: minitest
84
74
  prerelease: false
85
- requirement: &id007 !ruby/object:Gem::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: *id007
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: []