hugs 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []