koala 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ v0.7.0
2
+ -- Added RealtimeUpdates class, which can be used to manage subscriptions for user updates (see http://developers.facebook.com/docs/api/realtime)
3
+ -- Added picture method to graph API, which fetches an object's picture from the redirect headers.
4
+ -- Added _greatly_ improved testing with result mocking, which is now the default set of tests
5
+ -- Renamed live testing spec to koala_spec_without_mocks.rb
6
+ -- Added Koala::Response class, which encapsulates HTTP results since Facebook sometimes sends data in the status or headers
7
+ -- Much internal refactoring
8
+ -- Updated readme, changelog, etc.
9
+
10
+
1
11
  v0.6.0
2
12
  -- Added support for the old REST API thanks to cbaclig's great work
3
13
  -- Updated tests to conform to RSpec standards
data/Manifest CHANGED
@@ -5,12 +5,23 @@ init.rb
5
5
  lib/graph_api.rb
6
6
  lib/http_services.rb
7
7
  lib/koala.rb
8
+ lib/realtime_updates.rb
8
9
  lib/rest_api.rb
9
10
  readme.md
10
11
  spec/facebook_data.yml
11
- spec/koala/facebook_no_access_token_tests.rb
12
- spec/koala/facebook_oauth_tests.rb
13
- spec/koala/facebook_rest_api_no_access_token_test.rb
14
- spec/koala/facebook_rest_api_with_access_token_test.rb
15
- spec/koala/facebook_with_access_token_tests.rb
12
+ spec/koala/api_base_tests.rb
13
+ spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb
14
+ spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb
15
+ spec/koala/graph_api/graph_api_no_access_token_tests.rb
16
+ spec/koala/graph_api/graph_api_with_access_token_tests.rb
17
+ spec/koala/live_testing_data_helper.rb
18
+ spec/koala/net_http_service_tests.rb
19
+ spec/koala/oauth/oauth_tests.rb
20
+ spec/koala/realtime_updates/realtime_updates_tests.rb
21
+ spec/koala/rest_api/rest_api_no_access_token_tests.rb
22
+ spec/koala/rest_api/rest_api_with_access_token_tests.rb
16
23
  spec/koala_spec.rb
24
+ spec/koala_spec_helper.rb
25
+ spec/koala_spec_without_mocks.rb
26
+ spec/mock_facebook_responses.yml
27
+ spec/mock_http_service.rb
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'rake'
4
4
  require 'echoe'
5
5
 
6
6
  # gem management
7
- Echoe.new('koala', '0.6.0') do |p|
7
+ Echoe.new('koala', '0.7.0') do |p|
8
8
  p.summary = "A lightweight, flexible library for Facebook's new Graph API"
9
9
  p.description = "Koala is a lightweight, flexible Ruby SDK for Facebook's new Graph API. It allows read/write access to the Facebook Graph and provides OAuth URLs and cookie validation for Facebook Connect sites; it also supports access-token based interaction with the old REST API. Koala supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services."
10
10
  p.url = "http://github.com/arsduo/koala"
data/koala.gemspec CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{koala}
5
- s.version = "0.6.0"
5
+ s.version = "0.7.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional"]
9
- s.date = %q{2010-05-17}
9
+ s.date = %q{2010-05-26}
10
10
  s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook's new Graph API. It allows read/write access to the Facebook Graph and provides OAuth URLs and cookie validation for Facebook Connect sites; it also supports access-token based interaction with the old REST API. Koala supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
11
11
  s.email = %q{alex@alexkoppel.com}
12
- s.extra_rdoc_files = ["CHANGELOG", "lib/graph_api.rb", "lib/http_services.rb", "lib/koala.rb", "lib/rest_api.rb"]
13
- s.files = ["CHANGELOG", "Manifest", "Rakefile", "init.rb", "lib/graph_api.rb", "lib/http_services.rb", "lib/koala.rb", "lib/rest_api.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/facebook_no_access_token_tests.rb", "spec/koala/facebook_oauth_tests.rb", "spec/koala/facebook_rest_api_no_access_token_test.rb", "spec/koala/facebook_rest_api_with_access_token_test.rb", "spec/koala/facebook_with_access_token_tests.rb", "spec/koala_spec.rb", "koala.gemspec"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/graph_api.rb", "lib/http_services.rb", "lib/koala.rb", "lib/realtime_updates.rb", "lib/rest_api.rb"]
13
+ s.files = ["CHANGELOG", "Manifest", "Rakefile", "init.rb", "lib/graph_api.rb", "lib/http_services.rb", "lib/koala.rb", "lib/realtime_updates.rb", "lib/rest_api.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/net_http_service_tests.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb", "koala.gemspec"]
14
14
  s.homepage = %q{http://github.com/arsduo/koala}
15
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala", "--main", "readme.md"]
16
16
  s.require_paths = ["lib"]
data/lib/graph_api.rb CHANGED
@@ -32,19 +32,24 @@ module Koala
32
32
 
33
33
  def get_object(id, args = {})
34
34
  # Fetchs the given object from the graph.
35
- api(id, args)
35
+ graph_call(id, args)
36
36
  end
37
37
 
38
38
  def get_objects(ids, args = {})
39
39
  # Fetchs all of the given object from the graph.
40
40
  # We return a map from ID to object. If any of the IDs are invalid,
41
41
  # we raise an exception.
42
- api("", args.merge("ids" => ids.join(",")))
42
+ graph_call("", args.merge("ids" => ids.join(",")))
43
43
  end
44
44
 
45
45
  def get_connections(id, connection_name, args = {})
46
46
  # Fetchs the connections for given object.
47
- api("#{id}/#{connection_name}", args)
47
+ graph_call("#{id}/#{connection_name}", args)
48
+ end
49
+
50
+ def get_picture(object)
51
+ result = graph_call("#{object}/picture", {}, "get", :http_component => :headers)
52
+ result["Location"]
48
53
  end
49
54
 
50
55
  def put_object(parent_object, connection_name, args = {})
@@ -70,7 +75,7 @@ module Koala
70
75
  # extended permissions.
71
76
 
72
77
  raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "Write operations require an access token"}) unless @access_token
73
- api("#{parent_object}/#{connection_name}", args, "post")
78
+ graph_call("#{parent_object}/#{connection_name}", args, "post")
74
79
  end
75
80
 
76
81
  def put_wall_post(message, attachment = {}, profile_id = "me")
@@ -103,20 +108,20 @@ module Koala
103
108
 
104
109
  def delete_object(id)
105
110
  # Deletes the object with the given ID from the graph.
106
- api(id, {}, "delete")
111
+ graph_call(id, {}, "delete")
107
112
  end
108
113
 
109
114
  def search(search_terms, args = {})
110
115
  # Searches for a given term
111
- api("search", args.merge({:q => search_terms}))
116
+ graph_call("search", args.merge({:q => search_terms}))
112
117
  end
113
118
 
114
- def api(*args)
115
- response = super
116
-
117
- # check for errors
118
- if response.is_a?(Hash) && error_details = response["error"]
119
- raise APIError.new(error_details)
119
+ def graph_call(*args)
120
+ response = api(*args) do |response|
121
+ # check for Graph API-specific errors
122
+ if response.is_a?(Hash) && error_details = response["error"]
123
+ raise APIError.new(error_details)
124
+ end
120
125
  end
121
126
 
122
127
  response
data/lib/http_services.rb CHANGED
@@ -1,4 +1,13 @@
1
1
  module Koala
2
+ class Response
3
+ attr_reader :status, :body, :headers
4
+ def initialize(status, body, headers)
5
+ @status = status
6
+ @body = body
7
+ @headers = headers
8
+ end
9
+ end
10
+
2
11
  module NetHTTPService
3
12
  # this service uses Net::HTTP to send requests to the graph
4
13
  def self.included(base)
@@ -24,7 +33,7 @@ module Koala
24
33
 
25
34
  result = http.start { |http|
26
35
  response, body = (verb == "post" ? http.post(path, encode_params(args)) : http.get("#{path}?#{encode_params(args)}"))
27
- body
36
+ Koala::Response.new(response.code.to_i, body, response.to_hash)
28
37
  }
29
38
  end
30
39
 
@@ -52,7 +61,8 @@ module Koala
52
61
  # if the verb isn't get or post, send it as a post argument
53
62
  args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
54
63
  server = options[:rest_api] ? Facebook::REST_SERVER : Facebook::GRAPH_SERVER
55
- self.send(verb, "https://#{server}/#{path}", :params => args).body
64
+ response = self.send(verb, "https://#{server}/#{path}", :params => args)
65
+ Koala::Response.new(response.code, response.body, response.headers_hash)
56
66
  end
57
67
  end # class_eval
58
68
  end
data/lib/koala.rb CHANGED
@@ -14,6 +14,8 @@ require 'graph_api'
14
14
  # add REST API methods
15
15
  require 'rest_api'
16
16
 
17
+ require 'realtime_updates'
18
+
17
19
  module Koala
18
20
 
19
21
  module Facebook
@@ -44,19 +46,26 @@ module Koala
44
46
  @access_token = access_token
45
47
  end
46
48
 
47
- def api(path, args = {}, verb = "get", options = {})
49
+ def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
48
50
  # Fetches the given path in the Graph API.
49
- args["access_token"] = @access_token if @access_token
50
-
51
+ args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
51
52
  # make the request via the provided service
52
53
  result = Koala.make_request(path, args, verb, options)
53
-
54
- # Facebook sometimes sends results like "true" and "false", which aren't strictly object
55
- # and cause JSON.parse to fail
56
- # so we account for that
57
- response = JSON.parse("[#{result}]")[0]
58
54
 
59
- response
55
+ # Parse the body as JSON and check for errors if provided a mechanism to do so
56
+ # Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
57
+ # and cause JSON.parse to fail -- so we account for that by wrapping the result in []
58
+ body = response = JSON.parse("[#{result.body.to_s}]")[0]
59
+ if error_checking_block
60
+ yield(body)
61
+ end
62
+
63
+ # now return the desired information
64
+ if options[:http_component]
65
+ result.send(options[:http_component])
66
+ else
67
+ body
68
+ end
60
69
  end
61
70
  end
62
71
 
@@ -73,6 +82,10 @@ module Koala
73
82
  include RestAPIMethods
74
83
  end
75
84
 
85
+ class RealtimeUpdates < API
86
+ include RealtimeUpdateMethods
87
+ end
88
+
76
89
  class APIError < Exception
77
90
  attr_accessor :fb_error_type
78
91
  def initialize(details = {})
@@ -83,7 +96,7 @@ module Koala
83
96
 
84
97
 
85
98
  class OAuth
86
- attr_accessor :app_id, :app_secret, :oauth_callback_url
99
+ attr_reader :app_id, :app_secret, :oauth_callback_url
87
100
  def initialize(app_id, app_secret, oauth_callback_url = nil)
88
101
  @app_id = app_id
89
102
  @app_secret = app_secret
@@ -144,6 +157,30 @@ module Koala
144
157
  "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
145
158
  end
146
159
 
160
+ def get_access_token(code)
161
+ # convenience method to get a parsed token from Facebook for a given code
162
+ # should this require an OAuth callback URL?
163
+ get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
164
+ end
165
+
166
+ def get_app_access_token
167
+ # convenience method to get a the application's sessionless access token
168
+ get_token_from_server({:type => 'client_cred'}, true)
169
+ end
170
+
171
+ protected
172
+
173
+ def get_token_from_server(args, post = false)
174
+ # fetch the result from Facebook's servers
175
+ result = fetch_token_string(args, post)
176
+
177
+ # if we have an error, parse the error JSON and raise an error
178
+ raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
179
+
180
+ # otherwise, parse the access token
181
+ parse_access_token(result)
182
+ end
183
+
147
184
  def parse_access_token(response_text)
148
185
  components = response_text.split("&").inject({}) do |hash, bit|
149
186
  key, value = bit.split("=")
@@ -152,22 +189,11 @@ module Koala
152
189
  components
153
190
  end
154
191
 
155
- def fetch_token_string(code)
192
+ def fetch_token_string(args, post = false)
156
193
  Koala.make_request("oauth/access_token", {
157
194
  :client_id => @app_id,
158
- :redirect_uri => @oauth_callback_url,
159
- :client_secret => @app_secret,
160
- :code => code
161
- }, "get")
162
- end
163
-
164
- def get_access_token(code)
165
- result = fetch_token_string(code)
166
-
167
- # if we have an error, parse the error JSON and raise an error
168
- raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
169
- # otherwise, parse the access token
170
- parse_access_token(result)
195
+ :client_secret => @app_secret
196
+ }.merge!(args), post ? "post" : "get").body
171
197
  end
172
198
  end
173
199
  end
@@ -0,0 +1,95 @@
1
+ require 'koala'
2
+
3
+ module Koala
4
+ module Facebook
5
+ module RealtimeUpdateMethods
6
+ # note: to subscribe to real-time updates, you must have an application access token
7
+
8
+ def self.included(base)
9
+ # make the attributes readable
10
+ base.class_eval do
11
+ attr_reader :app_id, :app_access_token, :secret
12
+
13
+ # parses the challenge params and makes sure the call is legitimate
14
+ # returns the challenge string to be sent back to facebook if true
15
+ # returns false otherwise
16
+ # this is a class method, since you don't need to know anything about the app
17
+ # saves a potential trip fetching the app access token
18
+ def self.meet_challenge(params, verify_token = nil, &verification_block)
19
+ if params["hub.mode"] == "subscribe" &&
20
+ # you can make sure this is legitimate through two ways
21
+ # if your store the token across the calls, you can pass in the token value
22
+ # and we'll make sure it matches
23
+ (verify_token && params["hub.verify_token"] == verify_token) ||
24
+ # alternately, if you sent a specially-constructed value (such as a hash of various secret values)
25
+ # you can pass in a block, which we'll call with the verify_token sent by Facebook
26
+ # if it's legit, return anything that evaluates to true; otherwise, return nil or false
27
+ (verification_block && yield(params["hub.verify_token"]))
28
+ params["hub.challenge"]
29
+ else
30
+ false
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def initialize(options = {})
37
+ @app_id = options[:app_id]
38
+ @app_access_token = options[:app_access_token]
39
+ @secret = options[:secret]
40
+ unless @app_id && (@app_access_token || @secret) # make sure we have what we need
41
+ raise ArgumentError, "Initialize must receive a hash with :app_id and either :app_access_token or :secret! (received #{options.inspect})"
42
+ end
43
+
44
+ # fetch the access token if we're provided a secret
45
+ if @secret && !@app_access_token
46
+ oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
47
+ @app_access_token = oauth.get_app_access_token["access_token"]
48
+ end
49
+ end
50
+
51
+ # subscribes for realtime updates
52
+ # your callback_url must be set up to handle the verification request or the subscription will not be set up
53
+ # http://developers.facebook.com/docs/api/realtime
54
+ def subscribe(object, fields, callback_url, verify_token)
55
+ args = {
56
+ :object => object,
57
+ :fields => fields,
58
+ :callback_url => callback_url,
59
+ :verify_token => verify_token
60
+ }
61
+ # a subscription is a success if Facebook returns a 200 (after hitting your server for verification)
62
+ api(subscription_path, args, 'post', :http_component => :status) == 200
63
+ end
64
+
65
+ # removes subscription for object
66
+ # if object is nil, it will remove all subscriptions
67
+ def unsubscribe(object = nil)
68
+ args = {}
69
+ args[:object] = object if object
70
+ api(subscription_path, args, 'delete', :http_component => :status) == 200
71
+ end
72
+
73
+ def list_subscriptions
74
+ api(subscription_path)
75
+ end
76
+
77
+ def api(*args) # same as GraphAPI
78
+ response = super(*args) do |response|
79
+ # check for subscription errors
80
+ if response.is_a?(Hash) && error_details = response["error"]
81
+ raise APIError.new(error_details)
82
+ end
83
+ end
84
+
85
+ response
86
+ end
87
+
88
+ protected
89
+
90
+ def subscription_path
91
+ @subscription_path ||= "#{@app_id}/subscriptions"
92
+ end
93
+ end
94
+ end
95
+ end
data/lib/rest_api.rb CHANGED
@@ -4,20 +4,15 @@ module Koala
4
4
 
5
5
  module RestAPIMethods
6
6
  def fql_query(fql)
7
- args = {
8
- "query" => fql,
9
- "format" => "json",
10
- }
11
-
12
- api('method/fql.query', args, 'get', :rest_api => true)
7
+ rest_call('fql.query', 'query' => fql)
13
8
  end
14
9
 
15
- def api(*args)
16
- response = super
17
-
18
- # check for REST API-specific errors
19
- if response.is_a?(Hash) && response["error_code"]
20
- raise APIError.new("type" => response["error_code"], "message" => response["error_msg"])
10
+ def rest_call(method, args = {})
11
+ response = api("method/#{method}", args.merge('format' => 'json'), 'get', :rest_api => true) do |response|
12
+ # check for REST API-specific errors
13
+ if response.is_a?(Hash) && response["error_code"]
14
+ raise APIError.new("type" => response["error_code"], "message" => response["error_msg"])
15
+ end
21
16
  end
22
17
 
23
18
  response
data/readme.md CHANGED
@@ -1,53 +1,99 @@
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 Graph library for Ruby. 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, 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 300 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 500 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
- * Tested: Koala should have complete test coverage, so you can rely on it. (By the time you read this the final tests are being written.)
9
-
10
- Basic usage:
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.)
11
9
 
10
+ Graph API
11
+ ----
12
+ The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
12
13
  graph = Koala::Facebook::GraphAPI.new(oauth_access_token)
13
14
  profile = graph.get_object("me")
14
- friends = graph.get_connections("me", "friends")
15
+ friends = graph.get_connection("me", "friends")
15
16
  graph.put_object("me", "feed", :message => "I am writing on my wall!")
16
17
 
17
- If you're using Koala within a web application with the Facebook
18
- [JavaScript SDK](http://github.com/facebook/connect-js), you can use the Koala::Facebook::OAuth class
19
- to parse the cookies set by the JavaScript SDK for logged in users.
18
+ Check out the wiki for more examples.
20
19
 
21
- FQL and the old-school REST API
20
+ The old-school REST API
22
21
  -----
23
22
  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 -- including fql.query -- that can't yet be done via the Graph.
24
23
 
25
- Koala now supports the old-school REST API using OAuth access tokens; to use this, instantiate your class using the GraphAndRestAPI class:
24
+ Koala now supports the old-school REST API using OAuth access tokens; to use this, instantiate your class using the RestAPI class:
26
25
 
27
- api = Koala::Facebook::GraphAndRestAPI.new(oauth_access_token)
26
+ @rest = Koala::Facebook::RestAPI.new(oauth_access_token)
27
+ @rest.fql_query(my_fql_query) # convenience method
28
+ @rest.rest_call("stream.publish", arguments_hash) # generic version
29
+
30
+ 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.
28
31
 
29
- The GraphAndRestAPI class provides access to all the Graph API methods, as well as an fql method that you can use to make FQL calls. (You can pass the :rest\_api => true option to the api method to make REST API calls; check out lib/rest\_api.rb to see how it's done.) We reserve the right to expand the built-in REST API coverage to additional methods in the future, depending on how fast Facebook moves to fill in the gaps.
32
+ (If you want the power of both APIs in the palm of your hand, try out the GraphAndRestAPI class.)
30
33
 
31
- Examples and More Details
34
+ OAuth
32
35
  -----
33
- Complete Koala documentation can now be found <a href="http://wiki.github.com/arsduo/koala/">on the wiki</a>!
36
+ 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:
37
+ @oauth = Koala::Facebook::OAuth.new(app_id, code, callback_url)
34
38
 
35
- You can easily generate OAuth access tokens and any other data needed to play with the Graph API or OAuth at the Koala-powered <a href="http://oauth.twoalex.com" target="_blank">OAuth Playground</a>.
39
+ 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:
40
+ @oauth.get_user_from_cookie(cookies)
36
41
 
42
+ And if you have to use the more complicated [redirect-based OAuth process](http://developers.facebook.com/docs/authentication/), Koala helps out there, too:
43
+ # generate authenticating URL
44
+ @oauth.url_for_oauth_code
45
+ # fetch the access token once you have the code
46
+ @oauth.get_access_token(code)
37
47
 
38
- Testing
48
+ You can also get your application's own access token, which can be used without a user session for subscriptions and certain other requests:
49
+ @oauth.get_app_access_token
50
+
51
+ That's it! It's pretty simple once you get the hang of it. If you're new to OAuth, though, check out the wiki and the OAuth Playground example site (see below).
52
+
53
+
54
+ Real-time Updates
39
55
  -----
56
+ The Graph API now allows your application to subscribe to real-time updates for certain objects in the graph.
40
57
 
41
- Unit tests are provided for all of Koala's methods; however, because the OAuth access tokens and cookies expire, 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. (The file also provides valid values for other tests, which you're welcome to sub out for data specific to your own application.)
58
+ Currently, Facebook only supports subscribing to users, permissions and errors. On top of that, there are limitations on what attributes and connections for each of these objects you can subscribe to updates for. Check the [official Facebook documentation](http://developers.facebook.com/docs/api/realtime) for more details.
42
59
 
43
- Insert the required values into the file test/facebook_data.yml, then run the test as follows:
44
- spec koala_tests.rb
45
-
60
+ Koala makes it easy to interact with your applications using the RealTimeUpdates class:
61
+
62
+ @updates = Koala::Facebook::RealTimeUpdates.new(:app_id => app_id, :secret => secret)
63
+
64
+ You can do just about anything with your real-time update subscriptions using the RealTimeUpdates class:
65
+
66
+ # Add/modify a subscription to updates for when the first_name or last_name fields of any of your users is changed
67
+ @updates.subscribe("user", "first_name, last_name", callback_token, verify_token)
68
+
69
+ # Get an array of your current subscriptions (one hash for each object you've subscribed to)
70
+ @updates.list_subscriptions
46
71
 
47
- Coming Soon
72
+ # Unsubscribe from updates for an object
73
+ @updates.unsubscribe("user")
74
+
75
+ And to top it all off, RealTimeUpdates provides a static method to respond to Facebook servers' verification of your callback URLs:
76
+
77
+ # Returns the hub.challenge parameter in params if the verify token in params matches verify_token
78
+ Koala::Facebook::RealTimeUpdates.meet_challenge(params, your_verify_token)
79
+
80
+ For more information about meet_challenge and the RealTimeUpdates class, check out the Real-Time Updates page on the wiki.
81
+
82
+ See examples, ask questions
48
83
  -----
49
- * Support for real-time updates
84
+ Some resources to help you as you play with Koala and the Graph API:
85
+
86
+ * Complete Koala documentation <a href="http://wiki.github.com/arsduo/koala/">on the wiki</a>
87
+ * 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
88
+ * 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
50
89
 
51
- Known Issues
90
+ Testing
52
91
  -----
53
- 1. gem install koala produces "Could not find main page readme.md" message
92
+
93
+ 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:
94
+ spec koala_tests.rb
95
+
96
+ You can also run live tests against Facebook's servers:
97
+ spec koala_tests_without_mocks.rb
98
+
99
+ 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.)
@@ -5,18 +5,19 @@
5
5
 
6
6
  # You must supply this value yourself to test the GraphAPI class.
7
7
  # Your OAuth token should have publish_stream and read_stream permissions.
8
- oauth_token:
8
+ oauth_token: 119908831367602|2.HlL6n4vuEhDKRyF_7dSipg__.3600.1274893200-2905623|NELx9TxQgEa2uL87wfg0arHIBvk.
9
9
 
10
10
  # for testing the OAuth class
11
11
  # baseline app
12
12
  oauth_test_data:
13
13
  # You must supply this value yourself, since they will expire.
14
- code:
14
+ code: 2.HlL6n4vuEhDKRyF_7dSipg__.3600.1274893200-2905623|HgKssdjuTn4tMSStxS-LsMb_p4M.
15
15
 
16
16
  # These values will work out of the box
17
17
  app_id: 119908831367602
18
18
  secret: e45e55a333eec232d4206d2703de1307
19
19
  callback_url: http://oauth.twoalex.com/
20
+ app_access_token: 119908831367602|o3wswWQ88LYjEC9-ukR_gjRIOMw.
20
21
  raw_token_string: "access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.&expires=6621"
21
22
  raw_offline_access_token_string: access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.
22
23
  valid_cookies:
@@ -27,4 +28,12 @@ oauth_test_data:
27
28
  fbs_119908831367602: '"access_token=119908831367602|2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623|yVt5WH_S6J5p3gFa5_5lBzckhws.&expires=1273287600&secret=V_E79ovQnXqxGctFuC_n5A__&session_key=2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623&sig=eeef60838c0c800258d89b7e6ddddddb&uid=2905623"'
28
29
  offline_access_cookies:
29
30
  # note: I've revoked the offline access for security reasons, so you can't make calls against this :)
30
- fbs_119908831367602: '"access_token=119908831367602|08170230801eb225068e7a70-2905623|Q3LDCYYF8CX9cstxnZLsxiR0nwg.&expires=0&secret=78abaee300b392e275072a9f2727d436&session_key=08170230801eb225068e7a70-2905623&sig=423b8aa4b6fa1f9a571955f8e929d567&uid=2905623"'
31
+ fbs_119908831367602: '"access_token=119908831367602|08170230801eb225068e7a70-2905623|Q3LDCYYF8CX9cstxnZLsxiR0nwg.&expires=0&secret=78abaee300b392e275072a9f2727d436&session_key=08170230801eb225068e7a70-2905623&sig=423b8aa4b6fa1f9a571955f8e929d567&uid=2905623"'
32
+
33
+ subscription_test_data:
34
+ subscription_path: http://oauth.twoalex.com/subscriptions
35
+ verify_token: "myverificationtoken|1f54545d5f722733e17faae15377928f"
36
+ challenge_data:
37
+ "hub.challenge": "1290024882"
38
+ "hub.verify_token": "myverificationtoken|1f54545d5f722733e17faae15377928f"
39
+ "hub.mode": "subscribe"
@@ -0,0 +1,44 @@
1
+ class ApiBaseTests < Test::Unit::TestCase
2
+ describe "Koala API base class" do
3
+ before(:each) do
4
+ @service = Koala::Facebook::API.new
5
+ end
6
+
7
+ it "should not include an access token if none was given" do
8
+ Koala.should_receive(:make_request).with(
9
+ anything,
10
+ hash_not_including('access_token' => 1),
11
+ anything,
12
+ anything
13
+ ).and_return(Koala::Response.new(200, "", ""))
14
+
15
+ @service.api('anything')
16
+ end
17
+
18
+ it "should include an access token if given" do
19
+ token = 'adfadf'
20
+ service = Koala::Facebook::API.new token
21
+
22
+ Koala.should_receive(:make_request).with(
23
+ anything,
24
+ hash_including('access_token' => token),
25
+ anything,
26
+ anything
27
+ ).and_return(Koala::Response.new(200, "", ""))
28
+
29
+ service.api('anything')
30
+ end
31
+
32
+ it "should properly handle the http_component parameter"
33
+
34
+ it "should execute a block to test for errors if passed one"
35
+
36
+ it "should handle rogue true/false as responses" do
37
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
38
+ @service.api('anything').should be_true
39
+
40
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'false', {}))
41
+ @service.api('anything').should be_false
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ class GraphAndRestAPINoTokenTests < Test::Unit::TestCase
2
+ describe "Koala GraphAndRestAPI without an access token" do
3
+ before(:each) do
4
+ @api = Koala::Facebook::GraphAndRestAPI.new
5
+ end
6
+
7
+ it_should_behave_like "Koala RestAPI without an access token"
8
+ it_should_behave_like "Koala GraphAPI without an access token"
9
+ end
10
+ end