hugs 2.0.0 → 2.1.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/Gemfile CHANGED
@@ -3,6 +3,7 @@ source "http://rubygems.org"
3
3
  gem "yajl-ruby", "~> 0.7.8"
4
4
  gem "nokogiri", "~> 1.4.4"
5
5
  gem "net-http-persistent", "~> 1.4.1"
6
+ gem "multipart-post", "~> 1.0.1"
6
7
 
7
8
  group :development do
8
9
  gem "rake"
data/Gemfile.lock CHANGED
@@ -9,6 +9,7 @@ GEM
9
9
  git (>= 1.2.5)
10
10
  rake
11
11
  minitest (2.0.0)
12
+ multipart-post (1.0.1)
12
13
  net-http-persistent (1.4.1)
13
14
  nokogiri (1.4.4)
14
15
  rake (0.8.7)
@@ -23,6 +24,7 @@ PLATFORMS
23
24
  DEPENDENCIES
24
25
  jeweler (~> 1.5.1)
25
26
  minitest (~> 2.0.0)
27
+ multipart-post (~> 1.0.1)
26
28
  net-http-persistent (~> 1.4.1)
27
29
  nokogiri (~> 1.4.4)
28
30
  rake
data/README.md CHANGED
@@ -30,3 +30,7 @@ will be implemented against an XML OCCI API.
30
30
  ### Examples
31
31
 
32
32
  See the 'Examples' section in the [wiki](http://github.com/retr0h/hugs/wiki/).
33
+
34
+ ## Testing
35
+
36
+ $ bundle exec rake
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.1.0
data/hugs.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{hugs}
8
- s.version = "2.0.0"
8
+ s.version = "2.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["retr0h"]
12
- s.date = %q{2010-12-16}
12
+ s.date = %q{2010-12-23}
13
13
  s.email = %q{john@dewey.ws}
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE",
@@ -45,6 +45,7 @@ Gem::Specification.new do |s|
45
45
  s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
46
46
  s.add_runtime_dependency(%q<nokogiri>, ["~> 1.4.4"])
47
47
  s.add_runtime_dependency(%q<net-http-persistent>, ["~> 1.4.1"])
48
+ s.add_runtime_dependency(%q<multipart-post>, ["~> 1.0.1"])
48
49
  s.add_development_dependency(%q<rake>, [">= 0"])
49
50
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
50
51
  s.add_development_dependency(%q<minitest>, ["~> 2.0.0"])
@@ -53,6 +54,7 @@ Gem::Specification.new do |s|
53
54
  s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
54
55
  s.add_dependency(%q<nokogiri>, ["~> 1.4.4"])
55
56
  s.add_dependency(%q<net-http-persistent>, ["~> 1.4.1"])
57
+ s.add_dependency(%q<multipart-post>, ["~> 1.0.1"])
56
58
  s.add_dependency(%q<rake>, [">= 0"])
57
59
  s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
58
60
  s.add_dependency(%q<minitest>, ["~> 2.0.0"])
@@ -62,6 +64,7 @@ Gem::Specification.new do |s|
62
64
  s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
63
65
  s.add_dependency(%q<nokogiri>, ["~> 1.4.4"])
64
66
  s.add_dependency(%q<net-http-persistent>, ["~> 1.4.1"])
67
+ s.add_dependency(%q<multipart-post>, ["~> 1.0.1"])
65
68
  s.add_dependency(%q<rake>, [">= 0"])
66
69
  s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
67
70
  s.add_dependency(%q<minitest>, ["~> 2.0.0"])
data/lib/hugs.rb CHANGED
@@ -1,6 +1,4 @@
1
- require "net/http/persistent"
2
- require "yajl"
3
- require "nokogiri"
1
+ %w(net/http/persistent net/http/post/multipart yajl nokogiri).each { |r| require r }
4
2
 
5
3
  class Hugs
6
4
  Headers = {
@@ -8,7 +6,12 @@ class Hugs
8
6
  :xml => "application/xml",
9
7
  }.freeze
10
8
 
11
- Verbs = %w(get delete post put).freeze
9
+ Classes = [
10
+ Net::HTTP::Get,
11
+ Net::HTTP::Delete,
12
+ Net::HTTP::Post,
13
+ Net::HTTP::Put,
14
+ ].freeze
12
15
 
13
16
  ##
14
17
  # Required options:
@@ -36,11 +39,12 @@ class Hugs
36
39
  # - +:body+: A sub Hash to be JSON encoded, and posted in
37
40
  # the message body.
38
41
 
39
- Verbs.each do |verb|
42
+ Classes.each do |clazz|
43
+ verb = clazz.to_s.split("::")[-1].tr 'A-Z', 'a-z'
44
+
40
45
  define_method verb do |*args|
41
46
  path = args[0]
42
47
  params = args[1] || {}
43
- clazz = eval "Net::HTTP::#{verb.capitalize}"
44
48
 
45
49
  response = response_for(clazz, path, params)
46
50
  response.body = parse response.body
@@ -66,27 +70,40 @@ private
66
70
  # Method arguments have been documented in the callers.
67
71
 
68
72
  def response_for request, path, params
69
- query = params[:query] && params.delete(:query)
70
- body = params[:body] && params.delete(:body)
73
+ query = params[:query] && params.delete(:query)
74
+ body = params[:body] && params.delete(:body)
75
+ upload = params[:upload] && params.delete(:upload)
71
76
 
72
77
  @http ||= Net::HTTP::Persistent.new
73
78
  @url ||= URI.parse "#{@scheme}://#{@host}:#{@port}"
74
79
 
75
- request = request.new [path, query].compact.join "?"
76
- request.body = encode(body) if body
77
- common_headers request
80
+ if upload && request.class === Net::HTTP::Post
81
+ parts = upload[:parts] || {}
82
+ parts[:file] = UploadIO.new(parts[:file], upload[:content_type]) if parts[:file]
83
+
84
+ request = Net::HTTP::Post::Multipart.new path_with_query(path, query), parts
85
+ else
86
+ request = request.new path_with_query path, query
87
+ request.body = encode(body) if body
88
+
89
+ common_headers request
90
+ end
78
91
 
92
+ request.basic_auth(@user, @password) if requires_authentication?
79
93
  @http.request(@url, request)
80
94
  end
81
95
 
96
+ def path_with_query path, query
97
+ [path, query].compact.join "?"
98
+ end
99
+
82
100
  def common_headers request
83
- request.basic_auth(@user, @password) if requires_authentication?
84
- case request.class.to_s
85
- when Net::HTTP::Get.to_s, Net::HTTP::Delete.to_s
86
- request.add_field "accept", Headers[@type]
87
- when Net::HTTP::Post.to_s, Net::HTTP::Put.to_s
88
- request.add_field "accept", Headers[@type]
89
- request.add_field "content-type", Headers[@type]
101
+ case request
102
+ when Net::HTTP::Get, Net::HTTP::Delete
103
+ request.add_field "Accept", Headers[@type]
104
+ when Net::HTTP::Post, Net::HTTP::Put
105
+ request.add_field "Accept", Headers[@type]
106
+ request.add_field "Content-Type", Headers[@type]
90
107
  end
91
108
  end
92
109
 
data/test/test_hugs.rb CHANGED
@@ -2,10 +2,14 @@
2
2
 
3
3
  describe Hugs do
4
4
  before do
5
+ @scheme = "https"
6
+ @host = "example.com"
7
+ @port = 80
8
+ @base = "#{@host}:#{@port}"
5
9
  @valid_options = {
6
- :host => "example.com",
7
- :port => 80,
8
- :scheme => "https",
10
+ :host => @host,
11
+ :port => @port,
12
+ :scheme => @scheme,
9
13
  }
10
14
 
11
15
  WebMock.reset!
@@ -17,39 +21,91 @@ describe Hugs do
17
21
  @request = Net::HTTP::Get
18
22
  end
19
23
 
20
- it "generates a path" do
21
- stub_request :get, "https://example.com:80/"
24
+ describe "path" do
25
+ it "is valid" do
26
+ stub_request :get, "#{@scheme}://#{@base}/"
22
27
 
23
- @instance.send :response_for, @request, "/", {}
28
+ @instance.send :response_for, @request, "/", {}
24
29
 
25
- assert_requested :get, "https://example.com:80/"
26
- end
30
+ assert_requested :get, "#{@scheme}://#{@base}/"
31
+ end
32
+
33
+ it "is valid when an invalid :query is supplied" do
34
+ stub_request :get, "#{@scheme}://#{@base}/"
35
+
36
+ @instance.send :response_for, @request, "/", :query => nil
37
+
38
+ assert_requested :get, "#{@scheme}://#{@base}/"
39
+ end
27
40
 
28
- it "generates a path when a valid :query exists" do
29
- stub_request(:get, "https://example.com:80/").with:query => {"foo" => "bar"}
41
+ it "also has a query string" do
42
+ stub_request(:get, "#{@scheme}://#{@base}/").with:query => {"foo" => "bar"}
30
43
 
31
- @instance.send :response_for, @request, "/", :query => "foo=bar"
44
+ @instance.send :response_for, @request, "/", :query => "foo=bar"
32
45
 
33
- assert_requested :get, "https://example.com:80/", :query => {"foo" => "bar"}
46
+ assert_requested :get, "#{@scheme}://#{@base}/", :query => {"foo" => "bar"}
47
+ end
34
48
  end
35
49
 
36
- it "generates a path when a nil :query exists" do
37
- stub_request :get, "https://example.com:80/"
50
+ describe "multi-part" do
51
+ Content_Type_Matcher = %r{multipart/form-data}
52
+
53
+ before do
54
+ @request = Net::HTTP::Post
55
+ end
56
+
57
+ it "uploads a file" do
58
+ stub_request :post, "#{@scheme}://#{@base}/"
38
59
 
39
- @instance.send :response_for, @request, "/", :query => nil
60
+ upload = {
61
+ :upload => {
62
+ :parts => { :file => "/dev/null" },
63
+ :content_type => "type/subtype"
64
+ }
65
+ }
40
66
 
41
- assert_requested :get, "https://example.com:80/"
67
+ @instance.send :response_for, @request, "/", upload
68
+
69
+ assert_requested :post, "#{@scheme}://#{@base}/", :body => %r{Content-Type: type/subtype}, :headers => {
70
+ "Content-Type" => Content_Type_Matcher
71
+ }
72
+ end
73
+
74
+ it "has parts" do
75
+ stub_request :post, "#{@scheme}://#{@base}/"
76
+
77
+ upload = {
78
+ :upload => {
79
+ :parts => { :foo => :bar, :baz => :xyzzy },
80
+ :content_type => "foo/bar"
81
+ }
82
+ }
83
+
84
+ @instance.send :response_for, @request, "/", upload
85
+
86
+ ### wtf can't use mx together.
87
+ content_disposition_matcher = %r{^Content-Disposition: form-data; name="foo".*^bar.*^Content-Disposition: form-data; name="baz".*^xyzzy.*}m
88
+ assert_requested :post, "#{@scheme}://#{@base}/", :body => content_disposition_matcher, :headers => {
89
+ "Content-Type" => Content_Type_Matcher
90
+ }
91
+ end
42
92
  end
43
93
 
44
94
  describe "body" do
45
95
  before do
46
- stub_request :get, "https://example.com:80/"
96
+ stub_request :get, "#{@scheme}://#{@base}/"
47
97
  end
48
98
 
49
- it "doesn't set the body when an invalid :body exists" do
99
+ it "is not set when :body invalid" do
50
100
  @instance.send :response_for, @request, "/", :body => nil
51
101
 
52
- assert_requested :get, "https://example.com:80/", :body => nil
102
+ assert_requested :get, "#{@scheme}://#{@base}/", :body => nil
103
+ end
104
+
105
+ it "is not set when :body is missing" do
106
+ @instance.send :response_for, @request, "/", {}
107
+
108
+ assert_requested :get, "#{@scheme}://#{@base}/", {}
53
109
  end
54
110
 
55
111
  describe "json" do
@@ -57,10 +113,10 @@ describe Hugs do
57
113
  @instance = Hugs.new @valid_options.merge(:type => :json)
58
114
  end
59
115
 
60
- it "sets the body when a valid :body exists" do
116
+ it "is valid" do
61
117
  @instance.send :response_for, @request, "/", :body => {:foo => :bar}
62
118
 
63
- assert_requested :get, "https://example.com:80/", :body => '{"foo":"bar"}'
119
+ assert_requested :get, "#{@scheme}://#{@base}/", :body => '{"foo":"bar"}'
64
120
  end
65
121
  end
66
122
 
@@ -69,35 +125,35 @@ describe Hugs do
69
125
  @instance = Hugs.new @valid_options.merge(:type => :xml)
70
126
  end
71
127
 
72
- it "sets the body when a valid :body exists" do
128
+ it "is valid" do
73
129
  @instance.send :response_for, @request, "/", :body => "foo bar"
74
130
 
75
- assert_requested :get, "https://example.com:80/", :body => "foo bar"
131
+ assert_requested :get, "#{@scheme}://#{@base}/", :body => "foo bar"
76
132
  end
77
133
  end
78
134
  end
79
135
 
80
136
  describe "headers" do
81
137
  it "authenticates" do
82
- stub_request :get, "https://user:credentials@example.com:80/"
138
+ stub_request :get, "#{@scheme}://user:credentials@#{@base}/"
83
139
 
84
140
  @instance = Hugs.new @valid_options.merge(:user => "user", :password => "credentials")
85
141
 
86
142
  @instance.send :response_for, @request, "/", {}
87
143
 
88
- assert_requested :get, "https://user:credentials@example.com:80/"
144
+ assert_requested :get, "#{@scheme}://user:credentials@#{@base}/"
89
145
  end
90
146
 
91
147
  [:user, :password].each do |option|
92
148
  it "doesn't authenticate when '#{option}' missing" do
93
- stub_request :get, "https://example.com:80/"
149
+ stub_request :get, "#{@scheme}://#{@base}/"
94
150
 
95
151
  invalid_options = @valid_options.reject { |k,v| k == option }
96
152
  @instance = Hugs.new invalid_options
97
153
 
98
154
  @instance.send :response_for, @request, "/", {}
99
155
 
100
- assert_requested :get, "https://example.com:80/"
156
+ assert_requested :get, "#{@scheme}://#{@base}/"
101
157
  end
102
158
  end
103
159
 
@@ -110,13 +166,13 @@ describe Hugs do
110
166
  clazz = eval "Net::HTTP::#{verb.capitalize}"
111
167
 
112
168
  it "has '#{subtype}' Content-Type and Accept for '#{clazz}'" do
113
- stub_request verb, "https://example.com:80/"
169
+ stub_request verb, "#{@scheme}://#{@base}/"
114
170
 
115
171
  @instance = Hugs.new @valid_options.merge(:type => type)
116
172
 
117
173
  @instance.send :response_for, clazz, "/", {}
118
174
 
119
- assert_requested verb, "https://example.com:80/", :headers => {
175
+ assert_requested verb, "#{@scheme}://#{@base}/", :headers => {
120
176
  "Accept" => ["*/*", subtype], "Content-Type" => subtype }
121
177
  end
122
178
  end
@@ -125,13 +181,13 @@ describe Hugs do
125
181
  clazz = eval "Net::HTTP::#{verb.capitalize}"
126
182
 
127
183
  it "has '#{subtype}' Accept for '#{clazz}'" do
128
- stub_request verb, "https://example.com:80/"
184
+ stub_request verb, "#{@scheme}://#{@base}/"
129
185
 
130
186
  @instance = Hugs.new @valid_options.merge(:type => type)
131
187
 
132
188
  @instance.send :response_for, clazz, "/", {}
133
189
 
134
- assert_requested verb, "https://example.com:80/", :headers => {
190
+ assert_requested verb, "#{@scheme}://#{@base}/", :headers => {
135
191
  "Accept" => ["*/*", subtype] }
136
192
  end
137
193
  end
@@ -146,8 +202,8 @@ describe Hugs do
146
202
  @instance = Hugs.new @valid_options.merge(:type => :json)
147
203
  end
148
204
 
149
- it "objectifies the json and returns a hash" do
150
- stub_request(:get, "https://example.com:80/").to_return :body => '{"foo":"bar"}'
205
+ it "objectifies and returns a hash" do
206
+ stub_request(:get, "#{@scheme}://#{@base}/").to_return :body => '{"foo":"bar"}'
151
207
 
152
208
  response = @instance.get "/", :body => { :foo => :bar }
153
209
 
@@ -160,8 +216,8 @@ describe Hugs do
160
216
  @instance = Hugs.new @valid_options.merge(:type => :xml)
161
217
  end
162
218
 
163
- it "parses xml and returns a Nokogiri object" do
164
- stub_request(:get, "https://example.com:80/").to_return :body => "<STORAGE></STORAGE>"
219
+ it "parses and returns a Nokogiri object" do
220
+ stub_request(:get, "#{@scheme}://#{@base}/").to_return :body => "<STORAGE></STORAGE>"
165
221
 
166
222
  response = @instance.get "/", :body => { :foo => :bar }
167
223
 
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 2
7
+ - 1
7
8
  - 0
8
- - 0
9
- version: 2.0.0
9
+ version: 2.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - retr0h
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-16 00:00:00 -08:00
17
+ date: 2010-12-23 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -63,8 +63,23 @@ dependencies:
63
63
  prerelease: false
64
64
  version_requirements: *id003
65
65
  - !ruby/object:Gem::Dependency
66
- name: rake
66
+ name: multipart-post
67
67
  requirement: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 1
74
+ - 0
75
+ - 1
76
+ version: 1.0.1
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *id004
80
+ - !ruby/object:Gem::Dependency
81
+ name: rake
82
+ requirement: &id005 !ruby/object:Gem::Requirement
68
83
  none: false
69
84
  requirements:
70
85
  - - ">="
@@ -74,10 +89,10 @@ dependencies:
74
89
  version: "0"
75
90
  type: :development
76
91
  prerelease: false
77
- version_requirements: *id004
92
+ version_requirements: *id005
78
93
  - !ruby/object:Gem::Dependency
79
94
  name: jeweler
80
- requirement: &id005 !ruby/object:Gem::Requirement
95
+ requirement: &id006 !ruby/object:Gem::Requirement
81
96
  none: false
82
97
  requirements:
83
98
  - - ~>
@@ -89,10 +104,10 @@ dependencies:
89
104
  version: 1.5.1
90
105
  type: :development
91
106
  prerelease: false
92
- version_requirements: *id005
107
+ version_requirements: *id006
93
108
  - !ruby/object:Gem::Dependency
94
109
  name: minitest
95
- requirement: &id006 !ruby/object:Gem::Requirement
110
+ requirement: &id007 !ruby/object:Gem::Requirement
96
111
  none: false
97
112
  requirements:
98
113
  - - ~>
@@ -104,10 +119,10 @@ dependencies:
104
119
  version: 2.0.0
105
120
  type: :development
106
121
  prerelease: false
107
- version_requirements: *id006
122
+ version_requirements: *id007
108
123
  - !ruby/object:Gem::Dependency
109
124
  name: webmock
110
- requirement: &id007 !ruby/object:Gem::Requirement
125
+ requirement: &id008 !ruby/object:Gem::Requirement
111
126
  none: false
112
127
  requirements:
113
128
  - - "="
@@ -119,7 +134,7 @@ dependencies:
119
134
  version: 1.6.1
120
135
  type: :development
121
136
  prerelease: false
122
- version_requirements: *id007
137
+ version_requirements: *id008
123
138
  description:
124
139
  email: john@dewey.ws
125
140
  executables: []