koala 0.4 → 3.7.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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +32 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +1 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +25 -0
  7. data/ISSUE_TEMPLATE +25 -0
  8. data/LICENSE +22 -0
  9. data/Manifest +32 -5
  10. data/PULL_REQUEST_TEMPLATE +11 -0
  11. data/Rakefile +12 -12
  12. data/changelog.md +781 -0
  13. data/code_of_conduct.md +74 -0
  14. data/koala.gemspec +28 -24
  15. data/lib/koala/api/batch_operation.rb +86 -0
  16. data/lib/koala/api/graph_api_methods.rb +504 -0
  17. data/lib/koala/api/graph_batch_api.rb +167 -0
  18. data/lib/koala/api/graph_collection.rb +129 -0
  19. data/lib/koala/api/graph_error_checker.rb +72 -0
  20. data/lib/koala/api.rb +159 -0
  21. data/lib/koala/configuration.rb +56 -0
  22. data/lib/koala/errors.rb +126 -0
  23. data/lib/koala/http_service/request.rb +133 -0
  24. data/lib/koala/http_service/response.rb +20 -0
  25. data/lib/koala/http_service/uploadable_io.rb +183 -0
  26. data/lib/koala/http_service.rb +108 -0
  27. data/lib/koala/oauth.rb +342 -0
  28. data/lib/koala/realtime_updates.rb +151 -0
  29. data/lib/koala/test_users.rb +189 -0
  30. data/lib/koala/utils.rb +41 -0
  31. data/lib/koala/version.rb +3 -0
  32. data/lib/koala.rb +51 -291
  33. data/readme.md +269 -21
  34. data/spec/cases/api_spec.rb +362 -0
  35. data/spec/cases/configuration_spec.rb +11 -0
  36. data/spec/cases/error_spec.rb +143 -0
  37. data/spec/cases/graph_api_batch_spec.rb +788 -0
  38. data/spec/cases/graph_api_spec.rb +76 -0
  39. data/spec/cases/graph_collection_spec.rb +192 -0
  40. data/spec/cases/graph_error_checker_spec.rb +147 -0
  41. data/spec/cases/http_service/request_spec.rb +250 -0
  42. data/spec/cases/http_service/response_spec.rb +24 -0
  43. data/spec/cases/http_service_spec.rb +280 -0
  44. data/spec/cases/koala_spec.rb +57 -0
  45. data/spec/cases/koala_test_spec.rb +5 -0
  46. data/spec/cases/oauth_spec.rb +647 -0
  47. data/spec/cases/realtime_updates_spec.rb +327 -0
  48. data/spec/cases/test_users_spec.rb +383 -0
  49. data/spec/cases/uploadable_io_spec.rb +266 -0
  50. data/spec/cases/utils_spec.rb +55 -0
  51. data/spec/fixtures/beach.jpg +0 -0
  52. data/spec/fixtures/cat.m4v +0 -0
  53. data/spec/fixtures/facebook_data.yml +63 -0
  54. data/spec/fixtures/mock_facebook_responses.yml +483 -0
  55. data/spec/fixtures/vcr_cassettes/app_test_accounts.yml +97 -0
  56. data/spec/fixtures/vcr_cassettes/friend_list_next_page.yml +121 -0
  57. data/spec/integration/graph_collection_spec.rb +24 -0
  58. data/spec/spec_helper.rb +25 -0
  59. data/spec/support/custom_matchers.rb +28 -0
  60. data/spec/support/graph_api_shared_examples.rb +534 -0
  61. data/spec/support/koala_test.rb +251 -0
  62. data/spec/support/mock_http_service.rb +140 -0
  63. data/spec/support/uploadable_io_shared_examples.rb +70 -0
  64. metadata +206 -62
  65. data/CHANGELOG +0 -24
  66. data/init.rb +0 -2
  67. data/lib/http_services.rb +0 -60
  68. data/test/facebook_data.yml +0 -5
  69. data/test/koala/facebook_no_access_token_tests.rb +0 -119
  70. data/test/koala/facebook_with_access_token_tests.rb +0 -106
  71. data/test/koala_tests.rb +0 -30
data/lib/koala.rb CHANGED
@@ -1,307 +1,67 @@
1
- require 'cgi'
1
+ # useful tools
2
2
  require 'digest/md5'
3
-
4
- # rubygems is required to support json, how facebook returns data
5
- require 'rubygems'
6
3
  require 'json'
7
4
 
8
- # include default http services
9
- require 'http_services'
5
+ # include koala modules
6
+ require 'koala/errors'
7
+ require 'koala/api'
8
+ require 'koala/api/graph_batch_api'
9
+ require 'koala/oauth'
10
+ require 'koala/realtime_updates'
11
+ require 'koala/test_users'
10
12
 
11
- module Koala
12
- # Ruby client library for the Facebook Platform.
13
- # Copyright 2010 Facebook
14
- # Adapted from the Python library by Alex Koppel, Rafi Jacoby, and the team at Context Optional
15
- #
16
- # Licensed under the Apache License, Version 2.0 (the "License"); you may
17
- # not use this file except in compliance with the License. You may obtain
18
- # a copy of the License at
19
- # http://www.apache.org/licenses/LICENSE-2.0
20
- #
21
- # Unless required by applicable law or agreed to in writing, software
22
- # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
23
- # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
24
- # License for the specific language governing permissions and limitations
25
- # under the License.
26
- #
27
- # This client library is designed to support the Graph API and the official
28
- # Facebook JavaScript SDK, which is the canonical way to implement
29
- # Facebook authentication. Read more about the Graph API at
30
- # http://developers.facebook.com/docs/api. You can download the Facebook
31
- # JavaScript SDK at http://github.com/facebook/connect-js/.
13
+ # HTTP module so we can communicate with Facebook
14
+ require 'koala/http_service'
32
15
 
33
- FACEBOOK_GRAPH_SERVER = "graph.facebook.com"
16
+ # miscellaneous
17
+ require 'koala/configuration'
18
+ require 'koala/utils'
19
+ require 'koala/version'
20
+ require 'ostruct'
34
21
 
35
- class GraphAPI
36
- # A client for the Facebook Graph API.
37
- #
38
- # See http://developers.facebook.com/docs/api for complete documentation
39
- # for the API.
40
- #
41
- # The Graph API is made up of the objects in Facebook (e.g., people, pages,
42
- # events, photos) and the connections between them (e.g., friends,
43
- # photo tags, and event RSVPs). This client provides access to those
44
- # primitive types in a generic way. For example, given an OAuth access
45
- # token, this will fetch the profile of the active user and the list
46
- # of the user's friends:
47
- #
48
- # graph = Facebook::GraphAPI.new(access_token)
49
- # user = graph.get_object("me")
50
- # friends = graph.get_connections(user["id"], "friends")
51
- #
52
- # You can see a list of all of the objects and connections supported
53
- # by the API at http://developers.facebook.com/docs/reference/api/.
54
- #
55
- # You can obtain an access token via OAuth or by using the Facebook
56
- # JavaScript SDK. See http://developers.facebook.com/docs/authentication/
57
- # for details.
58
- #
59
- # If you are using the JavaScript SDK, you can use the
60
- # Facebook::get_user_from_cookie() method below to get the OAuth access token
61
- # for the active user from the cookie saved by the SDK.
62
-
63
- # initialize with an access token
64
- def initialize(access_token = nil)
65
- @access_token = access_token
66
- end
67
-
68
- def get_object(id, args = {})
69
- # Fetchs the given object from the graph.
70
- request(id, args)
71
- end
72
-
73
- def get_objects(ids, args = {})
74
- # Fetchs all of the given object from the graph.
75
- # We return a map from ID to object. If any of the IDs are invalid,
76
- # we raise an exception.
77
- request("", args.merge("ids" => ids.join(",")))
78
- end
79
-
80
- def get_connections(id, connection_name, args = {})
81
- # Fetchs the connections for given object.
82
- request("#{id}/#{connection_name}", args)
83
- end
84
-
85
- def put_object(parent_object, connection_name, args = {})
86
- # Writes the given object to the graph, connected to the given parent.
87
- #
88
- # For example,
89
- #
90
- # graph.put_object("me", "feed", :message => "Hello, world")
91
- #
92
- # writes "Hello, world" to the active user's wall. Likewise, this
93
- # will comment on a the first post of the active user's feed:
94
- #
95
- # feed = graph.get_connections("me", "feed")
96
- # post = feed["data"][0]
97
- # graph.put_object(post["id"], "comments", :message => "First!")
98
- #
99
- # See http://developers.facebook.com/docs/api#publishing for all of
100
- # the supported writeable objects.
101
- #
102
- # Most write operations require extended permissions. For example,
103
- # publishing wall posts requires the "publish_stream" permission. See
104
- # http://developers.facebook.com/docs/authentication/ for details about
105
- # extended permissions.
22
+ module Koala
23
+ # A Ruby client library for the Facebook Platform.
24
+ # See http://github.com/arsduo/koala/wiki for a general introduction to Koala
25
+ # and the Graph API.
106
26
 
107
- raise GraphAPIError.new(nil, "Write operations require an access token") unless @access_token
108
- request("#{parent_object}/#{connection_name}", args, "post")
109
- end
110
-
111
- def put_wall_post(message, attachment = {}, profile_id = "me")
112
- # Writes a wall post to the given profile's wall.
113
- #
114
- # We default to writing to the authenticated user's wall if no
115
- # profile_id is specified.
116
- #
117
- # attachment adds a structured attachment to the status message being
118
- # posted to the Wall. It should be a dictionary of the form:
119
- #
120
- # {"name": "Link name"
121
- # "link": "http://www.example.com/",
122
- # "caption": "{*actor*} posted a new review",
123
- # "description": "This is a longer description of the attachment",
124
- # "picture": "http://www.example.com/thumbnail.jpg"}
27
+ # Making HTTP requests
28
+ class << self
29
+ # Control which HTTP service framework Koala uses.
30
+ # Primarily used to switch between the mock-request framework used in testing
31
+ # and the live framework used in real life (and live testing).
32
+ # In theory, you could write your own HTTPService module if you need different functionality,
33
+ # but since the switch to {https://github.com/arsduo/koala/wiki/HTTP-Services Faraday} almost all such goals can be accomplished with middleware.
34
+ attr_accessor :http_service
125
35
 
126
- self.put_object(profile_id, "feed", attachment.merge({:message => message}))
127
- end
128
-
129
- def put_comment(object_id, message)
130
- # Writes the given comment on the given post.
131
- self.put_object(object_id, "comments", {:message => message})
132
- end
133
-
134
- def put_like(object_id)
135
- # Likes the given post.
136
- self.put_object(object_id, "likes")
36
+ def configure
37
+ yield config
137
38
  end
138
-
139
- def delete_object(id)
140
- # Deletes the object with the given ID from the graph.
141
- request(id, {}, "delete")
142
- end
143
-
144
- def search(search_terms, args = {})
145
- # Searches for a given term
146
- request("search", args.merge({:q => search_terms}))
147
- end
148
-
149
- def request(path, args = {}, verb = "get")
150
- # Fetches the given path in the Graph API.
151
- args["access_token"] = @access_token if @access_token
152
-
153
- # make the request via the provided service
154
- result = make_request(path, args, verb)
155
-
156
- # Facebook sometimes sends results like "true" and "false", which aren't strictly object
157
- # and cause JSON.parse to fail
158
- # so we account for that
159
- response = JSON.parse("[#{result}]")[0]
160
39
 
161
- # check for errors
162
- if response.is_a?(Hash) && error = response["error"]
163
- raise GraphAPIError.new(error["code"], error["message"])
164
- end
165
-
166
- response
167
- end
168
-
169
- # set up the http service used to make requests
170
- # you can use your own (for HTTParty, etc.) by calling Koala::API.http_service = YourModule
171
- def self.http_service=(service)
172
- self.send(:include, service)
40
+ # Allows you to control various Koala configuration options.
41
+ # NOTE: this is not currently threadsafe.
42
+ # See Koala::Configuration.
43
+ def config
44
+ @config ||= Configuration.new
173
45
  end
174
-
175
- # by default, try requiring Typhoeus -- if that works, use it
176
- begin
177
- require 'typhoeus'
178
- Koala::GraphAPI.http_service = TyphoeusService
179
- rescue LoadError
180
- Koala::GraphAPI.http_service = NetHTTPService
181
- end
182
- end
183
-
184
- class GraphAPIError < Exception
185
- attr_accessor :code
186
- def initialize(code, message)
187
- super(message)
188
- self.code = code
46
+
47
+ # Used for testing.
48
+ def reset_config
49
+ @config = nil
189
50
  end
190
51
  end
191
-
192
- class OAuth
193
- def initialize(app_id, app_secret, oauth_callback_url = nil)
194
- @app_id = app_id
195
- @app_secret = app_secret
196
- @oauth_callback_url = oauth_callback_url
197
- end
198
-
199
- def get_user_from_cookie(cookie_hash)
200
- # Parses the cookie set by the official Facebook JavaScript SDK.
201
- #
202
- # cookies should be a dictionary-like object mapping cookie names to
203
- # cookie values.
204
- #
205
- # If the user is logged in via Facebook, we return a dictionary with the
206
- # keys "uid" and "access_token". The former is the user's Facebook ID,
207
- # and the latter can be used to make authenticated requests to the Graph API.
208
- # If the user is not logged in, we return None.
209
- #
210
- # Download the official Facebook JavaScript SDK at
211
- # http://github.com/facebook/connect-js/. Read more about Facebook
212
- # authentication at http://developers.facebook.com/docs/authentication/.
213
-
214
- if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
215
- # remove the opening/closing quote
216
- fb_cookie = fb_cookie.gsub(/\"/, "")
217
-
218
- # since we no longer get individual cookies, we have to separate out the components ourselves
219
- components = {}
220
- fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
221
-
222
- auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
223
- sig = Digest::MD5.hexdigest(auth_string + @app_secret)
224
52
 
225
- sig == components["sig"] && Time.now.to_i < components["expires"].to_i ? components : nil
226
- end
227
- end
228
-
229
- def url_for_oauth_code(options = {})
230
- callback = options[:callback] || @oauth_callback_url
231
- permissions = options[:permissions]
232
- scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
53
+ # @private
54
+ # Switch the HTTP service -- mostly used for testing.
55
+ def self.http_service=(service)
56
+ # if it's a real http_service, use it
57
+ @http_service = service
58
+ end
233
59
 
234
- # Creates the URL for oauth authorization for a given callback and optional set of permissions
235
- "https://#{FACEBOOK_GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
236
- end
237
-
238
- def url_for_access_token(code, callback = @oauth_callback_url)
239
- # Creates the URL for the token corresponding to a given code generated by Facebook
240
- "https://#{FACEBOOK_GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
241
- end
242
-
243
- # for more details and up to date information, see http://developers.facebook.com/docs/authentication/permissions
244
- def self.all_permissions
245
- USER_PERMISSIONS.concat(FRIEND_PERMISSIONS)
246
- end
247
-
248
- USER_PERMISSIONS = [
249
- # PUBLISHING
250
- "publish_stream", # Enables your application to post content, comments, and likes to a user's stream and to the streams of the user's friends, without prompting the user each time.
251
- "create_event", # Enables your application to create and modify events on the user's behalf
252
- "rsvp_event", # Enables your application to RSVP to events on the user's behalf
253
- "sms", # Enables your application to send messages to the user and respond to messages from the user via text message
254
- "offline_access", # Enables your application to perform authorized requests on behalf of the user at any time (e.g. permanent access token)
255
-
256
- # DATA ACCESS
257
- "email", # Provides access to the user's primary email address in the email property
258
- "read_stream", # Provides access to all the posts in the user's News Feed and enables your application to perform searches against the user's News Feed
259
- "user_about_me", # Provides access to the "About Me" section of the profile in the about property
260
- "user_activities", # Provides access to the user's list of activities as the activities connection
261
- "user_birthday", # Provides access to the full birthday with year as the birthday_date property
262
- "user_education_history", # Provides access to education history as the education property
263
- "user_events", # Provides access to the list of events the user is attending as the events connection
264
- "user_groups", # Provides access to the list of groups the user is a member of as the groups connection
265
- "user_hometown", # Provides access to the user's hometown in the hometown property
266
- "user_interests", # Provides access to the user's list of interests as the interests connection
267
- "user_likes", # Provides access to the list of all of the pages the user has liked as the likes connection
268
- "user_location", # Provides access to the user's current location as the current_location property
269
- "user_notes", # Provides access to the user's notes as the notes connection
270
- "user_online_presence", # Provides access to the user's online/offline presence
271
- "user_photo_video_tags", # Provides access to the photos the user has been tagged in as the photos connection
272
- "user_photos", # Provides access to the photos the user has uploaded
273
- "user_relationships", # Provides access to the user's family and personal relationships and relationship status
274
- "user_religion_politics", # Provides access to the user's religious and political affiliations
275
- "user_status", # Provides access to the user's most recent status message
276
- "user_videos", # Provides access to the videos the user has uploaded
277
- "user_website", # Provides access to the user's web site URL
278
- "user_work_history", # Provides access to work history as the work property
279
- "read_friendlists", # Provides read access to the user's friend lists
280
- "read_requests" # Provides read access to the user's friend requests
281
- ]
282
-
283
- FRIEND_PERMISSIONS = [
284
- # DATA ACCESS
285
- "friends_about_me", # Provides access to the "About Me" section of the profile in the about property
286
- "friends_activities", # Provides access to the user's list of activities as the activities connection
287
- "friends_birthday", # Provides access to the full birthday with year as the birthday_date property
288
- "friends_education_history", # Provides access to education history as the education property
289
- "friends_events", # Provides access to the list of events the user is attending as the events connection
290
- "friends_groups", # Provides access to the list of groups the user is a member of as the groups connection
291
- "friends_hometown", # Provides access to the user's hometown in the hometown property
292
- "friends_interests", # Provides access to the user's list of interests as the interests connection
293
- "friends_likes", # Provides access to the list of all of the pages the user has liked as the likes connection
294
- "friends_location", # Provides access to the user's current location as the current_location property
295
- "friends_notes", # Provides access to the user's notes as the notes connection
296
- "friends_online_presence", # Provides access to the user's online/offline presence
297
- "friends_photo_video_tags", # Provides access to the photos the user has been tagged in as the photos connection
298
- "friends_photos", # Provides access to the photos the user has uploaded
299
- "friends_relationships", # Provides access to the user's family and personal relationships and relationship status
300
- "friends_religion_politics", # Provides access to the user's religious and political affiliations
301
- "friends_status", # Provides access to the user's most recent status message
302
- "friends_videos", # Provides access to the videos the user has uploaded
303
- "friends_website", # Provides access to the user's web site URL
304
- "friends_work_history" # Provides access to work history as the work property
305
- ]
60
+ # An convenenient alias to Koala.http_service.make_request.
61
+ def self.make_request(path, args, verb, options = {})
62
+ http_service.make_request(HTTPService::Request.new(path: path, args: args, verb: verb, options: options))
306
63
  end
307
- end
64
+
65
+ # we use Faraday as our main service, with mock as the other main one
66
+ self.http_service = HTTPService
67
+ end
data/readme.md CHANGED
@@ -1,29 +1,277 @@
1
- Facebook Graph
1
+ Koala [![Version](https://img.shields.io/gem/v/koala.svg)](https://rubygems.org/gems/koala) [![Build Status](https://img.shields.io/travis/arsduo/koala.svg)](http://travis-ci.org/arsduo/koala) [![Code Climate](https://img.shields.io/codeclimate/coverage-letter/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala) [![Code Coverage](https://img.shields.io/codeclimate/coverage/arsduo/koala.svg)](https://codeclimate.com/github/arsduo/koala)
2
2
  ====
3
+ [Koala](http://github.com/arsduo/koala) is a Facebook library for Ruby, supporting the Graph API (including the batch requests and photo uploads), the Marketing API, the Atlas API, realtime updates, test users, and OAuth validation. We wrote Koala with four goals:
3
4
 
4
- This Ruby client library is designed to support the
5
- [Facebook Graph API](http://developers.facebook.com/docs/api) and the official
6
- [Facebook JavaScript SDK](http://github.com/facebook/connect-js), which is
7
- the canonical way to implement Facebook authentication. You can read more
8
- about the Graph API at [http://developers.facebook.com/docs/api](http://developers.facebook.com/docs/api).
5
+ * Lightweight: Koala should be as light and simple as Facebook’s own libraries, providing API accessors and returning simple JSON.
6
+ * Fast: Koala should, out of the box, be quick. Out of the box, we use Facebook's faster read-only servers when possible and if available, the Typhoeus gem to make snappy Facebook requests. Of course, that brings us to our next topic:
7
+ * Flexible: Koala should be useful to everyone, regardless of their current configuration. We support all currently-supported Ruby versions (MRI 2.1-2.4) and Koala should work on JRuby and Rubinius.
8
+ * Tested: Koala should have complete test coverage, so you can rely on it. Our test coverage is complete and can be run against either mocked responses or the live Facebook servers; we're also on [Travis CI](http://travis-ci.org/arsduo/koala/).
9
9
 
10
- Basic usage:
10
+ **Found a bug? Interested in contributing?** Check out the Maintenance section below!
11
11
 
12
- graph = Facebook::GraphAPI.new(oauth_access_token)
13
- profile = graph.get_object("me")
14
- friends = graph.get_connections("me", "friends")
15
- graph.put_object("me", "feed", :message => "I am writing on my wall!")
12
+ Installation
13
+ ------------
16
14
 
17
- If you are using the module within a web application with the
18
- [JavaScript SDK](http://github.com/facebook/connect-js), you can also use the
19
- module to use Facebook for login, parsing the cookie set by the JavaScript SDK
20
- for logged in users.
15
+ **Koala 3.0 is out! There should be no significant changes** for most users. If you encounter any
16
+ problems, please file an issue and I'll take a look.
21
17
 
22
- Testing
18
+ In Bundler:
19
+ ```ruby
20
+ gem "koala"
21
+ ```
22
+
23
+ Otherwise:
24
+ ```bash
25
+ [sudo|rvm] gem install koala
26
+ ```
27
+
28
+ Configuration
29
+ -------------
30
+
31
+ Most applications will only use one application configuration. Rather than having to provide that
32
+ value every time, you can configure Koala to use global settings:
33
+
34
+ ```ruby
35
+ # In Rails, you could put this in config/initializers/koala.rb
36
+ Koala.configure do |config|
37
+ config.access_token = MY_TOKEN
38
+ config.app_access_token = MY_APP_ACCESS_TOKEN
39
+ config.app_id = MY_APP_ID
40
+ config.app_secret = MY_APP_SECRET
41
+ # See Koala::Configuration for more options, including details on how to send requests through
42
+ # your own proxy servers.
43
+ end
44
+ ```
45
+
46
+ **Note**: this is not currently threadsafe. (PRs welcome as long as they support both threaded and
47
+ non-threaded configuration.)
48
+
49
+ Graph API
50
+ ---------
51
+
52
+ The Graph API is the interface to Facebook's data. Using it with Koala is quite straightforward.
53
+ First, you'll need an access token, which you can get through Facebook's [Graph API
54
+ Explorer](https://developers.facebook.com/tools/explorer) (click on 'Get Access Token').
55
+
56
+ Then, go exploring:
57
+
58
+ ```ruby
59
+ require 'koala'
60
+
61
+ # access_token and other values aren't required if you set the defaults as described above
62
+ @graph = Koala::Facebook::API.new(access_token)
63
+
64
+ profile = @graph.get_object("me")
65
+ friends = @graph.get_connections("me", "friends")
66
+ @graph.put_connections("me", "feed", message: "I am writing on my wall!")
67
+
68
+ # Three-part queries are easy too!
69
+ @graph.get_connections("me", "mutualfriends/#{friend_id}")
70
+
71
+ # You can use the Timeline API:
72
+ # (see https://developers.facebook.com/docs/beta/opengraph/tutorial/)
73
+ @graph.put_connections("me", "namespace:action", object: object_url)
74
+
75
+ # For extra security (recommended), you can provide an appsecret parameter,
76
+ # tying your access tokens to your app secret.
77
+ # (See https://developers.facebook.com/docs/reference/api/securing-graph-api/
78
+
79
+ # You may need to turn on 'Require proof on all calls' in the advanced section
80
+ # of your app's settings when doing this.
81
+ @graph = Koala::Facebook::API.new(access_token, app_secret)
82
+
83
+ # Facebook is now versioning their API. # If you don't specify a version, Facebook
84
+ # will default to the oldest version your app is allowed to use.
85
+ # See https://developers.facebook.com/docs/apps/versions for more information.
86
+ #
87
+ # You can specify version either globally:
88
+ Koala.config.api_version = "v2.0"
89
+ # or on a per-request basis
90
+ @graph.get_object("me", {}, api_version: "v2.0")
91
+ ```
92
+
93
+ The response of most requests is the JSON data returned from the Facebook servers as a Hash.
94
+
95
+ When retrieving data that returns an array of results (for example, when calling `API#get_connections` or `API#search`)
96
+ a GraphCollection object will be returned, which makes it easy to page through the results:
97
+
98
+ ```ruby
99
+ # Returns the feed items for the currently logged-in user as a GraphCollection
100
+ feed = @graph.get_connections("me", "feed")
101
+ feed.each {|f| do_something_with_item(f) } # it's a subclass of Array
102
+ next_feed = feed.next_page
103
+
104
+ # You can also get an array describing the URL for the next page: [path, arguments]
105
+ # This is useful for storing page state across multiple browser requests
106
+ next_page_params = feed.next_page_params
107
+ page = @graph.get_page(next_page_params)
108
+ ```
109
+
110
+ You can also make multiple calls at once using Facebook's batch API:
111
+ ```ruby
112
+ # Returns an array of results as if they were called non-batch
113
+ @graph.batch do |batch_api|
114
+ batch_api.get_object('me')
115
+ batch_api.put_wall_post('Making a post in a batch.')
116
+ end
117
+ ```
118
+
119
+ You can pass a "post-processing" block to each of Koala's Graph API methods. This is handy for two reasons:
120
+
121
+ 1. You can modify the result returned by the Graph API method:
122
+
123
+ education = @graph.get_object("me") { |data| data['education'] }
124
+ # returned value only contains the "education" portion of the profile
125
+
126
+ 2. You can consume the data in place which is particularly useful in the batch case, so you don't have to pull
127
+ the results apart from a long list of array entries:
128
+
129
+ @graph.batch do |batch_api|
130
+ # Assuming you have database fields "about_me" and "photos"
131
+ batch_api.get_object('me') {|me| self.about_me = me }
132
+ batch_api.get_connections('me', 'photos') {|photos| self.photos = photos }
133
+ end
134
+
135
+ Check out the wiki for more details and examples.
136
+
137
+ App Access Tokens
23
138
  -----
24
139
 
25
- Unit tests are provided for Graph API methods. However, because the Graph API uses access tokens, which expire, you have to provide your own token with stream publishing permissions for the tests. Insert the token value into the file test/facebook_data.yml, then run the test as follows:
26
- spec facebook_tests.rb
27
-
28
- Unit tests for cookie validation will be provided shortly. (You'll also need to add that information into the yml.)
29
-
140
+ You get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
141
+ ```ruby
142
+ @oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
143
+ @oauth.get_app_access_token
144
+ ```
145
+ For those building apps on Facebook, parsing signed requests is simple:
146
+ ```ruby
147
+ @oauth.parse_signed_request(signed_request_string)
148
+ ```
149
+
150
+ The OAuth class has additional methods that may occasionally be useful.
151
+
152
+ Real-time Updates
153
+ -----------------
154
+
155
+ Sometimes, reaching out to Facebook is a pain -- let it reach out to you instead. The Graph API allows your application to subscribe to real-time updates for certain objects in the graph; check the [official Facebook documentation](http://developers.facebook.com/docs/api/realtime) for more details on what objects you can subscribe to and what limitations may apply.
156
+
157
+ Koala makes it easy to interact with your applications using the RealtimeUpdates class:
158
+ ```ruby
159
+ # This class also supports the defaults as described above
160
+ @updates = Koala::Facebook::RealtimeUpdates.new(app_id: app_id, secret: secret)
161
+ ```
162
+ You can do just about anything with your real-time update subscriptions using the RealtimeUpdates class:
163
+ ```ruby
164
+ # Add/modify a subscription to updates for when the first_name or last_name fields of any of your users is changed
165
+ @updates.subscribe("user", "first_name, last_name", callback_url, verify_token)
166
+
167
+ # Get an array of your current subscriptions (one hash for each object you've subscribed to)
168
+ @updates.list_subscriptions
169
+
170
+ # Unsubscribe from updates for an object
171
+ @updates.unsubscribe("user")
172
+ ```
173
+ And to top it all off, RealtimeUpdates provides a static method to respond to Facebook servers' verification of your callback URLs:
174
+ ```ruby
175
+ # Returns the hub.challenge parameter in params if the verify token in params matches verify_token
176
+ Koala::Facebook::RealtimeUpdates.meet_challenge(params, your_verify_token)
177
+ ```
178
+ For more information about meet_challenge and the RealtimeUpdates class, check out the Real-Time Updates page on the wiki.
179
+
180
+ Rate limits
181
+ -----------
182
+
183
+ We support Facebook rate limit informations as defined here: [https://developers.facebook.com/docs/graph-api/overview/rate-limiting/](https://developers.facebook.com/docs/graph-api/overview/rate-limiting/)
184
+
185
+ The information is available either via the `Facebook::APIError`:
186
+
187
+ ```ruby
188
+ error.fb_buc_usage
189
+ error.fb_ada_usage
190
+ error.fb_app_usage
191
+ ```
192
+
193
+ Or with the rate_limit_hook:
194
+
195
+ ```ruby
196
+ # App level configuration
197
+
198
+ Koala.configure do |config|
199
+ config.rate_limit_hook = ->(limits) {
200
+ limits["x-app-usage"] # {"call_count"=>0, "total_cputime"=>0, "total_time"=>0}
201
+ limits["x-ad-account-usage"] # {"acc_id_util_pct"=>9.67}
202
+ limits["x-business-use-case-usage"] # {"123456789012345"=>[{"type"=>"messenger", "call_count"=>1, "total_cputime"=>1, "total_time"=>1, "estimated_time_to_regain_access"=>0}]}
203
+ }
204
+ end
205
+
206
+ # Per API configuration
207
+
208
+ Koala::Facebook::API.new('', '', ->(limits) {})
209
+ ```
210
+
211
+ Test Users
212
+ ----------
213
+
214
+ We also support the test users API, allowing you to conjure up fake users and command them to do your bidding using the Graph API:
215
+ ```ruby
216
+ # This class also supports the defaults as described above
217
+ @test_users = Koala::Facebook::TestUsers.new(app_id: id, secret: secret)
218
+ user = @test_users.create(is_app_installed, desired_permissions)
219
+ user_graph_api = Koala::Facebook::API.new(user["access_token"])
220
+ # or, if you want to make a whole community:
221
+ @test_users.create_network(network_size, is_app_installed, common_permissions)
222
+ ```
223
+
224
+ Talking to Facebook
225
+ -------------------
226
+
227
+ Koala uses Faraday to make HTTP requests, which means you have complete control over how your app makes HTTP requests to Facebook. You can set Faraday options globally or pass them in on a per-request (or both):
228
+ ```ruby
229
+ # Set an SSL certificate to avoid Net::HTTP errors
230
+ Koala.http_service.http_options = {
231
+ ssl: { ca_path: "/etc/ssl/certs" }
232
+ }
233
+ # or on a per-request basis
234
+ @api.get_object(id, args_hash, { request: { timeout: 10 } })
235
+ ```
236
+ The <a href="https://github.com/arsduo/koala/wiki/HTTP-Services">HTTP Services wiki page</a> has more information on what options are available, as well as on how to configure your own Faraday middleware stack (for instance, to implement request logging).
237
+
238
+ See examples, ask questions
239
+ ---------------------------
240
+
241
+ Some resources to help you as you play with Koala and the Graph API:
242
+
243
+ * Complete Koala documentation <a href="https://github.com/arsduo/koala/wiki">on the wiki</a>
244
+ * Facebook's <a href="http://facebook.stackoverflow.com/">Stack Overflow site</a> is a stupendous place to ask questions, filled with people who will help you figure out what's up with the Facebook API.
245
+ * Facebook's <a href="http://developers.facebook.com/tools/explorer/">Graph API Explorer</a>, where you can play with the Graph API in your browser
246
+
247
+ Testing
248
+ -------
249
+
250
+ Unit tests are provided for all of Koala's methods. By default, these tests run against mock responses and hence are ready out of the box:
251
+ ```bash
252
+ # From anywhere in the project directory:
253
+ bundle exec rake spec
254
+ ```
255
+
256
+ You can also run live tests against Facebook's servers:
257
+ ```bash
258
+ # Again from anywhere in the project directory:
259
+ LIVE=true bundle exec rake spec
260
+ # you can also test against Facebook's beta tier
261
+ LIVE=true BETA=true bundle exec rake spec
262
+ ```
263
+
264
+ By default, the live tests are run against test users, so you can run them as frequently as you want. If you want to run them against a real user, however, you can fill in the OAuth token, code, and access\_token values in spec/fixtures/facebook_data.yml. See the wiki for more details.
265
+
266
+ Maintenance
267
+ -----------
268
+
269
+ _Pull requests_: Koala exists as it does thanks to the amazing support and work of community members of all
270
+ backgrounds and levels of experience. Pull requests are very welcome!
271
+
272
+ _Issues_: If you have any questions about the gem, found an issue in the Ruby code or
273
+ documentation, or have another question that isn't right for StackOverflow, just open an issue and fill out the template.
274
+
275
+ Please note that this project is released with a Contributor Code of Conduct. By participating in
276
+ this project you agree to abide by its terms. See
277
+ [code_of_conduct.md](https://github.com/arsduo/koala/blob/master/code_of_conduct.md) for more information.