koala 1.2.0 → 1.3.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.
- data/.gitignore +3 -1
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/.yardopts +3 -0
- data/CHANGELOG +43 -0
- data/Gemfile +14 -0
- data/Guardfile +6 -0
- data/koala.gemspec +4 -4
- data/lib/koala/api/batch_operation.rb +83 -0
- data/lib/koala/api/graph_api.rb +476 -0
- data/lib/koala/{graph_batch_api.rb → api/graph_batch_api.rb} +22 -17
- data/lib/koala/api/graph_collection.rb +107 -0
- data/lib/koala/api/legacy.rb +26 -0
- data/lib/koala/{rest_api.rb → api/rest_api.rb} +36 -10
- data/lib/koala/api.rb +93 -0
- data/lib/koala/http_service/multipart_request.rb +41 -0
- data/lib/koala/http_service/response.rb +18 -0
- data/lib/koala/http_service/uploadable_io.rb +187 -0
- data/lib/koala/http_service.rb +73 -23
- data/lib/koala/oauth.rb +173 -36
- data/lib/koala/realtime_updates.rb +89 -51
- data/lib/koala/test_users.rb +122 -32
- data/lib/koala/utils.rb +11 -4
- data/lib/koala/version.rb +1 -1
- data/lib/koala.rb +16 -95
- data/readme.md +30 -13
- data/spec/cases/api_spec.rb +28 -21
- data/spec/cases/error_spec.rb +10 -0
- data/spec/cases/graph_api_batch_spec.rb +100 -58
- data/spec/cases/graph_collection_spec.rb +23 -7
- data/spec/cases/http_service_spec.rb +14 -34
- data/spec/cases/koala_spec.rb +22 -4
- data/spec/cases/legacy_spec.rb +115 -0
- data/spec/cases/multipart_request_spec.rb +66 -0
- data/spec/cases/oauth_spec.rb +232 -108
- data/spec/cases/realtime_updates_spec.rb +154 -47
- data/spec/cases/test_users_spec.rb +276 -217
- data/spec/cases/uploadable_io_spec.rb +1 -1
- data/spec/cases/utils_spec.rb +29 -5
- data/spec/fixtures/mock_facebook_responses.yml +50 -26
- data/spec/spec_helper.rb +3 -0
- data/spec/support/custom_matchers.rb +28 -0
- data/spec/support/graph_api_shared_examples.rb +274 -70
- data/spec/support/koala_test.rb +27 -16
- data/spec/support/mock_http_service.rb +2 -2
- data/spec/support/rest_api_shared_examples.rb +46 -162
- metadata +33 -25
- data/lib/koala/batch_operation.rb +0 -74
- data/lib/koala/graph_api.rb +0 -270
- data/lib/koala/graph_collection.rb +0 -59
- data/lib/koala/uploadable_io.rb +0 -181
- data/spec/cases/graph_and_rest_api_spec.rb +0 -22
- data/spec/cases/graph_api_spec.rb +0 -22
- data/spec/cases/rest_api_spec.rb +0 -41
data/lib/koala/test_users.rb
CHANGED
|
@@ -2,15 +2,34 @@ require 'koala'
|
|
|
2
2
|
|
|
3
3
|
module Koala
|
|
4
4
|
module Facebook
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
|
|
6
|
+
# Create and manage test users for your application.
|
|
7
|
+
# A test user is a user account associated with an app created for the purpose
|
|
8
|
+
# of testing the functionality of that app.
|
|
9
|
+
# You can use test users for manual or automated testing --
|
|
10
|
+
# Koala's live test suite uses test users to verify the library works with Facebook.
|
|
11
|
+
#
|
|
12
|
+
# @note the test user API is fairly slow compared to other interfaces
|
|
13
|
+
# (which makes sense -- it's creating whole new user accounts!).
|
|
14
|
+
#
|
|
15
|
+
# See http://developers.facebook.com/docs/test_users/.
|
|
16
|
+
class TestUsers
|
|
17
|
+
|
|
18
|
+
# The application API interface used to communicate with Facebook.
|
|
19
|
+
# @return [Koala::Facebook::API]
|
|
20
|
+
attr_reader :api
|
|
21
|
+
attr_reader :app_id, :app_access_token, :secret
|
|
22
|
+
|
|
23
|
+
# Create a new TestUsers instance.
|
|
24
|
+
# If you don't have your app's access token, provide the app's secret and
|
|
25
|
+
# Koala will make a request to Facebook for the appropriate token.
|
|
26
|
+
#
|
|
27
|
+
# @param options initialization options.
|
|
28
|
+
# @option options :app_id the application's ID.
|
|
29
|
+
# @option options :app_access_token an application access token, if known.
|
|
30
|
+
# @option options :secret the application's secret.
|
|
31
|
+
#
|
|
32
|
+
# @raise ArgumentError if the application ID and one of the app access token or the secret are not provided.
|
|
14
33
|
def initialize(options = {})
|
|
15
34
|
@app_id = options[:app_id]
|
|
16
35
|
@app_access_token = options[:app_access_token]
|
|
@@ -24,35 +43,94 @@ module Koala
|
|
|
24
43
|
oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
|
|
25
44
|
@app_access_token = oauth.get_app_access_token
|
|
26
45
|
end
|
|
46
|
+
|
|
27
47
|
@api = API.new(@app_access_token)
|
|
28
48
|
end
|
|
29
49
|
|
|
50
|
+
# Create a new test user.
|
|
51
|
+
#
|
|
52
|
+
# @param installed whether the user has installed your app
|
|
53
|
+
# @param permissions a comma-separated string or array of permissions the user has granted (if installed)
|
|
54
|
+
# @param args any additional arguments for the create call (name, etc.)
|
|
55
|
+
# @param options (see Koala::Facebook::API#api)
|
|
56
|
+
#
|
|
57
|
+
# @return a hash of information for the new user (id, access token, login URL, etc.)
|
|
30
58
|
def create(installed, permissions = nil, args = {}, options = {})
|
|
31
59
|
# Creates and returns a test user
|
|
32
60
|
args['installed'] = installed
|
|
33
61
|
args['permissions'] = (permissions.is_a?(Array) ? permissions.join(",") : permissions) if installed
|
|
34
|
-
|
|
62
|
+
@api.graph_call(test_user_accounts_path, args, "post", options)
|
|
35
63
|
end
|
|
36
64
|
|
|
37
|
-
|
|
38
|
-
|
|
65
|
+
# List all test users for the app.
|
|
66
|
+
#
|
|
67
|
+
# @param options (see Koala::Facebook::API#api)
|
|
68
|
+
#
|
|
69
|
+
# @return an array of hashes of user information (id, access token, etc.)
|
|
70
|
+
def list(options = {})
|
|
71
|
+
@api.graph_call(test_user_accounts_path, {}, "get", options)
|
|
39
72
|
end
|
|
40
73
|
|
|
41
|
-
|
|
74
|
+
# Delete a test user.
|
|
75
|
+
#
|
|
76
|
+
# @param test_user the user to delete; can be either a Facebook ID or the hash returned by {#create}
|
|
77
|
+
# @param options (see Koala::Facebook::API#api)
|
|
78
|
+
#
|
|
79
|
+
# @return true if successful, false (or an {Koala::Facebook::APIError APIError}) if not
|
|
80
|
+
def delete(test_user, options = {})
|
|
42
81
|
test_user = test_user["id"] if test_user.is_a?(Hash)
|
|
43
|
-
@api.delete_object(test_user)
|
|
82
|
+
@api.delete_object(test_user, options)
|
|
44
83
|
end
|
|
45
84
|
|
|
46
|
-
|
|
47
|
-
|
|
85
|
+
# Deletes all test users in batches of 50.
|
|
86
|
+
#
|
|
87
|
+
# @note if you have a lot of test users (> 20), this operation can take a long time.
|
|
88
|
+
#
|
|
89
|
+
# @param options (see Koala::Facebook::API#api)
|
|
90
|
+
#
|
|
91
|
+
# @return a list of the test users that have been deleted
|
|
92
|
+
def delete_all(options = {})
|
|
93
|
+
# ideally we'd save a call by checking next_page_params, but at the time of writing
|
|
94
|
+
# Facebook isn't consistently returning full pages after the first one
|
|
95
|
+
previous_list = nil
|
|
96
|
+
while (test_user_list = list(options)).length > 0
|
|
97
|
+
# avoid infinite loops if Facebook returns buggy users you can't delete
|
|
98
|
+
# see http://developers.facebook.com/bugs/223629371047398
|
|
99
|
+
break if test_user_list == previous_list
|
|
100
|
+
|
|
101
|
+
test_user_list.each_slice(50) do |users|
|
|
102
|
+
self.api.batch(options) {|batch_api| users.each {|u| batch_api.delete_object(u["id"]) }}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
previous_list = test_user_list
|
|
106
|
+
end
|
|
48
107
|
end
|
|
49
108
|
|
|
50
|
-
|
|
109
|
+
# Updates a test user's attributes.
|
|
110
|
+
#
|
|
111
|
+
# @note currently, only name and password can be changed;
|
|
112
|
+
# see {http://developers.facebook.com/docs/test_users/ the Facebook documentation}.
|
|
113
|
+
#
|
|
114
|
+
# @param test_user the user to update; can be either a Facebook ID or the hash returned by {#create}
|
|
115
|
+
# @param args the updates to make
|
|
116
|
+
# @param options (see Koala::Facebook::API#api)
|
|
117
|
+
#
|
|
118
|
+
# @return true if successful, false (or an {Koala::Facebook::APIError APIError}) if not
|
|
119
|
+
def update(test_user, args = {}, options = {})
|
|
51
120
|
test_user = test_user["id"] if test_user.is_a?(Hash)
|
|
52
|
-
@api.graph_call(test_user, args, "post",
|
|
121
|
+
@api.graph_call(test_user, args, "post", options)
|
|
53
122
|
end
|
|
54
123
|
|
|
55
|
-
|
|
124
|
+
# Make two test users friends.
|
|
125
|
+
#
|
|
126
|
+
# @note there's no way to unfriend test users; you can always just create a new one.
|
|
127
|
+
#
|
|
128
|
+
# @param user1_hash one of the users to friend; the hash must contain both ID and access token (as returned by {#create})
|
|
129
|
+
# @param user2_hash the other user to friend
|
|
130
|
+
# @param options (see Koala::Facebook::API#api)
|
|
131
|
+
#
|
|
132
|
+
# @return true if successful, false (or an {Koala::Facebook::APIError APIError}) if not
|
|
133
|
+
def befriend(user1_hash, user2_hash, options = {})
|
|
56
134
|
user1_id = user1_hash["id"] || user1_hash[:id]
|
|
57
135
|
user2_id = user2_hash["id"] || user2_hash[:id]
|
|
58
136
|
user1_token = user1_hash["access_token"] || user1_hash[:access_token]
|
|
@@ -67,35 +145,47 @@ module Koala
|
|
|
67
145
|
u1_graph_api = API.new(user1_token)
|
|
68
146
|
u2_graph_api = API.new(user2_token)
|
|
69
147
|
|
|
70
|
-
u1_graph_api.graph_call("#{user1_id}/friends/#{user2_id}", {}, "post") &&
|
|
71
|
-
u2_graph_api.graph_call("#{user2_id}/friends/#{user1_id}", {}, "post")
|
|
148
|
+
u1_graph_api.graph_call("#{user1_id}/friends/#{user2_id}", {}, "post", options) &&
|
|
149
|
+
u2_graph_api.graph_call("#{user2_id}/friends/#{user1_id}", {}, "post", options)
|
|
72
150
|
end
|
|
73
151
|
|
|
74
|
-
|
|
75
|
-
|
|
152
|
+
# Create a network of test users, all of whom are friends and have the same permissions.
|
|
153
|
+
#
|
|
154
|
+
# @note this call slows down dramatically the more users you create
|
|
155
|
+
# (test user calls are slow, and more users => more 1-on-1 connections to be made).
|
|
156
|
+
# Use carefully.
|
|
157
|
+
#
|
|
158
|
+
# @param network_size how many users to create
|
|
159
|
+
# @param installed whether the users have installed your app (see {#create})
|
|
160
|
+
# @param permissions what permissions the users have granted (see {#create})
|
|
161
|
+
# @param options (see Koala::Facebook::API#api)
|
|
162
|
+
#
|
|
163
|
+
# @return the list of users created
|
|
164
|
+
def create_network(network_size, installed = true, permissions = '', options = {})
|
|
165
|
+
users = (0...network_size).collect { create(installed, permissions, options) }
|
|
76
166
|
friends = users.clone
|
|
77
167
|
users.each do |user|
|
|
78
168
|
# Remove this user from list of friends
|
|
79
169
|
friends.delete_at(0)
|
|
80
170
|
# befriend all the others
|
|
81
171
|
friends.each do |friend|
|
|
82
|
-
befriend(user, friend)
|
|
172
|
+
befriend(user, friend, options)
|
|
83
173
|
end
|
|
84
174
|
end
|
|
85
175
|
return users
|
|
86
176
|
end
|
|
87
|
-
|
|
177
|
+
|
|
178
|
+
# The Facebook test users management URL for your application.
|
|
179
|
+
def test_user_accounts_path
|
|
180
|
+
@test_user_accounts_path ||= "/#{@app_id}/accounts/test-users"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @private
|
|
184
|
+
# Legacy accessor for before GraphAPI was unified into API
|
|
88
185
|
def graph_api
|
|
89
186
|
Koala::Utils.deprecate("the TestUsers.graph_api accessor is deprecated and will be removed in a future version; please use .api instead.")
|
|
90
187
|
@api
|
|
91
188
|
end
|
|
92
|
-
|
|
93
|
-
protected
|
|
94
|
-
|
|
95
|
-
def accounts_path
|
|
96
|
-
@accounts_path ||= "/#{@app_id}/accounts/test-users"
|
|
97
|
-
end
|
|
98
|
-
|
|
99
189
|
end # TestUserMethods
|
|
100
190
|
end # Facebook
|
|
101
191
|
end # Koala
|
data/lib/koala/utils.rb
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
module Koala
|
|
2
2
|
module Utils
|
|
3
|
+
|
|
4
|
+
# @private
|
|
5
|
+
DEPRECATION_PREFIX = "KOALA: Deprecation warning: "
|
|
6
|
+
|
|
7
|
+
# Prints a deprecation message.
|
|
8
|
+
# Each individual message will only be printed once to avoid spamming.
|
|
3
9
|
def self.deprecate(message)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
10
|
+
@posted_deprecations ||= []
|
|
11
|
+
unless @posted_deprecations.include?(message)
|
|
12
|
+
# only include each message once
|
|
13
|
+
Kernel.warn("#{DEPRECATION_PREFIX}#{message}")
|
|
14
|
+
@posted_deprecations << message
|
|
8
15
|
end
|
|
9
16
|
end
|
|
10
17
|
end
|
data/lib/koala/version.rb
CHANGED
data/lib/koala.rb
CHANGED
|
@@ -1,120 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
# useful tools
|
|
2
2
|
require 'digest/md5'
|
|
3
|
-
|
|
4
3
|
require 'multi_json'
|
|
5
4
|
|
|
6
|
-
# OpenSSL and Base64 are required to support signed_request
|
|
7
|
-
require 'openssl'
|
|
8
|
-
require 'base64'
|
|
9
|
-
|
|
10
5
|
# include koala modules
|
|
6
|
+
require 'koala/api'
|
|
11
7
|
require 'koala/oauth'
|
|
12
|
-
require 'koala/graph_api'
|
|
13
|
-
require 'koala/graph_batch_api'
|
|
14
|
-
require 'koala/batch_operation'
|
|
15
|
-
require 'koala/graph_collection'
|
|
16
|
-
require 'koala/rest_api'
|
|
17
8
|
require 'koala/realtime_updates'
|
|
18
9
|
require 'koala/test_users'
|
|
19
10
|
|
|
20
11
|
# HTTP module so we can communicate with Facebook
|
|
21
12
|
require 'koala/http_service'
|
|
22
13
|
|
|
23
|
-
# add KoalaIO class
|
|
24
|
-
require 'koala/uploadable_io'
|
|
25
|
-
|
|
26
14
|
# miscellaneous
|
|
27
15
|
require 'koala/utils'
|
|
28
16
|
require 'koala/version'
|
|
29
17
|
|
|
30
18
|
module Koala
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# Contributors: Alex Koppel, Chris Baclig, Rafi Jacoby, and the team at Context Optional
|
|
36
|
-
# http://github.com/arsduo/koala
|
|
37
|
-
|
|
38
|
-
# APIs
|
|
39
|
-
class API
|
|
40
|
-
# initialize with an access token
|
|
41
|
-
def initialize(access_token = nil)
|
|
42
|
-
@access_token = access_token
|
|
43
|
-
end
|
|
44
|
-
attr_reader :access_token
|
|
45
|
-
|
|
46
|
-
include GraphAPIMethods
|
|
47
|
-
include RestAPIMethods
|
|
48
|
-
|
|
49
|
-
def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
|
|
50
|
-
# Fetches the given path in the Graph API.
|
|
51
|
-
args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
|
|
52
|
-
|
|
53
|
-
# add a leading /
|
|
54
|
-
path = "/#{path}" unless path =~ /^\//
|
|
55
|
-
|
|
56
|
-
# make the request via the provided service
|
|
57
|
-
result = Koala.make_request(path, args, verb, options)
|
|
58
|
-
|
|
59
|
-
# Check for any 500 errors before parsing the body
|
|
60
|
-
# since we're not guaranteed that the body is valid JSON
|
|
61
|
-
# in the case of a server error
|
|
62
|
-
raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
|
|
63
|
-
|
|
64
|
-
# parse the body as JSON and run it through the error checker (if provided)
|
|
65
|
-
# Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
|
|
66
|
-
# and cause MultiJson.decode to fail -- so we account for that by wrapping the result in []
|
|
67
|
-
body = MultiJson.decode("[#{result.body.to_s}]")[0]
|
|
68
|
-
yield body if error_checking_block
|
|
69
|
-
|
|
70
|
-
# if we want a component other than the body (e.g. redirect header for images), return that
|
|
71
|
-
options[:http_component] ? result.send(options[:http_component]) : body
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# special enhanced APIs
|
|
76
|
-
class GraphBatchAPI < API
|
|
77
|
-
include GraphBatchAPIMethods
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
class RealtimeUpdates
|
|
81
|
-
include RealtimeUpdateMethods
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
class TestUsers
|
|
85
|
-
include TestUserMethods
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# legacy support for old APIs
|
|
89
|
-
class OldAPI < API;
|
|
90
|
-
def initialize(*args)
|
|
91
|
-
Koala::Utils.deprecate("#{self.class.name} is deprecated and will be removed in a future version; please use the API class instead.")
|
|
92
|
-
super
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
class GraphAPI < OldAPI; end
|
|
96
|
-
class RestAPI < OldAPI; end
|
|
97
|
-
class GraphAndRestAPI < OldAPI; end
|
|
98
|
-
|
|
99
|
-
# Errors
|
|
100
|
-
|
|
101
|
-
class APIError < StandardError
|
|
102
|
-
attr_accessor :fb_error_type
|
|
103
|
-
def initialize(details = {})
|
|
104
|
-
self.fb_error_type = details["type"]
|
|
105
|
-
super("#{fb_error_type}: #{details["message"]}")
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
19
|
+
# A Ruby client library for the Facebook Platform.
|
|
20
|
+
# See http://github.com/arsduo/koala/wiki for a general introduction to Koala
|
|
21
|
+
# and the Graph API.
|
|
22
|
+
|
|
110
23
|
class KoalaError < StandardError; end
|
|
111
24
|
|
|
112
|
-
|
|
113
|
-
# finally, the few things defined on the Koala module itself
|
|
25
|
+
# Making HTTP requests
|
|
114
26
|
class << self
|
|
27
|
+
# Control which HTTP service framework Koala uses.
|
|
28
|
+
# Primarily used to switch between the mock-request framework used in testing
|
|
29
|
+
# and the live framework used in real life (and live testing).
|
|
30
|
+
# In theory, you could write your own HTTPService module if you need different functionality,
|
|
31
|
+
# but since the switch to {https://github.com/arsduo/koala/wiki/HTTP-Services Faraday} almost all such goals can be accomplished with middleware.
|
|
115
32
|
attr_accessor :http_service
|
|
116
33
|
end
|
|
117
34
|
|
|
35
|
+
# @private
|
|
36
|
+
# For current HTTPServices, switch the service as expected.
|
|
37
|
+
# For deprecated services (Typhoeus and Net::HTTP), print a warning and set the default Faraday adapter appropriately.
|
|
118
38
|
def self.http_service=(service)
|
|
119
39
|
if service.respond_to?(:deprecated_interface)
|
|
120
40
|
# if this is a deprecated module, support the old interface
|
|
@@ -127,6 +47,7 @@ module Koala
|
|
|
127
47
|
end
|
|
128
48
|
end
|
|
129
49
|
|
|
50
|
+
# An convenenient alias to Koala.http_service.make_request.
|
|
130
51
|
def self.make_request(path, args, verb, options = {})
|
|
131
52
|
http_service.make_request(path, args, verb, options)
|
|
132
53
|
end
|
data/readme.md
CHANGED
|
@@ -6,17 +6,8 @@ Koala
|
|
|
6
6
|
|
|
7
7
|
* Lightweight: Koala should be as light and simple as Facebook’s own libraries, providing API accessors and returning simple JSON.
|
|
8
8
|
* 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:
|
|
9
|
-
* Flexible: Koala should be useful to everyone, regardless of their current configuration.
|
|
10
|
-
* Tested: Koala should have complete test coverage, so you can rely on it.
|
|
11
|
-
|
|
12
|
-
Facebook Changes on October 1, 2011
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
**Koala 1.2 supports all of Facebook's new authentication schemes**, which will be introduced on October 1, 2011; the old Javascript library and older authentication schemes will be deprecated at the same time.
|
|
16
|
-
|
|
17
|
-
To test your application, upgrade to the latest version of Koala (see below) and configure your application according to Facebook's [OAuth 2.0 and HTTPS Migration](https://developers.facebook.com/docs/oauth2-https-migration/) guide. If you have the appropriate calls to get_user_info_from_cookies (apps using the Javascript SDK) and/or parse_signed_params (for Canvas and tab apps), your application should work without a hitch.
|
|
18
|
-
|
|
19
|
-
_Note_: in their new secure cookie format, Facebook provides an OAuth code, which Koala automatically exchanges for an access token. Because this involves a call to Facebook's servers, you should consider storing the user's access token in their session and only calling get_user_info_from_cookies when necessary (access_token not present, you discover it's expired, etc.). Otherwise, you'll be calling out to Facebook each time the user loads a page, slowing down your site. (As we figure out best practices for this, we'll update the wiki.)
|
|
9
|
+
* Flexible: Koala should be useful to everyone, regardless of their current configuration. We support JRuby, Rubinius, and REE as well as vanilla Ruby (1.8.7, 1.9.2, and 1.9.3), and use the Faraday library to provide complete flexibility over how HTTP requests are made.
|
|
10
|
+
* 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](travis-ci.org/arsduo/koala/).
|
|
20
11
|
|
|
21
12
|
Installation
|
|
22
13
|
---
|
|
@@ -39,7 +30,10 @@ The Graph API is the simple, slick new interface to Facebook's data. Using it w
|
|
|
39
30
|
profile = @graph.get_object("me")
|
|
40
31
|
friends = @graph.get_connections("me", "friends")
|
|
41
32
|
@graph.put_object("me", "feed", :message => "I am writing on my wall!")
|
|
42
|
-
|
|
33
|
+
|
|
34
|
+
# three-part queries are easy too!
|
|
35
|
+
@graph.get_connection("me", "mutualfriends/#{friend_id}")
|
|
36
|
+
|
|
43
37
|
# you can even use the new Timeline API
|
|
44
38
|
# see https://developers.facebook.com/docs/beta/opengraph/tutorial/
|
|
45
39
|
@graph.put_connections("me", "namespace:action", :object => object_url)
|
|
@@ -94,25 +88,31 @@ Of course, you can use the Graph API methods on the same object -- the power of
|
|
|
94
88
|
OAuth
|
|
95
89
|
-----
|
|
96
90
|
You can use the Graph and REST APIs without an OAuth access token, but the real magic happens when you provide Facebook an OAuth token to prove you're authenticated. Koala provides an OAuth class to make that process easy:
|
|
91
|
+
|
|
97
92
|
@oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
|
|
98
93
|
|
|
99
94
|
If your application uses Koala and the Facebook [JavaScript SDK](http://github.com/facebook/connect-js) (formerly Facebook Connect), you can use the OAuth class to parse the cookies:
|
|
95
|
+
|
|
100
96
|
@oauth.get_user_from_cookies(cookies) # gets the user's ID
|
|
101
97
|
@oauth.get_user_info_from_cookies(cookies) # parses and returns the entire hash
|
|
102
98
|
|
|
103
99
|
And if you have to use the more complicated [redirect-based OAuth process](http://developers.facebook.com/docs/authentication/), Koala helps out there, too:
|
|
100
|
+
|
|
104
101
|
# generate authenticating URL
|
|
105
102
|
@oauth.url_for_oauth_code
|
|
106
103
|
# fetch the access token once you have the code
|
|
107
104
|
@oauth.get_access_token(code)
|
|
108
105
|
|
|
109
106
|
You can also get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
|
|
107
|
+
|
|
110
108
|
@oauth.get_app_access_token
|
|
111
109
|
|
|
112
110
|
For those building apps on Facebook, parsing signed requests is simple:
|
|
111
|
+
|
|
113
112
|
@oauth.parse_signed_request(signed_request_string)
|
|
114
113
|
|
|
115
114
|
Or, if for some horrible reason, you're still using session keys, despair not! It's easy to turn them into shiny, modern OAuth tokens:
|
|
115
|
+
|
|
116
116
|
@oauth.get_token_from_session_key(session_key)
|
|
117
117
|
@oauth.get_tokens_from_session_keys(array_of_session_keys)
|
|
118
118
|
|
|
@@ -129,7 +129,7 @@ Koala makes it easy to interact with your applications using the RealtimeUpdates
|
|
|
129
129
|
You can do just about anything with your real-time update subscriptions using the RealtimeUpdates class:
|
|
130
130
|
|
|
131
131
|
# Add/modify a subscription to updates for when the first_name or last_name fields of any of your users is changed
|
|
132
|
-
@updates.subscribe("user", "first_name, last_name",
|
|
132
|
+
@updates.subscribe("user", "first_name, last_name", callback_url, verify_token)
|
|
133
133
|
|
|
134
134
|
# Get an array of your current subscriptions (one hash for each object you've subscribed to)
|
|
135
135
|
@updates.list_subscriptions
|
|
@@ -155,13 +155,30 @@ We also support the test users API, allowing you to conjure up fake users and co
|
|
|
155
155
|
# or, if you want to make a whole community:
|
|
156
156
|
@test_users.create_network(network_size, is_app_installed, common_permissions)
|
|
157
157
|
|
|
158
|
+
Talking to Facebook
|
|
159
|
+
-----
|
|
160
|
+
|
|
161
|
+
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):
|
|
162
|
+
|
|
163
|
+
# Set an SSL certificate to avoid Net::HTTP errors
|
|
164
|
+
Koala.http_service.http_options = {
|
|
165
|
+
:ssl => { :ca_path => "/etc/ssl/certs" }
|
|
166
|
+
}
|
|
167
|
+
# or on a per-request basis
|
|
168
|
+
@api.get_object(id, args_hash, { :timeout => 10 })
|
|
169
|
+
|
|
170
|
+
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).
|
|
171
|
+
|
|
158
172
|
See examples, ask questions
|
|
159
173
|
-----
|
|
160
174
|
Some resources to help you as you play with Koala and the Graph API:
|
|
161
175
|
|
|
162
176
|
* Complete Koala documentation <a href="http://wiki.github.com/arsduo/koala/">on the wiki</a>
|
|
163
177
|
* The <a href="http://groups.google.com/group/koala-users">Koala users group</a> on Google Groups, the place for your Koala and API questions
|
|
178
|
+
* 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
|
|
164
179
|
* The Koala-powered <a href="http://oauth.twoalex.com" target="_blank">OAuth Playground</a>, where you can easily generate OAuth access tokens and any other data needed to test out the APIs or OAuth
|
|
180
|
+
* Follow Koala on <a href="http://www.facebook.com/pages/Koala/315368291823667">Facebook</a> and <a href="https://twitter.com/#!/koala_fb">Twitter</a> for SDK updates and occasional news about Facebook API changes.
|
|
181
|
+
|
|
165
182
|
|
|
166
183
|
Testing
|
|
167
184
|
-----
|
data/spec/cases/api_spec.rb
CHANGED
|
@@ -5,18 +5,18 @@ describe "Koala::Facebook::API" do
|
|
|
5
5
|
@service = Koala::Facebook::API.new
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
it "
|
|
8
|
+
it "doesn't include an access token if none was given" do
|
|
9
9
|
Koala.should_receive(:make_request).with(
|
|
10
10
|
anything,
|
|
11
11
|
hash_not_including('access_token' => 1),
|
|
12
12
|
anything,
|
|
13
13
|
anything
|
|
14
|
-
).and_return(Koala::Response.new(200, "", ""))
|
|
14
|
+
).and_return(Koala::HTTPService::Response.new(200, "", ""))
|
|
15
15
|
|
|
16
16
|
@service.api('anything')
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
it "
|
|
19
|
+
it "includes an access token if given" do
|
|
20
20
|
token = 'adfadf'
|
|
21
21
|
service = Koala::Facebook::API.new token
|
|
22
22
|
|
|
@@ -25,29 +25,36 @@ describe "Koala::Facebook::API" do
|
|
|
25
25
|
hash_including('access_token' => token),
|
|
26
26
|
anything,
|
|
27
27
|
anything
|
|
28
|
-
).and_return(Koala::Response.new(200, "", ""))
|
|
28
|
+
).and_return(Koala::HTTPService::Response.new(200, "", ""))
|
|
29
29
|
|
|
30
30
|
service.api('anything')
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
it "
|
|
33
|
+
it "has an attr_reader for access token" do
|
|
34
34
|
token = 'adfadf'
|
|
35
35
|
service = Koala::Facebook::API.new token
|
|
36
36
|
service.access_token.should == token
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
it "
|
|
39
|
+
it "gets the attribute of a Koala::HTTPService::Response given by the http_component parameter" do
|
|
40
40
|
http_component = :method_name
|
|
41
41
|
|
|
42
42
|
response = mock('Mock KoalaResponse', :body => '', :status => 200)
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
result = stub("result")
|
|
44
|
+
response.stub(http_component).and_return(result)
|
|
45
45
|
Koala.stub(:make_request).and_return(response)
|
|
46
46
|
|
|
47
|
-
@service.api('anything', {}, 'get', :http_component => http_component)
|
|
47
|
+
@service.api('anything', {}, 'get', :http_component => http_component).should == result
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "returns the entire response if http_component => :response" do
|
|
51
|
+
http_component = :response
|
|
52
|
+
response = mock('Mock KoalaResponse', :body => '', :status => 200)
|
|
53
|
+
Koala.stub(:make_request).and_return(response)
|
|
54
|
+
@service.api('anything', {}, 'get', :http_component => http_component).should == response
|
|
48
55
|
end
|
|
49
56
|
|
|
50
|
-
it "
|
|
57
|
+
it "returns the body of the request as JSON if no http_component is given" do
|
|
51
58
|
response = stub('response', :body => 'body', :status => 200)
|
|
52
59
|
Koala.stub(:make_request).and_return(response)
|
|
53
60
|
|
|
@@ -57,9 +64,9 @@ describe "Koala::Facebook::API" do
|
|
|
57
64
|
@service.api('anything').should == json_body
|
|
58
65
|
end
|
|
59
66
|
|
|
60
|
-
it "
|
|
67
|
+
it "executes an error checking block if provided" do
|
|
61
68
|
body = '{}'
|
|
62
|
-
Koala.stub(:make_request).and_return(Koala::Response.new(200, body, {}))
|
|
69
|
+
Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, body, {}))
|
|
63
70
|
|
|
64
71
|
yield_test = mock('Yield Tester')
|
|
65
72
|
yield_test.should_receive(:pass)
|
|
@@ -70,30 +77,30 @@ describe "Koala::Facebook::API" do
|
|
|
70
77
|
end
|
|
71
78
|
end
|
|
72
79
|
|
|
73
|
-
it "
|
|
74
|
-
Koala.stub(:make_request).and_return(Koala::Response.new(500, 'response body', {}))
|
|
80
|
+
it "raises an API error if the HTTP response code is greater than or equal to 500" do
|
|
81
|
+
Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(500, 'response body', {}))
|
|
75
82
|
|
|
76
83
|
lambda { @service.api('anything') }.should raise_exception(Koala::Facebook::APIError)
|
|
77
84
|
end
|
|
78
85
|
|
|
79
|
-
it "
|
|
80
|
-
Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
|
|
86
|
+
it "handles rogue true/false as responses" do
|
|
87
|
+
Koala.should_receive(:make_request).and_return(Koala::HTTPService::Response.new(200, 'true', {}))
|
|
81
88
|
@service.api('anything').should be_true
|
|
82
89
|
|
|
83
|
-
Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'false', {}))
|
|
90
|
+
Koala.should_receive(:make_request).and_return(Koala::HTTPService::Response.new(200, 'false', {}))
|
|
84
91
|
@service.api('anything').should be_false
|
|
85
92
|
end
|
|
86
93
|
|
|
87
94
|
describe "with regard to leading slashes" do
|
|
88
|
-
it "
|
|
95
|
+
it "adds a leading / to the path if not present" do
|
|
89
96
|
path = "anything"
|
|
90
|
-
Koala.should_receive(:make_request).with("/#{path}", anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
|
|
97
|
+
Koala.should_receive(:make_request).with("/#{path}", anything, anything, anything).and_return(Koala::HTTPService::Response.new(200, 'true', {}))
|
|
91
98
|
@service.api(path)
|
|
92
99
|
end
|
|
93
100
|
|
|
94
|
-
it "
|
|
101
|
+
it "doesn't change the path if a leading / is present" do
|
|
95
102
|
path = "/anything"
|
|
96
|
-
Koala.should_receive(:make_request).with(path, anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
|
|
103
|
+
Koala.should_receive(:make_request).with(path, anything, anything, anything).and_return(Koala::HTTPService::Response.new(200, 'true', {}))
|
|
97
104
|
@service.api(path)
|
|
98
105
|
end
|
|
99
106
|
end
|
data/spec/cases/error_spec.rb
CHANGED
|
@@ -10,6 +10,16 @@ describe Koala::Facebook::APIError do
|
|
|
10
10
|
Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:fb_error_type=)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
it "has an accessor for raw_response" do
|
|
14
|
+
Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:raw_response)
|
|
15
|
+
Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:raw_response=)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "sets raw_response to the provided error details" do
|
|
19
|
+
error_response = {"type" => "foo", "other_details" => "bar"}
|
|
20
|
+
Koala::Facebook::APIError.new(error_response).raw_response.should == error_response
|
|
21
|
+
end
|
|
22
|
+
|
|
13
23
|
it "sets fb_error_type to details['type']" do
|
|
14
24
|
type = "foo"
|
|
15
25
|
Koala::Facebook::APIError.new("type" => type).fb_error_type.should == type
|