httparty 0.4.3 → 0.5.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/lib/httparty.rb CHANGED
@@ -1,35 +1,41 @@
1
- $:.unshift(File.dirname(__FILE__))
2
-
1
+ require 'pathname'
3
2
  require 'net/http'
4
3
  require 'net/https'
5
- require 'httparty/module_inheritable_attributes'
6
4
  require 'rubygems'
7
- gem 'crack'
5
+ gem 'crack', '>= 0.1.1'
8
6
  require 'crack'
9
7
 
8
+ dir = Pathname(__FILE__).dirname.expand_path
9
+
10
+ require dir + 'httparty/module_inheritable_attributes'
11
+ require dir + 'httparty/cookie_hash'
12
+
10
13
  module HTTParty
11
-
12
- AllowedFormats = {
13
- 'text/xml' => :xml,
14
- 'application/xml' => :xml,
15
- 'application/json' => :json,
16
- 'text/json' => :json,
17
- 'application/javascript' => :json,
18
- 'text/javascript' => :json,
19
- 'text/html' => :html,
20
- 'application/x-yaml' => :yaml,
21
- 'text/yaml' => :yaml,
22
- 'text/plain' => :plain
23
- } unless defined?(AllowedFormats)
24
-
14
+ module AllowedFormatsDeprecation
15
+ def const_missing(const)
16
+ if const.to_s =~ /AllowedFormats$/
17
+ Kernel.warn("Deprecated: Use HTTParty::Parser::SupportedFormats")
18
+ HTTParty::Parser::SupportedFormats
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+
25
+ extend AllowedFormatsDeprecation
26
+
25
27
  def self.included(base)
26
28
  base.extend ClassMethods
27
29
  base.send :include, HTTParty::ModuleInheritableAttributes
28
30
  base.send(:mattr_inheritable, :default_options)
31
+ base.send(:mattr_inheritable, :default_cookies)
29
32
  base.instance_variable_set("@default_options", {})
33
+ base.instance_variable_set("@default_cookies", CookieHash.new)
30
34
  end
31
-
35
+
32
36
  module ClassMethods
37
+ extend AllowedFormatsDeprecation
38
+
33
39
  # Allows setting http proxy information to be used
34
40
  #
35
41
  # class Foo
@@ -40,7 +46,7 @@ module HTTParty
40
46
  default_options[:http_proxyaddr] = addr
41
47
  default_options[:http_proxyport] = port
42
48
  end
43
-
49
+
44
50
  # Allows setting a base uri to be used for each request.
45
51
  # Will normalize uri to include http, etc.
46
52
  #
@@ -52,7 +58,7 @@ module HTTParty
52
58
  return default_options[:base_uri] unless uri
53
59
  default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
54
60
  end
55
-
61
+
56
62
  # Allows setting basic authentication username and password.
57
63
  #
58
64
  # class Foo
@@ -62,7 +68,7 @@ module HTTParty
62
68
  def basic_auth(u, p)
63
69
  default_options[:basic_auth] = {:username => u, :password => p}
64
70
  end
65
-
71
+
66
72
  # Allows setting default parameters to be appended to each request.
67
73
  # Great for api keys and such.
68
74
  #
@@ -75,7 +81,7 @@ module HTTParty
75
81
  default_options[:default_params] ||= {}
76
82
  default_options[:default_params].merge!(h)
77
83
  end
78
-
84
+
79
85
  # Allows setting a base uri to be used for each request.
80
86
  #
81
87
  # class Foo
@@ -90,10 +96,9 @@ module HTTParty
90
96
 
91
97
  def cookies(h={})
92
98
  raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
93
- default_options[:cookies] ||= CookieHash.new
94
- default_options[:cookies].add_cookies(h)
99
+ default_cookies.add_cookies(h)
95
100
  end
96
-
101
+
97
102
  # Allows setting the format with which to parse.
98
103
  # Must be one of the allowed formats ie: json, xml
99
104
  #
@@ -101,37 +106,67 @@ module HTTParty
101
106
  # include HTTParty
102
107
  # format :json
103
108
  # end
104
- def format(f)
105
- raise UnsupportedFormat, "Must be one of: #{AllowedFormats.values.uniq.join(', ')}" unless AllowedFormats.value?(f)
106
- default_options[:format] = f
109
+ def format(f = nil)
110
+ if f.nil?
111
+ default_options[:format]
112
+ else
113
+ parser(Parser) if parser.nil?
114
+ default_options[:format] = f
115
+ validate_format
116
+ end
117
+ end
118
+
119
+ # Allows setting a PEM file to be used
120
+ #
121
+ # class Foo
122
+ # include HTTParty
123
+ # pem File.read('/home/user/my.pem')
124
+ # end
125
+ def pem(pem_contents)
126
+ default_options[:pem] = pem_contents
127
+ end
128
+
129
+ # Allows setting a custom parser for the response.
130
+ #
131
+ # class Foo
132
+ # include HTTParty
133
+ # parser Proc.new {|data| ...}
134
+ # end
135
+ def parser(customer_parser = nil)
136
+ if customer_parser.nil?
137
+ default_options[:parser]
138
+ else
139
+ default_options[:parser] = customer_parser
140
+ validate_format
141
+ end
107
142
  end
108
-
143
+
109
144
  # Allows making a get request to a url.
110
145
  #
111
146
  # class Foo
112
147
  # include HTTParty
113
148
  # end
114
- #
149
+ #
115
150
  # # Simple get with full url
116
151
  # Foo.get('http://foo.com/resource.json')
117
- #
152
+ #
118
153
  # # Simple get with full url and query parameters
119
154
  # # ie: http://foo.com/resource.json?limit=10
120
155
  # Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
121
156
  def get(path, options={})
122
157
  perform_request Net::HTTP::Get, path, options
123
158
  end
124
-
159
+
125
160
  # Allows making a post request to a url.
126
161
  #
127
162
  # class Foo
128
163
  # include HTTParty
129
164
  # end
130
- #
165
+ #
131
166
  # # Simple post with full url and setting the body
132
167
  # Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
133
168
  #
134
- # # Simple post with full url using :query option,
169
+ # # Simple post with full url using :query option,
135
170
  # # which gets set as form data on the request.
136
171
  # Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
137
172
  def post(path, options={})
@@ -145,24 +180,37 @@ module HTTParty
145
180
  def delete(path, options={})
146
181
  perform_request Net::HTTP::Delete, path, options
147
182
  end
148
-
183
+
184
+ def head(path, options={})
185
+ perform_request Net::HTTP::Head, path, options
186
+ end
187
+
188
+ def options(path, options={})
189
+ perform_request Net::HTTP::Options, path, options
190
+ end
191
+
149
192
  def default_options #:nodoc:
150
193
  @default_options
151
194
  end
152
195
 
153
196
  private
197
+
154
198
  def perform_request(http_method, path, options) #:nodoc:
199
+ options = default_options.dup.merge(options)
155
200
  process_cookies(options)
156
- Request.new(http_method, path, default_options.dup.merge(options)).perform
201
+ Request.new(http_method, path, options).perform
157
202
  end
158
203
 
159
204
  def process_cookies(options) #:nodoc:
160
- return unless options[:cookies] || default_options[:cookies]
161
- options[:headers] ||= {}
162
- options[:headers]["cookie"] = cookies(options[:cookies] || {}).to_cookie_string
205
+ return unless options[:cookies] || default_cookies.any?
206
+ options[:headers] ||= headers.dup
207
+ options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string
208
+ end
163
209
 
164
- default_options.delete(:cookies)
165
- options.delete(:cookies)
210
+ def validate_format
211
+ if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format)
212
+ raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{parser.supported_formats.map{|f| f.to_s}.sort.join(', ')}"
213
+ end
166
214
  end
167
215
  end
168
216
 
@@ -170,21 +218,21 @@ module HTTParty
170
218
  normalized_url = url.dup
171
219
  use_ssl = (normalized_url =~ /^https/) || normalized_url.include?(':443')
172
220
  ends_with_slash = normalized_url =~ /\/$/
173
-
221
+
174
222
  normalized_url.chop! if ends_with_slash
175
223
  normalized_url.gsub!(/^https?:\/\//i, '')
176
-
224
+
177
225
  "http#{'s' if use_ssl}://#{normalized_url}"
178
226
  end
179
-
227
+
180
228
  class Basement #:nodoc:
181
229
  include HTTParty
182
230
  end
183
-
231
+
184
232
  def self.get(*args)
185
233
  Basement.get(*args)
186
234
  end
187
-
235
+
188
236
  def self.post(*args)
189
237
  Basement.post(*args)
190
238
  end
@@ -196,10 +244,20 @@ module HTTParty
196
244
  def self.delete(*args)
197
245
  Basement.delete(*args)
198
246
  end
247
+
248
+ def self.head(*args)
249
+ Basement.head(*args)
250
+ end
251
+
252
+ def self.options(*args)
253
+ Basement.options(*args)
254
+ end
255
+
199
256
  end
200
257
 
201
- require 'httparty/cookie_hash'
202
- require 'httparty/core_extensions'
203
- require 'httparty/exceptions'
204
- require 'httparty/request'
205
- require 'httparty/response'
258
+ require dir + 'httparty/core_extensions'
259
+ require dir + 'httparty/exceptions'
260
+ require dir + 'httparty/parser'
261
+ require dir + 'httparty/request'
262
+ require dir + 'httparty/response'
263
+
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), '../spec_helper')
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
2
2
 
3
3
  describe HTTParty::CookieHash do
4
4
  before(:each) do
@@ -6,17 +6,44 @@ describe HTTParty::CookieHash do
6
6
  end
7
7
 
8
8
  describe "#add_cookies" do
9
- it "should add new key/value pairs to the hash" do
10
- @cookie_hash.add_cookies(:foo => "bar")
11
- @cookie_hash.add_cookies(:rofl => "copter")
12
- @cookie_hash.length.should eql(2)
9
+
10
+ describe "with a hash" do
11
+ it "should add new key/value pairs to the hash" do
12
+ @cookie_hash.add_cookies(:foo => "bar")
13
+ @cookie_hash.add_cookies(:rofl => "copter")
14
+ @cookie_hash.length.should eql(2)
15
+ end
16
+
17
+ it "should overwrite any existing key" do
18
+ @cookie_hash.add_cookies(:foo => "bar")
19
+ @cookie_hash.add_cookies(:foo => "copter")
20
+ @cookie_hash.length.should eql(1)
21
+ @cookie_hash[:foo].should eql("copter")
22
+ end
13
23
  end
14
24
 
15
- it "should overwrite any existing key" do
16
- @cookie_hash.add_cookies(:foo => "bar")
17
- @cookie_hash.add_cookies(:foo => "copter")
18
- @cookie_hash.length.should eql(1)
19
- @cookie_hash[:foo].should eql("copter")
25
+ describe "with a string" do
26
+ it "should add new key/value pairs to the hash" do
27
+ @cookie_hash.add_cookies("first=one; second=two; third")
28
+ @cookie_hash[:first].should == 'one'
29
+ @cookie_hash[:second].should == 'two'
30
+ @cookie_hash[:third].should == nil
31
+ end
32
+
33
+ it "should overwrite any existing key" do
34
+ @cookie_hash[:foo] = 'bar'
35
+ @cookie_hash.add_cookies("foo=tar")
36
+ @cookie_hash.length.should eql(1)
37
+ @cookie_hash[:foo].should eql("tar")
38
+ end
39
+ end
40
+
41
+ describe 'with other class' do
42
+ it "should error" do
43
+ lambda {
44
+ @cookie_hash.add_cookies(Array.new)
45
+ }.should raise_error
46
+ end
20
47
  end
21
48
  end
22
49
 
@@ -34,5 +61,11 @@ describe HTTParty::CookieHash do
34
61
  @s.should match(/rofl=copter/)
35
62
  @s.should match(/^\w+=\w+; \w+=\w+$/)
36
63
  end
64
+
65
+ it "should not include client side only cookies" do
66
+ @cookie_hash.add_cookies(:path => "/")
67
+ @s = @cookie_hash.to_cookie_string
68
+ @s.should_not match(/path=\//)
69
+ end
37
70
  end
38
71
  end
@@ -0,0 +1,154 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ class CustomParser < HTTParty::Parser; end
4
+
5
+ describe HTTParty::Parser do
6
+ describe ".SupportedFormats" do
7
+ it "returns a hash" do
8
+ HTTParty::Parser::SupportedFormats.should be_instance_of(Hash)
9
+ end
10
+ end
11
+
12
+ describe ".call" do
13
+ it "generates an HTTParty::Parser instance with the given body and format" do
14
+ HTTParty::Parser.should_receive(:new).with('body', :plain).and_return(stub(:parse => nil))
15
+ HTTParty::Parser.call('body', :plain)
16
+ end
17
+
18
+ it "calls #parse on the parser" do
19
+ parser = mock('Parser')
20
+ parser.should_receive(:parse)
21
+ HTTParty::Parser.stub(:new => parser)
22
+ parser = HTTParty::Parser.call('body', :plain)
23
+ end
24
+ end
25
+
26
+ describe ".formats" do
27
+ it "returns the SupportedFormats constant" do
28
+ HTTParty::Parser.formats.should == HTTParty::Parser::SupportedFormats
29
+ end
30
+
31
+ it "returns the SupportedFormats constant for subclasses" do
32
+ class MyParser < HTTParty::Parser
33
+ SupportedFormats = {"application/atom+xml" => :atom}
34
+ end
35
+ MyParser.formats.should == {"application/atom+xml" => :atom}
36
+ end
37
+ end
38
+
39
+ describe ".format_from_mimetype" do
40
+ it "returns a symbol representing the format mimetype" do
41
+ HTTParty::Parser.format_from_mimetype("text/plain").should == :plain
42
+ end
43
+
44
+ it "returns nil when the mimetype is not supported" do
45
+ HTTParty::Parser.format_from_mimetype("application/atom+xml").should be_nil
46
+ end
47
+ end
48
+
49
+ describe ".supported_formats" do
50
+ it "returns a unique set of supported formats represented by symbols" do
51
+ HTTParty::Parser.supported_formats.should == HTTParty::Parser::SupportedFormats.values.uniq
52
+ end
53
+ end
54
+
55
+ describe ".supports_format?" do
56
+ it "returns true for a supported format" do
57
+ HTTParty::Parser.stub(:supported_formats => [:json])
58
+ HTTParty::Parser.supports_format?(:json).should be_true
59
+ end
60
+
61
+ it "returns false for an unsupported format" do
62
+ HTTParty::Parser.stub(:supported_formats => [])
63
+ HTTParty::Parser.supports_format?(:json).should be_false
64
+ end
65
+ end
66
+
67
+ describe "#parse" do
68
+ before do
69
+ @parser = HTTParty::Parser.new('body', :json)
70
+ end
71
+
72
+ it "attempts to parse supported formats" do
73
+ @parser.stub(:supports_format? => true)
74
+ @parser.should_receive(:parse_supported_format)
75
+ @parser.parse
76
+ end
77
+
78
+ it "returns the unparsed body when the format is unsupported" do
79
+ @parser.stub(:supports_format? => false)
80
+ @parser.parse.should == @parser.body
81
+ end
82
+
83
+ it "returns nil for an empty body" do
84
+ @parser.stub(:body => '')
85
+ @parser.parse.should be_nil
86
+ end
87
+
88
+ it "returns nil for a nil body" do
89
+ @parser.stub(:body => nil)
90
+ @parser.parse.should be_nil
91
+ end
92
+ end
93
+
94
+ describe "#supports_format?" do
95
+ it "utilizes the class method to determine if the format is supported" do
96
+ HTTParty::Parser.should_receive(:supports_format?).with(:json)
97
+ parser = HTTParty::Parser.new('body', :json)
98
+ parser.send(:supports_format?)
99
+ end
100
+ end
101
+
102
+ describe "#parse_supported_format" do
103
+ it "calls the parser for the given format" do
104
+ parser = HTTParty::Parser.new('body', :json)
105
+ parser.should_receive(:json)
106
+ parser.send(:parse_supported_format)
107
+ end
108
+
109
+ context "when a parsing method does not exist for the given format" do
110
+ it "raises an exception" do
111
+ parser = HTTParty::Parser.new('body', :atom)
112
+ expect do
113
+ parser.send(:parse_supported_format)
114
+ end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.")
115
+ end
116
+
117
+ it "raises a useful exception message for subclasses" do
118
+ parser = CustomParser.new('body', :atom)
119
+ expect do
120
+ parser.send(:parse_supported_format)
121
+ end.to raise_error(NotImplementedError, "CustomParser has not implemented a parsing method for the :atom format.")
122
+ end
123
+ end
124
+ end
125
+
126
+ context "parsers" do
127
+ subject do
128
+ HTTParty::Parser.new('body', nil)
129
+ end
130
+
131
+ it "parses xml with Crack" do
132
+ Crack::XML.should_receive(:parse).with('body')
133
+ subject.send(:xml)
134
+ end
135
+
136
+ it "parses json with Crack" do
137
+ Crack::JSON.should_receive(:parse).with('body')
138
+ subject.send(:json)
139
+ end
140
+
141
+ it "parses yaml" do
142
+ YAML.should_receive(:load).with('body')
143
+ subject.send(:yaml)
144
+ end
145
+
146
+ it "parses html by simply returning the body" do
147
+ subject.send(:html).should == 'body'
148
+ end
149
+
150
+ it "parses plain text by simply returning the body" do
151
+ subject.send(:plain).should == 'body'
152
+ end
153
+ end
154
+ end