koala 1.1.0rc2 → 1.1.0rc3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@ pkg
2
2
  .project
3
3
  Gemfile.lock
4
4
  .rvmrc
5
+ *.rbc
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - 1.8.7 # (current default)
3
+ - 1.9.2
4
+ - rbx
5
+ - rbx-2.0
6
+ - ree
7
+ - jruby
8
+ - ruby-head
data/CHANGELOG CHANGED
@@ -1,9 +1,12 @@
1
1
  v1.1
2
2
  New methods:
3
- -- Batch API support through Koala::Facebook::GraphAPI.batch (thanks, seejohnrun!)
3
+ -- Added Batch API support (thanks, seejohnrun and spiegela!)
4
4
  -- includes file uploads, error handling, and FQL
5
+ -- Added GraphAPI#put_video
5
6
  -- Added GraphAPI#get_comments_for_urls (thanks, amrnt!)
6
- -- Added RestAPI#fql_multiquery, which simplifies the results (thanks, amrnt!)
7
+ -- Added RestAPI#fql_multiquery, which simplifies the results (thanks, amrnt!)
8
+ -- HTTP services support global proxy and timeout settings (thanks, itchy!)
9
+ -- Net::HTTP supports global ca_path, ca_file, and verify_mode settings (thanks, spiegela!)
7
10
  Updated methods:
8
11
  -- RealtimeUpdates now uses a GraphAPI object instead of its own API
9
12
  -- RestAPI#rest_call now has an optional last argument for method, for calls requiring POST, DELETE, etc. (thanks, sshilo!)
@@ -12,9 +15,10 @@ Updated methods:
12
15
  Internal improvements:
13
16
  -- HTTP services are more modular and can be changed on the fly (thanks, chadk!)
14
17
  -- Includes support for uploading StringIOs and other non-files via Net::HTTP even when using TyphoeusService
15
- -- Support for global proxy and timeout settings (thanks, itchy!)
16
- -- Support for setting certificate path and file to address Net::HTTP errors under Ruby 1.9.2
18
+ -- Koala now uses multi_json to improve compatibility with Rubinius and other Ruby versions
17
19
  -- Koala now uses the modern Typhoeus API (thanks, aselder!)
20
+ -- Koala now uses the current modern Net::HTTP interface (thanks, romanbsd!)
21
+ -- Fixed bugs and typos (thanks, waynn, mokevnin, and tikh!)
18
22
 
19
23
  v1.0
20
24
  New methods:
@@ -24,13 +28,13 @@ New methods:
24
28
  -- Added put_connection and delete_connection convenience methods
25
29
  Updated methods:
26
30
  -- Search can now search places, checkins, etc. (thanks, rickyc!)
27
- -- You can now pass :beta => true in the http options to use Facebook's beta tier.
31
+ -- You can now pass :beta => true in the http options to use Facebook's beta tier
28
32
  -- TestUser#befriend now requires user info hashes (id and access token) due to Facebook API changes (thanks, pulsd and kbighorse!)
29
33
  -- All methods now accept an http_options hash as their optional last parameter (thanks, spiegela!)
30
34
  -- url_for_oauth_code can now take a :display option (thanks, netbe!)
31
35
  -- Net::HTTP can now accept :timeout and :proxy options (thanks, gilles!)
32
36
  -- Test users now supports using test accounts across multiple apps
33
- Internal improvements:
37
+ Internal improvements:
34
38
  -- For public requests, Koala now uses http by default (instead of https) to improve speed
35
39
  -- This can be overridden through Koala.always_use_ssl= or by passing :use_ssl => true in the options hash for an api call
36
40
  -- Read-only REST API requests now go through the faster api-read server
@@ -40,6 +44,7 @@ Internal improvements:
40
44
  -- Updated parse_signed_request to match Facebook's current implementation (thanks, imajes!)
41
45
  -- APIError is now < StandardError, not Exception
42
46
  -- Added KoalaError for non-API errors
47
+ -- Net::HTTP's SSL verification is no longer disabled by default
43
48
  Test improvements:
44
49
  -- Incorporated joshk's awesome rewrite of the entire Koala test suite (thanks, joshk!)
45
50
  -- Expanded HTTP service tests (added Typhoeus test suite and additional Net::HTTP test cases)
data/koala.gemspec CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{koala}
5
- s.version = "1.1.0rc2"
6
- s.date = %q{2011-06-06}
5
+ s.version = "1.1.0rc3"
6
+ s.date = %q{2011-06-30}
7
7
 
8
8
  s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the REST API, realtime updates, and OAuth authentication.}
9
9
  s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph and REST APIs, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
@@ -28,20 +28,20 @@ Gem::Specification.new do |s|
28
28
  s.specification_version = 3
29
29
 
30
30
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
31
- s.add_runtime_dependency(%q<json>, ["~> 1.0"])
31
+ s.add_runtime_dependency(%q<multi_json>, ["~> 1.0"])
32
32
  s.add_runtime_dependency(%q<multipart-post>, ["~> 1.0"])
33
33
  s.add_development_dependency(%q<rspec>, ["~> 2.5"])
34
34
  s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
35
35
  s.add_development_dependency(%q<typhoeus>, ["~> 0.2.4"])
36
36
  else
37
- s.add_dependency(%q<json>, ["~> 1.0"])
37
+ s.add_dependency(%q<multi_json>, ["~> 1.0"])
38
38
  s.add_dependency(%q<multipart-post>, ["~> 1.0"])
39
39
  s.add_dependency(%q<rspec>, ["~> 2.5"])
40
40
  s.add_dependency(%q<rake>, ["~> 0.8.7"])
41
41
  s.add_dependency(%q<typhoeus>, ["~> 0.2.4"])
42
42
  end
43
43
  else
44
- s.add_dependency(%q<json>, ["~> 1.0"])
44
+ s.add_dependency(%q<multi_json>, ["~> 1.0"])
45
45
  s.add_dependency(%q<multipart-post>, ["~> 1.0"])
46
46
  s.add_dependency(%q<rspec>, ["~> 2.5"])
47
47
  s.add_dependency(%q<rake>, ["~> 0.8.7"])
data/lib/koala.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'cgi'
2
2
  require 'digest/md5'
3
3
 
4
- require 'json'
4
+ require 'multi_json'
5
5
 
6
6
  # OpenSSL and Base64 are required to support signed_request
7
7
  require 'openssl'
@@ -12,7 +12,9 @@ require 'koala/http_services'
12
12
  require 'koala/http_services/net_http_service'
13
13
  require 'koala/oauth'
14
14
  require 'koala/graph_api'
15
- require 'koala/graph_api_batch'
15
+ require 'koala/graph_batch_api'
16
+ require 'koala/batch_operation'
17
+ require 'koala/graph_collection'
16
18
  require 'koala/rest_api'
17
19
  require 'koala/realtime_updates'
18
20
  require 'koala/test_users'
@@ -53,8 +55,8 @@ module Koala
53
55
 
54
56
  # parse the body as JSON and run it through the error checker (if provided)
55
57
  # Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
56
- # and cause JSON.parse to fail -- so we account for that by wrapping the result in []
57
- body = JSON.parse("[#{result.body.to_s}]")[0]
58
+ # and cause MultiJson.decode to fail -- so we account for that by wrapping the result in []
59
+ body = MultiJson.decode("[#{result.body.to_s}]")[0]
58
60
  yield body if error_checking_block
59
61
 
60
62
  # if we want a component other than the body (e.g. redirect header for images), return that
@@ -66,9 +68,12 @@ module Koala
66
68
 
67
69
  class GraphAPI < API
68
70
  include GraphAPIMethods
69
- include GraphAPIBatchMethods
70
71
  end
71
72
 
73
+ class GraphBatchAPI < GraphAPI
74
+ include GraphBatchAPIMethods
75
+ end
76
+
72
77
  class RestAPI < API
73
78
  include RestAPIMethods
74
79
  end
@@ -0,0 +1,74 @@
1
+ module Koala
2
+ module Facebook
3
+ class BatchOperation
4
+ attr_reader :access_token, :http_options, :post_processing, :files, :batch_api, :identifier
5
+
6
+ @identifier = 0
7
+
8
+ def self.next_identifier
9
+ @identifier += 1
10
+ end
11
+
12
+ def initialize(options = {})
13
+ @identifier = self.class.next_identifier
14
+ @args = (options[:args] || {}).dup # because we modify it below
15
+ @access_token = options[:access_token]
16
+ @http_options = (options[:http_options] || {}).dup # dup because we modify it below
17
+ @batch_args = @http_options.delete(:batch_args) || {}
18
+ @url = options[:url]
19
+ @method = options[:method].to_sym
20
+ @post_processing = options[:post_processing]
21
+
22
+ process_binary_args
23
+
24
+ raise Koala::KoalaError, "Batch operations require an access token, none provided." unless @access_token
25
+ end
26
+
27
+ def to_batch_params(main_access_token)
28
+ # set up the arguments
29
+ args_string = Koala.http_service.encode_params(@access_token == main_access_token ? @args : @args.merge(:access_token => @access_token))
30
+
31
+ response = {
32
+ :method => @method.to_s,
33
+ :relative_url => @url,
34
+ }
35
+
36
+ # handle batch-level arguments, such as name, depends_on, and attached_files
37
+ @batch_args[:attached_files] = @files.keys.join(",") if @files
38
+ response.merge!(@batch_args) if @batch_args
39
+
40
+ # for get and delete, we append args to the URL string
41
+ # otherwise, they go in the body
42
+ if args_string.length > 0
43
+ if args_in_url?
44
+ response[:relative_url] += (@url =~ /\?/ ? "&" : "?") + args_string if args_string.length > 0
45
+ else
46
+ response[:body] = args_string if args_string.length > 0
47
+ end
48
+ end
49
+
50
+ response
51
+ end
52
+
53
+ protected
54
+
55
+ def process_binary_args
56
+ # collect binary files
57
+ @args.each_pair do |key, value|
58
+ if UploadableIO.binary_content?(value)
59
+ @files ||= {}
60
+ # we use a class-level counter to ensure unique file identifiers across multiple batch operations
61
+ # (this is thread safe, since we just care about uniqueness)
62
+ # so remove the file from the original hash and add it to the file store
63
+ id = "op#{identifier}_file#{@files.keys.length}"
64
+ @files[id] = @args.delete(key).is_a?(UploadableIO) ? value : UploadableIO.new(value)
65
+ end
66
+ end
67
+ end
68
+
69
+ def args_in_url?
70
+ @method == :get || @method == :delete
71
+ end
72
+ end
73
+ end
74
+ end
@@ -29,19 +29,6 @@ module Koala
29
29
  # Koala::Facebook::OAuth.get_user_from_cookie() method below to get the OAuth access token
30
30
  # for the active user from the cookie saved by the SDK.
31
31
 
32
- def self.included(base)
33
- base.class_eval do
34
- def self.check_response(response)
35
- # check for Graph API-specific errors
36
- # this returns an error, which is immediately raised (non-batch)
37
- # or added to the list of batch results (batch)
38
- if response.is_a?(Hash) && error_details = response["error"]
39
- APIError.new(error_details)
40
- end
41
- end
42
- end
43
- end
44
-
45
32
  # Objects
46
33
 
47
34
  def get_object(id, args = {}, options = {})
@@ -109,9 +96,9 @@ module Koala
109
96
  graph_call("#{id}/#{connection_name}", args, "delete", options)
110
97
  end
111
98
 
112
- # Pictures
113
- # to delete pictures, use delete_object(photo_id)
114
- # note: you'll need the user_photos permission to actually access photos after uploading them
99
+ # Media (photos and videos)
100
+ # to delete photos or videos, use delete_object(object_id)
101
+ # note: you'll need the user_photos or user_videos permissions to actually access media after upload
115
102
 
116
103
  def get_picture(object, args = {}, options = {})
117
104
  # Gets a picture object, returning the URL (which Facebook sends as a header)
@@ -120,36 +107,30 @@ module Koala
120
107
  end
121
108
  end
122
109
 
110
+ # Can be called in multiple ways:
111
+ #
112
+ # put_picture(file, [content_type], ...)
113
+ # put_picture(path_to_file, [content_type], ...)
114
+ #
115
+ # You can pass in uploaded files directly from Rails or Sinatra.
116
+ # (See lib/koala/uploadable_io.rb for supported frameworks)
117
+ #
118
+ # Optional parameters can be added to the end of the argument list:
119
+ # - args: a hash of request parameters (default: {})
120
+ # - target_id: ID of the target where to post the picture (default: "me")
121
+ # - options: a hash of http options passed to the HTTPService module
122
+ #
123
+ # put_picture(file, content_type, {:message => "Message"}, 01234560)
124
+ # put_picture(params[:file], {:message => "Message"})
125
+
123
126
  def put_picture(*picture_args)
124
- # Can be called in multiple ways:
125
- #
126
- # put_picture(file, [content_type], ...)
127
- # put_picture(path_to_file, [content_type], ...)
128
- #
129
- # You can pass in uploaded files directly from Rails or Sinatra.
130
- # (See lib/koala/uploadable_io.rb for supported frameworks)
131
- #
132
- # Optional parameters can be added to the end of the argument list:
133
- # - args: a hash of request parameters (default: {})
134
- # - target_id: ID of the target where to post the picture (default: "me")
135
- # - options: a hash of http options passed to the HTTPService module
136
- #
137
- # put_picture(file, content_type, {:message => "Message"}, 01234560)
138
- # put_picture(params[:file], {:message => "Message"})
139
-
140
- raise KoalaError.new("Wrong number of arguments for put_picture") unless picture_args.size.between?(1, 5)
141
-
142
- args_offset = picture_args[1].kind_of?(Hash) || picture_args.size == 1 ? 0 : 1
143
-
144
- args = picture_args[1 + args_offset] || {}
145
- target_id = picture_args[2 + args_offset] || "me"
146
- options = picture_args[3 + args_offset] || {}
127
+ put_object(*parse_media_args(picture_args, "photos"))
128
+ end
147
129
 
148
- args["source"] = Koala::UploadableIO.new(*picture_args.slice(0, 1 + args_offset))
149
-
150
- options[:http_service] = Koala.base_http_service if args["source"].requires_base_http_service
151
-
152
- self.put_object(target_id, "photos", args, options)
130
+ def put_video(*video_args)
131
+ args = parse_media_args(video_args, "videos")
132
+ args.last[:video] = true
133
+ put_object(*args)
153
134
  end
154
135
 
155
136
  # Wall posts
@@ -202,34 +183,6 @@ module Koala
202
183
  end
203
184
  end
204
185
 
205
- # API access
206
-
207
- # Make a call which may or may not be batched
208
- def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
209
- # Direct access to the Facebook API
210
- # see any of the above methods for example invocations
211
- unless GraphAPI.batch_mode?
212
- result = api(path, args, verb, options) do |response|
213
- if error = GraphAPI.check_response(response)
214
- raise error
215
- end
216
- end
217
-
218
- # now process as appropriate (get picture header, make GraphCollection, etc.)
219
- post_processing ? post_processing.call(result) : result
220
- else
221
- # for batch APIs, we queue up the call details (incl. post-processing)
222
- GraphAPI.batch_calls << BatchOperation.new(
223
- :url => path,
224
- :args => args,
225
- :method => verb,
226
- :access_token => @access_token,
227
- :http_options => options,
228
- :post_processing => post_processing
229
- )
230
- nil # batch operations return nothing immediately
231
- end
232
- end
233
186
 
234
187
  # GraphCollection support
235
188
  def get_page(params)
@@ -240,58 +193,66 @@ module Koala
240
193
  end
241
194
  end
242
195
 
243
- end
244
-
245
-
246
- class GraphCollection < Array
247
- # This class is a light wrapper for collections returned
248
- # from the Graph API.
249
- #
250
- # It extends Array to allow direct access to the data colleciton
251
- # which should allow it to drop in seamlessly.
252
- #
253
- # It also allows access to paging information and the
254
- # ability to get the next/previous page in the collection
255
- # by calling next_page or previous_page.
256
- attr_reader :paging
257
- attr_reader :api
258
196
 
259
- def initialize(response, api)
260
- super response["data"]
261
- @paging = response["paging"]
262
- @api = api
263
- end
264
-
265
- # defines methods for NEXT and PREVIOUS pages
266
- %w{next previous}.each do |this|
267
-
268
- # def next_page
269
- # def previous_page
270
- define_method "#{this.to_sym}_page" do
271
- base, args = send("#{this}_page_params")
272
- base ? @api.get_page([base, args]) : nil
197
+ # Batch API
198
+ def batch(http_options = {}, &block)
199
+ batch_client = GraphBatchAPI.new(access_token)
200
+ if block
201
+ yield batch_client
202
+ batch_client.execute(http_options)
203
+ else
204
+ batch_client
273
205
  end
274
-
275
- # def next_page_params
276
- # def previous_page_params
277
- define_method "#{this.to_sym}_page_params" do
278
- return nil unless @paging and @paging[this]
279
- parse_page_url(@paging[this])
206
+ end
207
+
208
+ def self.included(base)
209
+ base.class_eval do
210
+ def self.batch
211
+ raise NoMethodError, "The BatchAPI signature has changed (the original implementation was not thread-safe). Please see https://github.com/arsduo/koala/wiki/Batch-requests. (This message will be removed in the final 1.1 release.)"
212
+ end
280
213
  end
281
214
  end
282
215
 
283
- def parse_page_url(url)
284
- match = url.match(/.com\/(.*)\?(.*)/)
285
- base = match[1]
286
- args = match[2]
287
- params = CGI.parse(args)
288
- new_params = {}
289
- params.each_pair do |key,value|
290
- new_params[key] = value.join ","
216
+ # Direct access to the Facebook API
217
+ # see any of the above methods for example invocations
218
+ def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
219
+ result = api(path, args, verb, options) do |response|
220
+ error = check_response(response)
221
+ raise error if error
291
222
  end
292
- [base,new_params]
223
+
224
+ # now process as appropriate (get picture header, make GraphCollection, etc.)
225
+ post_processing ? post_processing.call(result) : result
293
226
  end
294
227
 
228
+ def check_response(response)
229
+ # check for Graph API-specific errors
230
+ # this returns an error, which is immediately raised (non-batch)
231
+ # or added to the list of batch results (batch)
232
+ if response.is_a?(Hash) && error_details = response["error"]
233
+ APIError.new(error_details)
234
+ end
235
+ end
236
+
237
+ private
238
+
239
+ def parse_media_args(media_args, method)
240
+ # photo and video uploads can accept different types of arguments (see above)
241
+ # so here, we parse the arguments into a form directly usable in put_object
242
+ raise KoalaError.new("Wrong number of arguments for put_#{method == "photos" ? "picture" : "video"}") unless media_args.size.between?(1, 5)
243
+
244
+ args_offset = media_args[1].kind_of?(Hash) || media_args.size == 1 ? 0 : 1
245
+
246
+ args = media_args[1 + args_offset] || {}
247
+ target_id = media_args[2 + args_offset] || "me"
248
+ options = media_args[3 + args_offset] || {}
249
+
250
+ args["source"] = Koala::UploadableIO.new(*media_args.slice(0, 1 + args_offset))
251
+
252
+ options[:http_service] = Koala.base_http_service if args["source"].requires_base_http_service
253
+
254
+ [target_id, method, args, options]
255
+ end
295
256
  end
296
257
  end
297
- end
258
+ end