koala 1.0.0 → 1.1.0rc
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/.autotest +12 -0
- data/.gitignore +2 -1
- data/CHANGELOG +18 -0
- data/autotest/discover.rb +1 -0
- data/koala.gemspec +5 -5
- data/lib/koala.rb +29 -194
- data/lib/koala/graph_api.rb +71 -31
- data/lib/koala/graph_api_batch.rb +151 -0
- data/lib/koala/http_services.rb +10 -112
- data/lib/koala/http_services/net_http_service.rb +87 -0
- data/lib/koala/http_services/typhoeus_service.rb +37 -0
- data/lib/koala/oauth.rb +181 -0
- data/lib/koala/realtime_updates.rb +5 -14
- data/lib/koala/rest_api.rb +13 -8
- data/lib/koala/uploadable_io.rb +35 -7
- data/readme.md +19 -7
- data/spec/cases/api_base_spec.rb +2 -2
- data/spec/cases/graph_api_batch_spec.rb +600 -0
- data/spec/cases/http_services/http_service_spec.rb +76 -1
- data/spec/cases/http_services/net_http_service_spec.rb +164 -48
- data/spec/cases/http_services/typhoeus_service_spec.rb +27 -19
- data/spec/cases/koala_spec.rb +55 -0
- data/spec/cases/test_users_spec.rb +1 -1
- data/spec/cases/uploadable_io_spec.rb +56 -14
- data/spec/fixtures/mock_facebook_responses.yml +89 -5
- data/spec/support/graph_api_shared_examples.rb +34 -7
- data/spec/support/mock_http_service.rb +54 -56
- data/spec/support/rest_api_shared_examples.rb +131 -7
- data/spec/support/setup_mocks_or_live.rb +3 -3
- metadata +36 -24
@@ -17,6 +17,38 @@ describe "Koala::HTTPService" do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
describe "proxy accessor" do
|
21
|
+
it "should be added" do
|
22
|
+
# in Ruby 1.8, .methods returns strings
|
23
|
+
# in Ruby 1.9, .method returns symbols
|
24
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:proxy)
|
25
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:proxy=)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "timeout accessor" do
|
30
|
+
it "should be added" do
|
31
|
+
# in Ruby 1.8, .methods returns strings
|
32
|
+
# in Ruby 1.9, .method returns symbols
|
33
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:timeout)
|
34
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:timeout=)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "ca_file accessor" do
|
39
|
+
it "should be added" do
|
40
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:ca_file)
|
41
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:ca_file=)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "ca_path accessor" do
|
46
|
+
it "should be added" do
|
47
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:ca_path)
|
48
|
+
Bear.methods.collect {|m| m.to_sym}.should include(:ca_path=)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
20
52
|
describe "server" do
|
21
53
|
describe "without options[:beta]" do
|
22
54
|
it "should return the rest server if options[:rest_api]" do
|
@@ -46,9 +78,52 @@ describe "Koala::HTTPService" do
|
|
46
78
|
server.should =~ /beta\./
|
47
79
|
end
|
48
80
|
end
|
49
|
-
|
50
81
|
end
|
51
82
|
|
83
|
+
describe "#encode_params" do
|
84
|
+
it "should return an empty string if param_hash evaluates to false" do
|
85
|
+
Bear.encode_params(nil).should == ''
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should convert values to JSON if the value is not a String" do
|
89
|
+
val = 'json_value'
|
90
|
+
not_a_string = 'not_a_string'
|
91
|
+
not_a_string.stub(:is_a?).and_return(false)
|
92
|
+
not_a_string.should_receive(:to_json).and_return(val)
|
93
|
+
|
94
|
+
string = "hi"
|
95
|
+
|
96
|
+
args = {
|
97
|
+
not_a_string => not_a_string,
|
98
|
+
string => string
|
99
|
+
}
|
100
|
+
|
101
|
+
result = Bear.encode_params(args)
|
102
|
+
result.split('&').find do |key_and_val|
|
103
|
+
key_and_val.match("#{not_a_string}=#{val}")
|
104
|
+
end.should be_true
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should escape all values" do
|
108
|
+
args = Hash[*(1..4).map {|i| [i.to_s, "Value #{i}($"]}.flatten]
|
109
|
+
|
110
|
+
result = Bear.encode_params(args)
|
111
|
+
result.split('&').each do |key_val|
|
112
|
+
key, val = key_val.split('=')
|
113
|
+
val.should == CGI.escape(args[key])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should convert all keys to Strings" do
|
118
|
+
args = Hash[*(1..4).map {|i| [i, "val#{i}"]}.flatten]
|
119
|
+
|
120
|
+
result = Bear.encode_params(args)
|
121
|
+
result.split('&').each do |key_val|
|
122
|
+
key, val = key_val.split('=')
|
123
|
+
key.should == args.find{|key_val_arr| key_val_arr.last == val}.first.to_s
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
52
127
|
end
|
53
128
|
|
54
129
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
|
4
|
-
|
5
|
-
include Koala::NetHTTPService
|
6
|
-
end
|
4
|
+
Horse = Koala::NetHTTPService
|
7
5
|
|
8
6
|
describe "NetHTTPService module holder class Horse" do
|
9
7
|
before :each do
|
@@ -37,6 +35,8 @@ describe "NetHTTPService module holder class Horse" do
|
|
37
35
|
|
38
36
|
@http_mock = stub('Net::HTTP object', 'use_ssl=' => true, 'verify_mode=' => true)
|
39
37
|
@http_mock.stub(:start).and_yield(@http_yield_mock)
|
38
|
+
@http_mock.stub(:ca_path=)
|
39
|
+
@http_mock.stub(:ca_file=)
|
40
40
|
|
41
41
|
Net::HTTP.stub(:new).and_return(@http_mock)
|
42
42
|
end
|
@@ -128,7 +128,168 @@ describe "NetHTTPService module holder class Horse" do
|
|
128
128
|
Horse.make_request('anything', {}, 'anything')
|
129
129
|
end
|
130
130
|
end
|
131
|
+
|
132
|
+
describe "proxy options" do
|
133
|
+
before :each do
|
134
|
+
Horse.proxy = "http://defaultproxy"
|
135
|
+
end
|
136
|
+
after :all do
|
137
|
+
Horse.proxy = nil
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should use passed proxy option if provided" do
|
141
|
+
Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything, "passedproxy", 80, nil, nil).and_return(@http_mock)
|
142
|
+
Horse.make_request('anything', {} , 'anything', {:proxy => "http://passedproxy"})
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should use default proxy if default is provided and NO proxy option passed" do
|
146
|
+
Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything, "defaultproxy", 80, nil, nil).and_return(@http_mock)
|
147
|
+
Horse.make_request('anything', {} , 'anything', {})
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should NOT use a proxy if default is NOT provided and NO proxy option passed" do
|
151
|
+
Horse.proxy = nil
|
152
|
+
Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything).and_return(@http_mock)
|
153
|
+
Horse.make_request('anything', {} , 'anything', {})
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "timeout options" do
|
158
|
+
before :each do
|
159
|
+
Horse.timeout = 20 # seconds
|
160
|
+
end
|
161
|
+
after :all do
|
162
|
+
Horse.timeout = nil # seconds
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should use passed timeout option if provided" do
|
166
|
+
@http_mock.should_receive('open_timeout=').with(10)
|
167
|
+
@http_mock.should_receive('read_timeout=').with(10)
|
168
|
+
Horse.make_request('anything', {} , 'anything', {:timeout => 10})
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should use default timout if default is provided and NO timeout option passed" do
|
172
|
+
@http_mock.should_receive('open_timeout=').with(20)
|
173
|
+
@http_mock.should_receive('read_timeout=').with(20)
|
174
|
+
Horse.make_request('anything', {} , 'anything', {})
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should NOT use a timeout if default is NOT provided and NO timeout option passed" do
|
178
|
+
Horse.timeout = nil # seconds
|
179
|
+
@http_mock.should_not_receive('open_timeout=')
|
180
|
+
@http_mock.should_not_receive('read_timeout=')
|
181
|
+
Horse.make_request('anything', {} , 'anything', {})
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "ca_file options" do
|
186
|
+
after :each do
|
187
|
+
Horse.always_use_ssl = nil
|
188
|
+
Horse.ca_file = nil
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should not use a ca_file if the request is not via SSL" do
|
192
|
+
Horse.always_use_ssl = false
|
193
|
+
@http_mock.should_not_receive(:ca_file=)
|
194
|
+
Horse.make_request('anything', {} , 'anything', {:ca_file => '/no/file'})
|
195
|
+
end
|
196
|
+
|
197
|
+
describe "when via SSL" do
|
198
|
+
before :each do
|
199
|
+
Horse.always_use_ssl = true
|
200
|
+
|
201
|
+
@global_ca_file_path = '/global/ca/file/path'
|
202
|
+
File.stub(:exists?).and_return(true)
|
203
|
+
end
|
131
204
|
|
205
|
+
it "should not use a default ca_file if the default ca_file does not exist" do
|
206
|
+
Horse.ca_file = @global_ca_file_path
|
207
|
+
|
208
|
+
File.should_receive(:exists?).with(@global_ca_file_path).and_return(false)
|
209
|
+
Horse.should_not_receive(:ca_file=).with(@global_ca_file_path)
|
210
|
+
|
211
|
+
Horse.make_request('anything', {} , 'anything', {})
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should use passed ca_file options if provided" do
|
215
|
+
given_ca_file = '/ca/file'
|
216
|
+
|
217
|
+
Horse.ca_file = @global_ca_file_path
|
218
|
+
@http_mock.should_not_receive(:ca_file=).with(@global_ca_file_path)
|
219
|
+
@http_mock.should_receive(:ca_file=).with(given_ca_file)
|
220
|
+
|
221
|
+
Horse.make_request('anything', {} , 'anything', {:ca_file => given_ca_file})
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should use default ca_file if default is provided and NO ca_file option is passed" do
|
225
|
+
Horse.ca_file = @global_ca_file_path
|
226
|
+
@http_mock.should_receive(:ca_file=).with(@global_ca_file_path)
|
227
|
+
|
228
|
+
Horse.make_request('anything', {} , 'anything', {})
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should NOT use a ca_file if default is NOT provided and NO ca_file option is passed" do
|
232
|
+
@http_mock.should_not_receive(:ca_file=)
|
233
|
+
|
234
|
+
Horse.make_request('anything', {} , 'anything', {})
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "ca_path options" do
|
240
|
+
after :each do
|
241
|
+
Horse.always_use_ssl = nil
|
242
|
+
Horse.ca_path = nil
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should not use a ca_path if the request is not via SSL" do
|
246
|
+
Horse.always_use_ssl = false
|
247
|
+
@http_mock.should_not_receive('ca_path=')
|
248
|
+
Horse.make_request('anything', {} , 'anything', {:ca_file => '/no/file'})
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "when via SSL" do
|
252
|
+
before :each do
|
253
|
+
Horse.always_use_ssl = true
|
254
|
+
|
255
|
+
@global_ca_path = '/global/ca/path'
|
256
|
+
Dir.stub(:exists?).and_return(true)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should not use a default ca_path if the default ca_path does not exist" do
|
260
|
+
Horse.ca_path = @global_ca_path
|
261
|
+
|
262
|
+
Dir.should_receive(:exists?).with(@global_ca_path).and_return(false)
|
263
|
+
Horse.should_not_receive(:ca_path=).with(@global_ca_path)
|
264
|
+
|
265
|
+
Horse.make_request('anything', {} , 'anything', {})
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should use passed ca_path options if provided" do
|
269
|
+
given_ca_path = '/ca/path'
|
270
|
+
|
271
|
+
Horse.ca_path = @global_ca_path
|
272
|
+
@http_mock.should_not_receive(:ca_ath=).with(@global_ca_path)
|
273
|
+
@http_mock.should_receive(:ca_path=).with(given_ca_path)
|
274
|
+
|
275
|
+
Horse.make_request('anything', {} , 'anything', {:ca_path => given_ca_path})
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should use default ca_path if default is provided and NO ca_path option is passed" do
|
279
|
+
Horse.ca_path = @global_ca_path
|
280
|
+
@http_mock.should_receive(:ca_path=).with(@global_ca_path)
|
281
|
+
|
282
|
+
Horse.make_request('anything', {} , 'anything', {})
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should NOT use a ca_path if default is NOT provided and NO ca_path option is passed" do
|
286
|
+
@http_mock.should_not_receive(:ca_path=)
|
287
|
+
|
288
|
+
Horse.make_request('anything', {} , 'anything', {})
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
132
293
|
it "should use the graph server by default" do
|
133
294
|
Net::HTTP.should_receive(:new).with(Koala::Facebook::GRAPH_SERVER, anything).and_return(@http_mock)
|
134
295
|
Horse.make_request('anything', {}, 'anything')
|
@@ -267,51 +428,6 @@ describe "NetHTTPService module holder class Horse" do
|
|
267
428
|
end # describe return value
|
268
429
|
end # describe when making a request
|
269
430
|
|
270
|
-
describe "when encoding parameters" do
|
271
|
-
it "should return an empty string if param_hash evaluates to false" do
|
272
|
-
Horse.encode_params(nil).should == ''
|
273
|
-
end
|
274
|
-
|
275
|
-
it "should convert values to JSON if the value is not a String" do
|
276
|
-
val = 'json_value'
|
277
|
-
not_a_string = 'not_a_string'
|
278
|
-
not_a_string.stub(:class).and_return('NotAString')
|
279
|
-
not_a_string.should_receive(:to_json).and_return(val)
|
280
|
-
|
281
|
-
string = "hi"
|
282
|
-
|
283
|
-
args = {
|
284
|
-
not_a_string => not_a_string,
|
285
|
-
string => string
|
286
|
-
}
|
287
|
-
|
288
|
-
result = Horse.encode_params(args)
|
289
|
-
result.split('&').find do |key_and_val|
|
290
|
-
key_and_val.match("#{not_a_string}=#{val}")
|
291
|
-
end.should be_true
|
292
|
-
end
|
293
|
-
|
294
|
-
it "should escape all values" do
|
295
|
-
args = Hash[*(1..4).map {|i| [i.to_s, "Value #{i}($"]}.flatten]
|
296
|
-
|
297
|
-
result = Horse.encode_params(args)
|
298
|
-
result.split('&').each do |key_val|
|
299
|
-
key, val = key_val.split('=')
|
300
|
-
val.should == CGI.escape(args[key])
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
it "should convert all keys to Strings" do
|
305
|
-
args = Hash[*(1..4).map {|i| [i, "val#{i}"]}.flatten]
|
306
|
-
|
307
|
-
result = Horse.encode_params(args)
|
308
|
-
result.split('&').each do |key_val|
|
309
|
-
key, val = key_val.split('=')
|
310
|
-
key.should == args.find{|key_val_arr| key_val_arr.last == val}.first.to_s
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
431
|
describe "when detecting if multipart posting is needed" do
|
316
432
|
it "should be true if any parameter value requires multipart post" do
|
317
433
|
koala_io = mock("Koala::IO")
|
@@ -1,9 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
|
4
|
-
|
5
|
-
include Koala::TyphoeusService
|
6
|
-
end
|
4
|
+
Deer = Koala::TyphoeusService
|
7
5
|
|
8
6
|
describe "TyphoeusService" do
|
9
7
|
|
@@ -29,55 +27,55 @@ describe "TyphoeusService" do
|
|
29
27
|
@mock_http_response = stub(Typhoeus::Response, :code => 1, :headers_hash => @mock_headers_hash, :body => @mock_body)
|
30
28
|
|
31
29
|
# Typhoeus is an included module, so we stub methods on Deer itself
|
32
|
-
|
33
|
-
|
30
|
+
Typhoeus::Request.stub(:post).and_return(@mock_http_response)
|
31
|
+
Typhoeus::Request.stub(:get).and_return(@mock_http_response)
|
34
32
|
end
|
35
33
|
|
36
34
|
it "should use POST if verb is not GET" do
|
37
|
-
|
35
|
+
Typhoeus::Request.should_receive(:post).and_return(@mock_http_response)
|
38
36
|
Deer.make_request('anything', {}, 'anything')
|
39
37
|
end
|
40
38
|
|
41
39
|
it "should use GET if that verb is specified" do
|
42
|
-
|
40
|
+
Typhoeus::Request.should_receive(:get).and_return(@mock_http_response)
|
43
41
|
Deer.make_request('anything', {}, 'get')
|
44
42
|
end
|
45
43
|
|
46
44
|
describe "the connection" do
|
47
45
|
it "should use SSL if the request has an access token" do
|
48
|
-
|
46
|
+
Typhoeus::Request.should_receive(:post).with(/https\:/, anything)
|
49
47
|
|
50
48
|
Deer.make_request('anything', {"access_token" => "123"}, 'anything')
|
51
49
|
end
|
52
50
|
|
53
51
|
it "should use SSL if always_use_ssl is true, even if there's no token" do
|
54
|
-
|
52
|
+
Typhoeus::Request.should_receive(:post).with(/https\:/, anything)
|
55
53
|
|
56
54
|
Deer.always_use_ssl = true
|
57
55
|
Deer.make_request('anything', {}, 'anything')
|
58
56
|
end
|
59
57
|
|
60
58
|
it "should use SSL if the :use_ssl option is provided, even if there's no token" do
|
61
|
-
|
59
|
+
Typhoeus::Request.should_receive(:post).with(/https\:/, anything)
|
62
60
|
|
63
61
|
Deer.always_use_ssl = true
|
64
62
|
Deer.make_request('anything', {}, 'anything', :use_ssl => true)
|
65
63
|
end
|
66
64
|
|
67
65
|
it "should not use SSL if always_use_ssl is false and there's no token" do
|
68
|
-
|
66
|
+
Typhoeus::Request.should_receive(:post).with(/http\:/, anything)
|
69
67
|
|
70
68
|
Deer.make_request('anything', {}, 'anything')
|
71
69
|
end
|
72
70
|
|
73
71
|
it "should use the graph server by default" do
|
74
|
-
|
72
|
+
Typhoeus::Request.should_receive(:post).with(Regexp.new(Koala::Facebook::GRAPH_SERVER), anything)
|
75
73
|
|
76
74
|
Deer.make_request('anything', {}, 'anything')
|
77
75
|
end
|
78
76
|
|
79
77
|
it "should use the REST server if the :rest_api option is true" do
|
80
|
-
|
78
|
+
Typhoeus::Request.should_receive(:post).with(Regexp.new(Koala::Facebook::REST_SERVER), anything)
|
81
79
|
|
82
80
|
Deer.make_request('anything', {}, 'anything', :rest_api => true)
|
83
81
|
end
|
@@ -85,35 +83,45 @@ describe "TyphoeusService" do
|
|
85
83
|
|
86
84
|
it "should pass the arguments to Typhoeus under the :params key" do
|
87
85
|
args = {:a => 2}
|
88
|
-
|
86
|
+
Typhoeus::Request.should_receive(:post).with(anything, hash_including(:params => args))
|
89
87
|
|
90
88
|
Deer.make_request('anything', args, "post")
|
91
89
|
end
|
92
90
|
|
93
91
|
it "should add the method to the arguments if the method isn't get or post" do
|
94
92
|
method = "telekenesis"
|
95
|
-
|
93
|
+
Typhoeus::Request.should_receive(:post).with(anything, hash_including(:params => {:method => method}))
|
96
94
|
|
97
95
|
Deer.make_request('anything', {}, method)
|
98
96
|
end
|
99
97
|
|
100
98
|
it "should pass :typhoeus_options to Typhoeus if provided" do
|
101
99
|
t_options = {:a => :b}
|
102
|
-
|
100
|
+
Typhoeus::Request.should_receive(:post).with(anything, hash_including(t_options))
|
103
101
|
|
104
102
|
Deer.make_request("anything", {}, "post", :typhoeus_options => t_options)
|
105
103
|
end
|
106
|
-
|
104
|
+
|
105
|
+
it "should pass proxy and timeout :typhoeus_options to Typhoeus if set globally" do
|
106
|
+
Deer.proxy = "http://defaultproxy"
|
107
|
+
Deer.timeout = 20
|
108
|
+
|
109
|
+
t_options = {:proxy => "http://defaultproxy", :timeout => 20}
|
110
|
+
Typhoeus::Request.should_receive(:post).with(anything, hash_including(t_options))
|
111
|
+
|
112
|
+
Deer.make_request("anything", {}, "post")
|
113
|
+
end
|
114
|
+
|
107
115
|
# for live tests, run the Graph API tests with Typhoues, which will run file uploads
|
108
116
|
it "should pass any files directly on to Typhoues" do
|
109
117
|
args = {:file => File.new(__FILE__, "r")}
|
110
|
-
|
118
|
+
Typhoeus::Request.should_receive(:post).with(anything, hash_including(:params => args)).and_return(Typhoeus::Response.new)
|
111
119
|
Deer.make_request("anything", args, :post)
|
112
120
|
end
|
113
121
|
|
114
122
|
it "should include the path in the request" do
|
115
123
|
path = "/a/b/c/1"
|
116
|
-
|
124
|
+
Typhoeus::Request.should_receive(:post).with(Regexp.new(path), anything)
|
117
125
|
|
118
126
|
Deer.make_request(path, {}, "post")
|
119
127
|
end
|