koala 0.7.1 → 0.7.3
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 +18 -0
- data/LICENSE +22 -0
- data/Manifest +19 -4
- data/Rakefile +2 -1
- data/examples/oauth_playground/Capfile +2 -0
- data/examples/oauth_playground/LICENSE +22 -0
- data/examples/oauth_playground/Rakefile +4 -0
- data/examples/oauth_playground/config/deploy.rb +39 -0
- data/examples/oauth_playground/config/facebook.yml +13 -0
- data/examples/oauth_playground/config.ru +27 -0
- data/examples/oauth_playground/lib/load_facebook.rb +3 -0
- data/examples/oauth_playground/lib/oauth_playground.rb +187 -0
- data/examples/oauth_playground/readme.md +8 -0
- data/examples/oauth_playground/spec/oauth_playground_spec.rb +35 -0
- data/examples/oauth_playground/spec/spec_helper.rb +36 -0
- data/examples/oauth_playground/tmp/restart.txt +0 -0
- data/examples/oauth_playground/views/index.erb +206 -0
- data/examples/oauth_playground/views/layout.erb +39 -0
- data/koala.gemspec +4 -4
- data/lib/{graph_api.rb → koala/graph_api.rb} +4 -4
- data/lib/koala.rb +95 -12
- data/readme.md +4 -1
- data/spec/facebook_data.yml +8 -3
- data/spec/koala/api_base_tests.rb +69 -2
- data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +4 -0
- data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +4 -0
- data/spec/koala/net_http_service_tests.rb +169 -5
- data/spec/koala/oauth/oauth_tests.rb +193 -40
- data/spec/mock_facebook_responses.yml +21 -1
- data/spec/mock_http_service.rb +6 -1
- metadata +27 -11
- /data/lib/{http_services.rb → koala/http_services.rb} +0 -0
- /data/lib/{realtime_updates.rb → koala/realtime_updates.rb} +0 -0
- /data/lib/{rest_api.rb → koala/rest_api.rb} +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<div id="header">
|
|
2
|
+
<h1>Facebook OAuth Playground</h1>
|
|
3
|
+
<h4>Powered by <a href="http://github.com/arsduo/koala" target="_blank">Koala</a></h2>
|
|
4
|
+
<h4>For novelty use only. Remember, the application's secret key is public.</h4>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<style>
|
|
8
|
+
#header {
|
|
9
|
+
text-align: center;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
h1, h2, h3, h4 { margin: 0; }
|
|
13
|
+
|
|
14
|
+
.section {
|
|
15
|
+
border: 1px solid black;
|
|
16
|
+
padding: 10px;
|
|
17
|
+
-moz-border-radius: 9px;
|
|
18
|
+
-webkit-border-radius: 9px;
|
|
19
|
+
margin-bottom: 10px;
|
|
20
|
+
width: 100%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#contents {
|
|
24
|
+
border: 2px solid #CCC;
|
|
25
|
+
border-width: 2px 0;
|
|
26
|
+
background-color: #EEE;
|
|
27
|
+
padding: 14px 3px 8px 8px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#configurationInfo {
|
|
31
|
+
float:left;
|
|
32
|
+
margin-right: 2.5%;
|
|
33
|
+
width: 25%;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#configurationInfo .section {
|
|
37
|
+
background-color: #CCC;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#permissions .header {
|
|
41
|
+
margin-bottom: 5px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#permissions .list {
|
|
45
|
+
height: 380px;
|
|
46
|
+
overflow: auto;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#generatedInfo {
|
|
50
|
+
float: left;
|
|
51
|
+
width: 70%;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#generatedInfo .section {
|
|
55
|
+
border-color: gray;
|
|
56
|
+
background: #DDD;
|
|
57
|
+
overflow: auto;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.clearFloat {
|
|
61
|
+
height: 1px;
|
|
62
|
+
height: 0px;
|
|
63
|
+
clear: both;
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
ul, li { margin: 0; padding: 0; list-style: none; }
|
|
68
|
+
ul { margin: 12px 0; }
|
|
69
|
+
|
|
70
|
+
.code {
|
|
71
|
+
font-family: Courier, fixed;
|
|
72
|
+
font-size: 1.15em;
|
|
73
|
+
width: 100%;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.explanation {
|
|
77
|
+
font-size: 0.9em;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.datumName {
|
|
81
|
+
font-weight: bold;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.permissionType {
|
|
85
|
+
font-style: italic;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
88
|
+
|
|
89
|
+
</style>
|
|
90
|
+
|
|
91
|
+
<div id="contents">
|
|
92
|
+
|
|
93
|
+
<div id="configurationInfo">
|
|
94
|
+
<div id="fbApp" class="section">
|
|
95
|
+
<h3>Facebook App Info</h3>
|
|
96
|
+
<ul>
|
|
97
|
+
<% @app_data.each_pair do |key, value| %>
|
|
98
|
+
<li><span class="datumName"><%= key %>:</span> <%= value %></li>
|
|
99
|
+
<% end %>
|
|
100
|
+
</ul>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div id="permissions" class="section">
|
|
104
|
+
<form method="get">
|
|
105
|
+
<div class="header">
|
|
106
|
+
<h3>Permissions</h3>
|
|
107
|
+
<input type="submit" value="Update permissions" />
|
|
108
|
+
<a href="/">Reset Selection</a>
|
|
109
|
+
<div class="explanation">Choose permissions for the OAuth URL and the fb:login button.</div>
|
|
110
|
+
<% if @fetched_permissions %>
|
|
111
|
+
<div class="permissionType">Showing currently-active permissions</div>
|
|
112
|
+
<div class="explanation">(These were fetched by Koala as the page loaded!)</div>
|
|
113
|
+
<% else %>
|
|
114
|
+
<div class="permissionType">Showing selected permissions</div>
|
|
115
|
+
<% end %>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div class="list">
|
|
119
|
+
<% @available_permissions.each do |permissions| %>
|
|
120
|
+
<h4><%= permissions[:name] %> Permissions</h4>
|
|
121
|
+
<ul>
|
|
122
|
+
<% permissions[:perms].each do |p| %>
|
|
123
|
+
<li>
|
|
124
|
+
<input type="checkbox" id="permission<%= p %>" name="permissions[]" value="<%= p %>" <%= @active_permissions.include?(p) ? "checked='checked'" : "" %> />
|
|
125
|
+
<label for="permission<%= p %>"><%= p %></a> (<a href="#" onclick="FB.api({method: 'auth.revokeExtendedPermission', perm: '<%= p %>'}, function(response) { alert(response) }); return false;">revoke</a>)
|
|
126
|
+
</li>
|
|
127
|
+
<% end %>
|
|
128
|
+
</ul>
|
|
129
|
+
<% end %>
|
|
130
|
+
</div>
|
|
131
|
+
</form>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div id="generatedInfo">
|
|
136
|
+
<div id="oauthURLs" class="section">
|
|
137
|
+
<h3>OAuth URLs</h3>
|
|
138
|
+
<ul>
|
|
139
|
+
<li><span class="datumName">Generate a code:</span> <a href="<%= @oauth.url_for_oauth_code(:permissions => @permissions) %>"><%= @oauth.url_for_oauth_code(:permissions => @permissions) %></a></li>
|
|
140
|
+
<li><span class="datumName">OAuth code:</span> <%= @code || "click on the link above" %></li>
|
|
141
|
+
<li>
|
|
142
|
+
<span class="datumName">Access token:</span> <%= @oauth_access_token || "click on the link above" %>
|
|
143
|
+
<% if @oauth_access_token %><div class="explanation">This was fetched by Koala as the page loaded!</div><% end %>
|
|
144
|
+
</li>
|
|
145
|
+
<li><span class="datumName">Expiration:</span> <%= @expiration || "click on the link above" %></li>
|
|
146
|
+
<li><span class="datumName">Raw access response:</span> <%= @raw_access_response || "click on the link above" %></li>
|
|
147
|
+
<li><span class="datumName">URL for access code:</span>
|
|
148
|
+
<% if @code %>
|
|
149
|
+
<a href="<%= @oauth.url_for_access_token(@code) %>"><%= @oauth.url_for_access_token(@code) %></a>
|
|
150
|
+
<% else %>
|
|
151
|
+
click on the link above
|
|
152
|
+
<% end %>
|
|
153
|
+
</li>
|
|
154
|
+
</ul>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div id="jsLogin" class="section">
|
|
158
|
+
<h3>Javascript Login (e.g. Facebook Connect)</h3>
|
|
159
|
+
<p>
|
|
160
|
+
<fb:login-button onlogin="location.reload()" perms="<%= (@permissions || []).join(",") %>"></fb:login-button>
|
|
161
|
+
<% if @permissions %>
|
|
162
|
+
and prompt for <%= @permissions.join(", ") %>
|
|
163
|
+
<% end %>
|
|
164
|
+
<% if @facebook_cookies %>
|
|
165
|
+
<div class="logout">
|
|
166
|
+
<a href="#" onclick="FB.logout(function() { location.reload() }); return false;">Logout</a>
|
|
167
|
+
</div>
|
|
168
|
+
<% end %>
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div id="cookieInfo" class="section">
|
|
173
|
+
<h3>Cookie info</h3>
|
|
174
|
+
<ul>
|
|
175
|
+
<% if @facebook_cookies %>
|
|
176
|
+
<% @facebook_cookies.each_pair do |key, value| %>
|
|
177
|
+
<li><span class="datumName"><%= key %>:</span> <%= value %></li>
|
|
178
|
+
<% end %>
|
|
179
|
+
<% else %>
|
|
180
|
+
<li>You're not signed in via Javascript. Login below.</li>
|
|
181
|
+
<% end %>
|
|
182
|
+
<li><span class="datumName">Raw hash</span>:
|
|
183
|
+
<div class="code"><%= request.cookies.inspect %></div>
|
|
184
|
+
</li>
|
|
185
|
+
</ul>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div id="koala" class="section">
|
|
189
|
+
<h3>Koala</h3>
|
|
190
|
+
<ul>
|
|
191
|
+
<li><span class="datumName">GraphAPI:</span>
|
|
192
|
+
<div class="code"><%= @access_token ? "@graph = Koala::Facebook::GraphAPI.new(\"#{@access_token}\")" : "sign in above" %></div>
|
|
193
|
+
</li>
|
|
194
|
+
<li><span class="datumName">OAuth:</span>
|
|
195
|
+
<div class="code">@oauth = Koala::Facebook::OAuth.new(<%= @app_data["app_id"] %>, "<%= @app_data["secret_key"] %>", "<%= @app_data["callback_url"] %>")</div>
|
|
196
|
+
</li>
|
|
197
|
+
</ul>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div class="clearFloat"> </div>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<center>
|
|
205
|
+
<h5>Check out the playground's code at <a href="http://github.com/arsduo/oauth_playground">http://github.com/arsduo/oauth_playground</a>!</h5>
|
|
206
|
+
</center>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<title>Facebook OAuth Playground</title>
|
|
4
|
+
<meta name="description" content="Making it easier to play with Facebook's new Graph API and OAuth authentication."></meta>
|
|
5
|
+
<style>
|
|
6
|
+
body {
|
|
7
|
+
font-family: verdana, arial, sans-serif;
|
|
8
|
+
font-size: 12px;
|
|
9
|
+
margin: 0px;
|
|
10
|
+
}
|
|
11
|
+
</style>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
|
|
15
|
+
<script type="text/javascript">
|
|
16
|
+
|
|
17
|
+
var _gaq = _gaq || [];
|
|
18
|
+
_gaq.push(['_setAccount', 'UA-16395421-1']);
|
|
19
|
+
_gaq.push(['_trackPageview']);
|
|
20
|
+
|
|
21
|
+
(function() {
|
|
22
|
+
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
|
23
|
+
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
|
24
|
+
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
|
25
|
+
})();
|
|
26
|
+
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<%= yield %>
|
|
30
|
+
|
|
31
|
+
<div id="fb-root"></div>
|
|
32
|
+
<script src="http://connect.facebook.net/en_US/all.js"></script>
|
|
33
|
+
<script>
|
|
34
|
+
FB.init({ appId: <%= @app_data["app_id"] %>, cookie: true, status: true, xfbml: true });
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
</body>
|
|
38
|
+
|
|
39
|
+
</html>
|
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.7.
|
|
5
|
+
s.version = "0.7.3"
|
|
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-
|
|
9
|
+
s.date = %q{2010-06-22}
|
|
10
10
|
s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, 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.}
|
|
11
11
|
s.email = %q{alex@alexkoppel.com}
|
|
12
|
-
s.extra_rdoc_files = ["CHANGELOG", "lib/
|
|
13
|
-
s.files = ["CHANGELOG", "Manifest", "Rakefile", "init.rb", "lib/
|
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb"]
|
|
13
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest", "Rakefile", "examples/oauth_playground/Capfile", "examples/oauth_playground/LICENSE", "examples/oauth_playground/Rakefile", "examples/oauth_playground/config.ru", "examples/oauth_playground/config/deploy.rb", "examples/oauth_playground/config/facebook.yml", "examples/oauth_playground/lib/load_facebook.rb", "examples/oauth_playground/lib/oauth_playground.rb", "examples/oauth_playground/readme.md", "examples/oauth_playground/spec/oauth_playground_spec.rb", "examples/oauth_playground/spec/spec_helper.rb", "examples/oauth_playground/tmp/restart.txt", "examples/oauth_playground/views/index.erb", "examples/oauth_playground/views/layout.erb", "init.rb", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/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"]
|
|
@@ -47,11 +47,11 @@ module Koala
|
|
|
47
47
|
graph_call("#{id}/#{connection_name}", args)["data"]
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def get_picture(object)
|
|
51
|
-
result = graph_call("#{object}/picture",
|
|
50
|
+
def get_picture(object, args = {})
|
|
51
|
+
result = graph_call("#{object}/picture", args, "get", :http_component => :headers)
|
|
52
52
|
result["Location"]
|
|
53
|
-
end
|
|
54
|
-
|
|
53
|
+
end
|
|
54
|
+
|
|
55
55
|
def put_object(parent_object, connection_name, args = {})
|
|
56
56
|
# Writes the given object to the graph, connected to the given parent.
|
|
57
57
|
#
|
data/lib/koala.rb
CHANGED
|
@@ -6,15 +6,15 @@ require 'rubygems'
|
|
|
6
6
|
require 'json'
|
|
7
7
|
|
|
8
8
|
# include default http services
|
|
9
|
-
require 'http_services'
|
|
9
|
+
require 'koala/http_services'
|
|
10
10
|
|
|
11
11
|
# add Graph API methods
|
|
12
|
-
require 'graph_api'
|
|
12
|
+
require 'koala/graph_api'
|
|
13
13
|
|
|
14
14
|
# add REST API methods
|
|
15
|
-
require 'rest_api'
|
|
15
|
+
require 'koala/rest_api'
|
|
16
16
|
|
|
17
|
-
require 'realtime_updates'
|
|
17
|
+
require 'koala/realtime_updates'
|
|
18
18
|
|
|
19
19
|
module Koala
|
|
20
20
|
|
|
@@ -52,6 +52,11 @@ module Koala
|
|
|
52
52
|
# make the request via the provided service
|
|
53
53
|
result = Koala.make_request(path, args, verb, options)
|
|
54
54
|
|
|
55
|
+
# Check for any 500 errors before parsing the body
|
|
56
|
+
# since we're not guaranteed that the body is valid JSON
|
|
57
|
+
# in the case of a server error
|
|
58
|
+
raise APIError.new({"type" => "HTTP #{result.status.to_s}", "message" => "Response body: #{result.body}"}) if result.status >= 500
|
|
59
|
+
|
|
55
60
|
# Parse the body as JSON and check for errors if provided a mechanism to do so
|
|
56
61
|
# Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
|
|
57
62
|
# and cause JSON.parse to fail -- so we account for that by wrapping the result in []
|
|
@@ -103,11 +108,10 @@ module Koala
|
|
|
103
108
|
@oauth_callback_url = oauth_callback_url
|
|
104
109
|
end
|
|
105
110
|
|
|
106
|
-
def
|
|
111
|
+
def get_user_info_from_cookie(cookie_hash)
|
|
107
112
|
# Parses the cookie set by the official Facebook JavaScript SDK.
|
|
108
113
|
#
|
|
109
|
-
# cookies should be a
|
|
110
|
-
# cookie values.
|
|
114
|
+
# cookies should be a Hash, like the one Rails provides
|
|
111
115
|
#
|
|
112
116
|
# If the user is logged in via Facebook, we return a dictionary with the
|
|
113
117
|
# keys "uid" and "access_token". The former is the user's Facebook ID,
|
|
@@ -132,8 +136,20 @@ module Koala
|
|
|
132
136
|
sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
|
|
133
137
|
end
|
|
134
138
|
end
|
|
139
|
+
alias_method :get_user_info_from_cookies, :get_user_info_from_cookie
|
|
140
|
+
|
|
141
|
+
def get_user_from_cookie(cookies)
|
|
142
|
+
# overload this to be backward compatible with older implementations
|
|
143
|
+
# icky ruby magic, but it's _really_ cool we can do this
|
|
144
|
+
info = get_user_info_from_cookies(cookies)
|
|
145
|
+
string = info["uid"]
|
|
146
|
+
|
|
147
|
+
overload_as_hash(string, info)
|
|
148
|
+
end
|
|
135
149
|
alias_method :get_user_from_cookies, :get_user_from_cookie
|
|
136
150
|
|
|
151
|
+
# URLs
|
|
152
|
+
|
|
137
153
|
def url_for_oauth_code(options = {})
|
|
138
154
|
# for permissions, see http://developers.facebook.com/docs/authentication/permissions
|
|
139
155
|
permissions = options[:permissions]
|
|
@@ -156,18 +172,68 @@ module Koala
|
|
|
156
172
|
raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
|
|
157
173
|
"https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
|
|
158
174
|
end
|
|
159
|
-
|
|
160
|
-
def
|
|
175
|
+
|
|
176
|
+
def get_access_token_info(code)
|
|
161
177
|
# convenience method to get a parsed token from Facebook for a given code
|
|
162
178
|
# should this require an OAuth callback URL?
|
|
163
179
|
get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
|
|
164
180
|
end
|
|
165
181
|
|
|
166
|
-
def
|
|
182
|
+
def get_access_token(code)
|
|
183
|
+
info = get_access_token_info(code)
|
|
184
|
+
# upstream methods will throw errors if needed
|
|
185
|
+
string = info["access_token"]
|
|
186
|
+
|
|
187
|
+
overload_as_hash(string, info)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def get_app_access_token_info
|
|
167
191
|
# convenience method to get a the application's sessionless access token
|
|
168
192
|
get_token_from_server({:type => 'client_cred'}, true)
|
|
169
193
|
end
|
|
170
194
|
|
|
195
|
+
def get_app_access_token
|
|
196
|
+
info = get_app_access_token_info
|
|
197
|
+
string = info["access_token"]
|
|
198
|
+
|
|
199
|
+
overload_as_hash(string, info)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# from session keys
|
|
203
|
+
def get_token_info_from_session_keys(sessions)
|
|
204
|
+
# fetch the OAuth tokens from Facebook
|
|
205
|
+
response = fetch_token_string({
|
|
206
|
+
:type => 'client_cred',
|
|
207
|
+
:sessions => sessions.join(",")
|
|
208
|
+
}, true, "exchange_sessions")
|
|
209
|
+
|
|
210
|
+
# get_token_from_session_key should return an empty body if an empty string or nil is provided
|
|
211
|
+
# if invalid tokens are provided, it returns an array of nulls, which is a valid result
|
|
212
|
+
if response == ""
|
|
213
|
+
raise APIError.new("ArgumentError", "get_token_from_session_key received an error (empty response body) for sessions #{sessions.inspect}!")
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
JSON.parse(response)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def get_tokens_from_session_keys(sessions)
|
|
220
|
+
# get the original hash results
|
|
221
|
+
results = get_token_info_from_session_keys(sessions)
|
|
222
|
+
# now recollect them as backward-compatible strings
|
|
223
|
+
# for easier use
|
|
224
|
+
results.collect do |r|
|
|
225
|
+
string = r["access_token"]
|
|
226
|
+
|
|
227
|
+
overload_as_hash string, r
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def get_token_from_session_key(session)
|
|
232
|
+
# convenience method for a single key
|
|
233
|
+
# gets the overlaoded strings automatically
|
|
234
|
+
get_tokens_from_session_keys([session])[0]
|
|
235
|
+
end
|
|
236
|
+
|
|
171
237
|
protected
|
|
172
238
|
|
|
173
239
|
def get_token_from_server(args, post = false)
|
|
@@ -189,12 +255,29 @@ module Koala
|
|
|
189
255
|
components
|
|
190
256
|
end
|
|
191
257
|
|
|
192
|
-
def fetch_token_string(args, post = false)
|
|
193
|
-
Koala.make_request("oauth
|
|
258
|
+
def fetch_token_string(args, post = false, endpoint = "access_token")
|
|
259
|
+
Koala.make_request("oauth/#{endpoint}", {
|
|
194
260
|
:client_id => @app_id,
|
|
195
261
|
:client_secret => @app_secret
|
|
196
262
|
}.merge!(args), post ? "post" : "get").body
|
|
197
263
|
end
|
|
264
|
+
|
|
265
|
+
# overload the object to be accessible as a hash
|
|
266
|
+
# used to make get_*_token methods backward compatible
|
|
267
|
+
# icky ruby magic, but it's _really_ cool we can do this
|
|
268
|
+
def overload_as_hash(obj, hash)
|
|
269
|
+
command = <<-EOS
|
|
270
|
+
def [](index)
|
|
271
|
+
puts "WARNING: get_app_access_token now provides the access token as a string; use get_app_access_token_info if you want the hash with expirations. Otherwise you no longer need to call [] to get the token itself."
|
|
272
|
+
hash = #{hash.inspect}
|
|
273
|
+
hash[index]
|
|
274
|
+
end
|
|
275
|
+
EOS
|
|
276
|
+
|
|
277
|
+
(class << obj; self; end).class_eval command
|
|
278
|
+
|
|
279
|
+
obj
|
|
280
|
+
end
|
|
198
281
|
end
|
|
199
282
|
end
|
|
200
283
|
|
data/readme.md
CHANGED
|
@@ -34,7 +34,7 @@ We reserve the right to expand the built-in REST API coverage to additional conv
|
|
|
34
34
|
OAuth
|
|
35
35
|
-----
|
|
36
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
|
-
|
|
37
|
+
@oauth = Koala::Facebook::OAuth.new(app_id, code, callback_url)
|
|
38
38
|
|
|
39
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
40
|
@oauth.get_user_from_cookie(cookies)
|
|
@@ -50,6 +50,9 @@ You can also get your application's own access token, which can be used without
|
|
|
50
50
|
|
|
51
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
52
|
|
|
53
|
+
*Exchanging session keys:* Stuck building tab applications on Facebook? Wishing you had an OAuth token so you could use the Graph API? You're in luck! Koala now allows you to exchange session keys for OAuth access tokens:
|
|
54
|
+
@oauth.get_token_from_session_key(session_key)
|
|
55
|
+
@oauth.get_tokens_from_session_keys(array_of_session_keys)
|
|
53
56
|
|
|
54
57
|
Real-time Updates
|
|
55
58
|
-----
|
data/spec/facebook_data.yml
CHANGED
|
@@ -5,14 +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:
|
|
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:
|
|
15
|
-
|
|
14
|
+
code:
|
|
15
|
+
# easiest way to get session keys: use multiple test accounts with the Javascript login at http://oauth.twoalex.com
|
|
16
|
+
session_key:
|
|
17
|
+
multiple_session_keys:
|
|
18
|
+
-
|
|
19
|
+
-
|
|
20
|
+
|
|
16
21
|
# These values will work out of the box
|
|
17
22
|
app_id: 119908831367602
|
|
18
23
|
secret: e45e55a333eec232d4206d2703de1307
|
|
@@ -29,9 +29,45 @@ class ApiBaseTests < Test::Unit::TestCase
|
|
|
29
29
|
service.api('anything')
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
it "should
|
|
32
|
+
it "should get the attribute of a Koala::Response given by the http_component parameter" do
|
|
33
|
+
http_component = :method_name
|
|
34
|
+
|
|
35
|
+
response = mock('Mock KoalaResponse', :body => '', :status => 200)
|
|
36
|
+
response.should_receive(http_component).and_return('')
|
|
37
|
+
|
|
38
|
+
Koala.stub(:make_request).and_return(response)
|
|
39
|
+
|
|
40
|
+
@service.api('anything', 'get', {}, :http_component => http_component)
|
|
41
|
+
end
|
|
33
42
|
|
|
34
|
-
it "should
|
|
43
|
+
it "should return the body of the request as JSON if no http_component is given" do
|
|
44
|
+
response = stub('response', :body => 'body', :status => 200)
|
|
45
|
+
Koala.stub(:make_request).and_return(response)
|
|
46
|
+
|
|
47
|
+
json_body = mock('JSON body')
|
|
48
|
+
JSON.stub(:parse).and_return([json_body])
|
|
49
|
+
|
|
50
|
+
@service.api('anything').should == json_body
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should execute a block with the response body if passed one" do
|
|
54
|
+
body = '{}'
|
|
55
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(200, body, {}))
|
|
56
|
+
|
|
57
|
+
yield_test = mock('Yield Tester')
|
|
58
|
+
yield_test.should_receive(:pass)
|
|
59
|
+
|
|
60
|
+
@service.api('anything') do |arg|
|
|
61
|
+
yield_test.pass
|
|
62
|
+
arg.should == JSON.parse(body)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should raise an API error if the HTTP response code is greater than or equal to 500" do
|
|
67
|
+
Koala.stub(:make_request).and_return(Koala::Response.new(500, 'response body', {}))
|
|
68
|
+
|
|
69
|
+
lambda { @service.api('anything') }.should raise_exception(Koala::Facebook::APIError)
|
|
70
|
+
end
|
|
35
71
|
|
|
36
72
|
it "should handle rogue true/false as responses" do
|
|
37
73
|
Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
|
|
@@ -41,4 +77,35 @@ class ApiBaseTests < Test::Unit::TestCase
|
|
|
41
77
|
@service.api('anything').should be_false
|
|
42
78
|
end
|
|
43
79
|
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
shared_examples_for "methods that return overloaded strings" do
|
|
83
|
+
before :each do
|
|
84
|
+
@key ||= "access_token"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should be overloaded to be backward compatible" do
|
|
88
|
+
@result.respond_to?(:[]).should be_true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should allow hash access to the access token info" do
|
|
92
|
+
@result[@key].should == @result
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should output a deprecation warning when the result is used as a hash" do
|
|
96
|
+
out = nil
|
|
97
|
+
|
|
98
|
+
begin
|
|
99
|
+
# we want to capture the deprecation warning as well as the output
|
|
100
|
+
# credit to http://thinkingdigitally.com/archive/capturing-output-from-puts-in-ruby/ for the technique
|
|
101
|
+
out = StringIO.new
|
|
102
|
+
$stdout = out
|
|
103
|
+
@result[@key]
|
|
104
|
+
ensure
|
|
105
|
+
$stdout = STDOUT
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# ensure we got a warning
|
|
109
|
+
out.should_not be_nil
|
|
110
|
+
end
|
|
44
111
|
end
|
|
@@ -34,6 +34,10 @@ shared_examples_for "Koala GraphAPI without an access token" do
|
|
|
34
34
|
@api.get_picture("chris.baclig").should =~ /http\:\/\//
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
it "should be able to access a user's picture, given a picture type" do
|
|
38
|
+
@api.get_picture("chris.baclig", {:type => 'large'}).should =~ /^http\:\/\//
|
|
39
|
+
end
|
|
40
|
+
|
|
37
41
|
it "should be able to access connections from public Pages" do
|
|
38
42
|
result = @api.get_connections("contextoptional", "likes")
|
|
39
43
|
result.should be_a(Array)
|
|
@@ -31,6 +31,10 @@ it "should get public data about a user" do
|
|
|
31
31
|
@api.get_picture("chris.baclig").should =~ /http\:\/\//
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
it "should be able to access a user's picture, given a picture type" do
|
|
35
|
+
@api.get_picture("chris.baclig", {:type => 'large'}).should =~ /^http\:\/\//
|
|
36
|
+
end
|
|
37
|
+
|
|
34
38
|
it "should be able to access connections from users" do
|
|
35
39
|
result = @api.get_connections("lukeshepard", "likes")
|
|
36
40
|
result.length.should > 0
|