koala 0.4 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,5 +1,12 @@
1
+ v0.4.1
2
+ -- Encapsulated GraphAPI and OAuth classes in the Koala::Facebook module for clarity (and to avoid claiming the global Facebook class)
3
+ -- Moved make_request method to Koala class from GraphAPI instance (for use by future OAuth class functionality)
4
+ -- Renamed request method to api for consistancy with Javascript library
5
+ -- Updated tests and readme
6
+
1
7
  v0.4.0
2
8
  -- Adopted the Koala name
9
+ -- Updated readme and tests
3
10
  -- Fixed cookie verification bug for non-expiring OAuth tokens
4
11
 
5
12
  v0.3.1
data/Rakefile CHANGED
@@ -4,12 +4,12 @@ require 'rake'
4
4
  require 'echoe'
5
5
 
6
6
  # gem management
7
- Echoe.new('koala', '0.4') do |p|
7
+ Echoe.new('koala', '0.4.1') 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. 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/ruby-sdk"
11
11
  p.author = ["Alex Koppel", "Rafi Jacoby", "Context Optional"]
12
12
  p.email = "alex@alexkoppel.com"
13
- p.ignore_pattern = ["tmp/*", "script/*"]
13
+ p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"]
14
14
  p.development_dependencies = []
15
15
  end
data/koala.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{koala}
5
- s.version = "0.4"
5
+ s.version = "0.4.1"
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, Rafi Jacoby, Context Optional"]
9
- s.date = %q{2010-05-01}
9
+ s.date = %q{2010-05-03}
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. 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
12
  s.extra_rdoc_files = ["CHANGELOG", "lib/http_services.rb", "lib/koala.rb"]
data/lib/http_services.rb CHANGED
@@ -1,60 +1,58 @@
1
1
  module Koala
2
- class GraphAPI
3
- module NetHTTPService
4
- # this service uses Net::HTTP to send requests to the graph
5
- def self.included(base)
6
- base.class_eval do
7
- require 'net/http' unless defined?(Net::HTTP)
8
- require 'net/https'
9
- end
10
- end
11
-
12
- def make_request(path, args, verb)
13
- # We translate args to a valid query string. If post is specified,
14
- # we send a POST request to the given path with the given arguments.
2
+ module NetHTTPService
3
+ # this service uses Net::HTTP to send requests to the graph
4
+ def self.included(base)
5
+ base.class_eval do
6
+ require 'net/http' unless defined?(Net::HTTP)
7
+ require 'net/https'
15
8
 
16
- # if the verb isn't get or post, send it as a post argument
17
- args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
9
+ def self.make_request(path, args, verb)
10
+ # We translate args to a valid query string. If post is specified,
11
+ # we send a POST request to the given path with the given arguments.
18
12
 
19
- http = Net::HTTP.new(FACEBOOK_GRAPH_SERVER, 443)
20
- http.use_ssl = true
21
- # we turn off certificate validation to avoid the
22
- # "warning: peer certificate won't be verified in this SSL session" warning
23
- # not sure if this is the right way to handle it
24
- # see http://redcorundum.blogspot.com/2008/03/ssl-certificates-and-nethttps.html
25
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
13
+ # if the verb isn't get or post, send it as a post argument
14
+ args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
26
15
 
27
- result = http.start { |http|
28
- response, body = (verb == "post" ? http.post(path, encode_params(args)) : http.get("#{path}?#{encode_params(args)}"))
29
- body
30
- }
31
- end
16
+ http = Net::HTTP.new(Facebook::GRAPH_SERVER, 443)
17
+ http.use_ssl = true
18
+ # we turn off certificate validation to avoid the
19
+ # "warning: peer certificate won't be verified in this SSL session" warning
20
+ # not sure if this is the right way to handle it
21
+ # see http://redcorundum.blogspot.com/2008/03/ssl-certificates-and-nethttps.html
22
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
23
+
24
+ result = http.start { |http|
25
+ response, body = (verb == "post" ? http.post(path, encode_params(args)) : http.get("#{path}?#{encode_params(args)}"))
26
+ body
27
+ }
28
+ end
32
29
 
33
- protected
34
- def encode_params(param_hash)
35
- # TODO investigating whether a built-in method handles this
36
- # if no hash (e.g. no auth token) return empty string
37
- ((param_hash || {}).collect do |key_and_value|
38
- key_and_value[1] = key_and_value[1].to_json if key_and_value[1].class != String
39
- "#{key_and_value[0].to_s}=#{CGI.escape key_and_value[1]}"
40
- end).join("&")
30
+ protected
31
+ def self.encode_params(param_hash)
32
+ # TODO investigating whether a built-in method handles this
33
+ # if no hash (e.g. no auth token) return empty string
34
+ ((param_hash || {}).collect do |key_and_value|
35
+ key_and_value[1] = key_and_value[1].to_json if key_and_value[1].class != String
36
+ "#{key_and_value[0].to_s}=#{CGI.escape key_and_value[1]}"
37
+ end).join("&")
38
+ end
41
39
  end
42
40
  end
41
+ end
43
42
 
44
- module TyphoeusService
45
- # this service uses Typhoeus to send requests to the graph
46
- def self.included(base)
47
- base.class_eval do
48
- require 'typhoeus' unless defined?(Typhoeus)
49
- include Typhoeus
50
- end
51
- end
43
+ module TyphoeusService
44
+ # this service uses Typhoeus to send requests to the graph
45
+ def self.included(base)
46
+ base.class_eval do
47
+ require 'typhoeus' unless defined?(Typhoeus)
48
+ include Typhoeus
52
49
 
53
- def make_request(path, args, verb)
54
- # if the verb isn't get or post, send it as a post argument
55
- args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
56
- self.class.send(verb, "https://#{FACEBOOK_GRAPH_SERVER}/#{path}", :params => args).body
57
- end
50
+ def self.make_request(path, args, verb)
51
+ # if the verb isn't get or post, send it as a post argument
52
+ args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
53
+ self.send(verb, "https://#{Facebook::GRAPH_SERVER}/#{path}", :params => args).body
54
+ end
55
+ end
58
56
  end
59
57
  end
60
58
  end
data/lib/koala.rb CHANGED
@@ -9,299 +9,240 @@ require 'json'
9
9
  require 'http_services'
10
10
 
11
11
  module Koala
12
- # Ruby client library for the Facebook Platform.
13
- # Copyright 2010 Facebook
14
- # Adapted from the Python library by Alex Koppel, Rafi Jacoby, and the team at Context Optional
15
- #
16
- # Licensed under the Apache License, Version 2.0 (the "License"); you may
17
- # not use this file except in compliance with the License. You may obtain
18
- # a copy of the License at
19
- # http://www.apache.org/licenses/LICENSE-2.0
20
- #
21
- # Unless required by applicable law or agreed to in writing, software
22
- # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
23
- # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
24
- # License for the specific language governing permissions and limitations
25
- # under the License.
26
- #
27
- # This client library is designed to support the Graph API and the official
28
- # Facebook JavaScript SDK, which is the canonical way to implement
29
- # Facebook authentication. Read more about the Graph API at
30
- # http://developers.facebook.com/docs/api. You can download the Facebook
31
- # JavaScript SDK at http://github.com/facebook/connect-js/.
12
+
13
+ module Facebook
14
+ # Ruby client library for the Facebook Platform.
15
+ # Copyright 2010 Facebook
16
+ # Adapted from the Python library by Alex Koppel, Rafi Jacoby, and the team at Context Optional
17
+ #
18
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
19
+ # not use this file except in compliance with the License. You may obtain
20
+ # a copy of the License at
21
+ # http://www.apache.org/licenses/LICENSE-2.0
22
+ #
23
+ # Unless required by applicable law or agreed to in writing, software
24
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
25
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
26
+ # License for the specific language governing permissions and limitations
27
+ # under the License.
28
+ #
29
+ # This client library is designed to support the Graph API and the official
30
+ # Facebook JavaScript SDK, which is the canonical way to implement
31
+ # Facebook authentication. Read more about the Graph API at
32
+ # http://developers.facebook.com/docs/api. You can download the Facebook
33
+ # JavaScript SDK at http://github.com/facebook/connect-js/.
32
34
 
33
- FACEBOOK_GRAPH_SERVER = "graph.facebook.com"
35
+ GRAPH_SERVER = "graph.facebook.com"
34
36
 
35
- class GraphAPI
36
- # A client for the Facebook Graph API.
37
- #
38
- # See http://developers.facebook.com/docs/api for complete documentation
39
- # for the API.
40
- #
41
- # The Graph API is made up of the objects in Facebook (e.g., people, pages,
42
- # events, photos) and the connections between them (e.g., friends,
43
- # photo tags, and event RSVPs). This client provides access to those
44
- # primitive types in a generic way. For example, given an OAuth access
45
- # token, this will fetch the profile of the active user and the list
46
- # of the user's friends:
47
- #
48
- # graph = Facebook::GraphAPI.new(access_token)
49
- # user = graph.get_object("me")
50
- # friends = graph.get_connections(user["id"], "friends")
51
- #
52
- # You can see a list of all of the objects and connections supported
53
- # by the API at http://developers.facebook.com/docs/reference/api/.
54
- #
55
- # You can obtain an access token via OAuth or by using the Facebook
56
- # JavaScript SDK. See http://developers.facebook.com/docs/authentication/
57
- # for details.
58
- #
59
- # If you are using the JavaScript SDK, you can use the
60
- # Facebook::get_user_from_cookie() method below to get the OAuth access token
61
- # for the active user from the cookie saved by the SDK.
62
-
63
- # initialize with an access token
64
- def initialize(access_token = nil)
65
- @access_token = access_token
66
- end
67
-
68
- def get_object(id, args = {})
69
- # Fetchs the given object from the graph.
70
- request(id, args)
71
- end
72
-
73
- def get_objects(ids, args = {})
74
- # Fetchs all of the given object from the graph.
75
- # We return a map from ID to object. If any of the IDs are invalid,
76
- # we raise an exception.
77
- request("", args.merge("ids" => ids.join(",")))
78
- end
79
-
80
- def get_connections(id, connection_name, args = {})
81
- # Fetchs the connections for given object.
82
- request("#{id}/#{connection_name}", args)
83
- end
84
-
85
- def put_object(parent_object, connection_name, args = {})
86
- # Writes the given object to the graph, connected to the given parent.
37
+ class GraphAPI
38
+ # A client for the Facebook Graph API.
87
39
  #
88
- # For example,
40
+ # See http://developers.facebook.com/docs/api for complete documentation
41
+ # for the API.
89
42
  #
90
- # graph.put_object("me", "feed", :message => "Hello, world")
43
+ # The Graph API is made up of the objects in Facebook (e.g., people, pages,
44
+ # events, photos) and the connections between them (e.g., friends,
45
+ # photo tags, and event RSVPs). This client provides access to those
46
+ # primitive types in a generic way. For example, given an OAuth access
47
+ # token, this will fetch the profile of the active user and the list
48
+ # of the user's friends:
91
49
  #
92
- # writes "Hello, world" to the active user's wall. Likewise, this
93
- # will comment on a the first post of the active user's feed:
50
+ # graph = Koala::Facebook::GraphAPI.new(access_token)
51
+ # user = graph.get_object("me")
52
+ # friends = graph.get_connections(user["id"], "friends")
94
53
  #
95
- # feed = graph.get_connections("me", "feed")
96
- # post = feed["data"][0]
97
- # graph.put_object(post["id"], "comments", :message => "First!")
54
+ # You can see a list of all of the objects and connections supported
55
+ # by the API at http://developers.facebook.com/docs/reference/api/.
98
56
  #
99
- # See http://developers.facebook.com/docs/api#publishing for all of
100
- # the supported writeable objects.
57
+ # You can obtain an access token via OAuth or by using the Facebook
58
+ # JavaScript SDK. See http://developers.facebook.com/docs/authentication/
59
+ # for details.
101
60
  #
102
- # Most write operations require extended permissions. For example,
103
- # publishing wall posts requires the "publish_stream" permission. See
104
- # http://developers.facebook.com/docs/authentication/ for details about
105
- # extended permissions.
61
+ # If you are using the JavaScript SDK, you can use the
62
+ # Koala::Facebook::OAuth.get_user_from_cookie() method below to get the OAuth access token
63
+ # for the active user from the cookie saved by the SDK.
64
+
65
+ # initialize with an access token
66
+ def initialize(access_token = nil)
67
+ @access_token = access_token
68
+ end
69
+
70
+ def get_object(id, args = {})
71
+ # Fetchs the given object from the graph.
72
+ api(id, args)
73
+ end
74
+
75
+ def get_objects(ids, args = {})
76
+ # Fetchs all of the given object from the graph.
77
+ # We return a map from ID to object. If any of the IDs are invalid,
78
+ # we raise an exception.
79
+ api("", args.merge("ids" => ids.join(",")))
80
+ end
81
+
82
+ def get_connections(id, connection_name, args = {})
83
+ # Fetchs the connections for given object.
84
+ api("#{id}/#{connection_name}", args)
85
+ end
86
+
87
+ def put_object(parent_object, connection_name, args = {})
88
+ # Writes the given object to the graph, connected to the given parent.
89
+ #
90
+ # For example,
91
+ #
92
+ # graph.put_object("me", "feed", :message => "Hello, world")
93
+ #
94
+ # writes "Hello, world" to the active user's wall. Likewise, this
95
+ # will comment on a the first post of the active user's feed:
96
+ #
97
+ # feed = graph.get_connections("me", "feed")
98
+ # post = feed["data"][0]
99
+ # graph.put_object(post["id"], "comments", :message => "First!")
100
+ #
101
+ # See http://developers.facebook.com/docs/api#publishing for all of
102
+ # the supported writeable objects.
103
+ #
104
+ # Most write operations require extended permissions. For example,
105
+ # publishing wall posts requires the "publish_stream" permission. See
106
+ # http://developers.facebook.com/docs/authentication/ for details about
107
+ # extended permissions.
106
108
 
107
- raise GraphAPIError.new(nil, "Write operations require an access token") unless @access_token
108
- request("#{parent_object}/#{connection_name}", args, "post")
109
- end
109
+ raise GraphAPIError.new(nil, "Write operations require an access token") unless @access_token
110
+ api("#{parent_object}/#{connection_name}", args, "post")
111
+ end
110
112
 
111
- def put_wall_post(message, attachment = {}, profile_id = "me")
112
- # Writes a wall post to the given profile's wall.
113
- #
114
- # We default to writing to the authenticated user's wall if no
115
- # profile_id is specified.
116
- #
117
- # attachment adds a structured attachment to the status message being
118
- # posted to the Wall. It should be a dictionary of the form:
119
- #
120
- # {"name": "Link name"
121
- # "link": "http://www.example.com/",
122
- # "caption": "{*actor*} posted a new review",
123
- # "description": "This is a longer description of the attachment",
124
- # "picture": "http://www.example.com/thumbnail.jpg"}
113
+ def put_wall_post(message, attachment = {}, profile_id = "me")
114
+ # Writes a wall post to the given profile's wall.
115
+ #
116
+ # We default to writing to the authenticated user's wall if no
117
+ # profile_id is specified.
118
+ #
119
+ # attachment adds a structured attachment to the status message being
120
+ # posted to the Wall. It should be a dictionary of the form:
121
+ #
122
+ # {"name": "Link name"
123
+ # "link": "http://www.example.com/",
124
+ # "caption": "{*actor*} posted a new review",
125
+ # "description": "This is a longer description of the attachment",
126
+ # "picture": "http://www.example.com/thumbnail.jpg"}
125
127
 
126
- self.put_object(profile_id, "feed", attachment.merge({:message => message}))
127
- end
128
+ self.put_object(profile_id, "feed", attachment.merge({:message => message}))
129
+ end
128
130
 
129
- def put_comment(object_id, message)
130
- # Writes the given comment on the given post.
131
- self.put_object(object_id, "comments", {:message => message})
132
- end
131
+ def put_comment(object_id, message)
132
+ # Writes the given comment on the given post.
133
+ self.put_object(object_id, "comments", {:message => message})
134
+ end
133
135
 
134
- def put_like(object_id)
135
- # Likes the given post.
136
- self.put_object(object_id, "likes")
137
- end
136
+ def put_like(object_id)
137
+ # Likes the given post.
138
+ self.put_object(object_id, "likes")
139
+ end
138
140
 
139
- def delete_object(id)
140
- # Deletes the object with the given ID from the graph.
141
- request(id, {}, "delete")
142
- end
141
+ def delete_object(id)
142
+ # Deletes the object with the given ID from the graph.
143
+ api(id, {}, "delete")
144
+ end
143
145
 
144
- def search(search_terms, args = {})
145
- # Searches for a given term
146
- request("search", args.merge({:q => search_terms}))
147
- end
146
+ def search(search_terms, args = {})
147
+ # Searches for a given term
148
+ api("search", args.merge({:q => search_terms}))
149
+ end
148
150
 
149
- def request(path, args = {}, verb = "get")
150
- # Fetches the given path in the Graph API.
151
- args["access_token"] = @access_token if @access_token
151
+ def api(path, args = {}, verb = "get")
152
+ # Fetches the given path in the Graph API.
153
+ args["access_token"] = @access_token if @access_token
152
154
 
153
- # make the request via the provided service
154
- result = make_request(path, args, verb)
155
+ # make the request via the provided service
156
+ result = Koala.make_request(path, args, verb)
155
157
 
156
- # Facebook sometimes sends results like "true" and "false", which aren't strictly object
157
- # and cause JSON.parse to fail
158
- # so we account for that
159
- response = JSON.parse("[#{result}]")[0]
158
+ # Facebook sometimes sends results like "true" and "false", which aren't strictly object
159
+ # and cause JSON.parse to fail
160
+ # so we account for that
161
+ response = JSON.parse("[#{result}]")[0]
160
162
 
161
- # check for errors
162
- if response.is_a?(Hash) && error = response["error"]
163
- raise GraphAPIError.new(error["code"], error["message"])
164
- end
163
+ # check for errors
164
+ if response.is_a?(Hash) && error = response["error"]
165
+ raise GraphAPIError.new(error["code"], error["message"])
166
+ end
165
167
 
166
- response
167
- end
168
-
169
- # set up the http service used to make requests
170
- # you can use your own (for HTTParty, etc.) by calling Koala::API.http_service = YourModule
171
- def self.http_service=(service)
172
- self.send(:include, service)
173
- end
174
-
175
- # by default, try requiring Typhoeus -- if that works, use it
176
- begin
177
- require 'typhoeus'
178
- Koala::GraphAPI.http_service = TyphoeusService
179
- rescue LoadError
180
- Koala::GraphAPI.http_service = NetHTTPService
168
+ response
169
+ end
181
170
  end
182
- end
183
171
 
184
- class GraphAPIError < Exception
185
- attr_accessor :code
186
- def initialize(code, message)
187
- super(message)
188
- self.code = code
172
+ class GraphAPIError < Exception
173
+ attr_accessor :code
174
+ def initialize(code, message)
175
+ super(message)
176
+ self.code = code
177
+ end
189
178
  end
190
- end
191
179
 
192
- class OAuth
193
- def initialize(app_id, app_secret, oauth_callback_url = nil)
194
- @app_id = app_id
195
- @app_secret = app_secret
196
- @oauth_callback_url = oauth_callback_url
197
- end
180
+ class OAuth
181
+ def initialize(app_id, app_secret, oauth_callback_url = nil)
182
+ @app_id = app_id
183
+ @app_secret = app_secret
184
+ @oauth_callback_url = oauth_callback_url
185
+ end
198
186
 
199
- def get_user_from_cookie(cookie_hash)
200
- # Parses the cookie set by the official Facebook JavaScript SDK.
201
- #
202
- # cookies should be a dictionary-like object mapping cookie names to
203
- # cookie values.
204
- #
205
- # If the user is logged in via Facebook, we return a dictionary with the
206
- # keys "uid" and "access_token". The former is the user's Facebook ID,
207
- # and the latter can be used to make authenticated requests to the Graph API.
208
- # If the user is not logged in, we return None.
209
- #
210
- # Download the official Facebook JavaScript SDK at
211
- # http://github.com/facebook/connect-js/. Read more about Facebook
212
- # authentication at http://developers.facebook.com/docs/authentication/.
187
+ def get_user_from_cookie(cookie_hash)
188
+ # Parses the cookie set by the official Facebook JavaScript SDK.
189
+ #
190
+ # cookies should be a dictionary-like object mapping cookie names to
191
+ # cookie values.
192
+ #
193
+ # If the user is logged in via Facebook, we return a dictionary with the
194
+ # keys "uid" and "access_token". The former is the user's Facebook ID,
195
+ # and the latter can be used to make authenticated requests to the Graph API.
196
+ # If the user is not logged in, we return None.
197
+ #
198
+ # Download the official Facebook JavaScript SDK at
199
+ # http://github.com/facebook/connect-js/. Read more about Facebook
200
+ # authentication at http://developers.facebook.com/docs/authentication/.
213
201
 
214
- if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
215
- # remove the opening/closing quote
216
- fb_cookie = fb_cookie.gsub(/\"/, "")
202
+ if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
203
+ # remove the opening/closing quote
204
+ fb_cookie = fb_cookie.gsub(/\"/, "")
217
205
 
218
- # since we no longer get individual cookies, we have to separate out the components ourselves
219
- components = {}
220
- fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
206
+ # since we no longer get individual cookies, we have to separate out the components ourselves
207
+ components = {}
208
+ fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
221
209
 
222
- auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
223
- sig = Digest::MD5.hexdigest(auth_string + @app_secret)
210
+ auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
211
+ sig = Digest::MD5.hexdigest(auth_string + @app_secret)
224
212
 
225
- sig == components["sig"] && Time.now.to_i < components["expires"].to_i ? components : nil
213
+ sig == components["sig"] && (components["expires"].to_i == 0 || Time.now.to_i < components["expires"].to_i) ? components : nil
214
+ end
226
215
  end
227
- end
228
216
 
229
- def url_for_oauth_code(options = {})
230
- callback = options[:callback] || @oauth_callback_url
231
- permissions = options[:permissions]
232
- scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
217
+ def url_for_oauth_code(options = {})
218
+ # for permissions, see http://developers.facebook.com/docs/authentication/permissions
219
+ permissions = options[:permissions]
220
+ scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
233
221
 
234
- # Creates the URL for oauth authorization for a given callback and optional set of permissions
235
- "https://#{FACEBOOK_GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
236
- end
222
+ callback = options[:callback] || @oauth_callback_url
223
+
224
+ # Creates the URL for oauth authorization for a given callback and optional set of permissions
225
+ "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
226
+ end
237
227
 
238
- def url_for_access_token(code, callback = @oauth_callback_url)
239
- # Creates the URL for the token corresponding to a given code generated by Facebook
240
- "https://#{FACEBOOK_GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
241
- end
242
-
243
- # for more details and up to date information, see http://developers.facebook.com/docs/authentication/permissions
244
- def self.all_permissions
245
- USER_PERMISSIONS.concat(FRIEND_PERMISSIONS)
228
+ def url_for_access_token(code, callback = @oauth_callback_url)
229
+ # Creates the URL for the token corresponding to a given code generated by Facebook
230
+ "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
231
+ end
246
232
  end
247
-
248
- USER_PERMISSIONS = [
249
- # PUBLISHING
250
- "publish_stream", # Enables your application to post content, comments, and likes to a user's stream and to the streams of the user's friends, without prompting the user each time.
251
- "create_event", # Enables your application to create and modify events on the user's behalf
252
- "rsvp_event", # Enables your application to RSVP to events on the user's behalf
253
- "sms", # Enables your application to send messages to the user and respond to messages from the user via text message
254
- "offline_access", # Enables your application to perform authorized requests on behalf of the user at any time (e.g. permanent access token)
255
-
256
- # DATA ACCESS
257
- "email", # Provides access to the user's primary email address in the email property
258
- "read_stream", # Provides access to all the posts in the user's News Feed and enables your application to perform searches against the user's News Feed
259
- "user_about_me", # Provides access to the "About Me" section of the profile in the about property
260
- "user_activities", # Provides access to the user's list of activities as the activities connection
261
- "user_birthday", # Provides access to the full birthday with year as the birthday_date property
262
- "user_education_history", # Provides access to education history as the education property
263
- "user_events", # Provides access to the list of events the user is attending as the events connection
264
- "user_groups", # Provides access to the list of groups the user is a member of as the groups connection
265
- "user_hometown", # Provides access to the user's hometown in the hometown property
266
- "user_interests", # Provides access to the user's list of interests as the interests connection
267
- "user_likes", # Provides access to the list of all of the pages the user has liked as the likes connection
268
- "user_location", # Provides access to the user's current location as the current_location property
269
- "user_notes", # Provides access to the user's notes as the notes connection
270
- "user_online_presence", # Provides access to the user's online/offline presence
271
- "user_photo_video_tags", # Provides access to the photos the user has been tagged in as the photos connection
272
- "user_photos", # Provides access to the photos the user has uploaded
273
- "user_relationships", # Provides access to the user's family and personal relationships and relationship status
274
- "user_religion_politics", # Provides access to the user's religious and political affiliations
275
- "user_status", # Provides access to the user's most recent status message
276
- "user_videos", # Provides access to the videos the user has uploaded
277
- "user_website", # Provides access to the user's web site URL
278
- "user_work_history", # Provides access to work history as the work property
279
- "read_friendlists", # Provides read access to the user's friend lists
280
- "read_requests" # Provides read access to the user's friend requests
281
- ]
282
-
283
- FRIEND_PERMISSIONS = [
284
- # DATA ACCESS
285
- "friends_about_me", # Provides access to the "About Me" section of the profile in the about property
286
- "friends_activities", # Provides access to the user's list of activities as the activities connection
287
- "friends_birthday", # Provides access to the full birthday with year as the birthday_date property
288
- "friends_education_history", # Provides access to education history as the education property
289
- "friends_events", # Provides access to the list of events the user is attending as the events connection
290
- "friends_groups", # Provides access to the list of groups the user is a member of as the groups connection
291
- "friends_hometown", # Provides access to the user's hometown in the hometown property
292
- "friends_interests", # Provides access to the user's list of interests as the interests connection
293
- "friends_likes", # Provides access to the list of all of the pages the user has liked as the likes connection
294
- "friends_location", # Provides access to the user's current location as the current_location property
295
- "friends_notes", # Provides access to the user's notes as the notes connection
296
- "friends_online_presence", # Provides access to the user's online/offline presence
297
- "friends_photo_video_tags", # Provides access to the photos the user has been tagged in as the photos connection
298
- "friends_photos", # Provides access to the photos the user has uploaded
299
- "friends_relationships", # Provides access to the user's family and personal relationships and relationship status
300
- "friends_religion_politics", # Provides access to the user's religious and political affiliations
301
- "friends_status", # Provides access to the user's most recent status message
302
- "friends_videos", # Provides access to the videos the user has uploaded
303
- "friends_website", # Provides access to the user's web site URL
304
- "friends_work_history" # Provides access to work history as the work property
305
- ]
233
+ end
234
+
235
+ # finally, set up the http service Koala methods used to make requests
236
+ # you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
237
+ def self.http_service=(service)
238
+ self.send(:include, service)
239
+ end
240
+
241
+ # by default, try requiring Typhoeus -- if that works, use it
242
+ begin
243
+ require 'typhoeus'
244
+ Koala.http_service = TyphoeusService
245
+ rescue LoadError
246
+ Koala.http_service = NetHTTPService
306
247
  end
307
248
  end
data/readme.md CHANGED
@@ -1,29 +1,43 @@
1
- Facebook Graph
1
+ Koala
2
2
  ====
3
+ Koala (<a href="http://github.com/arsduo/ruby-sdk" target="_blank">http://github.com/arsduo/ruby-sdk</a>) is a new Facebook Graph library for Ruby. We wrote Koala with four goals:
3
4
 
4
- This Ruby client library is designed to support the
5
- [Facebook Graph API](http://developers.facebook.com/docs/api) and the official
6
- [Facebook JavaScript SDK](http://github.com/facebook/connect-js), which is
7
- the canonical way to implement Facebook authentication. You can read more
8
- about the Graph API at [http://developers.facebook.com/docs/api](http://developers.facebook.com/docs/api).
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.)
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
+ * 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
9
 
10
10
  Basic usage:
11
11
 
12
- graph = Facebook::GraphAPI.new(oauth_access_token)
12
+ graph = Koala::Facebook::GraphAPI.new(oauth_access_token)
13
13
  profile = graph.get_object("me")
14
14
  friends = graph.get_connections("me", "friends")
15
15
  graph.put_object("me", "feed", :message => "I am writing on my wall!")
16
16
 
17
- If you are using the module within a web application with the
18
- [JavaScript SDK](http://github.com/facebook/connect-js), you can also use the
19
- module to use Facebook for login, parsing the cookie set by the JavaScript SDK
20
- for logged in users.
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.
20
+
21
+ Examples and More Details
22
+ -----
23
+ There's a very detailed description and walkthrough of Koala at http://blog.twoalex.com/2010/05/03/introducing-koala-a-new-gem-for-facebooks-new-graph-api/.
24
+
21
25
 
22
26
  Testing
23
27
  -----
24
28
 
25
29
  Unit tests are provided for Graph API methods. However, because the Graph API uses access tokens, which expire, you have to provide your own token with stream publishing permissions for the tests. Insert the token value into the file test/facebook_data.yml, then run the test as follows:
26
- spec facebook_tests.rb
30
+ spec koala_tests.rb
27
31
 
28
- Unit tests for cookie validation will be provided shortly. (You'll also need to add that information into the yml.)
29
-
32
+ Unit tests for cookie validation and other methods in the OAuth class will be provided shortly. (You'll also need to add that information into the yml.)
33
+
34
+
35
+ Coming Soon
36
+ -----
37
+ 1. OAuth class methods to parse the results of the access token call
38
+ 2. OAuth class method to directly fetch the access token when given a code value
39
+
40
+
41
+ Known Issues
42
+ -----
43
+ 1. gem install koala produces "Could not find main page readme.md" message
@@ -1,4 +1,8 @@
1
- oauth_token: 115349521819193|2.qpo_Ei2sucL3L_yjapnhhQ__.3600.1272762000-2905623|GKjZ-GL5nGIN4lFDZSa-zzFcXvM.
1
+ # for testing the GraphAPI classs
2
+ # OAuth token should have stream_publish permissions
3
+ oauth_token:
4
+
5
+ # for testing the OAuth class
2
6
  app_id:
3
7
  secret:
4
8
  callback_url:
@@ -1,7 +1,8 @@
1
1
  class FacebookNoAccessTokenTests < Test::Unit::TestCase
2
+
2
3
  describe "Koala GraphAPI without an access token" do
3
4
  before :each do
4
- @graph = Koala::GraphAPI.new
5
+ @graph = Koala::Facebook::GraphAPI.new
5
6
  end
6
7
 
7
8
  it "should get public data about a user" do
@@ -26,7 +27,7 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
26
27
  it "should not be able to get data about 'me'" do
27
28
  begin
28
29
  @graph.get_object("me")
29
- rescue Koala::GraphAPIError => @right_err
30
+ rescue Koala::Facebook::GraphAPIError => @right_err
30
31
  rescue Exception => wrong_err
31
32
  end
32
33
  @right_err.should_not be_nil
@@ -40,7 +41,7 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
40
41
  it "shouldn't be able to access connections from users" do
41
42
  begin
42
43
  @graph.get_connections("lukeshepard", "likes")
43
- rescue Koala::GraphAPIError => @right_err
44
+ rescue Koala::Facebook::GraphAPIError => @right_err
44
45
  rescue Exception => wrong_err
45
46
  end
46
47
  @right_err.should_not be_nil
@@ -54,7 +55,7 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
54
55
  it "should not be able to put an object" do
55
56
  begin
56
57
  @result = @graph.put_object("lukeshepard", "feed", :message => "Hello, world")
57
- rescue Koala::GraphAPIError => @right_err
58
+ rescue Koala::Facebook::GraphAPIError => @right_err
58
59
  rescue Exception => wrong_err
59
60
  end
60
61
  @right_err.should_not be_nil
@@ -64,7 +65,7 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
64
65
  it "should not be able to post to a feed" do
65
66
  begin
66
67
  @result = @graph.put_wall_post("Hello, world", {:name => "Context Optional", :link => "http://www.contextoptional.com/"}, "contextoptional")
67
- rescue Koala::GraphAPIError => @right_err
68
+ rescue Koala::Facebook::GraphAPIError => @right_err
68
69
  rescue Exception => wrong_err
69
70
  end
70
71
  @right_err.should_not be_nil
@@ -74,7 +75,7 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
74
75
  begin
75
76
  # random public post on the ContextOptional wall
76
77
  @result = @graph.put_comment("7204941866_119776748033392", "The hackathon was great!")
77
- rescue Koala::GraphAPIError => @right_err
78
+ rescue Koala::Facebook::GraphAPIError => @right_err
78
79
  rescue Exception => wrong_err
79
80
  end
80
81
  @right_err.should_not be_nil
@@ -83,7 +84,7 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
83
84
  it "should not be able to like an object" do
84
85
  begin
85
86
  @result = @graph.put_like("7204941866_119776748033392")
86
- rescue Koala::GraphAPIError => @right_err
87
+ rescue Koala::Facebook::GraphAPIError => @right_err
87
88
  rescue Exception => wrong_err
88
89
  end
89
90
  @right_err.should_not be_nil
@@ -95,7 +96,7 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
95
96
  begin
96
97
  # test post on the Ruby SDK Test application
97
98
  @result = @graph.delete_object("115349521819193_113815981982767")
98
- rescue Koala::GraphAPIError => @right_err
99
+ rescue Koala::Facebook::GraphAPIError => @right_err
99
100
  rescue Exception => wrong_err
100
101
  end
101
102
  @right_err.should_not be_nil
@@ -107,11 +108,8 @@ class FacebookNoAccessTokenTests < Test::Unit::TestCase
107
108
  result["data"].should be_an(Array)
108
109
  end
109
110
 
110
- # REQUEST
111
- # the above tests test this already, but we should do it explicitly
112
- it "should be able to send get-only requests" do
113
- # to be written
114
- end
111
+ # API
112
+ # the above tests test this already, but we should consider additional api tests
115
113
 
116
114
  end # describe
117
115
 
@@ -3,7 +3,7 @@ class FacebookWithAccessTokenTests < Test::Unit::TestCase
3
3
  before :each do
4
4
  token = $testing_data["oauth_token"]
5
5
  raise Exception, "Must supply access token to run FacebookWithAccessTokenTests!" unless token
6
- @graph = Koala::GraphAPI.new(token)
6
+ @graph = Koala::Facebook::GraphAPI.new(token)
7
7
  end
8
8
 
9
9
  after :each do
@@ -95,11 +95,8 @@ class FacebookWithAccessTokenTests < Test::Unit::TestCase
95
95
  result["data"].should be_an(Array)
96
96
  end
97
97
 
98
- # REQUEST
99
- # the above tests test this already, but we should do it explicitly
100
- it "should be able to send get and put requests" do
101
- # to be written
102
- end
98
+ # API
99
+ # the above tests test this already, but we should consider additional api tests
103
100
 
104
101
  end # describe
105
102
 
data/test/koala_tests.rb CHANGED
@@ -1,4 +1,3 @@
1
- puts __FILE__
2
1
  require 'test/unit'
3
2
  require 'rubygems'
4
3
  require 'spec/test/unit'
@@ -25,6 +24,8 @@ $testing_data = YAML.load_file("facebook_data.yml") rescue {}
25
24
  unless $testing_data["oauth_token"]
26
25
  puts "Access token tests will fail until you store a valid token in facebook_data.yml"
27
26
  end
28
- unless cookies = $testing_data["cookie_hash"]
29
- puts "Cookie tests will fail until you store a valid token in facebook_data.yml"
30
- end
27
+
28
+ # TODO finish tests for OAuth class
29
+ # unless $testing_data["cookie_hash"] && $testing_data["app_id"] && $testing_data["secret"]
30
+ # puts "Cookie tests will fail until you store valid data for the cookie hash, app_id, and app secret in facebook_data.yml"
31
+ # end
metadata CHANGED
@@ -5,7 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 4
8
- version: "0.4"
8
+ - 1
9
+ version: 0.4.1
9
10
  platform: ruby
10
11
  authors:
11
12
  - Alex Koppel, Rafi Jacoby, Context Optional
@@ -13,7 +14,7 @@ autorequire:
13
14
  bindir: bin
14
15
  cert_chain: []
15
16
 
16
- date: 2010-05-01 00:00:00 -07:00
17
+ date: 2010-05-03 00:00:00 -07:00
17
18
  default_executable:
18
19
  dependencies: []
19
20