koala 1.2.0beta1 → 1.2.1

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/CHANGELOG CHANGED
@@ -1,11 +1,34 @@
1
+ v1.2.1
2
+ New methods:
3
+ -- RestAPI.set_app_properties handles JSON-encoding application properties
4
+ Updated methods:
5
+ -- OAuth.get_user_from_cookie works with the new signed cookie format (thanks, gmccreight!)
6
+ -- Beta server URLs are now correct
7
+ -- OAuth.parse_signed_request now raises an informative error if the signed_request is malformed
8
+ Internal improvements:
9
+ -- Koala::Multipart middleware properly encoding nested parameters (hashes) in POSTs
10
+ -- Updated readme, changelog, etc.
11
+ Testing improvements:
12
+ -- Live tests with test users now clean up all fake users they create
13
+ -- Removed duplicate test cases
14
+ -- Live tests with test users no longer delete each object they create, speeding things up
15
+
1
16
  v1.2
2
17
  New methods:
3
18
  -- API is now the main API class, contains both Graph and REST methods
4
19
  -- Old classes are aliased with deprecation warnings (non-breaking change)
5
20
  -- TestUsers#update lets you update the name or password of an existing test user
6
21
  -- API.get_page_access_token lets you easily fetch the access token for a page you manage (thanks, marcgg!)
22
+ -- Added version.rb (Koala::VERSION)
7
23
  Updated methods:
24
+ -- OAuth now parses Facebook's new signed cookie format
8
25
  -- API.put_picture now accepts URLs to images (thanks, marcgg!)
26
+ -- Bug fixes to put_picture, parse_signed_request, and the test suite (thanks, johnbhall and Will S.!)
27
+ -- Smarter GraphCollection use
28
+ -- Any pageable result will now become a GraphCollection
29
+ -- Non-pageable results from get_connections no longer error
30
+ -- GraphCollection.raw_results allows access to original result data
31
+ -- Koala no longer enforces any limits on the number of test users you create at once
9
32
  Internal improvements:
10
33
  -- Koala now uses Faraday to make requests, replacing the HTTPServices (see wiki)
11
34
  -- Koala::HTTPService.http_options allows specification of default Faraday connection options
@@ -15,13 +38,15 @@ Internal improvements:
15
38
  -- Koala no longer automatically switches to Net::HTTP when uploading IO objects to Facebook
16
39
  -- RealTimeUpdates and TestUsers are no longer subclasses of API, but have their own .api objects
17
40
  -- The old .graph_api accessor is aliases to .api with a deprecation warning
41
+ -- Removed deprecation warnings for pre-1.1 batch interface
18
42
  Testing improvements:
19
43
  -- Live test suites now run against test users by default
20
44
  -- Test suite can be repeatedly run live without having to update facebook_data.yml
21
45
  -- OAuth code and session key tests cannot be run against test users
22
- -- Faraday adapter for live tests can be specified with ADAPTER=[your adapter] in the shell command
46
+ -- Faraday adapter for live tests can be specified with ADAPTER=[your adapter] in the rspec command
47
+ -- Live tests can be run against the beta server by specifying BETA=true in the rspec command
23
48
  -- Tests now pass against all rubies on Travis CI
24
- -- Expanded test coverage
49
+ -- Expanded and refactored test coverage
25
50
  -- Fixed bug with YAML parsing in Ruby 1.9
26
51
 
27
52
  v1.1
data/koala.gemspec CHANGED
@@ -1,9 +1,11 @@
1
1
  # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
+ require 'koala/version'
2
4
 
3
5
  Gem::Specification.new do |s|
4
6
  s.name = %q{koala}
5
- s.version = "1.2.0beta1"
6
- s.date = %q{2011-08-17}
7
+ s.version = Koala::VERSION
8
+ s.date = %q{2011-10-04}
7
9
 
8
10
  s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the REST API, realtime updates, and OAuth authentication.}
9
11
  s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph and REST APIs, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
@@ -29,22 +31,19 @@ Gem::Specification.new do |s|
29
31
 
30
32
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
31
33
  s.add_runtime_dependency(%q<multi_json>, ["~> 1.0"])
32
- s.add_runtime_dependency(%q<faraday>, ["~> 0.7.4"])
33
- s.add_runtime_dependency(%q<faraday-stack>, ["~> 0.1.3"])
34
+ s.add_runtime_dependency(%q<faraday>, ["~> 0.7.0"])
34
35
  s.add_development_dependency(%q<rspec>, ["~> 2.5"])
35
36
  s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
36
37
  else
37
38
  s.add_dependency(%q<multi_json>, ["~> 1.0"])
38
39
  s.add_dependency(%q<rspec>, ["~> 2.5"])
39
40
  s.add_dependency(%q<rake>, ["~> 0.8.7"])
40
- s.add_dependency(%q<faraday>, ["~> 0.7.4"])
41
- s.add_dependency(%q<faraday-stack>, ["~> 0.1.3"])
41
+ s.add_dependency(%q<faraday>, ["~> 0.7.0"])
42
42
  end
43
43
  else
44
44
  s.add_dependency(%q<multi_json>, ["~> 1.0"])
45
45
  s.add_dependency(%q<rspec>, ["~> 2.5"])
46
46
  s.add_dependency(%q<rake>, ["~> 0.8.7"])
47
- s.add_dependency(%q<faraday>, ["~> 0.7.4"])
48
- s.add_dependency(%q<faraday-stack>, ["~> 0.1.3"])
47
+ s.add_dependency(%q<faraday>, ["~> 0.7.0"])
49
48
  end
50
49
  end
@@ -71,9 +71,7 @@ module Koala
71
71
 
72
72
  def get_connections(id, connection_name, args = {}, options = {})
73
73
  # Fetchs the connections for given object.
74
- graph_call("#{id}/#{connection_name}", args, "get", options) do |result|
75
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
76
- end
74
+ graph_call("#{id}/#{connection_name}", args, "get", options)
77
75
  end
78
76
 
79
77
  def put_connections(id, connection_name, args = {}, options = {})
@@ -174,9 +172,7 @@ module Koala
174
172
 
175
173
  def search(search_terms, args = {}, options = {})
176
174
  args.merge!({:q => search_terms}) unless search_terms.nil?
177
- graph_call("search", args, "get", options) do |result|
178
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
179
- end
175
+ graph_call("search", args, "get", options)
180
176
  end
181
177
 
182
178
  # Convenience Methods
@@ -199,14 +195,12 @@ module Koala
199
195
  def get_page(params)
200
196
  # Pages through a set of results stored in a GraphCollection
201
197
  # Used for connections and search results
202
- graph_call(*params) do |result|
203
- result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
204
- end
198
+ graph_call(*params)
205
199
  end
206
200
 
207
201
  # Batch API
208
202
  def batch(http_options = {}, &block)
209
- batch_client = GraphBatchAPI.new(access_token)
203
+ batch_client = GraphBatchAPI.new(access_token, self)
210
204
  if block
211
205
  yield batch_client
212
206
  batch_client.execute(http_options)
@@ -214,15 +208,7 @@ module Koala
214
208
  batch_client
215
209
  end
216
210
  end
217
-
218
- def self.included(base)
219
- base.class_eval do
220
- def self.batch
221
- raise NoMethodError, "The BatchAPI signature has changed (the original implementation was not thread-safe). Please see https://github.com/arsduo/koala/wiki/Batch-requests. (This message will be removed in the final 1.1 release.)"
222
- end
223
- end
224
- end
225
-
211
+
226
212
  # Direct access to the Facebook API
227
213
  # see any of the above methods for example invocations
228
214
  def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
@@ -231,10 +217,15 @@ module Koala
231
217
  raise error if error
232
218
  end
233
219
 
234
- # now process as appropriate (get picture header, make GraphCollection, etc.)
220
+ # turn this into a GraphCollection if it's pageable
221
+ result = GraphCollection.evaluate(result, self)
222
+
223
+ # now process as appropriate for the given call (get picture header, etc.)
235
224
  post_processing ? post_processing.call(result) : result
236
225
  end
237
226
 
227
+ private
228
+
238
229
  def check_response(response)
239
230
  # check for Graph API-specific errors
240
231
  # this returns an error, which is immediately raised (non-batch)
@@ -244,8 +235,6 @@ module Koala
244
235
  end
245
236
  end
246
237
 
247
- private
248
-
249
238
  def parse_media_args(media_args, method)
250
239
  # photo and video uploads can accept different types of arguments (see above)
251
240
  # so here, we parse the arguments into a form directly usable in put_object
@@ -4,6 +4,13 @@ module Koala
4
4
 
5
5
  def self.included(base)
6
6
  base.class_eval do
7
+ attr_reader :original_api
8
+
9
+ def initialize(access_token, api)
10
+ super(access_token)
11
+ @original_api = api
12
+ end
13
+
7
14
  alias_method :graph_call_outside_batch, :graph_call
8
15
  alias_method :graph_call, :graph_call_in_batch
9
16
 
@@ -46,7 +53,7 @@ module Koala
46
53
  batch_op.to_batch_params(access_token)
47
54
  })
48
55
 
49
- graph_call_outside_batch('/', args, 'post', http_options) do |response|
56
+ batch_result = graph_call_outside_batch('/', args, 'post', http_options) do |response|
50
57
  # map the results with post-processing included
51
58
  index = 0 # keep compat with ruby 1.8 - no with_index for map
52
59
  response.map do |call_result|
@@ -80,6 +87,9 @@ module Koala
80
87
  end
81
88
  end
82
89
  end
90
+
91
+ # turn any results that are pageable into GraphCollections
92
+ batch_result.inject([]) {|processed_results, raw| processed_results << GraphCollection.evaluate(raw, @original_api)}
83
93
  end
84
94
 
85
95
  end
@@ -10,12 +10,17 @@ module Koala
10
10
  # It also allows access to paging information and the
11
11
  # ability to get the next/previous page in the collection
12
12
  # by calling next_page or previous_page.
13
- attr_reader :paging
14
- attr_reader :api
13
+ attr_reader :paging, :api, :raw_response
15
14
 
15
+ def self.evaluate(response, api)
16
+ # turn the response into a GraphCollection if it's pageable; if not, return the original response
17
+ response.is_a?(Hash) && response["data"].is_a?(Array) ? self.new(response, api) : response
18
+ end
19
+
16
20
  def initialize(response, api)
17
21
  super response["data"]
18
22
  @paging = response["paging"]
23
+ @raw_response = response
19
24
  @api = api
20
25
  end
21
26
 
@@ -1,5 +1,4 @@
1
1
  require 'faraday'
2
- require 'faraday_stack'
3
2
 
4
3
  module Koala
5
4
  class Response
@@ -21,7 +20,7 @@ module Koala
21
20
  @http_options ||= {}
22
21
 
23
22
  DEFAULT_MIDDLEWARE = Proc.new do |builder|
24
- builder.request :multipart
23
+ builder.use Koala::MultipartRequest
25
24
  builder.request :url_encoded
26
25
  builder.adapter Faraday.default_adapter
27
26
  end
@@ -29,7 +28,8 @@ module Koala
29
28
  def self.server(options = {})
30
29
  server = "#{options[:rest_api] ? Facebook::REST_SERVER : Facebook::GRAPH_SERVER}"
31
30
  server.gsub!(/\.facebook/, "-video.facebook") if options[:video]
32
- "#{options[:use_ssl] ? "https" : "http"}://#{options[:beta] ? "beta." : ""}#{server}"
31
+ server.gsub!(/\.facebook/, ".beta.facebook") if options[:beta]
32
+ "#{options[:use_ssl] ? "https" : "http"}://#{server}"
33
33
  end
34
34
 
35
35
  def self.make_request(path, args, verb, options = {})
@@ -0,0 +1,35 @@
1
+ require 'faraday'
2
+
3
+ module Koala
4
+ class MultipartRequest < Faraday::Request::Multipart
5
+ # Facebook expects nested parameters to be passed in a certain way
6
+ # Based on our testing (https://github.com/arsduo/koala/issues/125),
7
+ # Faraday needs two changes to make that work:
8
+ # 1) [] need to be escaped (e.g. params[foo]=bar ==> params%5Bfoo%5D=bar)
9
+ # 2) such messages need to be multipart-encoded
10
+
11
+ self.mime_type = 'multipart/form-data'.freeze
12
+
13
+ def process_request?(env)
14
+ # if the request values contain any hashes or arrays, multipart it
15
+ super || !!(env[:body].respond_to?(:values) && env[:body].values.find {|v| v.is_a?(Hash) || v.is_a?(Array)})
16
+ end
17
+
18
+
19
+ def process_params(params, prefix = nil, pieces = nil, &block)
20
+ params.inject(pieces || []) do |all, (key, value)|
21
+ key = "#{prefix}%5B#{key}%5D" if prefix
22
+
23
+ case value
24
+ when Array
25
+ values = value.inject([]) { |a,v| a << [nil, v] }
26
+ process_params(values, key, all, &block)
27
+ when Hash
28
+ process_params(value, key, all, &block)
29
+ else
30
+ all << block.call(key, value)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/koala/oauth.rb CHANGED
@@ -9,38 +9,26 @@ module Koala
9
9
  end
10
10
 
11
11
  def get_user_info_from_cookie(cookie_hash)
12
- # Parses the cookie set by the official Facebook JavaScript SDK.
13
- #
14
- # cookies should be a Hash, like the one Rails provides
12
+ # Parses the cookie set Facebook's JavaScript SDK.
13
+ # You can pass Rack/Rails/Sinatra's cookie hash directly to this method.
15
14
  #
16
15
  # If the user is logged in via Facebook, we return a dictionary with the
17
16
  # keys "uid" and "access_token". The former is the user's Facebook ID,
18
17
  # and the latter can be used to make authenticated requests to the Graph API.
19
18
  # If the user is not logged in, we return None.
20
- #
21
- # Download the official Facebook JavaScript SDK at
22
- # http://github.com/facebook/connect-js/. Read more about Facebook
23
- # authentication at http://developers.facebook.com/docs/authentication/.
24
-
25
- if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
26
- # remove the opening/closing quote
27
- fb_cookie = fb_cookie.gsub(/\"/, "")
28
-
29
- # since we no longer get individual cookies, we have to separate out the components ourselves
30
- components = {}
31
- fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
32
-
33
- # generate the signature and make sure it matches what we expect
34
- auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
35
- sig = Digest::MD5.hexdigest(auth_string + @app_secret)
36
- sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
19
+
20
+ if signed_cookie = cookie_hash["fbsr_#{@app_id}"]
21
+ parse_signed_cookie(signed_cookie)
22
+ elsif unsigned_cookie = cookie_hash["fbs_#{@app_id}"]
23
+ parse_unsigned_cookie(unsigned_cookie)
37
24
  end
38
25
  end
39
26
  alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
40
27
 
41
28
  def get_user_from_cookie(cookies)
42
29
  if info = get_user_info_from_cookies(cookies)
43
- string = info["uid"]
30
+ # signed cookie has user_id, unsigned cookie has uid
31
+ string = info["user_id"] || info["uid"]
44
32
  end
45
33
  end
46
34
  alias_method :get_user_from_cookies, :get_user_from_cookie
@@ -70,7 +58,7 @@ module Koala
70
58
  def get_access_token_info(code, options = {})
71
59
  # convenience method to get a parsed token from Facebook for a given code
72
60
  # should this require an OAuth callback URL?
73
- get_token_from_server({:code => code, :redirect_uri => @oauth_callback_url}, false, options)
61
+ get_token_from_server({:code => code, :redirect_uri => options[:redirect_uri] || @oauth_callback_url}, false, options)
74
62
  end
75
63
 
76
64
  def get_access_token(code, options = {})
@@ -98,16 +86,18 @@ module Koala
98
86
  # for a more accurate reference implementation strategy.
99
87
  def parse_signed_request(input)
100
88
  encoded_sig, encoded_envelope = input.split('.', 2)
89
+ raise 'SignedRequest: Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope
90
+
101
91
  signature = base64_url_decode(encoded_sig).unpack("H*").first
102
92
  envelope = MultiJson.decode(base64_url_decode(encoded_envelope))
103
93
 
104
94
  raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
105
95
 
106
96
  # now see if the signature is valid (digest, key, data)
107
- hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope.tr("-_", "+/"))
97
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope)
108
98
  raise 'SignedRequest: Invalid signature' if (signature != hmac)
109
99
 
110
- return envelope
100
+ envelope
111
101
  end
112
102
 
113
103
  # from session keys
@@ -162,6 +152,29 @@ module Koala
162
152
  end
163
153
  components
164
154
  end
155
+
156
+ def parse_unsigned_cookie(fb_cookie)
157
+ # remove the opening/closing quote
158
+ fb_cookie = fb_cookie.gsub(/\"/, "")
159
+
160
+ # since we no longer get individual cookies, we have to separate out the components ourselves
161
+ components = {}
162
+ fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
163
+
164
+ # generate the signature and make sure it matches what we expect
165
+ auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
166
+ sig = Digest::MD5.hexdigest(auth_string + @app_secret)
167
+ sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
168
+ end
169
+
170
+ def parse_signed_cookie(fb_cookie)
171
+ components = parse_signed_request(fb_cookie)
172
+ if (code = components["code"]) && token_info = get_access_token_info(code, :redirect_uri => '')
173
+ components.merge(token_info)
174
+ else
175
+ nil
176
+ end
177
+ end
165
178
 
166
179
  def fetch_token_string(args, post = false, endpoint = "access_token", options = {})
167
180
  Koala.make_request("/oauth/#{endpoint}", {
@@ -71,7 +71,7 @@ module Koala
71
71
  end
72
72
 
73
73
  def list_subscriptions
74
- @graph_api.graph_call(subscription_path)["data"]
74
+ @graph_api.graph_call(subscription_path)
75
75
  end
76
76
 
77
77
  def graph_api
@@ -13,6 +13,11 @@ module Koala
13
13
  results.inject({}) {|outcome, data| outcome[data["name"]] = data["fql_result_set"]; outcome}
14
14
  end
15
15
  end
16
+
17
+ def set_app_properties(properties, args = {}, options = {})
18
+ raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "setAppProperties requires an access token"}) unless @access_token
19
+ rest_call("admin.setAppProperties", args.merge(:properties => MultiJson.encode(properties)), options, "post")
20
+ end
16
21
 
17
22
  def rest_call(fb_method, args = {}, options = {}, method = "get")
18
23
  options = options.merge!(:rest_api => true, :read_only => READ_ONLY_METHODS.include?(fb_method.to_s))
@@ -31,11 +31,11 @@ module Koala
31
31
  # Creates and returns a test user
32
32
  args['installed'] = installed
33
33
  args['permissions'] = (permissions.is_a?(Array) ? permissions.join(",") : permissions) if installed
34
- result = @api.graph_call(accounts_path, args, "post", options)
34
+ @api.graph_call(accounts_path, args, "post", options)
35
35
  end
36
36
 
37
37
  def list
38
- @api.graph_call(accounts_path)["data"]
38
+ @api.graph_call(accounts_path)
39
39
  end
40
40
 
41
41
  def delete(test_user)
@@ -72,7 +72,6 @@ module Koala
72
72
  end
73
73
 
74
74
  def create_network(network_size, installed = true, permissions = '')
75
- network_size = 50 if network_size > 50 # FB's max is 50
76
75
  users = (0...network_size).collect { create(installed, permissions) }
77
76
  friends = users.clone
78
77
  users.each do |user|
@@ -40,7 +40,8 @@ module Koala
40
40
  :parse_rails_3_param,
41
41
  :parse_sinatra_param,
42
42
  :parse_file_object,
43
- :parse_string_path
43
+ :parse_string_path,
44
+ :parse_io
44
45
  ]
45
46
 
46
47
  def parse_init_mixed_param(mixed, content_type = nil)
@@ -0,0 +1,3 @@
1
+ module Koala
2
+ VERSION = "1.2.1"
3
+ end
data/lib/koala.rb CHANGED
@@ -8,7 +8,6 @@ require 'openssl'
8
8
  require 'base64'
9
9
 
10
10
  # include koala modules
11
- require 'koala/http_service'
12
11
  require 'koala/oauth'
13
12
  require 'koala/graph_api'
14
13
  require 'koala/graph_batch_api'
@@ -17,11 +16,18 @@ require 'koala/graph_collection'
17
16
  require 'koala/rest_api'
18
17
  require 'koala/realtime_updates'
19
18
  require 'koala/test_users'
20
- require 'koala/utils'
19
+
20
+ # HTTP module so we can communicate with Facebook
21
+ require 'koala/http_service'
22
+ require 'koala/multipart_request'
21
23
 
22
24
  # add KoalaIO class
23
25
  require 'koala/uploadable_io'
24
26
 
27
+ # miscellaneous
28
+ require 'koala/utils'
29
+ require 'koala/version'
30
+
25
31
  module Koala
26
32
 
27
33
  module Facebook
data/readme.md CHANGED
@@ -9,28 +9,37 @@ Koala
9
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
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
11
 
12
+ Facebook Changes on October 1, 2011
13
+ ---
14
+
15
+ Koala 1.2 supports all of Facebook's new authentication schemes, which were introduced on October 1, 2011. 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. For more information, see Facebook's [OAuth 2.0 and HTTPS Migration](https://developers.facebook.com/docs/oauth2-https-migration/) guide.
16
+
12
17
  Installation
13
18
  ---
14
19
 
15
20
  Easy:
16
21
 
17
- [sudo|rvm] gem install koala --pre # for 1.2 beta
18
- [sudo|rvm] gem install koala # for 1.1
22
+ [sudo|rvm] gem install koala
19
23
 
20
24
  Or in Bundler:
21
25
 
22
- gem "koala", "~> 1.2.0beta"
23
- gem "koala" # for 1.1
26
+ gem "koala"
24
27
 
25
28
  Graph API
26
29
  ----
27
30
  The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
28
-
31
+
29
32
  @graph = Koala::Facebook::API.new(oauth_access_token)
33
+ # in 1.1 or earlier, use GraphAPI instead of API
34
+
30
35
  profile = @graph.get_object("me")
31
36
  friends = @graph.get_connections("me", "friends")
32
37
  @graph.put_object("me", "feed", :message => "I am writing on my wall!")
33
38
 
39
+ # you can even use the new Timeline API
40
+ # see https://developers.facebook.com/docs/beta/opengraph/tutorial/
41
+ @graph.put_connections("me", "namespace:action", :object => object_url)
42
+
34
43
  The response of most requests is the JSON data returned from the Facebook servers as a Hash.
35
44
 
36
45
  When retrieving data that returns an array of results (for example, when calling API#get_connections or API#search) a GraphCollection object will be returned, which makes it easy to page through the results:
@@ -61,13 +70,18 @@ Where the Graph API and the old REST API overlap, you should choose the Graph AP
61
70
 
62
71
  Fortunately, Koala supports the REST API using the very same interface; to use this, instantiate an API:
63
72
 
64
- @rest = Koala::Facebook::API.new(oauth_access_token)
73
+ @rest = Koala::Facebook::API.new(oauth_access_token)
74
+ # in 1.1 or earlier, use RestAPI instead of API
75
+
65
76
  @rest.fql_query(my_fql_query) # convenience method
66
77
  @rest.fql_multiquery(fql_query_hash) # convenience method
67
78
  @rest.rest_call("stream.publish", arguments_hash) # generic version
68
79
 
69
80
  Of course, you can use the Graph API methods on the same object -- the power of two APIs right in the palm of your hand.
70
81
 
82
+ @api = Koala::Facebook::API.new(oauth_access_token)
83
+ # in 1.1 or earlier, use GraphAndRestAPI instead of API
84
+
71
85
  @api = Koala::Facebook::API.new(oauth_access_token)
72
86
  fql = @api.fql_query(my_fql_query)
73
87
  @api.put_wall_post(process_result(fql))
@@ -76,25 +90,31 @@ Of course, you can use the Graph API methods on the same object -- the power of
76
90
  OAuth
77
91
  -----
78
92
  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:
93
+
79
94
  @oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
80
95
 
81
96
  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:
97
+
82
98
  @oauth.get_user_from_cookies(cookies) # gets the user's ID
83
99
  @oauth.get_user_info_from_cookies(cookies) # parses and returns the entire hash
84
100
 
85
101
  And if you have to use the more complicated [redirect-based OAuth process](http://developers.facebook.com/docs/authentication/), Koala helps out there, too:
102
+
86
103
  # generate authenticating URL
87
104
  @oauth.url_for_oauth_code
88
105
  # fetch the access token once you have the code
89
106
  @oauth.get_access_token(code)
90
107
 
91
108
  You can also get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
109
+
92
110
  @oauth.get_app_access_token
93
111
 
94
112
  For those building apps on Facebook, parsing signed requests is simple:
95
- @oauth.parse_signed_request(request)
113
+
114
+ @oauth.parse_signed_request(signed_request_string)
96
115
 
97
116
  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:
117
+
98
118
  @oauth.get_token_from_session_key(session_key)
99
119
  @oauth.get_tokens_from_session_keys(array_of_session_keys)
100
120
 
@@ -137,12 +157,27 @@ We also support the test users API, allowing you to conjure up fake users and co
137
157
  # or, if you want to make a whole community:
138
158
  @test_users.create_network(network_size, is_app_installed, common_permissions)
139
159
 
160
+ Talking to Facebook
161
+ -----
162
+
163
+ 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):
164
+
165
+ # Set an SSL certificate to avoid Net::HTTP errors
166
+ Koala.http_service.http_options = {
167
+ :ssl => { :ca_path => "/etc/ssl/certs" }
168
+ }
169
+ # or on a per-request basis
170
+ @api.get_object(id, args_hash, { :timeout => 10 })
171
+
172
+ 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).
173
+
140
174
  See examples, ask questions
141
175
  -----
142
176
  Some resources to help you as you play with Koala and the Graph API:
143
177
 
144
178
  * Complete Koala documentation <a href="http://wiki.github.com/arsduo/koala/">on the wiki</a>
145
179
  * 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
180
+ * 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
146
181
  * 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
147
182
 
148
183
  Testing
@@ -158,5 +193,7 @@ You can also run live tests against Facebook's servers:
158
193
 
159
194
  # Again from anywhere in the project directory:
160
195
  LIVE=true bundle exec rake spec
196
+ # you can also test against Facebook's beta tier
197
+ LIVE=true BETA=true bundle exec rake spec
161
198
 
162
199
  By default, the live tests are run against test users, so you can run them as frequently as you want. If you want to run them against a real user, however, you can fill in the OAuth token, code, and access\_token values in spec/fixtures/facebook_data.yml. See the wiki for more details.