koala 1.0.0 → 1.1.0rc
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.autotest
ADDED
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
v1.1
|
2
|
+
New/updated methods:
|
3
|
+
-- Batch API support through Koala::Facebook::GraphAPI.batch (thanks, seejohnrun!)
|
4
|
+
-- includes file uploads, error handling, and FQL
|
5
|
+
-- Added GraphAPI#get_comments_for_urls (thanks, amrnt!)
|
6
|
+
-- Added RestAPI#fql_multiquery, which simplifies the results (thanks, amrnt!)
|
7
|
+
Updated methods:
|
8
|
+
-- RealtimeUpdates now uses a GraphAPI object instead of its own API
|
9
|
+
-- RestAPI#rest_call now has an optional last argument for method, for calls requiring POST, DELETE, etc. (thanks, sshilo!)
|
10
|
+
-- Filename can now be specified when uploading (e.g. for Ads API) (thanks, sshilo!)
|
11
|
+
-- get_objects([]) returns [] instead of a Facebook error in non-batch mode (thanks, aselder!)
|
12
|
+
Internal improvements:
|
13
|
+
-- HTTP services are more modular and can be changed on the fly (thanks, chadk!)
|
14
|
+
-- 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
|
17
|
+
-- Koala now uses the modern Typhoeus API (thanks, aselder!)
|
18
|
+
|
1
19
|
v1.0
|
2
20
|
New methods:
|
3
21
|
-- Photo and file upload now supported through #put_picture
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
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.
|
6
|
-
s.date = %q{2011-
|
5
|
+
s.version = "1.1.0rc"
|
6
|
+
s.date = %q{2011-06-06}
|
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.}
|
@@ -30,20 +30,20 @@ Gem::Specification.new do |s|
|
|
30
30
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
31
31
|
s.add_runtime_dependency(%q<json>, ["~> 1.0"])
|
32
32
|
s.add_runtime_dependency(%q<multipart-post>, ["~> 1.0"])
|
33
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.5
|
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
37
|
s.add_dependency(%q<json>, ["~> 1.0"])
|
38
38
|
s.add_dependency(%q<multipart-post>, ["~> 1.0"])
|
39
|
-
s.add_dependency(%q<rspec>, ["~> 2.5
|
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
44
|
s.add_dependency(%q<json>, ["~> 1.0"])
|
45
45
|
s.add_dependency(%q<multipart-post>, ["~> 1.0"])
|
46
|
-
s.add_dependency(%q<rspec>, ["~> 2.5
|
46
|
+
s.add_dependency(%q<rspec>, ["~> 2.5"])
|
47
47
|
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
48
48
|
s.add_dependency(%q<typhoeus>, ["~> 0.2.4"])
|
49
49
|
end
|
data/lib/koala.rb
CHANGED
@@ -9,10 +9,14 @@ require 'base64'
|
|
9
9
|
|
10
10
|
# include koala modules
|
11
11
|
require 'koala/http_services'
|
12
|
+
require 'koala/http_services/net_http_service'
|
13
|
+
require 'koala/oauth'
|
12
14
|
require 'koala/graph_api'
|
15
|
+
require 'koala/graph_api_batch'
|
13
16
|
require 'koala/rest_api'
|
14
17
|
require 'koala/realtime_updates'
|
15
18
|
require 'koala/test_users'
|
19
|
+
require 'koala/http_services'
|
16
20
|
|
17
21
|
# add KoalaIO class
|
18
22
|
require 'koala/uploadable_io'
|
@@ -47,27 +51,24 @@ module Koala
|
|
47
51
|
# in the case of a server error
|
48
52
|
raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
|
49
53
|
|
50
|
-
#
|
54
|
+
# parse the body as JSON and run it through the error checker (if provided)
|
51
55
|
# Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
|
52
56
|
# and cause JSON.parse to fail -- so we account for that by wrapping the result in []
|
53
|
-
body =
|
54
|
-
if error_checking_block
|
55
|
-
yield(body)
|
56
|
-
end
|
57
|
+
body = JSON.parse("[#{result.body.to_s}]")[0]
|
58
|
+
yield body if error_checking_block
|
57
59
|
|
58
|
-
#
|
59
|
-
|
60
|
-
result.send(options[:http_component])
|
61
|
-
else
|
62
|
-
body
|
63
|
-
end
|
60
|
+
# if we want a component other than the body (e.g. redirect header for images), return that
|
61
|
+
options[:http_component] ? result.send(options[:http_component]) : body
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
65
|
+
# APIs
|
66
|
+
|
67
67
|
class GraphAPI < API
|
68
68
|
include GraphAPIMethods
|
69
|
+
include GraphAPIBatchMethods
|
69
70
|
end
|
70
|
-
|
71
|
+
|
71
72
|
class RestAPI < API
|
72
73
|
include RestAPIMethods
|
73
74
|
end
|
@@ -87,6 +88,8 @@ module Koala
|
|
87
88
|
attr_reader :graph_api
|
88
89
|
end
|
89
90
|
|
91
|
+
# Errors
|
92
|
+
|
90
93
|
class APIError < StandardError
|
91
94
|
attr_accessor :fb_error_type
|
92
95
|
def initialize(details = {})
|
@@ -94,201 +97,33 @@ module Koala
|
|
94
97
|
super("#{fb_error_type}: #{details["message"]}")
|
95
98
|
end
|
96
99
|
end
|
100
|
+
end
|
97
101
|
|
102
|
+
class KoalaError < StandardError; end
|
98
103
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
@oauth_callback_url = oauth_callback_url
|
105
|
-
end
|
106
|
-
|
107
|
-
def get_user_info_from_cookie(cookie_hash)
|
108
|
-
# Parses the cookie set by the official Facebook JavaScript SDK.
|
109
|
-
#
|
110
|
-
# cookies should be a Hash, like the one Rails provides
|
111
|
-
#
|
112
|
-
# If the user is logged in via Facebook, we return a dictionary with the
|
113
|
-
# keys "uid" and "access_token". The former is the user's Facebook ID,
|
114
|
-
# and the latter can be used to make authenticated requests to the Graph API.
|
115
|
-
# If the user is not logged in, we return None.
|
116
|
-
#
|
117
|
-
# Download the official Facebook JavaScript SDK at
|
118
|
-
# http://github.com/facebook/connect-js/. Read more about Facebook
|
119
|
-
# authentication at http://developers.facebook.com/docs/authentication/.
|
120
|
-
|
121
|
-
if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
|
122
|
-
# remove the opening/closing quote
|
123
|
-
fb_cookie = fb_cookie.gsub(/\"/, "")
|
124
|
-
|
125
|
-
# since we no longer get individual cookies, we have to separate out the components ourselves
|
126
|
-
components = {}
|
127
|
-
fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
|
128
|
-
|
129
|
-
# generate the signature and make sure it matches what we expect
|
130
|
-
auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
|
131
|
-
sig = Digest::MD5.hexdigest(auth_string + @app_secret)
|
132
|
-
sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
|
133
|
-
end
|
134
|
-
end
|
135
|
-
alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
|
136
|
-
|
137
|
-
def get_user_from_cookie(cookies)
|
138
|
-
if info = get_user_info_from_cookies(cookies)
|
139
|
-
string = info["uid"]
|
140
|
-
end
|
141
|
-
end
|
142
|
-
alias_method :get_user_from_cookies, :get_user_from_cookie
|
143
|
-
|
144
|
-
# URLs
|
145
|
-
|
146
|
-
def url_for_oauth_code(options = {})
|
147
|
-
# for permissions, see http://developers.facebook.com/docs/authentication/permissions
|
148
|
-
permissions = options[:permissions]
|
149
|
-
scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
|
150
|
-
display = options.has_key?(:display) ? "&display=#{options[:display]}" : ""
|
151
|
-
|
152
|
-
callback = options[:callback] || @oauth_callback_url
|
153
|
-
raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
|
154
|
-
|
155
|
-
# Creates the URL for oauth authorization for a given callback and optional set of permissions
|
156
|
-
"https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}#{display}"
|
157
|
-
end
|
158
|
-
|
159
|
-
def url_for_access_token(code, options = {})
|
160
|
-
# Creates the URL for the token corresponding to a given code generated by Facebook
|
161
|
-
callback = options[:callback] || @oauth_callback_url
|
162
|
-
raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
|
163
|
-
"https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
|
164
|
-
end
|
165
|
-
|
166
|
-
def get_access_token_info(code, options = {})
|
167
|
-
# convenience method to get a parsed token from Facebook for a given code
|
168
|
-
# should this require an OAuth callback URL?
|
169
|
-
get_token_from_server({:code => code, :redirect_uri => @oauth_callback_url}, false, options)
|
170
|
-
end
|
171
|
-
|
172
|
-
def get_access_token(code, options = {})
|
173
|
-
# upstream methods will throw errors if needed
|
174
|
-
if info = get_access_token_info(code, options)
|
175
|
-
string = info["access_token"]
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def get_app_access_token_info(options = {})
|
180
|
-
# convenience method to get a the application's sessionless access token
|
181
|
-
get_token_from_server({:type => 'client_cred'}, true, options)
|
182
|
-
end
|
183
|
-
|
184
|
-
def get_app_access_token(options = {})
|
185
|
-
if info = get_app_access_token_info(options)
|
186
|
-
string = info["access_token"]
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
# Originally provided directly by Facebook, however this has changed
|
191
|
-
# as their concept of crypto changed. For historic purposes, this is their proposal:
|
192
|
-
# https://developers.facebook.com/docs/authentication/canvas/encryption_proposal/
|
193
|
-
# Currently see https://github.com/facebook/php-sdk/blob/master/src/facebook.php#L758
|
194
|
-
# for a more accurate reference implementation strategy.
|
195
|
-
def parse_signed_request(input)
|
196
|
-
encoded_sig, encoded_envelope = input.split('.', 2)
|
197
|
-
signature = base64_url_decode(encoded_sig).unpack("H*").first
|
198
|
-
envelope = JSON.parse(base64_url_decode(encoded_envelope))
|
199
|
-
|
200
|
-
raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
|
201
|
-
|
202
|
-
# now see if the signature is valid (digest, key, data)
|
203
|
-
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope.tr("-_", "+/"))
|
204
|
-
raise 'SignedRequest: Invalid signature' if (signature != hmac)
|
205
|
-
|
206
|
-
return envelope
|
207
|
-
end
|
208
|
-
|
209
|
-
# from session keys
|
210
|
-
def get_token_info_from_session_keys(sessions, options = {})
|
211
|
-
# fetch the OAuth tokens from Facebook
|
212
|
-
response = fetch_token_string({
|
213
|
-
:type => 'client_cred',
|
214
|
-
:sessions => sessions.join(",")
|
215
|
-
}, true, "exchange_sessions", options)
|
216
|
-
|
217
|
-
# Facebook returns an empty body in certain error conditions
|
218
|
-
if response == ""
|
219
|
-
raise APIError.new({
|
220
|
-
"type" => "ArgumentError",
|
221
|
-
"message" => "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!"
|
222
|
-
})
|
223
|
-
end
|
224
|
-
|
225
|
-
JSON.parse(response)
|
226
|
-
end
|
227
|
-
|
228
|
-
def get_tokens_from_session_keys(sessions, options = {})
|
229
|
-
# get the original hash results
|
230
|
-
results = get_token_info_from_session_keys(sessions, options)
|
231
|
-
# now recollect them as just the access tokens
|
232
|
-
results.collect { |r| r ? r["access_token"] : nil }
|
233
|
-
end
|
234
|
-
|
235
|
-
def get_token_from_session_key(session, options = {})
|
236
|
-
# convenience method for a single key
|
237
|
-
# gets the overlaoded strings automatically
|
238
|
-
get_tokens_from_session_keys([session], options)[0]
|
239
|
-
end
|
240
|
-
|
241
|
-
protected
|
242
|
-
|
243
|
-
def get_token_from_server(args, post = false, options = {})
|
244
|
-
# fetch the result from Facebook's servers
|
245
|
-
result = fetch_token_string(args, post, "access_token", options)
|
246
|
-
|
247
|
-
# if we have an error, parse the error JSON and raise an error
|
248
|
-
raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
|
249
|
-
|
250
|
-
# otherwise, parse the access token
|
251
|
-
parse_access_token(result)
|
252
|
-
end
|
253
|
-
|
254
|
-
def parse_access_token(response_text)
|
255
|
-
components = response_text.split("&").inject({}) do |hash, bit|
|
256
|
-
key, value = bit.split("=")
|
257
|
-
hash.merge!(key => value)
|
258
|
-
end
|
259
|
-
components
|
260
|
-
end
|
261
|
-
|
262
|
-
def fetch_token_string(args, post = false, endpoint = "access_token", options = {})
|
263
|
-
Koala.make_request("/oauth/#{endpoint}", {
|
264
|
-
:client_id => @app_id,
|
265
|
-
:client_secret => @app_secret
|
266
|
-
}.merge!(args), post ? "post" : "get", {:use_ssl => true}.merge!(options)).body
|
267
|
-
end
|
268
|
-
|
269
|
-
# base 64
|
270
|
-
# directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb
|
271
|
-
def base64_url_decode(str)
|
272
|
-
str += '=' * (4 - str.length.modulo(4))
|
273
|
-
Base64.decode64(str.tr('-_', '+/'))
|
274
|
-
end
|
275
|
-
end
|
104
|
+
# Make an api request using the provided api service or one passed by the caller
|
105
|
+
def self.make_request(path, args, verb, options = {})
|
106
|
+
http_service = options.delete(:http_service) || Koala.http_service
|
107
|
+
options = options.merge(:use_ssl => true) if @always_use_ssl
|
108
|
+
http_service.make_request(path, args, verb, options)
|
276
109
|
end
|
277
110
|
|
278
|
-
class KoalaError< StandardError; end
|
279
|
-
|
280
111
|
# finally, set up the http service Koala methods used to make requests
|
281
112
|
# you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
|
282
|
-
|
283
|
-
|
113
|
+
class << self
|
114
|
+
attr_accessor :http_service
|
115
|
+
attr_accessor :always_use_ssl
|
116
|
+
attr_accessor :base_http_service
|
284
117
|
end
|
118
|
+
Koala.base_http_service = NetHTTPService
|
285
119
|
|
286
120
|
# by default, try requiring Typhoeus -- if that works, use it
|
287
121
|
# if you have Typheous and don't want to use it (or want another service),
|
288
122
|
# you can run Koala.http_service = NetHTTPService (or MyHTTPService)
|
289
123
|
begin
|
124
|
+
require 'koala/http_services/typhoeus_service'
|
290
125
|
Koala.http_service = TyphoeusService
|
291
126
|
rescue LoadError
|
292
|
-
Koala.http_service =
|
127
|
+
Koala.http_service = Koala.base_http_service
|
293
128
|
end
|
294
129
|
end
|
data/lib/koala/graph_api.rb
CHANGED
@@ -28,7 +28,20 @@ module Koala
|
|
28
28
|
# If you are using the JavaScript SDK, you can use the
|
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
|
+
|
32
45
|
# Objects
|
33
46
|
|
34
47
|
def get_object(id, args = {}, options = {})
|
@@ -37,10 +50,10 @@ module Koala
|
|
37
50
|
end
|
38
51
|
|
39
52
|
def get_objects(ids, args = {}, options = {})
|
40
|
-
# Fetchs all of the given
|
41
|
-
#
|
42
|
-
|
43
|
-
graph_call("", args.merge("ids" => ids.join(",")), "get", options)
|
53
|
+
# Fetchs all of the given objects from the graph.
|
54
|
+
# If any of the IDs are invalid, they'll raise an exception.
|
55
|
+
return [] if ids.empty?
|
56
|
+
graph_call("", args.merge("ids" => ids.respond_to?(:join) ? ids.join(",") : ids), "get", options)
|
44
57
|
end
|
45
58
|
|
46
59
|
def put_object(parent_object, connection_name, args = {}, options = {})
|
@@ -71,10 +84,19 @@ module Koala
|
|
71
84
|
|
72
85
|
def get_connections(id, connection_name, args = {}, options = {})
|
73
86
|
# Fetchs the connections for given object.
|
74
|
-
|
75
|
-
|
87
|
+
graph_call("#{id}/#{connection_name}", args, "get", options) do |result|
|
88
|
+
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
89
|
+
end
|
76
90
|
end
|
77
91
|
|
92
|
+
def get_comments_for_urls(urls = [], args = {}, options = {})
|
93
|
+
# Fetchs the comments for given URLs (array or comma-separated string)
|
94
|
+
# see https://developers.facebook.com/blog/post/490
|
95
|
+
return [] if urls.empty?
|
96
|
+
args.merge!(:ids => urls.respond_to?(:join) ? urls.join(",") : urls)
|
97
|
+
get_object("comments", args, options)
|
98
|
+
end
|
99
|
+
|
78
100
|
def put_connections(id, connection_name, args = {}, options = {})
|
79
101
|
# Posts a certain connection
|
80
102
|
raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "Write operations require an access token"}) unless @access_token
|
@@ -93,8 +115,9 @@ module Koala
|
|
93
115
|
|
94
116
|
def get_picture(object, args = {}, options = {})
|
95
117
|
# Gets a picture object, returning the URL (which Facebook sends as a header)
|
96
|
-
|
97
|
-
|
118
|
+
graph_call("#{object}/picture", args, "get", options.merge(:http_component => :headers)) do |result|
|
119
|
+
result["Location"]
|
120
|
+
end
|
98
121
|
end
|
99
122
|
|
100
123
|
def put_picture(*picture_args)
|
@@ -123,7 +146,9 @@ module Koala
|
|
123
146
|
options = picture_args[3 + args_offset] || {}
|
124
147
|
|
125
148
|
args["source"] = Koala::UploadableIO.new(*picture_args.slice(0, 1 + args_offset))
|
126
|
-
|
149
|
+
|
150
|
+
options[:http_service] = Koala.base_http_service if args["source"].requires_base_http_service
|
151
|
+
|
127
152
|
self.put_object(target_id, "photos", args, options)
|
128
153
|
end
|
129
154
|
|
@@ -172,47 +197,62 @@ module Koala
|
|
172
197
|
|
173
198
|
def search(search_terms, args = {}, options = {})
|
174
199
|
args.merge!({:q => search_terms}) unless search_terms.nil?
|
175
|
-
|
176
|
-
|
200
|
+
graph_call("search", args, "get", options) do |result|
|
201
|
+
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
202
|
+
end
|
177
203
|
end
|
178
204
|
|
179
205
|
# API access
|
180
|
-
|
181
|
-
|
206
|
+
|
207
|
+
# Make a call which may or may not be batched
|
208
|
+
def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
|
182
209
|
# Direct access to the Facebook API
|
183
210
|
# see any of the above methods for example invocations
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
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
|
188
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
|
189
231
|
end
|
190
|
-
|
191
|
-
response
|
192
|
-
end
|
232
|
+
end
|
193
233
|
|
194
234
|
# GraphCollection support
|
195
|
-
|
196
235
|
def get_page(params)
|
197
236
|
# Pages through a set of results stored in a GraphCollection
|
198
237
|
# Used for connections and search results
|
199
|
-
|
200
|
-
|
238
|
+
graph_call(*params) do |result|
|
239
|
+
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
240
|
+
end
|
201
241
|
end
|
202
242
|
|
203
243
|
end
|
204
244
|
|
205
245
|
|
206
246
|
class GraphCollection < Array
|
207
|
-
#This class is a light wrapper for collections returned
|
208
|
-
#from the Graph API.
|
247
|
+
# This class is a light wrapper for collections returned
|
248
|
+
# from the Graph API.
|
209
249
|
#
|
210
|
-
#It extends Array to allow direct access to the data colleciton
|
211
|
-
#which should allow it to drop in seamlessly.
|
250
|
+
# It extends Array to allow direct access to the data colleciton
|
251
|
+
# which should allow it to drop in seamlessly.
|
212
252
|
#
|
213
|
-
#It also allows access to paging information and the
|
214
|
-
#ability to get the next/previous page in the collection
|
215
|
-
#by calling next_page or previous_page.
|
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.
|
216
256
|
attr_reader :paging
|
217
257
|
attr_reader :api
|
218
258
|
|