koala 1.1.0rc2 → 1.1.0rc3

Sign up to get free protection for your applications and to get access to all the features.
@@ -391,7 +391,7 @@ describe "Koala::Facebook::OAuth" do
391
391
  # the signed request code is ported directly from Facebook
392
392
  # so we only need to test at a high level that it works
393
393
  it "should throw an error if the algorithm is unsupported" do
394
- JSON.stub!(:parse).and_return("algorithm" => "my fun algorithm")
394
+ MultiJson.stub(:decode).and_return("algorithm" => "my fun algorithm")
395
395
  lambda { @oauth.parse_signed_request(@signed_request) }.should raise_error
396
396
  end
397
397
 
@@ -43,19 +43,19 @@ describe "Koala::Facebook::RealtimeUpdates" do
43
43
  it "should not allow write access to app_id" do
44
44
  updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :app_access_token => @app_access_token)
45
45
  # this should not throw errors
46
- lambda { updates.app_id = 2 }.should raise_error(NoMethodError)
46
+ updates.methods.map(&:to_sym).should_not include(:app_id=)
47
47
  end
48
48
 
49
49
  it "should not allow write access to app_access_token" do
50
50
  updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :app_access_token => @app_access_token)
51
51
  # this should not throw errors
52
- lambda { updates.app_access_token = 2 }.should raise_error(NoMethodError)
52
+ updates.methods.map(&:to_sym).should_not include(:app_access_token=)
53
53
  end
54
54
 
55
55
  it "should not allow write access to secret" do
56
56
  updates = Koala::Facebook::RealtimeUpdates.new(:app_id => @app_id, :app_access_token => @app_access_token)
57
57
  # this should not throw errors
58
- lambda { updates.secret = 2 }.should raise_error(NoMethodError)
58
+ updates.methods.map(&:to_sym).should_not include(:secret=)
59
59
  end
60
60
 
61
61
  # init with secret / fetching the token
Binary file
@@ -25,11 +25,11 @@ rest_api:
25
25
  no_token: '{"error_code":104,"error_msg":"Requires valid signature","request_args":[{"key":"method","value":"fql.query"},{"key":"format","value":"json"},{"key":"query","value":"select read_stream from permissions where uid = 216743"}]}'
26
26
 
27
27
  /method/fql.multiquery:
28
- 'queries={"query1":"select post_id from stream where source_id = me()","query2":"select fromid from comment where post_id in (select post_id from #query1)","query3":"select uid, name from user where uid in (select fromid from #query2)"}':
28
+ 'queries=<%= MultiJson.encode({"query1" => "select post_id from stream where source_id = me()", "query2" => "select fromid from comment where post_id in (select post_id from #query1)", "query3" => "select uid, name from user where uid in (select fromid from #query2)"}) %>':
29
29
  get:
30
30
  with_token: '[{"name":"query1", "fql_result_set":[]},{"name":"query2", "fql_result_set":[]},{"name":"query3", "fql_result_set":[]}]'
31
31
  no_token: '{"error_code":104,"error_msg":"Requires valid signature","request_args":[{"key":"method","value":"fql.query"},{"key":"format","value":"json"},{"key":"query","value":"select read_stream from permissions where uid = 216743"}]}'
32
- 'queries={"query1":"select first_name from user where uid = 216743","query2":"select first_name from user where uid = 2905623"}':
32
+ 'queries=<%= MultiJson.encode({"query1" => "select first_name from user where uid = 216743", "query2" => "select first_name from user where uid = 2905623"}) %>':
33
33
  get:
34
34
  with_token: '[{"name":"query1", "fql_result_set":[{"first_name":"Chris"}]},{"name":"query2", "fql_result_set":[{"first_name":"Alex"}]}]'
35
35
  no_token: '[{"name":"query1", "fql_result_set":[{"first_name":"Chris"}]},{"name":"query2", "fql_result_set":[{"first_name":"Alex"}]}]'
@@ -74,52 +74,53 @@ graph_api:
74
74
  with_token: '{"contextoptional":"{}","naitik":"{}"}'
75
75
  no_token: '{"contextoptional":"{}","naitik":"{}"}'
76
76
  # Ruby 1.8.7 and 1.9.2 generate JSON with different key ordering, hence we have to dynamically generate it here
77
- batch=<%= [{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "koppel"}].to_json %>:
77
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "koppel"}]) %>:
78
78
  post:
79
79
  with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"id\":\"456\"}"}]'
80
- batch=<%= [{"method" => "get", "relative_url" => "me/picture"}].to_json %>:
80
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/picture"}]) %>:
81
81
  post:
82
82
  with_token: '[{"headers":[{"name":"Location","value":"http://google.com"}]}]'
83
- batch=<%= [{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "me/friends"}].to_json %>:
83
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me"},{"method" => "get", "relative_url" => "me/friends"}]) %>:
84
84
  post:
85
85
  with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
86
- batch=<%= [{"method"=>"get", "relative_url"=>"me"}, {"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}].to_json %>:
86
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"me"}, {"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}]) %>:
87
87
  post:
88
88
  with_token: '[{"body":"{\"id\":\"123\"}"}, {"body":"{\"data\":[]}"}]'
89
- batch=<%= [{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights"}, {"method"=>"get", "relative_url"=>"koppel?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}].to_json %>:
89
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights"}, {"method"=>"get", "relative_url"=>"koppel?access_token=#{CGI.escape APP_ACCESS_TOKEN}"}]) %>:
90
90
  post:
91
91
  with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},{"body":"{\"id\":\"123\"}"}]'
92
- batch=<%= [{"method"=>"post", "relative_url"=>"FEED_ITEM_BATCH/likes"}, {"method"=>"delete", "relative_url"=> "FEED_ITEM_BATCH"}].to_json %>:
92
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"FEED_ITEM_BATCH/likes"}, {"method"=>"delete", "relative_url"=> "FEED_ITEM_BATCH"}]) %>:
93
93
  post:
94
94
  with_token: '[{"body": "{\"id\": \"MOCK_LIKE\"}"},{"body":true}]'
95
- batch=<%= [{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}].to_json %>:
95
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}]) %>:
96
96
  post:
97
97
  with_token: '[null,{"body":"{}"}]'
98
- batch=<%= [{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends", "omit_response_on_success" => false}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}].to_json %>:
98
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5", "name" => "get-friends", "omit_response_on_success" => false}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=get-friends:$.data.*.id}"}"}]) %>:
99
99
  post:
100
100
  with_token: '[{"body":"{\"data\":[]}"},{"body":"{}"}]'
101
- batch=<%= [{"method" => "get", "relative_url" => "me/friends?limit=5"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=i-dont-exist:$.data.*.id}"}"}].to_json %>:
101
+ batch=<%= MultiJson.encode([{"method" => "get", "relative_url" => "me/friends?limit=5"}, {"method" => "get", "relative_url" => "?ids=#{CGI.escape "{result=i-dont-exist:$.data.*.id}"}"}]) %>:
102
102
  post:
103
103
  with_token: '{"error":190,"error_description":"Error validating access token."}'
104
- batch=<%= [{"method" => "post", "relative_url" => "method/fql.query", "body" => "query=select+name+from+user+where+uid%3D4"}].to_json %>:
104
+ batch=<%= MultiJson.encode([{"method" => "post", "relative_url" => "method/fql.query", "body" => "query=select+name+from+user+where+uid%3D4"}]) %>:
105
105
  post:
106
106
  with_token: '[{"body":"[{\"name\":\"Mark Zuckerberg\"}]"}]'
107
- batch=<%= [{"method"=>"get", "relative_url"=>"me", "name" => "getme"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getme"}].to_json %>:
108
- post:
109
- with_token: '[null,{"body":"{\"id\":\"123\"}"}]'
110
- batch=<%= [{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights", "name" => "getdata"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getdata"}].to_json %>:
111
- post:
112
- with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
113
- batch=<%= [{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights", "name" => "getdata"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getdata"}].to_json %>:
107
+
108
+ # dependencies
109
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"me", "name" => "getme"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getme"}]) %>:
110
+ post: &batch_dependent
111
+ with_token: '[null,{"body":"{\"id\":\"123\"}"}]'
112
+ batch=<%= MultiJson.encode([{"method"=>"get", "relative_url"=>"#{OAUTH_DATA["app_id"]}/insights", "name" => "getdata"}, {"method"=>"get", "relative_url"=>"koppel", "depends_on" => "getdata"}]) %>:
114
113
  post:
115
114
  with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
116
- batch=<%= [{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "file0_0"}].to_json %>&file0_0=[FILE]:
115
+
116
+ # attached files tests
117
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "op1_file0"}]) %>&op1_file0=[FILE]:
117
118
  post:
118
119
  with_token: '[{"body": "{\"error\":{\"type\":\"AnError\", \"message\":\"An error occurred!.\"}}"},null]'
119
- batch=<%= [{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "file0_0"}].to_json %>&file0_0=[FILE]:
120
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "op1_file0"}]) %>&op1_file0=[FILE]:
120
121
  post:
121
122
  with_token: '[{"body":"{\"id\": \"MOCK_PHOTO\"}"}]'
122
- batch=<%= [{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "file0_0"}, {"method"=>"post", "relative_url"=>"koppel/photos", "attached_files" => "file1_0"}].to_json %>&file0_0=[FILE]&file1_0=[FILE]:
123
+ batch=<%= MultiJson.encode([{"method"=>"post", "relative_url"=>"me/photos", "attached_files" => "op1_file0"}, {"method"=>"post", "relative_url"=>"koppel/photos", "attached_files" => "op2_file0"}]) %>&op1_file0=[FILE]&op2_file0=[FILE]:
123
124
  post:
124
125
  with_token: '[{"body":"{\"id\": \"MOCK_PHOTO\"}"}, {"body":"{\"id\": \"MOCK_PHOTO\"}"}]'
125
126
 
@@ -161,6 +162,16 @@ graph_api:
161
162
  post:
162
163
  <<: *token_required
163
164
  with_token: '{"id": "MOCK_PHOTO"}'
165
+ /me/videos:
166
+ source=[FILE]:
167
+ post:
168
+ <<: *token_required
169
+ with_token: '{"id": "MOCK_PHOTO"}'
170
+ message=This is the test message&source=[FILE]:
171
+ post:
172
+ <<: *token_required
173
+ with_token: '{"id": "MOCK_PHOTO"}'
174
+
164
175
  /koppel:
165
176
  no_args:
166
177
  get:
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  begin
2
2
  require 'bundler/setup'
3
3
  rescue LoadError
4
- puts 'although not required, bundler is recommened for running the tests'
4
+ puts 'although not required, bundler is recommended for running the tests'
5
5
  end
6
6
 
7
7
  # load the libraries
@@ -166,44 +166,87 @@ shared_examples_for "Koala GraphAPI with an access token" do
166
166
  @temporary_object_id.should_not be_nil
167
167
  end
168
168
 
169
- it "should be able to post photos to the user's wall with an open file object" do
170
- content_type = "image/jpg"
171
- file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
172
-
173
- result = @api.put_picture(file, content_type)
174
- @temporary_object_id = result["id"]
175
- @temporary_object_id.should_not be_nil
176
- end
169
+ describe ".put_picture" do
170
+ it "should be able to post photos to the user's wall with an open file object" do
171
+ content_type = "image/jpg"
172
+ file = File.open(File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg"))
177
173
 
178
- it "uses the base HTTP service if the upload is a StringIO or similar" do
179
- source = stub("UploadIO")
180
- Koala::UploadableIO.stub(:new).and_return(source)
181
- source.stub(:requires_base_http_service).and_return(true)
182
- Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:http_service => Koala.base_http_service)).and_return(Koala::Response.new(200, "[]", {}))
183
- @api.put_picture(StringIO.new)
184
- end
174
+ result = @api.put_picture(file, content_type)
175
+ @temporary_object_id = result["id"]
176
+ @temporary_object_id.should_not be_nil
177
+ end
178
+
179
+ it "uses the base HTTP service if the upload is a StringIO or similar" do
180
+ source = stub("UploadIO")
181
+ Koala::UploadableIO.stub(:new).and_return(source)
182
+ source.stub(:requires_base_http_service).and_return(true)
183
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:http_service => Koala.base_http_service)).and_return(Koala::Response.new(200, "[]", {}))
184
+ @api.put_picture(StringIO.new)
185
+ end
186
+
187
+ it "should be able to post photos to the user's wall without an open file object" do
188
+ content_type = "image/jpg",
189
+ file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
185
190
 
186
- it "should be able to post photos to the user's wall without an open file object" do
187
- content_type = "image/jpg",
188
- file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
189
-
190
- result = @api.put_picture(file_path, content_type)
191
- @temporary_object_id = result["id"]
192
- @temporary_object_id.should_not be_nil
191
+ result = @api.put_picture(file_path, content_type)
192
+ @temporary_object_id = result["id"]
193
+ @temporary_object_id.should_not be_nil
194
+ end
195
+
196
+ it "should be able to verify a photo posted to a user's wall" do
197
+ content_type = "image/jpg",
198
+ file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
199
+
200
+ expected_message = "This is the test message"
201
+
202
+ result = @api.put_picture(file_path, content_type, :message => expected_message)
203
+ @temporary_object_id = result["id"]
204
+ @temporary_object_id.should_not be_nil
205
+
206
+ get_result = @api.get_object(@temporary_object_id)
207
+ get_result["name"].should == expected_message
208
+ end
193
209
  end
210
+
211
+ describe ".put_video" do
212
+ before :each do
213
+ @cat_movie = File.join(File.dirname(__FILE__), "..", "fixtures", "cat.m4v")
214
+ @content_type = "video/mpeg4"
215
+ end
194
216
 
195
- it "should be able to verify a photo posted to a user's wall" do
196
- content_type = "image/jpg",
197
- file_path = File.join(File.dirname(__FILE__), "..", "fixtures", "beach.jpg")
198
-
199
- expected_message = "This is the test message"
200
-
201
- result = @api.put_picture(file_path, content_type, :message => expected_message)
202
- @temporary_object_id = result["id"]
203
- @temporary_object_id.should_not be_nil
217
+ it "should set options[:video] to true" do
218
+ source = stub("UploadIO")
219
+ Koala::UploadableIO.stub(:new).and_return(source)
220
+ source.stub(:requires_base_http_service).and_return(false)
221
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:video => true)).and_return(Koala::Response.new(200, "[]", {}))
222
+ @api.put_video("foo")
223
+ end
204
224
 
205
- get_result = @api.get_object(@temporary_object_id)
206
- get_result["name"].should == expected_message
225
+ it "should be able to post videos to the user's wall with an open file object" do
226
+ file = File.open(@cat_movie)
227
+
228
+ result = @api.put_video(file, @content_type)
229
+ @temporary_object_id = result["id"]
230
+ @temporary_object_id.should_not be_nil
231
+ end
232
+
233
+ it "uses the base HTTP service if the upload is a StringIO or similar" do
234
+ source = stub("UploadIO")
235
+ Koala::UploadableIO.stub(:new).and_return(source)
236
+ source.stub(:requires_base_http_service).and_return(true)
237
+ Koala.should_receive(:make_request).with(anything, anything, anything, hash_including(:http_service => Koala.base_http_service)).and_return(Koala::Response.new(200, "[]", {}))
238
+ @api.put_video(StringIO.new)
239
+ end
240
+
241
+ it "should be able to post videos to the user's wall without an open file object" do
242
+ result = @api.put_video(@cat_movie, @content_type)
243
+ @temporary_object_id = result["id"]
244
+ @temporary_object_id.should_not be_nil
245
+ end
246
+
247
+ # note: Facebook doesn't post videos immediately to the wall, due to processing time
248
+ # during which get_object(video_id) will return false
249
+ # hence we can't do the same verify test we do for photos
207
250
  end
208
251
 
209
252
  it "should be able to verify a message with an attachment posted to a feed" do
@@ -267,6 +310,7 @@ shared_examples_for "Koala GraphAPI with an access token" do
267
310
  :search => 3,
268
311
  # methods that have special arguments
269
312
  :put_picture => ["x.jpg", "image/jpg", {}, "me"],
313
+ :put_video => ["x.mp4", "video/mpeg4", {}, "me"],
270
314
  :get_objects => [["x"], {}]
271
315
  }.each_pair do |method_name, params|
272
316
  it "should pass http options through for #{method_name}" do
@@ -343,8 +387,8 @@ shared_examples_for "Koala GraphAPI with GraphCollection" do
343
387
  end
344
388
 
345
389
  it "should have a read-only paging attribute" do
346
- lambda { @result.paging }.should_not raise_error
347
- lambda { @result.paging = "paging" }.should raise_error(NoMethodError)
390
+ @result.methods.map(&:to_sym).should include(:paging)
391
+ @result.methods.map(&:to_sym).should_not include(:paging=)
348
392
  end
349
393
 
350
394
  describe "when getting a whole page" do
@@ -5,6 +5,9 @@ module Koala
5
5
  module MockHTTPService
6
6
  include Koala::HTTPService
7
7
 
8
+ # fix our specs to use ok_json, so we always get the same results from to_json
9
+ MultiJson.engine = :ok_json
10
+
8
11
  # Mocks all HTTP requests for with koala_spec_with_mocks.rb
9
12
  # Mocked values to be included in TEST_DATA used in specs
10
13
  ACCESS_TOKEN = '*'
@@ -154,13 +154,13 @@ shared_examples_for "Koala RestAPI" do
154
154
  "fql.multiquery", anything, anything
155
155
  ).and_return({})
156
156
 
157
- @api.fql_multiquery stub('query string')
157
+ @api.fql_multiquery 'query string'
158
158
  end
159
159
 
160
160
  it "should pass a queries argument" do
161
161
  queries = stub('query string')
162
162
  queries_json = "some JSON"
163
- queries.stub(:to_json).and_return(queries_json)
163
+ MultiJson.stub(:encode).with(queries).and_return(queries_json)
164
164
 
165
165
  @api.should_receive(:rest_call).with(
166
166
  anything,
@@ -4,8 +4,7 @@ module KoalaTest
4
4
  print "Validating permissions for live testing..."
5
5
  # make sure we have the necessary permissions
6
6
  api = Koala::Facebook::GraphAndRestAPI.new(token)
7
- uid = api.get_object("me")["id"]
8
- perms = api.fql_query("select read_stream, publish_stream, user_photos, read_insights from permissions where uid = #{uid}")[0]
7
+ perms = api.fql_query("select read_stream, publish_stream, user_photos, user_videos, read_insights from permissions where uid = me()")[0]
9
8
  perms.each_pair do |perm, value|
10
9
  if value == (perm == "read_insights" ? 1 : 0) # live testing depends on insights calls failing
11
10
  puts "failed!\n" # put a new line after the print above
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: koala
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease: 5
5
- version: 1.1.0rc2
5
+ version: 1.1.0rc3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional
@@ -10,11 +10,11 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-06 00:00:00 +02:00
13
+ date: 2011-06-30 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
- name: json
17
+ name: multi_json
18
18
  requirement: &id001 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
@@ -80,6 +80,7 @@ extra_rdoc_files:
80
80
  files:
81
81
  - .autotest
82
82
  - .gitignore
83
+ - .travis.yml
83
84
  - CHANGELOG
84
85
  - Gemfile
85
86
  - LICENSE
@@ -88,8 +89,10 @@ files:
88
89
  - autotest/discover.rb
89
90
  - koala.gemspec
90
91
  - lib/koala.rb
92
+ - lib/koala/batch_operation.rb
91
93
  - lib/koala/graph_api.rb
92
- - lib/koala/graph_api_batch.rb
94
+ - lib/koala/graph_batch_api.rb
95
+ - lib/koala/graph_collection.rb
93
96
  - lib/koala/http_services.rb
94
97
  - lib/koala/http_services/net_http_service.rb
95
98
  - lib/koala/http_services/typhoeus_service.rb
@@ -113,6 +116,7 @@ files:
113
116
  - spec/cases/test_users_spec.rb
114
117
  - spec/cases/uploadable_io_spec.rb
115
118
  - spec/fixtures/beach.jpg
119
+ - spec/fixtures/cat.m4v
116
120
  - spec/fixtures/facebook_data.yml
117
121
  - spec/fixtures/mock_facebook_responses.yml
118
122
  - spec/spec_helper.rb
@@ -139,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
139
143
  requirements:
140
144
  - - ">="
141
145
  - !ruby/object:Gem::Version
142
- hash: -4094332028190876979
146
+ hash: 1796660115517042142
143
147
  segments:
144
148
  - 0
145
149
  version: "0"
@@ -171,6 +175,7 @@ test_files:
171
175
  - spec/cases/test_users_spec.rb
172
176
  - spec/cases/uploadable_io_spec.rb
173
177
  - spec/fixtures/beach.jpg
178
+ - spec/fixtures/cat.m4v
174
179
  - spec/fixtures/facebook_data.yml
175
180
  - spec/fixtures/mock_facebook_responses.yml
176
181
  - spec/spec_helper.rb
@@ -1,151 +0,0 @@
1
- module Koala
2
- module Facebook
3
- class BatchOperation
4
- attr_reader :access_token, :http_options, :post_processing, :files
5
-
6
- def initialize(options = {})
7
- @args = (options[:args] || {}).dup # because we modify it below
8
- @access_token = options[:access_token]
9
- @http_options = (options[:http_options] || {}).dup # dup because we modify it below
10
- @batch_args = @http_options.delete(:batch_args) || {}
11
- @url = options[:url]
12
- @method = options[:method].to_sym
13
- @post_processing = options[:post_processing]
14
-
15
- process_binary_args
16
-
17
- raise Koala::KoalaError, "Batch operations require an access token, none provided." unless @access_token
18
- end
19
-
20
- def to_batch_params(main_access_token)
21
- # set up the arguments
22
- args_string = Koala.http_service.encode_params(@access_token == main_access_token ? @args : @args.merge(:access_token => @access_token))
23
-
24
- response = {
25
- :method => @method,
26
- :relative_url => @url,
27
- }
28
-
29
- # handle batch-level arguments, such as name, depends_on, and attached_files
30
- @batch_args[:attached_files] = @files.keys.join(",") if @files
31
- response.merge!(@batch_args) if @batch_args
32
-
33
- # for get and delete, we append args to the URL string
34
- # otherwise, they go in the body
35
- if args_string.length > 0
36
- if args_in_url?
37
- response[:relative_url] += (@url =~ /\?/ ? "&" : "?") + args_string if args_string.length > 0
38
- else
39
- response[:body] = args_string if args_string.length > 0
40
- end
41
- end
42
-
43
- response
44
- end
45
-
46
- protected
47
-
48
- def process_binary_args
49
- # collect binary files
50
- @args.each_pair do |key, value|
51
- if UploadableIO.binary_content?(value)
52
- @files ||= {}
53
- # we use object_id to ensure unique file identifiers across multiple batch operations
54
- # remove it from the original hash and add it to the file store
55
- id = "file#{GraphAPI.batch_calls.length}_#{@files.keys.length}"
56
- @files[id] = @args.delete(key).is_a?(UploadableIO) ? value : UploadableIO.new(value)
57
- end
58
- end
59
- end
60
-
61
- def args_in_url?
62
- @method == :get || @method == :delete
63
- end
64
- end
65
-
66
- module GraphAPIBatchMethods
67
- def self.included(base)
68
- base.class_eval do
69
- # batch mode flags
70
- def self.batch_mode?
71
- !!@batch_mode
72
- end
73
-
74
- def self.batch_calls
75
- raise KoalaError, "GraphAPI.batch_calls accessed when not in batch block!" unless batch_mode?
76
- @batch_calls
77
- end
78
-
79
- def self.batch(http_options = {}, &block)
80
- @batch_mode = true
81
- @batch_http_options = http_options
82
- @batch_calls = []
83
- yield
84
- begin
85
- results = batch_api(@batch_calls)
86
- ensure
87
- @batch_mode = false
88
- end
89
- results
90
- end
91
-
92
- def self.batch_api(batch_calls)
93
- return [] unless batch_calls.length > 0
94
- # Facebook requires a top-level access token
95
-
96
- # Get the access token for the user and start building a hash to store params
97
- # Turn the call args collected into what facebook expects
98
- args = {}
99
- access_token = args["access_token"] = batch_calls.first.access_token
100
- args['batch'] = batch_calls.map { |batch_op|
101
- args.merge!(batch_op.files) if batch_op.files
102
- batch_op.to_batch_params(access_token)
103
- }.to_json
104
-
105
- # Make the POST request for the batch call
106
- # batch operations have to go over SSL, but since there's an access token, that secures that
107
- result = Koala.make_request('/', args, 'post', @batch_http_options)
108
- # Raise an error if we get a 500
109
- raise APIError.new("type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}") if result.status >= 500
110
-
111
- response = JSON.parse(result.body.to_s)
112
- # raise an error if we get a Batch API error message
113
- raise APIError.new("type" => "Error #{response["error"]}", "message" => response["error_description"]) if response.is_a?(Hash) && response["error"]
114
-
115
- # otherwise, map the results with post-processing included
116
- index = 0 # keep compat with ruby 1.8 - no with_index for map
117
- response.map do |call_result|
118
- # Get the options hash
119
- batch_op = batch_calls[index]
120
- index += 1
121
-
122
- if call_result
123
- # (see note in regular api method about JSON parsing)
124
- body = JSON.parse("[#{call_result['body'].to_s}]")[0]
125
- unless call_result["code"].to_i >= 500 || error = GraphAPI.check_response(body)
126
- # Get the HTTP component they want
127
- data = case batch_op.http_options[:http_component]
128
- when :status
129
- call_result["code"].to_i
130
- when :headers
131
- # facebook returns the headers as an array of k/v pairs, but we want a regular hash
132
- call_result['headers'].inject({}) { |headers, h| headers[h['name']] = h['value']; headers}
133
- else
134
- body
135
- end
136
-
137
- # process it if we are given a block to process with
138
- batch_op.post_processing ? batch_op.post_processing.call(data) : data
139
- else
140
- error || APIError.new({"type" => "HTTP #{call_result["code"].to_s}", "message" => "Response body: #{body}"})
141
- end
142
- else
143
- nil
144
- end
145
- end
146
- end
147
- end
148
- end
149
- end
150
- end
151
- end