koala 0.10.0 → 1.0.0.rc

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 (50) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG +39 -7
  3. data/Gemfile +3 -0
  4. data/Manifest +8 -1
  5. data/Rakefile +13 -14
  6. data/koala.gemspec +36 -19
  7. data/lib/koala/graph_api.rb +188 -123
  8. data/lib/koala/http_services.rb +93 -18
  9. data/lib/koala/rest_api.rb +73 -6
  10. data/lib/koala/test_users.rb +21 -8
  11. data/lib/koala/uploadable_io.rb +115 -0
  12. data/lib/koala.rb +86 -86
  13. data/readme.md +18 -12
  14. data/spec/cases/api_base_spec.rb +101 -0
  15. data/spec/cases/graph_and_rest_api_spec.rb +31 -0
  16. data/spec/cases/graph_api_spec.rb +25 -0
  17. data/spec/cases/http_services/http_service_spec.rb +54 -0
  18. data/spec/cases/http_services/net_http_service_spec.rb +350 -0
  19. data/spec/cases/http_services/typhoeus_service_spec.rb +144 -0
  20. data/spec/cases/oauth_spec.rb +374 -0
  21. data/spec/cases/realtime_updates_spec.rb +184 -0
  22. data/spec/cases/rest_api_spec.rb +25 -0
  23. data/spec/{koala/test_users/test_users_tests.rb → cases/test_users_spec.rb} +78 -72
  24. data/spec/cases/uploadable_io_spec.rb +151 -0
  25. data/spec/fixtures/beach.jpg +0 -0
  26. data/spec/{facebook_data.yml → fixtures/facebook_data.yml} +13 -9
  27. data/spec/{mock_facebook_responses.yml → fixtures/mock_facebook_responses.yml} +312 -289
  28. data/spec/spec_helper.rb +18 -0
  29. data/spec/support/graph_api_shared_examples.rb +424 -0
  30. data/spec/{koala → support}/live_testing_data_helper.rb +39 -42
  31. data/spec/{mock_http_service.rb → support/mock_http_service.rb} +94 -81
  32. data/spec/support/rest_api_shared_examples.rb +161 -0
  33. data/spec/support/setup_mocks_or_live.rb +52 -0
  34. data/spec/support/uploadable_io_shared_examples.rb +76 -0
  35. metadata +130 -43
  36. data/init.rb +0 -2
  37. data/spec/koala/api_base_tests.rb +0 -102
  38. data/spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb +0 -10
  39. data/spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb +0 -11
  40. data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +0 -114
  41. data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +0 -150
  42. data/spec/koala/graph_api/graph_collection_tests.rb +0 -104
  43. data/spec/koala/net_http_service_tests.rb +0 -186
  44. data/spec/koala/oauth/oauth_tests.rb +0 -438
  45. data/spec/koala/realtime_updates/realtime_updates_tests.rb +0 -187
  46. data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +0 -94
  47. data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +0 -36
  48. data/spec/koala_spec.rb +0 -18
  49. data/spec/koala_spec_helper.rb +0 -48
  50. data/spec/koala_spec_without_mocks.rb +0 -19
data/lib/koala.rb CHANGED
@@ -1,30 +1,24 @@
1
1
  require 'cgi'
2
2
  require 'digest/md5'
3
3
 
4
- # rubygems is required to support json, how facebook returns data
5
- require 'rubygems'
6
4
  require 'json'
7
5
 
8
- # openssl is required to support signed_request
6
+ # OpenSSL and Base64 are required to support signed_request
9
7
  require 'openssl'
8
+ require 'base64'
10
9
 
11
- # include default http services
10
+ # include koala modules
12
11
  require 'koala/http_services'
13
-
14
- # add Graph API methods
15
12
  require 'koala/graph_api'
16
-
17
- # add REST API methods
18
13
  require 'koala/rest_api'
19
-
20
- # add realtime update methods
21
14
  require 'koala/realtime_updates'
22
-
23
- # add test user methods
24
15
  require 'koala/test_users'
25
16
 
17
+ # add KoalaIO class
18
+ require 'koala/uploadable_io'
19
+
26
20
  module Koala
27
-
21
+
28
22
  module Facebook
29
23
  # Ruby client library for the Facebook Platform.
30
24
  # Copyright 2010 Facebook
@@ -40,7 +34,7 @@ module Koala
40
34
  # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
41
35
  # License for the specific language governing permissions and limitations
42
36
  # under the License.
43
- #
37
+ #
44
38
  # This client library is designed to support the Graph API and the official
45
39
  # Facebook JavaScript SDK, which is the canonical way to implement
46
40
  # Facebook authentication. Read more about the Graph API at
@@ -48,35 +42,35 @@ module Koala
48
42
  # JavaScript SDK at http://github.com/facebook/connect-js/.
49
43
 
50
44
  class API
51
- # initialize with an access token
45
+ # initialize with an access token
52
46
  def initialize(access_token = nil)
53
47
  @access_token = access_token
54
48
  end
55
49
  attr_reader :access_token
56
-
50
+
57
51
  def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
58
52
  # Fetches the given path in the Graph API.
59
53
  args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
60
-
54
+
61
55
  # add a leading /
62
56
  path = "/#{path}" unless path =~ /^\//
63
57
 
64
58
  # make the request via the provided service
65
59
  result = Koala.make_request(path, args, verb, options)
66
-
60
+
67
61
  # Check for any 500 errors before parsing the body
68
62
  # since we're not guaranteed that the body is valid JSON
69
63
  # in the case of a server error
70
64
  raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
71
-
72
- # Parse the body as JSON and check for errors if provided a mechanism to do so
65
+
66
+ # Parse the body as JSON and check for errors if provided a mechanism to do so
73
67
  # Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
74
68
  # and cause JSON.parse to fail -- so we account for that by wrapping the result in []
75
69
  body = response = JSON.parse("[#{result.body.to_s}]")[0]
76
70
  if error_checking_block
77
71
  yield(body)
78
72
  end
79
-
73
+
80
74
  # now return the desired information
81
75
  if options[:http_component]
82
76
  result.send(options[:http_component])
@@ -85,57 +79,57 @@ module Koala
85
79
  end
86
80
  end
87
81
  end
88
-
82
+
89
83
  class GraphAPI < API
90
84
  include GraphAPIMethods
91
85
  end
92
-
86
+
93
87
  class RestAPI < API
94
88
  include RestAPIMethods
95
89
  end
96
-
90
+
97
91
  class GraphAndRestAPI < API
98
92
  include GraphAPIMethods
99
93
  include RestAPIMethods
100
94
  end
101
-
95
+
102
96
  class RealtimeUpdates < API
103
97
  include RealtimeUpdateMethods
104
98
  end
105
-
99
+
106
100
  class TestUsers < API
107
101
  include TestUserMethods
108
102
  # make the Graph API accessible in case someone wants to make other calls to interact with their users
109
103
  attr_reader :graph_api
110
104
  end
111
-
112
- class APIError < Exception
105
+
106
+ class APIError < StandardError
113
107
  attr_accessor :fb_error_type
114
108
  def initialize(details = {})
115
- self.fb_error_type = details["type"]
109
+ self.fb_error_type = details["type"]
116
110
  super("#{fb_error_type}: #{details["message"]}")
117
111
  end
118
112
  end
119
-
120
-
113
+
114
+
121
115
  class OAuth
122
116
  attr_reader :app_id, :app_secret, :oauth_callback_url
123
117
  def initialize(app_id, app_secret, oauth_callback_url = nil)
124
118
  @app_id = app_id
125
119
  @app_secret = app_secret
126
- @oauth_callback_url = oauth_callback_url
120
+ @oauth_callback_url = oauth_callback_url
127
121
  end
128
122
 
129
123
  def get_user_info_from_cookie(cookie_hash)
130
124
  # Parses the cookie set by the official Facebook JavaScript SDK.
131
- #
125
+ #
132
126
  # cookies should be a Hash, like the one Rails provides
133
- #
127
+ #
134
128
  # If the user is logged in via Facebook, we return a dictionary with the
135
129
  # keys "uid" and "access_token". The former is the user's Facebook ID,
136
130
  # and the latter can be used to make authenticated requests to the Graph API.
137
131
  # If the user is not logged in, we return None.
138
- #
132
+ #
139
133
  # Download the official Facebook JavaScript SDK at
140
134
  # http://github.com/facebook/connect-js/. Read more about Facebook
141
135
  # authentication at http://developers.facebook.com/docs/authentication/.
@@ -150,78 +144,82 @@ module Koala
150
144
 
151
145
  # generate the signature and make sure it matches what we expect
152
146
  auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
153
- sig = Digest::MD5.hexdigest(auth_string + @app_secret)
147
+ sig = Digest::MD5.hexdigest(auth_string + @app_secret)
154
148
  sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
155
149
  end
156
150
  end
157
151
  alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
158
-
152
+
159
153
  def get_user_from_cookie(cookies)
160
154
  if info = get_user_info_from_cookies(cookies)
161
155
  string = info["uid"]
162
156
  end
163
157
  end
164
158
  alias_method :get_user_from_cookies, :get_user_from_cookie
165
-
159
+
166
160
  # URLs
167
-
161
+
168
162
  def url_for_oauth_code(options = {})
169
163
  # for permissions, see http://developers.facebook.com/docs/authentication/permissions
170
164
  permissions = options[:permissions]
171
165
  scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
172
-
166
+ display = options.has_key?(:display) ? "&display=#{options[:display]}" : ""
167
+
173
168
  callback = options[:callback] || @oauth_callback_url
174
169
  raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
175
170
 
176
171
  # Creates the URL for oauth authorization for a given callback and optional set of permissions
177
- "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
172
+ "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}#{display}"
178
173
  end
179
-
174
+
180
175
  def url_for_access_token(code, options = {})
181
176
  # Creates the URL for the token corresponding to a given code generated by Facebook
182
177
  callback = options[:callback] || @oauth_callback_url
183
178
  raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
184
179
  "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
185
180
  end
186
-
181
+
187
182
  def get_access_token_info(code)
188
183
  # convenience method to get a parsed token from Facebook for a given code
189
184
  # should this require an OAuth callback URL?
190
185
  get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
191
186
  end
192
-
187
+
193
188
  def get_access_token(code)
194
189
  # upstream methods will throw errors if needed
195
- if info = get_access_token_info(code)
196
- string = info["access_token"]
190
+ if info = get_access_token_info(code)
191
+ string = info["access_token"]
197
192
  end
198
193
  end
199
-
194
+
200
195
  def get_app_access_token_info
201
- # convenience method to get a the application's sessionless access token
196
+ # convenience method to get a the application's sessionless access token
202
197
  get_token_from_server({:type => 'client_cred'}, true)
203
198
  end
204
-
199
+
205
200
  def get_app_access_token
206
201
  if info = get_app_access_token_info
207
- string = info["access_token"]
202
+ string = info["access_token"]
208
203
  end
209
204
  end
210
-
211
- # signed_request
212
- def parse_signed_request(request)
213
- # Facebook's signed requests come in two parts -- the signature and the data payload
214
- # see http://developers.facebook.com/docs/authentication/canvas
215
- encoded_sig, payload = request.split(".")
216
-
217
- sig = base64_url_decode(encoded_sig)
218
205
 
219
- # if the signature matches, return the data, decoded and parsed as JSON
220
- if OpenSSL::HMAC.digest("sha256", @app_secret, payload) == sig
221
- JSON.parse(base64_url_decode(payload))
222
- else
223
- nil
224
- end
206
+ # Originally provided directly by Facebook, however this has changed
207
+ # as their concept of crypto changed. For historic purposes, this is their proposal:
208
+ # https://developers.facebook.com/docs/authentication/canvas/encryption_proposal/
209
+ # Currently see https://github.com/facebook/php-sdk/blob/master/src/facebook.php#L758
210
+ # for a more accurate reference implementation strategy.
211
+ def parse_signed_request(input)
212
+ encoded_sig, encoded_envelope = input.split('.', 2)
213
+ signature = base64_url_decode(encoded_sig).unpack("H*").first
214
+ envelope = JSON.parse(base64_url_decode(encoded_envelope))
215
+
216
+ raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
217
+
218
+ # now see if the signature is valid (digest, key, data)
219
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope.tr("-_", "+/"))
220
+ raise 'SignedRequest: Invalid signature' if (signature != hmac)
221
+
222
+ return envelope
225
223
  end
226
224
 
227
225
  # from session keys
@@ -231,69 +229,70 @@ module Koala
231
229
  :type => 'client_cred',
232
230
  :sessions => sessions.join(",")
233
231
  }, true, "exchange_sessions")
234
-
232
+
235
233
  # Facebook returns an empty body in certain error conditions
236
- if response == ""
234
+ if response == ""
237
235
  raise APIError.new({
238
- "type" => "ArgumentError",
236
+ "type" => "ArgumentError",
239
237
  "message" => "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!"
240
238
  })
241
239
  end
242
-
240
+
243
241
  JSON.parse(response)
244
242
  end
245
-
243
+
246
244
  def get_tokens_from_session_keys(sessions)
247
245
  # get the original hash results
248
246
  results = get_token_info_from_session_keys(sessions)
249
247
  # now recollect them as just the access tokens
250
248
  results.collect { |r| r ? r["access_token"] : nil }
251
249
  end
252
-
250
+
253
251
  def get_token_from_session_key(session)
254
252
  # convenience method for a single key
255
253
  # gets the overlaoded strings automatically
256
254
  get_tokens_from_session_keys([session])[0]
257
255
  end
258
-
256
+
259
257
  protected
260
-
258
+
261
259
  def get_token_from_server(args, post = false)
262
260
  # fetch the result from Facebook's servers
263
261
  result = fetch_token_string(args, post)
264
-
262
+
265
263
  # if we have an error, parse the error JSON and raise an error
266
264
  raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
267
265
 
268
266
  # otherwise, parse the access token
269
- parse_access_token(result)
267
+ parse_access_token(result)
270
268
  end
271
-
269
+
272
270
  def parse_access_token(response_text)
273
271
  components = response_text.split("&").inject({}) do |hash, bit|
274
272
  key, value = bit.split("=")
275
273
  hash.merge!(key => value)
276
274
  end
277
- components
275
+ components
278
276
  end
279
277
 
280
278
  def fetch_token_string(args, post = false, endpoint = "access_token")
281
279
  Koala.make_request("/oauth/#{endpoint}", {
282
- :client_id => @app_id,
280
+ :client_id => @app_id,
283
281
  :client_secret => @app_secret
284
- }.merge!(args), post ? "post" : "get").body
282
+ }.merge!(args), post ? "post" : "get", :use_ssl => true).body
285
283
  end
286
-
284
+
287
285
  # base 64
288
- def base64_url_decode(string)
289
- # to properly decode what Facebook provides, we need to add == to the end
290
- # and translate certain characters to others before running the actual decoding
291
- # see http://developers.facebook.com/docs/authentication/canvas
292
- "#{string}==".tr("-_", "+/").unpack("m")[0]
286
+ # directly from https://github.com/facebook/crypto-request-examples/raw/master/sample.rb
287
+ def base64_url_decode(str)
288
+ str += '=' * (4 - str.length.modulo(4))
289
+ Base64.decode64(str.tr('-_', '+/'))
293
290
  end
294
291
  end
295
292
  end
296
-
293
+
294
+ class KoalaError< StandardError; end
295
+
297
296
  # finally, set up the http service Koala methods used to make requests
298
297
  # you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
299
298
  def self.http_service=(service)
@@ -301,8 +300,9 @@ module Koala
301
300
  end
302
301
 
303
302
  # by default, try requiring Typhoeus -- if that works, use it
303
+ # if you have Typheous and don't want to use it (or want another service),
304
+ # you can run Koala.http_service = NetHTTPService (or MyHTTPService)
304
305
  begin
305
- require 'typhoeus'
306
306
  Koala.http_service = TyphoeusService
307
307
  rescue LoadError
308
308
  Koala.http_service = NetHTTPService
data/readme.md CHANGED
@@ -1,28 +1,34 @@
1
1
  Koala
2
2
  ====
3
- Koala (<a href="http://github.com/arsduo/koala" target="_blank">http://github.com/arsduo/koala</a>) is a new Facebook library for Ruby, supporting the Graph API, the old REST API, realtime updates, and OAuth validation. We wrote Koala with four goals:
3
+ Koala (<a href="http://github.com/arsduo/koala" target="_blank">http://github.com/arsduo/koala</a>) is a new Facebook library for Ruby, supporting the Graph API (including photo uploads), the old REST API, realtime updates, and OAuth validation. We wrote Koala with four goals:
4
4
 
5
- * Lightweight: Koala should be as light and simple as Facebook’s own new libraries, providing API accessors and returning simple JSON. (We clock in, with comments, just over 500 lines of code.)
5
+ * Lightweight: Koala should be as light and simple as Facebook’s own new libraries, providing API accessors and returning simple JSON. (We clock in, with comments, just over 750 lines of code.)
6
6
  * Fast: Koala should, out of the box, be quick. In addition to supporting the vanilla Ruby networking libraries, it natively supports Typhoeus, our preferred gem for making fast HTTP requests. Of course, That brings us to our next topic:
7
7
  * Flexible: Koala should be useful to everyone, regardless of their current configuration. (We have no dependencies beyond the JSON gem. Koala also has a built-in mechanism for using whichever HTTP library you prefer to make requests against the graph.)
8
8
  * Tested: Koala should have complete test coverage, so you can rely on it. (Our complete test coverage can be run against either mocked responses or the live Facebook servers.)
9
9
 
10
+ 1.0 beta
11
+ ---
12
+ Version 1.0 is currently in beta, chock-full of great new features. To download the beta, just add --pre when installing the gem:
13
+
14
+ sudo gem install koala --pre
15
+
10
16
  Graph API
11
17
  ----
12
- The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
18
+ The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
13
19
 
14
20
  graph = Koala::Facebook::GraphAPI.new(oauth_access_token)
15
21
  profile = graph.get_object("me")
16
22
  friends = graph.get_connections("me", "friends")
17
23
  graph.put_object("me", "feed", :message => "I am writing on my wall!")
18
24
 
19
- The response of most requests is the JSON data returned from the Facebook servers as a Hash.
25
+ The response of most requests is the JSON data returned from the Facebook servers as a Hash.
20
26
 
21
27
  When retrieving data that returns an array of results (for example, when calling GraphAPI#get_connections or GraphAPI#search) a GraphCollection object (a sub-class of Array) will be returned, which contains added methods for getting the next and previous page of results:
22
-
28
+
23
29
  # Returns the feed items for the currently logged-in user as a GraphCollection
24
30
  feed = graph.get_connections("me", "feed")
25
-
31
+
26
32
  # GraphCollection is a sub-class of Array, so you can use it as a usual Array
27
33
  first_entry = feed[0]
28
34
  last_entry = feed.last
@@ -33,7 +39,7 @@ When retrieving data that returns an array of results (for example, when calling
33
39
  # Returns an array describing the URL for the next page: [path, arguments]
34
40
  # This is useful for paging across multiple requests
35
41
  next_path, next_args = feed.next_page_params
36
-
42
+
37
43
  # You can use those params to easily get the next (or prevous) page
38
44
  page = graph.get_page(feed.next_page_params)
39
45
 
@@ -41,15 +47,15 @@ Check out the wiki for more examples.
41
47
 
42
48
  The old-school REST API
43
49
  -----
44
- Where the Graph API and the old REST API overlap, you should choose the Graph API. Unfortunately, that overlap is far from complete, and there are many important API calls that can't yet be done via the Graph.
50
+ Where the Graph API and the old REST API overlap, you should choose the Graph API. Unfortunately, that overlap is far from complete, and there are many important API calls that can't yet be done via the Graph.
45
51
 
46
52
  Koala now supports the old-school REST API using OAuth access tokens; to use this, instantiate your class using the RestAPI class:
47
53
 
48
54
  @rest = Koala::Facebook::RestAPI.new(oauth_access_token)
49
55
  @rest.fql_query(my_fql_query) # convenience method
50
56
  @rest.rest_call("stream.publish", arguments_hash) # generic version
51
-
52
- We reserve the right to expand the built-in REST API coverage to additional convenience methods in the future, depending on how fast Facebook moves to fill in the gaps.
57
+
58
+ We reserve the right to expand the built-in REST API coverage to additional convenience methods in the future, depending on how fast Facebook moves to fill in the gaps.
53
59
 
54
60
  (If you want the power of both APIs in the palm of your hand, try out the GraphAndRestAPI class.)
55
61
 
@@ -119,7 +125,7 @@ Some resources to help you as you play with Koala and the Graph API:
119
125
  Testing
120
126
  -----
121
127
 
122
- 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:
128
+ 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:
123
129
  # From the spec directory
124
130
  spec koala_spec.rb
125
131
 
@@ -127,4 +133,4 @@ You can also run live tests against Facebook's servers:
127
133
  # Again from the spec directory
128
134
  spec koala_spec_without_mocks.rb
129
135
 
130
- Important Note: to run the live tests, you have to provide some of your own data: a valid OAuth access token with publish\_stream and read\_stream permissions and an OAuth code that can be used to generate an access token. You can get these data at the OAuth Playground; if you want to use your own app, remember to swap out the app ID, secret, and other values. (The file also provides valid values for other tests, which you're welcome to swap out for data specific to your own application.)
136
+ Important Note: to run the live tests, you have to provide some of your own data: a valid OAuth access token with publish\_stream, read\_stream, and user\_photos permissions and an OAuth code that can be used to generate an access token. You can get these data at the OAuth Playground; if you want to use your own app, remember to swap out the app ID, secret, and other values. (The file also provides valid values for other tests, which you're welcome to swap out for data specific to your own application.)
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::API" do
4
+ before(:each) do
5
+ @service = Koala::Facebook::API.new
6
+ end
7
+
8
+ it "should not include an access token if none was given" do
9
+ Koala.should_receive(:make_request).with(
10
+ anything,
11
+ hash_not_including('access_token' => 1),
12
+ anything,
13
+ anything
14
+ ).and_return(Koala::Response.new(200, "", ""))
15
+
16
+ @service.api('anything')
17
+ end
18
+
19
+ it "should include an access token if given" do
20
+ token = 'adfadf'
21
+ service = Koala::Facebook::API.new token
22
+
23
+ Koala.should_receive(:make_request).with(
24
+ anything,
25
+ hash_including('access_token' => token),
26
+ anything,
27
+ anything
28
+ ).and_return(Koala::Response.new(200, "", ""))
29
+
30
+ service.api('anything')
31
+ end
32
+
33
+ it "should have an attr_reader for access token" do
34
+ token = 'adfadf'
35
+ service = Koala::Facebook::API.new token
36
+ service.access_token.should == token
37
+ end
38
+
39
+ it "should get the attribute of a Koala::Response given by the http_component parameter" do
40
+ http_component = :method_name
41
+
42
+ response = mock('Mock KoalaResponse', :body => '', :status => 200)
43
+ response.should_receive(http_component).and_return('')
44
+
45
+ Koala.stub(:make_request).and_return(response)
46
+
47
+ @service.api('anything', 'get', {}, :http_component => http_component)
48
+ end
49
+
50
+ it "should return the body of the request as JSON if no http_component is given" do
51
+ response = stub('response', :body => 'body', :status => 200)
52
+ Koala.stub(:make_request).and_return(response)
53
+
54
+ json_body = mock('JSON body')
55
+ JSON.stub(:parse).and_return([json_body])
56
+
57
+ @service.api('anything').should == json_body
58
+ end
59
+
60
+ it "should execute a block with the response body if passed one" do
61
+ body = '{}'
62
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, body, {}))
63
+
64
+ yield_test = mock('Yield Tester')
65
+ yield_test.should_receive(:pass)
66
+
67
+ @service.api('anything') do |arg|
68
+ yield_test.pass
69
+ arg.should == JSON.parse(body)
70
+ end
71
+ end
72
+
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', {}))
75
+
76
+ lambda { @service.api('anything') }.should raise_exception(Koala::Facebook::APIError)
77
+ end
78
+
79
+ it "should handle rogue true/false as responses" do
80
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
81
+ @service.api('anything').should be_true
82
+
83
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'false', {}))
84
+ @service.api('anything').should be_false
85
+ end
86
+
87
+ describe "with regard to leading slashes" do
88
+ it "should add a leading / to the path if not present" do
89
+ path = "anything"
90
+ Koala.should_receive(:make_request).with("/#{path}", anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
91
+ @service.api(path)
92
+ end
93
+
94
+ it "shouldn't change the path if a leading / is present" do
95
+ path = "/anything"
96
+ Koala.should_receive(:make_request).with(path, anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
97
+ @service.api(path)
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::GraphAndRestAPI" do
4
+ include LiveTestingDataHelper
5
+
6
+ describe "with an access token" do
7
+ before(:each) do
8
+ @api = Koala::Facebook::GraphAndRestAPI.new(@token)
9
+ end
10
+
11
+ it_should_behave_like "Koala RestAPI"
12
+ it_should_behave_like "Koala RestAPI with an access token"
13
+
14
+ it_should_behave_like "Koala GraphAPI"
15
+ it_should_behave_like "Koala GraphAPI with an access token"
16
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
17
+ end
18
+
19
+ describe "without an access token" do
20
+ before(:each) do
21
+ @api = Koala::Facebook::GraphAndRestAPI.new
22
+ end
23
+
24
+ it_should_behave_like "Koala RestAPI"
25
+ it_should_behave_like "Koala RestAPI without an access token"
26
+
27
+ it_should_behave_like "Koala GraphAPI"
28
+ it_should_behave_like "Koala GraphAPI without an access token"
29
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::GraphAPI" do
4
+ include LiveTestingDataHelper
5
+
6
+ context "with an access token" do
7
+ before :each do
8
+ @api = Koala::Facebook::GraphAPI.new(@token)
9
+ end
10
+
11
+ it_should_behave_like "Koala GraphAPI"
12
+ it_should_behave_like "Koala GraphAPI with an access token"
13
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
14
+ end
15
+
16
+ context "without an access token" do
17
+ before :each do
18
+ @api = Koala::Facebook::GraphAPI.new
19
+ end
20
+
21
+ it_should_behave_like "Koala GraphAPI"
22
+ it_should_behave_like "Koala GraphAPI without an access token"
23
+ it_should_behave_like "Koala GraphAPI with GraphCollection"
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ class Bear
5
+ include Koala::HTTPService
6
+ end
7
+
8
+ describe "Koala::HTTPService" do
9
+
10
+ describe "common methods" do
11
+ describe "always_use_ssl accessor" do
12
+ it "should be added" do
13
+ # in Ruby 1.8, .methods returns strings
14
+ # in Ruby 1.9, .method returns symbols
15
+ Bear.methods.collect {|m| m.to_sym}.should include(:always_use_ssl)
16
+ Bear.methods.collect {|m| m.to_sym}.should include(:always_use_ssl=)
17
+ end
18
+ end
19
+
20
+ describe "server" do
21
+ describe "without options[:beta]" do
22
+ it "should return the rest server if options[:rest_api]" do
23
+ Bear.server(:rest_api => true).should == Koala::Facebook::REST_SERVER
24
+ end
25
+
26
+ it "should return the rest server if !options[:rest_api]" do
27
+ Bear.server(:rest_api => false).should == Koala::Facebook::GRAPH_SERVER
28
+ Bear.server({}).should == Koala::Facebook::GRAPH_SERVER
29
+ end
30
+ end
31
+
32
+ describe "without options[:beta]" do
33
+ before :each do
34
+ @options = {:beta => true}
35
+ end
36
+
37
+ it "should return the rest server if options[:rest_api]" do
38
+ server = Bear.server(@options.merge(:rest_api => true))
39
+ server.should =~ Regexp.new(Koala::Facebook::REST_SERVER)
40
+ server.should =~ /beta\./
41
+ end
42
+
43
+ it "should return the rest server if !options[:rest_api]" do
44
+ server = Bear.server(:beta => true)
45
+ server.should =~ Regexp.new(Koala::Facebook::GRAPH_SERVER)
46
+ server.should =~ /beta\./
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end