koala 1.5.0 → 1.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/koala/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Koala
2
- VERSION = "1.5.0"
2
+ VERSION = "1.6.0.rc1"
3
3
  end
data/readme.md CHANGED
@@ -13,52 +13,78 @@ Installation
13
13
  ---
14
14
 
15
15
  Easy:
16
-
17
- [sudo|rvm] gem install koala
16
+ ```bash
17
+ [sudo|rvm] gem install koala
18
+ ```
18
19
 
19
20
  Or in Bundler:
20
-
21
- gem "koala"
21
+ ```ruby
22
+ gem "koala"
23
+ ```
22
24
 
23
25
  Graph API
24
26
  ----
25
- The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
27
+ The Graph API is the simple, slick new interface to Facebook's data.
28
+ Using it with Koala is quite straightforward. First, you'll need an access token, which you can get through
29
+ Facebook's [Graph API Explorer](https://developers.facebook.com/tools/explorer) (click on 'Get Access Token').
30
+ Then, go exploring:
26
31
 
27
- @graph = Koala::Facebook::API.new(oauth_access_token)
28
- # in 1.1 or earlier, use GraphAPI instead of API
32
+ ```ruby
33
+ @graph = Koala::Facebook::API.new(oauth_access_token)
29
34
 
30
- profile = @graph.get_object("me")
31
- friends = @graph.get_connections("me", "friends")
32
- @graph.put_connections("me", "feed", :message => "I am writing on my wall!")
35
+ profile = @graph.get_object("me")
36
+ friends = @graph.get_connections("me", "friends")
37
+ @graph.put_connections("me", "feed", :message => "I am writing on my wall!")
33
38
 
34
- # three-part queries are easy too!
35
- @graph.get_connections("me", "mutualfriends/#{friend_id}")
39
+ # three-part queries are easy too!
40
+ @graph.get_connections("me", "mutualfriends/#{friend_id}")
36
41
 
37
- # you can even use the new Timeline API
38
- # see https://developers.facebook.com/docs/beta/opengraph/tutorial/
39
- @graph.put_connections("me", "namespace:action", :object => object_url)
42
+ # you can even use the new Timeline API
43
+ # see https://developers.facebook.com/docs/beta/opengraph/tutorial/
44
+ @graph.put_connections("me", "namespace:action", :object => object_url)
45
+ ```
40
46
 
41
47
  The response of most requests is the JSON data returned from the Facebook servers as a Hash.
42
48
 
43
- 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:
49
+ When retrieving data that returns an array of results (for example, when calling `API#get_connections` or `API#search`)
50
+ a GraphCollection object will be returned, which makes it easy to page through the results:
44
51
 
45
- # Returns the feed items for the currently logged-in user as a GraphCollection
46
- feed = @graph.get_connections("me", "feed")
47
- feed.each {|f| do_something_with_item(f) } # it's a subclass of Array
48
- next_feed = feed.next_page
52
+ ```ruby
53
+ # Returns the feed items for the currently logged-in user as a GraphCollection
54
+ feed = @graph.get_connections("me", "feed")
55
+ feed.each {|f| do_something_with_item(f) } # it's a subclass of Array
56
+ next_feed = feed.next_page
49
57
 
50
- # You can also get an array describing the URL for the next page: [path, arguments]
51
- # This is useful for storing page state across multiple browser requests
52
- next_page_params = feed.next_page_params
53
- page = @graph.get_page(next_page_params)
58
+ # You can also get an array describing the URL for the next page: [path, arguments]
59
+ # This is useful for storing page state across multiple browser requests
60
+ next_page_params = feed.next_page_params
61
+ page = @graph.get_page(next_page_params)
62
+ ```
54
63
 
55
64
  You can also make multiple calls at once using Facebook's batch API:
65
+ ```ruby
66
+ # Returns an array of results as if they were called non-batch
67
+ @graph.batch do |batch_api|
68
+ batch_api.get_object('me')
69
+ batch_api.put_wall_post('Making a post in a batch.')
70
+ end
71
+ ```
72
+
73
+ You can pass a "post-processing" block to each of Koala's Graph API methods. This is handy for two reasons:
74
+
75
+ 1. You can modify the result returned by the Graph API method:
76
+
77
+ education = @graph.get_object("me") { |data| data['education'] }
78
+ # returned value only contains the "education" portion of the profile
56
79
 
57
- # Returns an array of results as if they were called non-batch
58
- @graph.batch do |batch_api|
59
- batch_api.get_object('me')
60
- batch_api.put_wall_post('Making a post in a batch.')
61
- end
80
+ 2. You can consume the data in place which is particularly useful in the batch case, so you don't have to pull
81
+ the results apart from a long list of array entries:
82
+
83
+ @graph.batch do |batch_api|
84
+ # Assuming you have database fields "about_me" and "photos"
85
+ batch_api.get_object('me') {|me| self.about_me = me }
86
+ batch_api.get_connections('me', 'photos') {|photos| self.photos = photos }
87
+ end
62
88
 
63
89
  Check out the wiki for more details and examples.
64
90
 
@@ -67,55 +93,56 @@ The REST API
67
93
  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.
68
94
 
69
95
  Fortunately, Koala supports the REST API using the very same interface; to use this, instantiate an API:
96
+ ```ruby
97
+ @rest = Koala::Facebook::API.new(oauth_access_token)
70
98
 
71
- @rest = Koala::Facebook::API.new(oauth_access_token)
72
- # in 1.1 or earlier, use RestAPI instead of API
73
-
74
- @rest.fql_query(my_fql_query) # convenience method
75
- @rest.fql_multiquery(fql_query_hash) # convenience method
76
- @rest.rest_call("stream.publish", arguments_hash) # generic version
99
+ @rest.fql_query(my_fql_query) # convenience method
100
+ @rest.fql_multiquery(fql_query_hash) # convenience method
101
+ @rest.rest_call("stream.publish", arguments_hash) # generic version
102
+ ```
77
103
 
78
104
  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.
105
+ ```ruby
106
+ @api = Koala::Facebook::API.new(oauth_access_token)
79
107
 
80
- @api = Koala::Facebook::API.new(oauth_access_token)
81
- # in 1.1 or earlier, use GraphAndRestAPI instead of API
82
-
83
- @api = Koala::Facebook::API.new(oauth_access_token)
84
- fql = @api.fql_query(my_fql_query)
85
- @api.put_wall_post(process_result(fql))
86
-
108
+ @api = Koala::Facebook::API.new(oauth_access_token)
109
+ fql = @api.fql_query(my_fql_query)
110
+ @api.put_wall_post(process_result(fql))
111
+ ```
87
112
 
88
113
  OAuth
89
114
  -----
90
115
  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
-
92
- @oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
116
+ ```ruby
117
+ @oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
118
+ ```
93
119
 
94
120
  If your application uses Koala and the Facebook [JavaScript SDK](http://github.com/facebook/facebook-js-sdk) (formerly Facebook Connect), you can use the OAuth class to parse the cookies:
95
-
96
- @oauth.get_user_from_cookies(cookies) # gets the user's ID
97
- @oauth.get_user_info_from_cookies(cookies) # parses and returns the entire hash
98
-
121
+ ```ruby
122
+ @oauth.get_user_from_cookies(cookies) # gets the user's ID
123
+ @oauth.get_user_info_from_cookies(cookies) # parses and returns the entire hash
124
+ ```
99
125
  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
-
101
- # generate authenticating URL
102
- @oauth.url_for_oauth_code
103
- # fetch the access token once you have the code
104
- @oauth.get_access_token(code)
126
+ ```ruby
127
+ # generate authenticating URL
128
+ @oauth.url_for_oauth_code
129
+ # fetch the access token once you have the code
130
+ @oauth.get_access_token(code)
131
+ ```
105
132
 
106
133
  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
-
108
- @oauth.get_app_access_token
109
-
134
+ ```ruby
135
+ @oauth.get_app_access_token
136
+ ```
110
137
  For those building apps on Facebook, parsing signed requests is simple:
111
-
112
- @oauth.parse_signed_request(signed_request_string)
113
-
138
+ ```ruby
139
+ @oauth.parse_signed_request(signed_request_string)
140
+ ```
114
141
  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
- @oauth.get_token_from_session_key(session_key)
117
- @oauth.get_tokens_from_session_keys(array_of_session_keys)
118
-
142
+ ```ruby
143
+ @oauth.get_token_from_session_key(session_key)
144
+ @oauth.get_tokens_from_session_keys(array_of_session_keys)
145
+ ```
119
146
  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).
120
147
 
121
148
  Real-time Updates
@@ -123,50 +150,50 @@ Real-time Updates
123
150
  Sometimes, reaching out to Facebook is a pain -- let it reach out to you instead. The Graph API allows your application to subscribe to real-time updates for certain objects in the graph; check the [official Facebook documentation](http://developers.facebook.com/docs/api/realtime) for more details on what objects you can subscribe to and what limitations may apply.
124
151
 
125
152
  Koala makes it easy to interact with your applications using the RealtimeUpdates class:
126
-
127
- @updates = Koala::Facebook::RealtimeUpdates.new(:app_id => app_id, :secret => secret)
128
-
153
+ ```ruby
154
+ @updates = Koala::Facebook::RealtimeUpdates.new(:app_id => app_id, :secret => secret)
155
+ ```
129
156
  You can do just about anything with your real-time update subscriptions using the RealtimeUpdates class:
157
+ ```ruby
158
+ # Add/modify a subscription to updates for when the first_name or last_name fields of any of your users is changed
159
+ @updates.subscribe("user", "first_name, last_name", callback_url, verify_token)
130
160
 
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_url, verify_token)
133
-
134
- # Get an array of your current subscriptions (one hash for each object you've subscribed to)
135
- @updates.list_subscriptions
136
-
137
- # Unsubscribe from updates for an object
138
- @updates.unsubscribe("user")
161
+ # Get an array of your current subscriptions (one hash for each object you've subscribed to)
162
+ @updates.list_subscriptions
139
163
 
164
+ # Unsubscribe from updates for an object
165
+ @updates.unsubscribe("user")
166
+ ```
140
167
  And to top it all off, RealtimeUpdates provides a static method to respond to Facebook servers' verification of your callback URLs:
141
-
142
- # Returns the hub.challenge parameter in params if the verify token in params matches verify_token
143
- Koala::Facebook::RealtimeUpdates.meet_challenge(params, your_verify_token)
144
-
168
+ ```ruby
169
+ # Returns the hub.challenge parameter in params if the verify token in params matches verify_token
170
+ Koala::Facebook::RealtimeUpdates.meet_challenge(params, your_verify_token)
171
+ ```
145
172
  For more information about meet_challenge and the RealtimeUpdates class, check out the Real-Time Updates page on the wiki.
146
173
 
147
174
  Test Users
148
175
  -----
149
176
 
150
177
  We also support the test users API, allowing you to conjure up fake users and command them to do your bidding using the Graph or REST API:
151
-
152
- @test_users = Koala::Facebook::TestUsers.new(:app_id => id, :secret => secret)
153
- user = @test_users.create(is_app_installed, desired_permissions)
154
- user_graph_api = Koala::Facebook::API.new(user["access_token"])
155
- # or, if you want to make a whole community:
156
- @test_users.create_network(network_size, is_app_installed, common_permissions)
157
-
178
+ ```ruby
179
+ @test_users = Koala::Facebook::TestUsers.new(:app_id => id, :secret => secret)
180
+ user = @test_users.create(is_app_installed, desired_permissions)
181
+ user_graph_api = Koala::Facebook::API.new(user["access_token"])
182
+ # or, if you want to make a whole community:
183
+ @test_users.create_network(network_size, is_app_installed, common_permissions)
184
+ ```
158
185
  Talking to Facebook
159
186
  -----
160
187
 
161
188
  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
-
189
+ ```ruby
190
+ # Set an SSL certificate to avoid Net::HTTP errors
191
+ Koala.http_service.http_options = {
192
+ :ssl => { :ca_path => "/etc/ssl/certs" }
193
+ }
194
+ # or on a per-request basis
195
+ @api.get_object(id, args_hash, { :timeout => 10 })
196
+ ```
170
197
  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
198
 
172
199
  See examples, ask questions
@@ -184,16 +211,16 @@ Testing
184
211
  -----
185
212
 
186
213
  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:
187
-
188
- # From anywhere in the project directory:
189
- bundle exec rake spec
190
-
214
+ ```bash
215
+ # From anywhere in the project directory:
216
+ bundle exec rake spec
217
+ ```
191
218
 
192
219
  You can also run live tests against Facebook's servers:
193
-
194
- # Again from anywhere in the project directory:
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
198
-
220
+ ```bash
221
+ # Again from anywhere in the project directory:
222
+ LIVE=true bundle exec rake spec
223
+ # you can also test against Facebook's beta tier
224
+ LIVE=true BETA=true bundle exec rake spec
225
+ ```
199
226
  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.
@@ -65,15 +65,15 @@ describe "Koala::Facebook::API" do
65
65
  end
66
66
 
67
67
  it "executes an error checking block if provided" do
68
- body = '{}'
69
- Koala.stub(:make_request).and_return(Koala::HTTPService::Response.new(200, body, {}))
68
+ response = Koala::HTTPService::Response.new(200, '{}', {})
69
+ Koala.stub(:make_request).and_return(response)
70
70
 
71
71
  yield_test = mock('Yield Tester')
72
72
  yield_test.should_receive(:pass)
73
73
 
74
74
  @service.api('anything', {}, "get") do |arg|
75
75
  yield_test.pass
76
- arg.should == MultiJson.load(body)
76
+ arg.should == response
77
77
  end
78
78
  end
79
79
 
@@ -1,40 +1,76 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Koala::Facebook::APIError do
4
- it "is a StandardError" do
5
- Koala::Facebook::APIError.new.should be_a(StandardError)
4
+ it "is a Koala::KoalaError" do
5
+ Koala::Facebook::APIError.new(nil, nil).should be_a(Koala::KoalaError)
6
6
  end
7
7
 
8
- [:fb_error_type, :fb_error_code, :fb_error_message, :raw_response].each do |accessor|
8
+ [:fb_error_type, :fb_error_code, :fb_error_subcode, :fb_error_message, :http_status, :response_body].each do |accessor|
9
9
  it "has an accessor for #{accessor}" do
10
10
  Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(accessor)
11
11
  Koala::Facebook::APIError.instance_methods.map(&:to_sym).should include(:"#{accessor}=")
12
12
  end
13
13
  end
14
14
 
15
- it "sets raw_response to the provided error details" do
16
- error_response = {"type" => "foo", "other_details" => "bar"}
17
- Koala::Facebook::APIError.new(error_response).raw_response.should == error_response
15
+ it "sets http_status to the provided status" do
16
+ error_response = '{ "error": {"type": "foo", "other_details": "bar"} }'
17
+ Koala::Facebook::APIError.new(400, error_response).response_body.should == error_response
18
+ end
19
+
20
+ it "sets response_body to the provided response body" do
21
+ Koala::Facebook::APIError.new(400, '').http_status.should == 400
22
+ end
23
+
24
+ context "with an error_info hash" do
25
+ let(:error) {
26
+ error_info = {
27
+ 'type' => 'type',
28
+ 'message' => 'message',
29
+ 'code' => 1,
30
+ 'error_subcode' => 'subcode'
31
+ }
32
+ Koala::Facebook::APIError.new(400, '', error_info)
33
+ }
34
+
35
+ {
36
+ :fb_error_type => 'type',
37
+ :fb_error_message => 'message',
38
+ :fb_error_code => 1,
39
+ :fb_error_subcode => 'subcode'
40
+ }.each_pair do |accessor, value|
41
+ it "sets #{accessor} to #{value}" do
42
+ error.send(accessor).should == value
43
+ end
44
+ end
45
+
46
+ it "sets the error message \"type: error_info['type'], code: error_info['code'], error_subcode: error_info['error_subcode'], message: error_info['message'] [HTTP http_status]\"" do
47
+ error.message.should == "type: type, code: 1, error_subcode: subcode, message: message [HTTP 400]"
48
+ end
18
49
  end
19
50
 
20
- {
21
- :fb_error_type => 'type',
22
- :fb_error_message => 'message',
23
- :fb_error_code => 'code'
24
- }.each_pair do |accessor, key|
25
- it "sets #{accessor} to details['#{key}']" do
26
- value = "foo"
27
- Koala::Facebook::APIError.new(key => value).send(accessor).should == value
51
+ context "with an error_info string" do
52
+ it "sets the error message \"error_info [HTTP http_status]\"" do
53
+ error_info = "Facebook is down."
54
+ error = Koala::Facebook::APIError.new(400, '', error_info)
55
+ error.message.should == "Facebook is down. [HTTP 400]"
28
56
  end
29
57
  end
30
58
 
31
- it "sets the error message details['type']: details['message']" do
32
- type = "foo"
33
- message = "bar"
34
- error = Koala::Facebook::APIError.new("type" => type, "message" => message)
35
- error.message.should =~ /#{type}/
36
- error.message.should =~ /#{message}/
59
+ context "with no error_info and a response_body containing error JSON" do
60
+ it "should extract the error info from the response body" do
61
+ response_body = '{ "error": { "type": "type", "message": "message", "code": 1, "error_subcode": "subcode" } }'
62
+ error = Koala::Facebook::APIError.new(400, response_body)
63
+ {
64
+ :fb_error_type => 'type',
65
+ :fb_error_message => 'message',
66
+ :fb_error_code => 1,
67
+ :fb_error_subcode => 'subcode'
68
+ }.each_pair do |accessor, value|
69
+ error.send(accessor).should == value
70
+ end
71
+ end
37
72
  end
73
+
38
74
  end
39
75
 
40
76
  describe Koala::KoalaError do
@@ -43,3 +79,38 @@ describe Koala::KoalaError do
43
79
  end
44
80
  end
45
81
 
82
+ describe Koala::Facebook::OAuthSignatureError do
83
+ it "is a Koala::KoalaError" do
84
+ Koala::KoalaError.new.should be_a(Koala::KoalaError)
85
+ end
86
+ end
87
+
88
+ describe Koala::Facebook::BadFacebookResponse do
89
+ it "is a Koala::Facebook::APIError" do
90
+ Koala::Facebook::BadFacebookResponse.new(nil, nil).should be_a(Koala::Facebook::APIError)
91
+ end
92
+ end
93
+
94
+ describe Koala::Facebook::OAuthTokenRequestError do
95
+ it "is a Koala::Facebook::APIError" do
96
+ Koala::Facebook::OAuthTokenRequestError.new(nil, nil).should be_a(Koala::Facebook::APIError)
97
+ end
98
+ end
99
+
100
+ describe Koala::Facebook::ServerError do
101
+ it "is a Koala::Facebook::APIError" do
102
+ Koala::Facebook::ServerError.new(nil, nil).should be_a(Koala::Facebook::APIError)
103
+ end
104
+ end
105
+
106
+ describe Koala::Facebook::ClientError do
107
+ it "is a Koala::Facebook::APIError" do
108
+ Koala::Facebook::ClientError.new(nil, nil).should be_a(Koala::Facebook::APIError)
109
+ end
110
+ end
111
+
112
+ describe Koala::Facebook::AuthenticationError do
113
+ it "is a Koala::Facebook::ClientError" do
114
+ Koala::Facebook::AuthenticationError.new(nil, nil).should be_a(Koala::Facebook::ClientError)
115
+ end
116
+ end