koala 0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ v0.4.0
2
+ -- Adopted the Koala name
3
+ -- Fixed cookie verification bug for non-expiring OAuth tokens
4
+
5
+ v0.3.1
6
+ -- Bug fixes.
7
+
8
+ v0.3
9
+ -- Renamed Graph API class from Facebook::GraphAPI to FacebookGraph::API
10
+ -- Created FacebookGraph::OAuth class for tokens and OAuth URLs
11
+ -- Updated method for including HTTP service (think we've got it this time)
12
+ -- Updated tests
13
+ -- Added CHANGELOG and gemspec
14
+
15
+ v0.2
16
+ -- Gemified the project
17
+ -- Split out HTTP services into their own file, and adjusted inclusion method
18
+
19
+ v0.1
20
+ -- Added modular support for Typhoeus
21
+ -- Added tests
22
+
23
+ v0.0
24
+ -- Hi from F8! Basic read/write from the graph is working
@@ -0,0 +1,12 @@
1
+ CHANGELOG
2
+ Manifest
3
+ Rakefile
4
+ init.rb
5
+ koala.gemspec
6
+ lib/http_services.rb
7
+ lib/koala.rb
8
+ readme.md
9
+ test/facebook_data.yml
10
+ test/koala/facebook_no_access_token_tests.rb
11
+ test/koala/facebook_with_access_token_tests.rb
12
+ test/koala_tests.rb
@@ -0,0 +1,15 @@
1
+ # Rakefile
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'echoe'
5
+
6
+ # gem management
7
+ Echoe.new('koala', '0.4') do |p|
8
+ p.summary = "A lightweight, flexible library for Facebook's new Graph API"
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
+ p.url = "http://github.com/arsduo/ruby-sdk"
11
+ p.author = ["Alex Koppel", "Rafi Jacoby", "Context Optional"]
12
+ p.email = "alex@alexkoppel.com"
13
+ p.ignore_pattern = ["tmp/*", "script/*"]
14
+ p.development_dependencies = []
15
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # init.rb
2
+ require 'facebook_graph'
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{koala}
5
+ s.version = "0.4"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Alex Koppel, Rafi Jacoby, Context Optional"]
9
+ s.date = %q{2010-05-01}
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
+ s.email = %q{alex@alexkoppel.com}
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/http_services.rb", "lib/koala.rb"]
13
+ s.files = ["CHANGELOG", "Manifest", "Rakefile", "init.rb", "koala.gemspec", "lib/http_services.rb", "lib/koala.rb", "readme.md", "test/facebook_data.yml", "test/koala/facebook_no_access_token_tests.rb", "test/koala/facebook_with_access_token_tests.rb", "test/koala_tests.rb"]
14
+ s.homepage = %q{http://github.com/arsduo/ruby-sdk}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala", "--main", "readme.md"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{koala}
18
+ s.rubygems_version = %q{1.3.6}
19
+ s.summary = %q{A lightweight, flexible library for Facebook's new Graph API}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
@@ -0,0 +1,60 @@
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.
15
+
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"
18
+
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
26
+
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
32
+
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("&")
41
+ end
42
+ end
43
+
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
52
+
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
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,307 @@
1
+ require 'cgi'
2
+ require 'digest/md5'
3
+
4
+ # rubygems is required to support json, how facebook returns data
5
+ require 'rubygems'
6
+ require 'json'
7
+
8
+ # include default http services
9
+ require 'http_services'
10
+
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/.
32
+
33
+ FACEBOOK_GRAPH_SERVER = "graph.facebook.com"
34
+
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.
87
+ #
88
+ # For example,
89
+ #
90
+ # graph.put_object("me", "feed", :message => "Hello, world")
91
+ #
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:
94
+ #
95
+ # feed = graph.get_connections("me", "feed")
96
+ # post = feed["data"][0]
97
+ # graph.put_object(post["id"], "comments", :message => "First!")
98
+ #
99
+ # See http://developers.facebook.com/docs/api#publishing for all of
100
+ # the supported writeable objects.
101
+ #
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.
106
+
107
+ raise GraphAPIError.new(nil, "Write operations require an access token") unless @access_token
108
+ request("#{parent_object}/#{connection_name}", args, "post")
109
+ end
110
+
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"}
125
+
126
+ self.put_object(profile_id, "feed", attachment.merge({:message => message}))
127
+ end
128
+
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
133
+
134
+ def put_like(object_id)
135
+ # Likes the given post.
136
+ self.put_object(object_id, "likes")
137
+ end
138
+
139
+ def delete_object(id)
140
+ # Deletes the object with the given ID from the graph.
141
+ request(id, {}, "delete")
142
+ end
143
+
144
+ def search(search_terms, args = {})
145
+ # Searches for a given term
146
+ request("search", args.merge({:q => search_terms}))
147
+ end
148
+
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
152
+
153
+ # make the request via the provided service
154
+ result = make_request(path, args, verb)
155
+
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]
160
+
161
+ # check for errors
162
+ if response.is_a?(Hash) && error = response["error"]
163
+ raise GraphAPIError.new(error["code"], error["message"])
164
+ end
165
+
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
181
+ end
182
+ end
183
+
184
+ class GraphAPIError < Exception
185
+ attr_accessor :code
186
+ def initialize(code, message)
187
+ super(message)
188
+ self.code = code
189
+ end
190
+ end
191
+
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
198
+
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/.
213
+
214
+ if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
215
+ # remove the opening/closing quote
216
+ fb_cookie = fb_cookie.gsub(/\"/, "")
217
+
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]}
221
+
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)
224
+
225
+ sig == components["sig"] && Time.now.to_i < components["expires"].to_i ? components : nil
226
+ end
227
+ end
228
+
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}" : ""
233
+
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
237
+
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)
246
+ 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
+ ]
306
+ end
307
+ end
@@ -0,0 +1,29 @@
1
+ Facebook Graph
2
+ ====
3
+
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).
9
+
10
+ Basic usage:
11
+
12
+ graph = Facebook::GraphAPI.new(oauth_access_token)
13
+ profile = graph.get_object("me")
14
+ friends = graph.get_connections("me", "friends")
15
+ graph.put_object("me", "feed", :message => "I am writing on my wall!")
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.
21
+
22
+ Testing
23
+ -----
24
+
25
+ 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
27
+
28
+ Unit tests for cookie validation will be provided shortly. (You'll also need to add that information into the yml.)
29
+
@@ -0,0 +1,5 @@
1
+ oauth_token: 115349521819193|2.qpo_Ei2sucL3L_yjapnhhQ__.3600.1272762000-2905623|GKjZ-GL5nGIN4lFDZSa-zzFcXvM.
2
+ app_id:
3
+ secret:
4
+ callback_url:
5
+ cookie_hash:
@@ -0,0 +1,119 @@
1
+ class FacebookNoAccessTokenTests < Test::Unit::TestCase
2
+ describe "Koala GraphAPI without an access token" do
3
+ before :each do
4
+ @graph = Koala::GraphAPI.new
5
+ end
6
+
7
+ it "should get public data about a user" do
8
+ result = @graph.get_object("koppel")
9
+ # the results should have an ID and a name, among other things
10
+ (result["id"] && result["name"]).should
11
+ end
12
+
13
+ it "should not get private data about a user" do
14
+ result = @graph.get_object("koppel")
15
+ # updated_time should be a pretty fixed test case
16
+ result["updated_time"].should be_nil
17
+ end
18
+
19
+
20
+ it "should get public data about a Page" do
21
+ result = @graph.get_object("contextoptional")
22
+ # the results should have an ID and a name, among other things
23
+ (result["id"] && result["name"]).should
24
+ end
25
+
26
+ it "should not be able to get data about 'me'" do
27
+ begin
28
+ @graph.get_object("me")
29
+ rescue Koala::GraphAPIError => @right_err
30
+ rescue Exception => wrong_err
31
+ end
32
+ @right_err.should_not be_nil
33
+ end
34
+
35
+ it "should be able to get multiple objects" do
36
+ results = @graph.get_objects(["contextoptional", "naitik"])
37
+ results.length.should == 2
38
+ end
39
+
40
+ it "shouldn't be able to access connections from users" do
41
+ begin
42
+ @graph.get_connections("lukeshepard", "likes")
43
+ rescue Koala::GraphAPIError => @right_err
44
+ rescue Exception => wrong_err
45
+ end
46
+ @right_err.should_not be_nil
47
+ end
48
+
49
+ it "should be able to access connections from public Pages" do
50
+ result = @graph.get_connections("contextoptional", "likes")
51
+ result["data"].should be_a(Array)
52
+ end
53
+
54
+ it "should not be able to put an object" do
55
+ begin
56
+ @result = @graph.put_object("lukeshepard", "feed", :message => "Hello, world")
57
+ rescue Koala::GraphAPIError => @right_err
58
+ rescue Exception => wrong_err
59
+ end
60
+ @right_err.should_not be_nil
61
+ end
62
+
63
+ # these are not strictly necessary as the other put methods resolve to put_object, but are here for completeness
64
+ it "should not be able to post to a feed" do
65
+ begin
66
+ @result = @graph.put_wall_post("Hello, world", {:name => "Context Optional", :link => "http://www.contextoptional.com/"}, "contextoptional")
67
+ rescue Koala::GraphAPIError => @right_err
68
+ rescue Exception => wrong_err
69
+ end
70
+ @right_err.should_not be_nil
71
+ end
72
+
73
+ it "should not be able to comment on an object" do
74
+ begin
75
+ # random public post on the ContextOptional wall
76
+ @result = @graph.put_comment("7204941866_119776748033392", "The hackathon was great!")
77
+ rescue Koala::GraphAPIError => @right_err
78
+ rescue Exception => wrong_err
79
+ end
80
+ @right_err.should_not be_nil
81
+ end
82
+
83
+ it "should not be able to like an object" do
84
+ begin
85
+ @result = @graph.put_like("7204941866_119776748033392")
86
+ rescue Koala::GraphAPIError => @right_err
87
+ rescue Exception => wrong_err
88
+ end
89
+ @right_err.should_not be_nil
90
+ end
91
+
92
+
93
+ # DELETE
94
+ it "should not be able to delete posts" do
95
+ begin
96
+ # test post on the Ruby SDK Test application
97
+ @result = @graph.delete_object("115349521819193_113815981982767")
98
+ rescue Koala::GraphAPIError => @right_err
99
+ rescue Exception => wrong_err
100
+ end
101
+ @right_err.should_not be_nil
102
+ end
103
+
104
+ # SEARCH
105
+ it "should be able to search" do
106
+ result = @graph.search("facebook")
107
+ result["data"].should be_an(Array)
108
+ end
109
+
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
115
+
116
+ end # describe
117
+
118
+ end #class
119
+
@@ -0,0 +1,106 @@
1
+ class FacebookWithAccessTokenTests < Test::Unit::TestCase
2
+ describe "Koala GraphAPI with an access token" do
3
+ before :each do
4
+ token = $testing_data["oauth_token"]
5
+ raise Exception, "Must supply access token to run FacebookWithAccessTokenTests!" unless token
6
+ @graph = Koala::GraphAPI.new(token)
7
+ end
8
+
9
+ after :each do
10
+ # clean up any temporary objects
11
+ if @temporary_object_id
12
+ puts "\nCleaning up temporary object #{@temporary_object_id.to_s}"
13
+ @graph.delete_object(@temporary_object_id)
14
+ end
15
+ end
16
+
17
+ it "should get public data about a user" do
18
+ result = @graph.get_object("koppel")
19
+ # the results should have an ID and a name, among other things
20
+ (result["id"] && result["name"]).should_not be_nil
21
+ end
22
+
23
+ it "should get private data about a user" do
24
+ result = @graph.get_object("koppel")
25
+ # updated_time should be a pretty fixed test case
26
+ result["updated_time"].should_not be_nil
27
+ end
28
+
29
+ it "should get public data about a Page" do
30
+ result = @graph.get_object("contextoptional")
31
+ # the results should have an ID and a name, among other things
32
+ (result["id"] && result["name"]).should
33
+ end
34
+
35
+ it "should get data about 'me'" do
36
+ result = @graph.get_object("me")
37
+ result["updated_time"].should
38
+ end
39
+
40
+ it "should be able to get multiple objects" do
41
+ result = @graph.get_objects(["contextoptional", "naitik"])
42
+ result.length.should == 2
43
+ end
44
+
45
+ it "should be able to access connections from users" do
46
+ result = @graph.get_connections("lukeshepard", "likes")
47
+ result["data"].length.should > 0
48
+ end
49
+
50
+ it "should be able to access connections from public Pages" do
51
+ result = @graph.get_connections("contextoptional", "likes")
52
+ result["data"].should be_a(Array)
53
+ end
54
+
55
+ # PUT
56
+ it "should be able to put an object" do
57
+ result = @graph.put_wall_post("Hello, world, from the test suite!")
58
+ @temporary_object_id = result["id"]
59
+ @temporary_object_id.should_not be_nil
60
+ end
61
+
62
+ # DELETE
63
+ it "should not be able to delete posts" do
64
+ result = @graph.put_wall_post("Hello, world, from the test suite delete method!")
65
+ object_id_to_delete = result["id"]
66
+ delete_result = @graph.delete_object(object_id_to_delete)
67
+ delete_result.should == true
68
+ end
69
+
70
+ # these are not strictly necessary as the other put methods resolve to put_object, but are here for completeness
71
+ it "should be able to post to a feed" do
72
+ result = @graph.put_wall_post("Hello, world, from the test suite again!", {:name => "Context Optional", :link => "http://www.contextoptional.com/"})
73
+ @temporary_object_id = result["id"]
74
+ @temporary_object_id.should_not be_nil
75
+ end
76
+
77
+ it "should be able to comment on an object" do
78
+ result = @graph.put_wall_post("Hello, world, from the test suite, testing comments!")
79
+ @temporary_object_id = result["id"]
80
+
81
+ # this will be deleted when the post gets deleted
82
+ comment_result = @graph.put_comment(@temporary_object_id, "it's my comment!")
83
+ comment_result.should_not be_nil
84
+ end
85
+
86
+ it "should be able to like an object" do
87
+ result = @graph.put_wall_post("Hello, world, from the test suite, testing comments!")
88
+ @temporary_object_id = result["id"]
89
+ like_result = @graph.put_like(@temporary_object_id)
90
+ end
91
+
92
+ # SEARCH
93
+ it "should be able to search" do
94
+ result = @graph.search("facebook")
95
+ result["data"].should be_an(Array)
96
+ end
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
103
+
104
+ end # describe
105
+
106
+ end #class
@@ -0,0 +1,30 @@
1
+ puts __FILE__
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ require 'spec/test/unit'
5
+
6
+ require 'koala'
7
+ require 'koala/facebook_no_access_token_tests'
8
+ require 'koala/facebook_with_access_token_tests'
9
+
10
+ class FacebookTestSuite
11
+ def self.suite
12
+ suite = Test::Unit::TestSuite.new
13
+ suite << FacebookNoAccessTokenTests.suite
14
+ suite << FacebookWithAccessTokenTests.suite
15
+ #suite << FacebookCookieTest.suite
16
+ suite
17
+ end
18
+ end
19
+
20
+ # load testing data (see note in readme.md)
21
+ # I'm seeing a bug with spec and gets where the facebook_test_suite.rb file gets read in when gets is called
22
+ # until that's solved, we'll need to store/update tokens in the access_token file
23
+ $testing_data = YAML.load_file("facebook_data.yml") rescue {}
24
+
25
+ unless $testing_data["oauth_token"]
26
+ puts "Access token tests will fail until you store a valid token in facebook_data.yml"
27
+ 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
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: koala
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 4
8
+ version: "0.4"
9
+ platform: ruby
10
+ authors:
11
+ - Alex Koppel, Rafi Jacoby, Context Optional
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-05-01 00:00:00 -07:00
17
+ default_executable:
18
+ dependencies: []
19
+
20
+ 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.
21
+ email: alex@alexkoppel.com
22
+ executables: []
23
+
24
+ extensions: []
25
+
26
+ extra_rdoc_files:
27
+ - CHANGELOG
28
+ - lib/http_services.rb
29
+ - lib/koala.rb
30
+ files:
31
+ - CHANGELOG
32
+ - Manifest
33
+ - Rakefile
34
+ - init.rb
35
+ - koala.gemspec
36
+ - lib/http_services.rb
37
+ - lib/koala.rb
38
+ - readme.md
39
+ - test/facebook_data.yml
40
+ - test/koala/facebook_no_access_token_tests.rb
41
+ - test/koala/facebook_with_access_token_tests.rb
42
+ - test/koala_tests.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/arsduo/ruby-sdk
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --line-numbers
50
+ - --inline-source
51
+ - --title
52
+ - Koala
53
+ - --main
54
+ - readme.md
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 1
70
+ - 2
71
+ version: "1.2"
72
+ requirements: []
73
+
74
+ rubyforge_project: koala
75
+ rubygems_version: 1.3.6
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: A lightweight, flexible library for Facebook's new Graph API
79
+ test_files: []
80
+