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.
Files changed (54) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +4 -0
  4. data/.yardopts +3 -0
  5. data/CHANGELOG +43 -0
  6. data/Gemfile +14 -0
  7. data/Guardfile +6 -0
  8. data/koala.gemspec +4 -4
  9. data/lib/koala/api/batch_operation.rb +83 -0
  10. data/lib/koala/api/graph_api.rb +476 -0
  11. data/lib/koala/{graph_batch_api.rb → api/graph_batch_api.rb} +22 -17
  12. data/lib/koala/api/graph_collection.rb +107 -0
  13. data/lib/koala/api/legacy.rb +26 -0
  14. data/lib/koala/{rest_api.rb → api/rest_api.rb} +36 -10
  15. data/lib/koala/api.rb +93 -0
  16. data/lib/koala/http_service/multipart_request.rb +41 -0
  17. data/lib/koala/http_service/response.rb +18 -0
  18. data/lib/koala/http_service/uploadable_io.rb +187 -0
  19. data/lib/koala/http_service.rb +73 -23
  20. data/lib/koala/oauth.rb +173 -36
  21. data/lib/koala/realtime_updates.rb +89 -51
  22. data/lib/koala/test_users.rb +122 -32
  23. data/lib/koala/utils.rb +11 -4
  24. data/lib/koala/version.rb +1 -1
  25. data/lib/koala.rb +16 -95
  26. data/readme.md +30 -13
  27. data/spec/cases/api_spec.rb +28 -21
  28. data/spec/cases/error_spec.rb +10 -0
  29. data/spec/cases/graph_api_batch_spec.rb +100 -58
  30. data/spec/cases/graph_collection_spec.rb +23 -7
  31. data/spec/cases/http_service_spec.rb +14 -34
  32. data/spec/cases/koala_spec.rb +22 -4
  33. data/spec/cases/legacy_spec.rb +115 -0
  34. data/spec/cases/multipart_request_spec.rb +66 -0
  35. data/spec/cases/oauth_spec.rb +232 -108
  36. data/spec/cases/realtime_updates_spec.rb +154 -47
  37. data/spec/cases/test_users_spec.rb +276 -217
  38. data/spec/cases/uploadable_io_spec.rb +1 -1
  39. data/spec/cases/utils_spec.rb +29 -5
  40. data/spec/fixtures/mock_facebook_responses.yml +50 -26
  41. data/spec/spec_helper.rb +3 -0
  42. data/spec/support/custom_matchers.rb +28 -0
  43. data/spec/support/graph_api_shared_examples.rb +274 -70
  44. data/spec/support/koala_test.rb +27 -16
  45. data/spec/support/mock_http_service.rb +2 -2
  46. data/spec/support/rest_api_shared_examples.rb +46 -162
  47. metadata +33 -25
  48. data/lib/koala/batch_operation.rb +0 -74
  49. data/lib/koala/graph_api.rb +0 -270
  50. data/lib/koala/graph_collection.rb +0 -59
  51. data/lib/koala/uploadable_io.rb +0 -181
  52. data/spec/cases/graph_and_rest_api_spec.rb +0 -22
  53. data/spec/cases/graph_api_spec.rb +0 -22
  54. data/spec/cases/rest_api_spec.rb +0 -41
@@ -2,15 +2,34 @@ require 'koala'
2
2
 
3
3
  module Koala
4
4
  module Facebook
5
- module TestUserMethods
6
-
7
- def self.included(base)
8
- base.class_eval do
9
- # make the Graph API accessible in case someone wants to make other calls to interact with their users
10
- attr_reader :api, :app_id, :app_access_token, :secret
11
- end
12
- end
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
- result = @api.graph_call(accounts_path, args, "post", options)
62
+ @api.graph_call(test_user_accounts_path, args, "post", options)
35
63
  end
36
64
 
37
- def list
38
- @api.graph_call(accounts_path)
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
- def delete(test_user)
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
- def delete_all
47
- list.each {|u| delete u}
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
- def update(test_user, args = {}, http_options = {})
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", http_options)
121
+ @api.graph_call(test_user, args, "post", options)
53
122
  end
54
123
 
55
- def befriend(user1_hash, user2_hash)
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
- def create_network(network_size, installed = true, permissions = '')
75
- users = (0...network_size).collect { create(installed, permissions) }
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
- begin
5
- send(:warn, "KOALA: Deprecation warning: #{message}")
6
- rescue Exception => err
7
- puts "Unable to issue Koala deprecation warning! #{err.message}"
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
@@ -1,3 +1,3 @@
1
1
  module Koala
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
data/lib/koala.rb CHANGED
@@ -1,120 +1,40 @@
1
- require 'cgi'
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
- module Facebook
33
- # Ruby client library for the Facebook Platform.
34
- # Copyright 2010-2011 Alex Koppel
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. (We support JRuby, Rubinius, and REE as well as vanilla Ruby, 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.)
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", callback_token, verify_token)
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
  -----
@@ -5,18 +5,18 @@ describe "Koala::Facebook::API" do
5
5
  @service = Koala::Facebook::API.new
6
6
  end
7
7
 
8
- it "should not include an access token if none was given" do
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 "should include an access token if given" do
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 "should have an attr_reader for access token" do
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 "should get the attribute of a Koala::Response given by the http_component parameter" do
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
- response.should_receive(http_component).and_return('')
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 "should return the body of the request as JSON if no http_component is given" do
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 "should execute an error checking block if provided" do
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 "should raise an API error if the HTTP response code is greater than or equal to 500" do
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 "should handle rogue true/false as responses" do
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 "should add a leading / to the path if not present" do
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 "shouldn't change the path if a leading / is present" do
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
@@ -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