cbaclig-koala 0.5.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ v0.5.0.1
2
+ -- Added #fql method to allow easy FQL queries
3
+ -- Added fql tests
4
+
5
+ v0.5.0
6
+ -- Added several new OAuth methods for making and parsing access token requests
7
+ -- Added test suite for the OAuth class
8
+ -- Made second argument to url_for_access_token a hash (strings still work but trigger a deprecation warning)
9
+ -- Added fields to facebook_data.yml
10
+ -- Updated readme
11
+
12
+ v0.4.1
13
+ -- Encapsulated GraphAPI and OAuth classes in the Koala::Facebook module for clarity (and to avoid claiming the global Facebook class)
14
+ -- Moved make_request method to Koala class from GraphAPI instance (for use by future OAuth class functionality)
15
+ -- Renamed request method to api for consistancy with Javascript library
16
+ -- Updated tests and readme
17
+
18
+ v0.4.0
19
+ -- Adopted the Koala name
20
+ -- Updated readme and tests
21
+ -- Fixed cookie verification bug for non-expiring OAuth tokens
22
+
23
+ v0.3.1
24
+ -- Bug fixes.
25
+
26
+ v0.3
27
+ -- Renamed Graph API class from Facebook::GraphAPI to FacebookGraph::API
28
+ -- Created FacebookGraph::OAuth class for tokens and OAuth URLs
29
+ -- Updated method for including HTTP service (think we've got it this time)
30
+ -- Updated tests
31
+ -- Added CHANGELOG and gemspec
32
+
33
+ v0.2
34
+ -- Gemified the project
35
+ -- Split out HTTP services into their own file, and adjusted inclusion method
36
+
37
+ v0.1
38
+ -- Added modular support for Typhoeus
39
+ -- Added tests
40
+
41
+ v0.0
42
+ -- 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
+ lib/http_services.rb
6
+ lib/koala.rb
7
+ readme.md
8
+ test/facebook_data.yml
9
+ test/koala/facebook_no_access_token_tests.rb
10
+ test/koala/facebook_oauth_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('cbaclig-koala') 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/koala"
11
+ p.author = ["Alex Koppel", "Rafi Jacoby", "Context Optional"]
12
+ p.email = "alex@alexkoppel.com"
13
+ p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"]
14
+ p.development_dependencies = []
15
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{cbaclig-koala}
5
+ s.version = "0.5.0.1"
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-11}
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", "lib/http_services.rb", "lib/koala.rb", "readme.md", "test/facebook_data.yml", "test/koala/facebook_no_access_token_tests.rb", "test/koala/facebook_oauth_tests.rb", "test/koala/facebook_with_access_token_tests.rb", "test/koala_tests.rb", "cbaclig-koala.gemspec"]
14
+ s.homepage = %q{http://github.com/arsduo/koala}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Cbaclig-koala", "--main", "readme.md"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{cbaclig-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
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # init.rb
2
+ require 'koala'
@@ -0,0 +1,58 @@
1
+ module Koala
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'
8
+
9
+ def self.make_request(path, args, verb, graph = true)
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.
12
+
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"
15
+
16
+ http = Net::HTTP.new(graph ? Facebook::GRAPH_SERVER : Facebook::REST_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
29
+
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
39
+ end
40
+ end
41
+ end
42
+
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
49
+
50
+ def self.make_request(path, args, verb, graph = true)
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://#{graph ? Facebook::GRAPH_SERVER : Facebook::REST_SERVER}/#{path}", :params => args).body
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,317 @@
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
+
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/.
34
+
35
+ GRAPH_SERVER = "graph.facebook.com"
36
+ REST_SERVER = "api.facebook.com"
37
+
38
+ class GraphAPI
39
+ # A client for the Facebook Graph API.
40
+ #
41
+ # See http://developers.facebook.com/docs/api for complete documentation
42
+ # for the API.
43
+ #
44
+ # The Graph API is made up of the objects in Facebook (e.g., people, pages,
45
+ # events, photos) and the connections between them (e.g., friends,
46
+ # photo tags, and event RSVPs). This client provides access to those
47
+ # primitive types in a generic way. For example, given an OAuth access
48
+ # token, this will fetch the profile of the active user and the list
49
+ # of the user's friends:
50
+ #
51
+ # graph = Koala::Facebook::GraphAPI.new(access_token)
52
+ # user = graph.get_object("me")
53
+ # friends = graph.get_connections(user["id"], "friends")
54
+ #
55
+ # You can see a list of all of the objects and connections supported
56
+ # by the API at http://developers.facebook.com/docs/reference/api/.
57
+ #
58
+ # You can obtain an access token via OAuth or by using the Facebook
59
+ # JavaScript SDK. See http://developers.facebook.com/docs/authentication/
60
+ # for details.
61
+ #
62
+ # If you are using the JavaScript SDK, you can use the
63
+ # Koala::Facebook::OAuth.get_user_from_cookie() method below to get the OAuth access token
64
+ # for the active user from the cookie saved by the SDK.
65
+
66
+ # initialize with an access token
67
+ def initialize(access_token = nil)
68
+ @access_token = access_token
69
+ end
70
+
71
+ def get_object(id, args = {})
72
+ # Fetchs the given object from the graph.
73
+ api(id, args)
74
+ end
75
+
76
+ def get_objects(ids, args = {})
77
+ # Fetchs all of the given object from the graph.
78
+ # We return a map from ID to object. If any of the IDs are invalid,
79
+ # we raise an exception.
80
+ api("", args.merge("ids" => ids.join(",")))
81
+ end
82
+
83
+ def get_connections(id, connection_name, args = {})
84
+ # Fetchs the connections for given object.
85
+ api("#{id}/#{connection_name}", args)
86
+ end
87
+
88
+ def put_object(parent_object, connection_name, args = {})
89
+ # Writes the given object to the graph, connected to the given parent.
90
+ #
91
+ # For example,
92
+ #
93
+ # graph.put_object("me", "feed", :message => "Hello, world")
94
+ #
95
+ # writes "Hello, world" to the active user's wall. Likewise, this
96
+ # will comment on a the first post of the active user's feed:
97
+ #
98
+ # feed = graph.get_connections("me", "feed")
99
+ # post = feed["data"][0]
100
+ # graph.put_object(post["id"], "comments", :message => "First!")
101
+ #
102
+ # See http://developers.facebook.com/docs/api#publishing for all of
103
+ # the supported writeable objects.
104
+ #
105
+ # Most write operations require extended permissions. For example,
106
+ # publishing wall posts requires the "publish_stream" permission. See
107
+ # http://developers.facebook.com/docs/authentication/ for details about
108
+ # extended permissions.
109
+
110
+ raise GraphAPIError.new({"type" => "KoalaMissingAccessToken", "message" => "Write operations require an access token"}) unless @access_token
111
+ api("#{parent_object}/#{connection_name}", args, "post")
112
+ end
113
+
114
+ def put_wall_post(message, attachment = {}, profile_id = "me")
115
+ # Writes a wall post to the given profile's wall.
116
+ #
117
+ # We default to writing to the authenticated user's wall if no
118
+ # profile_id is specified.
119
+ #
120
+ # attachment adds a structured attachment to the status message being
121
+ # posted to the Wall. It should be a dictionary of the form:
122
+ #
123
+ # {"name": "Link name"
124
+ # "link": "http://www.example.com/",
125
+ # "caption": "{*actor*} posted a new review",
126
+ # "description": "This is a longer description of the attachment",
127
+ # "picture": "http://www.example.com/thumbnail.jpg"}
128
+
129
+ self.put_object(profile_id, "feed", attachment.merge({:message => message}))
130
+ end
131
+
132
+ def put_comment(object_id, message)
133
+ # Writes the given comment on the given post.
134
+ self.put_object(object_id, "comments", {:message => message})
135
+ end
136
+
137
+ def put_like(object_id)
138
+ # Likes the given post.
139
+ self.put_object(object_id, "likes")
140
+ end
141
+
142
+ def delete_object(id)
143
+ # Deletes the object with the given ID from the graph.
144
+ api(id, {}, "delete")
145
+ end
146
+
147
+ def search(search_terms, args = {})
148
+ # Searches for a given term
149
+ api("search", args.merge({:q => search_terms}))
150
+ end
151
+
152
+ def api(path, args = {}, verb = "get")
153
+ # Fetches the given path in the Graph API.
154
+ args["access_token"] = @access_token if @access_token
155
+
156
+ # make the request via the provided service
157
+ result = Koala.make_request(path, args, verb)
158
+
159
+ # Facebook sometimes sends results like "true" and "false", which aren't strictly object
160
+ # and cause JSON.parse to fail
161
+ # so we account for that
162
+ response = JSON.parse("[#{result}]")[0]
163
+
164
+ # check for errors
165
+ if response.is_a?(Hash) && error_details = response["error"]
166
+ raise GraphAPIError.new(error_details)
167
+ end
168
+
169
+ response
170
+ end
171
+
172
+ def fql(query)
173
+ args = {
174
+ "query" => query,
175
+ "format" => "json",
176
+ }
177
+
178
+ # Adds access_token if available
179
+ args["access_token"] = @access_token if @access_token
180
+
181
+ response = Koala.make_request('method/fql.query', args, 'get', false)
182
+
183
+ # Facebook sometimes sends results like "true" and "false", which aren't strictly object
184
+ # and cause JSON.parse to fail
185
+ # so we account for that
186
+ response = JSON.parse("[#{response}]")[0]
187
+
188
+ # check for errors
189
+ if response.is_a?(Hash) && response["error_code"]
190
+ raise RestAPIError.new(response)
191
+ end
192
+
193
+ response
194
+ end
195
+ end
196
+
197
+ class GraphAPIError < Exception
198
+ attr_accessor :fb_error_type
199
+ def initialize(details = {})
200
+ self.fb_error_type = details["type"]
201
+ super("#{fb_error_type}: #{details["message"]}")
202
+ end
203
+ end
204
+
205
+ class RestAPIError < Exception
206
+ attr_accessor :fb_error_code
207
+ def initialize(details = {})
208
+ self.fb_error_code = details["error_code"]
209
+ super("#{fb_error_code}: #{details["error_msg"]} [#{details["request_args"].map{|h| "#{h['key']}=#{h['value']}" }.join('&')}]")
210
+ end
211
+ end
212
+
213
+
214
+ class OAuth
215
+ attr_accessor :app_id, :app_secret, :oauth_callback_url
216
+ def initialize(app_id, app_secret, oauth_callback_url = nil)
217
+ @app_id = app_id
218
+ @app_secret = app_secret
219
+ @oauth_callback_url = oauth_callback_url
220
+ end
221
+
222
+ def get_user_from_cookie(cookie_hash)
223
+ # Parses the cookie set by the official Facebook JavaScript SDK.
224
+ #
225
+ # cookies should be a dictionary-like object mapping cookie names to
226
+ # cookie values.
227
+ #
228
+ # If the user is logged in via Facebook, we return a dictionary with the
229
+ # keys "uid" and "access_token". The former is the user's Facebook ID,
230
+ # and the latter can be used to make authenticated requests to the Graph API.
231
+ # If the user is not logged in, we return None.
232
+ #
233
+ # Download the official Facebook JavaScript SDK at
234
+ # http://github.com/facebook/connect-js/. Read more about Facebook
235
+ # authentication at http://developers.facebook.com/docs/authentication/.
236
+
237
+ if fb_cookie = cookie_hash["fbs_" + @app_id.to_s]
238
+ # remove the opening/closing quote
239
+ fb_cookie = fb_cookie.gsub(/\"/, "")
240
+
241
+ # since we no longer get individual cookies, we have to separate out the components ourselves
242
+ components = {}
243
+ fb_cookie.split("&").map {|param| param = param.split("="); components[param[0]] = param[1]}
244
+
245
+ # generate the signature and make sure it matches what we expect
246
+ auth_string = components.keys.sort.collect {|a| a == "sig" ? nil : "#{a}=#{components[a]}"}.reject {|a| a.nil?}.join("")
247
+ sig = Digest::MD5.hexdigest(auth_string + @app_secret)
248
+ sig == components["sig"] && (components["expires"] == "0" || Time.now.to_i < components["expires"].to_i) ? components : nil
249
+ end
250
+ end
251
+ alias_method :get_user_from_cookies, :get_user_from_cookie
252
+
253
+ def url_for_oauth_code(options = {})
254
+ # for permissions, see http://developers.facebook.com/docs/authentication/permissions
255
+ permissions = options[:permissions]
256
+ scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
257
+
258
+ callback = options[:callback] || @oauth_callback_url
259
+ raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
260
+
261
+ # Creates the URL for oauth authorization for a given callback and optional set of permissions
262
+ "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
263
+ end
264
+
265
+ def url_for_access_token(code, options = {})
266
+ # Creates the URL for the token corresponding to a given code generated by Facebook
267
+ if options.is_a?(String) # changing the arguments
268
+ puts "Deprecation warning: url_for_access_token now takes an options hash as the second argument; pass the callback as :callback."
269
+ options = {:callback => options}
270
+ end
271
+ callback = options[:callback] || @oauth_callback_url
272
+ raise ArgumentError, "url_for_access_token must get a callback either from the OAuth object or in the parameters!" unless callback
273
+ "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
274
+ end
275
+
276
+ def parse_access_token(response_text)
277
+ components = response_text.split("&").inject({}) do |hash, bit|
278
+ key, value = bit.split("=")
279
+ hash.merge!(key => value)
280
+ end
281
+ components
282
+ end
283
+
284
+ def fetch_token_string(code)
285
+ Koala.make_request("oauth/access_token", {
286
+ :client_id => @app_id,
287
+ :redirect_uri => @oauth_callback_url,
288
+ :client_secret => @app_secret,
289
+ :code => code
290
+ }, "get")
291
+ end
292
+
293
+ def get_access_token(code)
294
+ result = fetch_token_string(code)
295
+
296
+ # if we have an error, parse the error JSON and raise an error
297
+ raise GraphAPIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
298
+ # otherwise, parse the access token
299
+ parse_access_token(result)
300
+ end
301
+ end
302
+ end
303
+
304
+ # finally, set up the http service Koala methods used to make requests
305
+ # you can use your own (for HTTParty, etc.) by calling Koala.http_service = YourModule
306
+ def self.http_service=(service)
307
+ self.send(:include, service)
308
+ end
309
+
310
+ # by default, try requiring Typhoeus -- if that works, use it
311
+ begin
312
+ require 'typhoeus'
313
+ Koala.http_service = TyphoeusService
314
+ rescue LoadError
315
+ Koala.http_service = NetHTTPService
316
+ end
317
+ end
@@ -0,0 +1,39 @@
1
+ Koala
2
+ ====
3
+ Koala (<a href="http://github.com/arsduo/koala" target="_blank">http://github.com/arsduo/koala</a>) is a new Facebook Graph library for Ruby. We wrote Koala with four goals:
4
+
5
+ * Lightweight: Koala should be as light and simple as Facebook’s own new libraries, providing API accessors and returning simple JSON. (We clock in, with comments, just over 300 lines of code.)
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
+
10
+ Basic usage:
11
+
12
+ graph = Koala::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'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 <a href="http://blog.twoalex.com/2010/05/03/introducing-koala-a-new-gem-for-facebooks-new-graph-api/">http://blog.twoalex.com/2010/05/03/introducing-koala-a-new-gem-for-facebooks-new-graph-api/</a>.
24
+
25
+ You can easily generate OAuth access tokens and any other data needed to play with the Graph API or OAuth at the Koala-powered <a href="http://oauth.twoalex.com" target="_blank">OAuth Playground</a>.
26
+
27
+
28
+ Testing
29
+ -----
30
+
31
+ Unit tests are provided for all of Koala's methods; however, because the OAuth access tokens and cookies expire, you have to provide some of your own data: a valid OAuth access token with publish_stream and read_stream permissions and an OAuth code that can be used to generate an access token. (The file also provides valid values for other tests, which you're welcome to sub out for data specific to your own application.)
32
+
33
+ Insert the required values into the file test/facebook_data.yml, then run the test as follows:
34
+ spec koala_tests.rb
35
+
36
+
37
+ Known Issues
38
+ -----
39
+ 1. gem install koala produces "Could not find main page readme.md" message
@@ -0,0 +1,31 @@
1
+ # Check out http://oauth.twoalex.com/ to easily generate tokens, cookies, etc.
2
+ # Those values will work with the default settings in this yaml.
3
+ # Of course, you can change this to work with your own app.
4
+ # Just remember to update all fields!
5
+
6
+ # You must supply this value yourself to test the GraphAPI class.
7
+ # Your OAuth token should have publish_stream and read_stream permissions
8
+ oauth_token:
9
+
10
+
11
+ # for testing the OAuth class
12
+ # baseline app
13
+ oauth_test_data:
14
+ # You must supply this value yourself, since they will expire.
15
+ code:
16
+
17
+ # These values will work out of the box
18
+ app_id: 119908831367602
19
+ secret: e45e55a333eec232d4206d2703de1307
20
+ callback_url: http://oauth.twoalex.com/
21
+ raw_token_string: "access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.&expires=6621"
22
+ raw_offline_access_token_string: access_token=119908831367602|2.6GneoQbnEqtSiPppZzDU4Q__.3600.1273366800-2905623|3OLa3w0x1K4C1S5cOgbs07TytAk.
23
+ valid_cookies:
24
+ # note: the tests stub the time class so these default cookies are always valid (if you're using the default app)
25
+ # if not you may want to remove the stubbing to test expiration
26
+ fbs_119908831367602: '"access_token=119908831367602|2.LKE7ksSPOx0V_8mHPr2NHQ__.3600.1273363200-2905623|CMpi0AYbn03Oukzv94AUha2qbO4.&expires=1273363200&secret=lT_9zm5r5IbJ6Aa5O54nFw__&session_key=2.LKE7ksSPOx0V_8mHPr2NHQ__.3600.1273363200-2905623&sig=9515e93113921f9476a4efbdd4a3c746&uid=2905623"'
27
+ expired_cookies:
28
+ fbs_119908831367602: '"access_token=119908831367602|2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623|yVt5WH_S6J5p3gFa5_5lBzckhws.&expires=1273287600&secret=V_E79ovQnXqxGctFuC_n5A__&session_key=2.xv9mi6QSOpr474s4n2X_pw__.3600.1273287600-2905623&sig=eeef60838c0c800258d89b7e6ddddddb&uid=2905623"'
29
+ offline_access_cookies:
30
+ # note: I've revoked the offline access for security reasons, so you can't make calls against this :)
31
+ fbs_119908831367602: '"access_token=119908831367602|08170230801eb225068e7a70-2905623|Q3LDCYYF8CX9cstxnZLsxiR0nwg.&expires=0&secret=78abaee300b392e275072a9f2727d436&session_key=08170230801eb225068e7a70-2905623&sig=423b8aa4b6fa1f9a571955f8e929d567&uid=2905623"'
@@ -0,0 +1,128 @@
1
+ class FacebookNoAccessTokenTests < Test::Unit::TestCase
2
+
3
+ describe "Koala GraphAPI without an access token" do
4
+ before :each do
5
+ @graph = Koala::Facebook::GraphAPI.new
6
+ end
7
+
8
+ it "should get public data about a user" do
9
+ result = @graph.get_object("koppel")
10
+ # the results should have an ID and a name, among other things
11
+ (result["id"] && result["name"]).should
12
+ end
13
+
14
+ it "should not get private data about a user" do
15
+ result = @graph.get_object("koppel")
16
+ # updated_time should be a pretty fixed test case
17
+ result["updated_time"].should be_nil
18
+ end
19
+
20
+
21
+ it "should get public data about a Page" do
22
+ result = @graph.get_object("contextoptional")
23
+ # the results should have an ID and a name, among other things
24
+ (result["id"] && result["name"]).should
25
+ end
26
+
27
+ it "should not be able to get data about 'me'" do
28
+ begin
29
+ @graph.get_object("me")
30
+ rescue Koala::Facebook::GraphAPIError => @right_err
31
+ rescue Exception => wrong_err
32
+ end
33
+ @right_err.should_not be_nil
34
+ end
35
+
36
+ it "should be able to get multiple objects" do
37
+ results = @graph.get_objects(["contextoptional", "naitik"])
38
+ results.length.should == 2
39
+ end
40
+
41
+ it "shouldn't be able to access connections from users" do
42
+ begin
43
+ @graph.get_connections("lukeshepard", "likes")
44
+ rescue Koala::Facebook::GraphAPIError => @right_err
45
+ rescue Exception => wrong_err
46
+ end
47
+ @right_err.should_not be_nil
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
+ it "should not be able to put an object" do
56
+ begin
57
+ @result = @graph.put_object("lukeshepard", "feed", :message => "Hello, world")
58
+ rescue Koala::Facebook::GraphAPIError => @right_err
59
+ rescue Exception => wrong_err
60
+ end
61
+ @right_err.should_not be_nil
62
+ end
63
+
64
+ # these are not strictly necessary as the other put methods resolve to put_object, but are here for completeness
65
+ it "should not be able to post to a feed" do
66
+ begin
67
+ @result = @graph.put_wall_post("Hello, world", {:name => "Context Optional", :link => "http://www.contextoptional.com/"}, "contextoptional")
68
+ rescue Koala::Facebook::GraphAPIError => @right_err
69
+ rescue Exception => wrong_err
70
+ end
71
+ @right_err.should_not be_nil
72
+ end
73
+
74
+ it "should not be able to comment on an object" do
75
+ begin
76
+ # random public post on the ContextOptional wall
77
+ @result = @graph.put_comment("7204941866_119776748033392", "The hackathon was great!")
78
+ rescue Koala::Facebook::GraphAPIError => @right_err
79
+ rescue Exception => wrong_err
80
+ end
81
+ @right_err.should_not be_nil
82
+ end
83
+
84
+ it "should not be able to like an object" do
85
+ begin
86
+ @result = @graph.put_like("7204941866_119776748033392")
87
+ rescue Koala::Facebook::GraphAPIError => @right_err
88
+ rescue Exception => wrong_err
89
+ end
90
+ @right_err.should_not be_nil
91
+ end
92
+
93
+
94
+ # DELETE
95
+ it "should not be able to delete posts" do
96
+ begin
97
+ # test post on the Ruby SDK Test application
98
+ @result = @graph.delete_object("115349521819193_113815981982767")
99
+ rescue Koala::Facebook::GraphAPIError => @right_err
100
+ rescue Exception => wrong_err
101
+ end
102
+ @right_err.should_not be_nil
103
+ end
104
+
105
+ # SEARCH
106
+ it "should be able to search" do
107
+ result = @graph.search("facebook")
108
+ result["data"].should be_an(Array)
109
+ end
110
+
111
+ # FQL
112
+ it "should be able to access public information via FQL" do
113
+ @result = @graph.fql('select first_name from user where uid = 216743')
114
+ @result.size.should == 1
115
+ @result.first['first_name'].should == 'Chris'
116
+ end
117
+
118
+ it "should not be able to access protected information via FQL" do
119
+ lambda { @graph.fql("select read_stream from permissions where uid = 216743") }.should raise_error(Koala::Facebook::RestAPIError)
120
+ end
121
+
122
+ # API
123
+ # the above tests test this already, but we should consider additional api tests
124
+
125
+ end # describe
126
+
127
+ end #class
128
+
@@ -0,0 +1,202 @@
1
+ # stub the Time class to always return a time for which the valid cookie is still valid
2
+ class Time
3
+ def self.now
4
+ self
5
+ end
6
+
7
+ def self.to_i
8
+ 1273363199
9
+ end
10
+ end
11
+
12
+ class FacebookOAuthTests < Test::Unit::TestCase
13
+ describe "Koala GraphAPI without an access token" do
14
+ before :each do
15
+ # make the relevant test data easily accessible
16
+ @oauth_data = $testing_data["oauth_test_data"]
17
+ @app_id = @oauth_data["app_id"]
18
+ @secret = @oauth_data["secret"]
19
+ @code = @oauth_data["code"]
20
+ @callback_url = @oauth_data["callback_url"]
21
+ @raw_token_string = @oauth_data["raw_token_string"]
22
+ @raw_offline_access_token_string = @oauth_data["raw_offline_access_token_string"]
23
+
24
+ # this should expanded to cover all variables
25
+ raise Exception, "Must supply app data to run FacebookOAuthTests!" unless @app_id && @secret && @callback_url &&
26
+ @code && @raw_token_string &&
27
+ @raw_offline_access_token_string
28
+
29
+ @oauth = Koala::Facebook::OAuth.new(@app_id, @secret, @callback_url)
30
+ end
31
+
32
+ # initialization
33
+ it "should properly initialize" do
34
+ @oauth.should
35
+ end
36
+
37
+ it "should properly set attributes" do
38
+ (@oauth.app_id == @app_id &&
39
+ @oauth.app_secret == @secret &&
40
+ @oauth.oauth_callback_url == @callback_url).should be_true
41
+ end
42
+
43
+ it "should properly initialize without a callback_url" do
44
+ @oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
45
+ end
46
+
47
+ it "should properly set attributes without a callback URL" do
48
+ @oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
49
+ (@oauth.app_id == @app_id &&
50
+ @oauth.app_secret == @secret &&
51
+ @oauth.oauth_callback_url == nil).should be_true
52
+ end
53
+
54
+ # cookie parsing
55
+ it "should properly parse valid cookies" do
56
+ result = @oauth.get_user_from_cookie(@oauth_data["valid_cookies"])
57
+ result["uid"].should
58
+ end
59
+
60
+ it "should return all the cookie components from valid cookie string" do
61
+ cookie_data = @oauth_data["valid_cookies"]
62
+ parsing_results = @oauth.get_user_from_cookie(cookie_data)
63
+ number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
64
+ parsing_results.length.should == number_of_components
65
+ end
66
+
67
+ it "should properly parse valid offline access cookies (e.g. no expiration)" do
68
+ result = @oauth.get_user_from_cookie(@oauth_data["offline_access_cookies"])
69
+ result["uid"].should
70
+ end
71
+
72
+ it "should return all the cookie components from offline access cookies" do
73
+ cookie_data = @oauth_data["offline_access_cookies"]
74
+ parsing_results = @oauth.get_user_from_cookie(cookie_data)
75
+ number_of_components = cookie_data["fbs_#{@app_id.to_s}"].scan(/\=/).length
76
+ parsing_results.length.should == number_of_components
77
+ end
78
+
79
+ it "shouldn't parse expired cookies" do
80
+ result = @oauth.get_user_from_cookie(@oauth_data["expired_cookies"])
81
+ result.should be_nil
82
+ end
83
+
84
+ it "shouldn't parse invalid cookies" do
85
+ # make an invalid string by replacing some values
86
+ bad_cookie_hash = @oauth_data["valid_cookies"].inject({}) { |hash, value| hash[value[0]] = value[1].gsub(/[0-9]/, "3") }
87
+ result = @oauth.get_user_from_cookie(bad_cookie_hash)
88
+ result.should be_nil
89
+ end
90
+
91
+ # OAuth URLs
92
+
93
+ # url_for_oauth_code
94
+ it "should generate a properly formatted OAuth code URL with the default values" do
95
+ url = @oauth.url_for_oauth_code
96
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}"
97
+ end
98
+
99
+ it "should generate a properly formatted OAuth code URL when a callback is given" do
100
+ callback = "foo.com"
101
+ url = @oauth.url_for_oauth_code(:callback => callback)
102
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}"
103
+ end
104
+
105
+ it "should generate a properly formatted OAuth code URL when permissions are requested as a string" do
106
+ permissions = "publish_stream,read_stream"
107
+ url = @oauth.url_for_oauth_code(:permissions => permissions)
108
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}&scope=#{permissions}"
109
+ end
110
+
111
+ it "should generate a properly formatted OAuth code URL when permissions are requested as a string" do
112
+ permissions = ["publish_stream", "read_stream"]
113
+ url = @oauth.url_for_oauth_code(:permissions => permissions)
114
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{@callback_url}&scope=#{permissions.join(",")}"
115
+ end
116
+
117
+ it "should generate a properly formatted OAuth code URL when both permissions and callback are provided" do
118
+ permissions = "publish_stream,read_stream"
119
+ callback = "foo.com"
120
+ url = @oauth.url_for_oauth_code(:callback => callback, :permissions => permissions)
121
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}&scope=#{permissions}"
122
+ end
123
+
124
+ it "should raise an exception if no callback is given in initialization or the call" do
125
+ oauth2 = Koala::Facebook::OAuth.new(@app_id, @secret)
126
+ begin
127
+ url = oauth2.url_for_oauth_code
128
+ rescue ArgumentError => @right_err
129
+ rescue
130
+ end
131
+
132
+ @right_err.should
133
+ end
134
+
135
+ # url_for_access_token
136
+ it "should generate a properly formatted OAuth token URL when provided a code" do
137
+ url = @oauth.url_for_access_token(@code)
138
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{@callback_url}&client_secret=#{@secret}&code=#{@code}"
139
+ end
140
+
141
+ it "should generate a properly formatted OAuth token URL when provided a callback" do
142
+ callback = "foo.com"
143
+ url = @oauth.url_for_access_token(@code, :callback => callback)
144
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@secret}&code=#{@code}"
145
+ end
146
+
147
+ it "should output a deprecation warning but generate a properly formatted OAuth token URL when provided a callback in the deprecated fashion" do
148
+ callback = "foo.com"
149
+ url = out = nil
150
+
151
+ begin
152
+ # we want to capture the deprecation warning as well as the output
153
+ # credit to http://thinkingdigitally.com/archive/capturing-output-from-puts-in-ruby/ for the technique
154
+ out = StringIO.new
155
+ $stdout = out
156
+ url = @oauth.url_for_access_token(@code, callback)
157
+ ensure
158
+ $stdout = STDOUT
159
+ end
160
+
161
+ # two assertions may be bad test writing, but this is for a deprecated method
162
+ url.should == "https://#{Koala::Facebook::GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@secret}&code=#{@code}"
163
+ out.should_not be_nil
164
+ end
165
+
166
+ # parse_access_token
167
+ it "should properly parse access token results" do
168
+ result = @oauth.parse_access_token(@raw_token_string)
169
+ has_both_parts = result["access_token"] && result["expires"]
170
+ has_both_parts.should
171
+ end
172
+
173
+ it "should properly parse offline access token results" do
174
+ result = @oauth.parse_access_token(@raw_offline_access_token_string)
175
+ has_both_parts = result["access_token"] && !result["expires"]
176
+ has_both_parts.should
177
+ end
178
+
179
+ # fetch_token_string
180
+ # note -- we just test that this looks right, since get_access_token tests the parsing too
181
+ it "should fetch a proper token string from Facebook when given a code" do
182
+ result = @oauth.fetch_token_string(@code)
183
+ result.should =~ /^access_token/
184
+ end
185
+
186
+ # get_access_token
187
+ it "should properly get and parse an access token token results" do
188
+ result = @oauth.get_access_token(@code)
189
+ result["access_token"].should
190
+ end
191
+
192
+ it "should raise an error when get_access_token is called with a bad code" do
193
+ begin
194
+ result = @oauth.get_access_token("foo")
195
+ rescue Koala::Facebook::GraphAPIError => @right_err
196
+ rescue
197
+ end
198
+ @right_err.should
199
+ end
200
+ end # describe
201
+
202
+ end #class
@@ -0,0 +1,155 @@
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::Facebook::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
+ result = @graph.delete_object(@temporary_object_id)
14
+ raise "Unable to clean up temporary Graph object #{@temporary_object_id}!" unless result
15
+ end
16
+ end
17
+
18
+ it "should get public data about a user" do
19
+ result = @graph.get_object("koppel")
20
+ # the results should have an ID and a name, among other things
21
+ (result["id"] && result["name"]).should_not be_nil
22
+ end
23
+
24
+ it "should get private data about a user" do
25
+ result = @graph.get_object("koppel")
26
+ # updated_time should be a pretty fixed test case
27
+ result["updated_time"].should_not be_nil
28
+ end
29
+
30
+ it "should get public data about a Page" do
31
+ result = @graph.get_object("contextoptional")
32
+ # the results should have an ID and a name, among other things
33
+ (result["id"] && result["name"]).should
34
+ end
35
+
36
+ it "should get data about 'me'" do
37
+ result = @graph.get_object("me")
38
+ result["updated_time"].should
39
+ end
40
+
41
+ it "should be able to get multiple objects" do
42
+ result = @graph.get_objects(["contextoptional", "naitik"])
43
+ result.length.should == 2
44
+ end
45
+
46
+ it "should be able to access connections from users" do
47
+ result = @graph.get_connections("lukeshepard", "likes")
48
+ result["data"].length.should > 0
49
+ end
50
+
51
+ it "should be able to access connections from public Pages" do
52
+ result = @graph.get_connections("contextoptional", "likes")
53
+ result["data"].should be_a(Array)
54
+ end
55
+
56
+ # PUT
57
+ it "should be able to write an object to the graph" do
58
+ result = @graph.put_wall_post("Hello, world, from the test suite!")
59
+ @temporary_object_id = result["id"]
60
+ @temporary_object_id.should_not be_nil
61
+ end
62
+
63
+ # DELETE
64
+ it "should be able to delete posts" do
65
+ result = @graph.put_wall_post("Hello, world, from the test suite delete method!")
66
+ object_id_to_delete = result["id"]
67
+ delete_result = @graph.delete_object(object_id_to_delete)
68
+ delete_result.should == true
69
+ end
70
+
71
+ # additional put tests
72
+ it "should be able to verify messages posted to a wall" do
73
+ message = "the cats are asleep"
74
+ put_result = @graph.put_wall_post(message)
75
+ @temporary_object_id = put_result["id"]
76
+ get_result = @graph.get_object(@temporary_object_id)
77
+
78
+ # make sure the message we sent is the message that got posted
79
+ get_result["message"].should == message
80
+ end
81
+
82
+ it "should be able to post a message with an attachment to a feed" do
83
+ result = @graph.put_wall_post("Hello, world, from the test suite again!", {:name => "Context Optional", :link => "http://www.contextoptional.com/"})
84
+ @temporary_object_id = result["id"]
85
+ @temporary_object_id.should_not be_nil
86
+ end
87
+
88
+ it "should be able to verify a message with an attachment posted to a feed" do
89
+ attachment = {"name" => "Context Optional", "link" => "http://www.contextoptional.com/"}
90
+ result = @graph.put_wall_post("Hello, world, from the test suite again!", attachment)
91
+ @temporary_object_id = result["id"]
92
+ get_result = @graph.get_object(@temporary_object_id)
93
+
94
+ # make sure the result we fetch includes all the parameters we sent
95
+ it_matches = attachment.inject(true) {|valid, param| valid && (get_result[param[0]] == attachment[param[0]])}
96
+ it_matches.should == true
97
+ end
98
+
99
+ it "should be able to comment on an object" do
100
+ result = @graph.put_wall_post("Hello, world, from the test suite, testing comments!")
101
+ @temporary_object_id = result["id"]
102
+
103
+ # this will be deleted when the post gets deleted
104
+ comment_result = @graph.put_comment(@temporary_object_id, "it's my comment!")
105
+ comment_result.should_not be_nil
106
+ end
107
+
108
+ it "should be able to verify a comment posted about an object" do
109
+ message_text = "Hello, world, from the test suite, testing comments!"
110
+ result = @graph.put_wall_post(message_text)
111
+ @temporary_object_id = result["id"]
112
+
113
+ # this will be deleted when the post gets deleted
114
+ comment_text = "it's my comment!"
115
+ comment_result = @graph.put_comment(@temporary_object_id, comment_text)
116
+ get_result = @graph.get_object(comment_result["id"])
117
+
118
+ # make sure the text of the comment matches what we sent
119
+ get_result["message"].should == comment_text
120
+ end
121
+
122
+ it "should be able to like an object" do
123
+ result = @graph.put_wall_post("Hello, world, from the test suite, testing comments!")
124
+ @temporary_object_id = result["id"]
125
+ like_result = @graph.put_like(@temporary_object_id)
126
+ end
127
+
128
+ # SEARCH
129
+ it "should be able to search" do
130
+ result = @graph.search("facebook")
131
+ result["data"].should be_an(Array)
132
+ end
133
+
134
+ # FQL
135
+ it "should be able to access public information via FQL" do
136
+ result = @graph.fql('select first_name from user where uid = 216743')
137
+ result.size.should == 1
138
+ result.first['first_name'].should == 'Chris'
139
+ end
140
+
141
+ it "should be able to access protected information via FQL" do
142
+ # Tests agains read_stream since these tests assume we have that permission
143
+ YOUR_ID = 216743
144
+ result = @graph.fql("select read_stream from permissions where uid = #{YOUR_ID}")
145
+
146
+ result.size.should == 1
147
+ result.first['read_stream'].should == 1
148
+ end
149
+
150
+ # API
151
+ # the above tests test this already, but we should consider additional api tests
152
+
153
+ end # describe
154
+
155
+ end #class
@@ -0,0 +1,35 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'spec/test/unit'
4
+
5
+ # load the libraries
6
+ require 'koala'
7
+
8
+ # load the tests
9
+ require 'koala/facebook_no_access_token_tests'
10
+ require 'koala/facebook_with_access_token_tests'
11
+ require 'koala/facebook_oauth_tests'
12
+
13
+ class FacebookTestSuite
14
+ def self.suite
15
+ suite = Test::Unit::TestSuite.new
16
+ suite << FacebookNoAccessTokenTests.suite
17
+ suite << FacebookWithAccessTokenTests.suite
18
+ suite << FacebookOAuthTests.suite
19
+ suite
20
+ end
21
+ end
22
+
23
+ # load testing data (see note in readme.md)
24
+ # I'm seeing a bug with spec and gets where the facebook_test_suite.rb file gets read in when gets is called
25
+ # until that's solved, we'll need to store/update tokens in the access_token file
26
+ $testing_data = YAML.load_file("facebook_data.yml") rescue {}
27
+
28
+ unless $testing_data["oauth_token"]
29
+ puts "Access token tests will fail until you store a valid token in facebook_data.yml"
30
+ end
31
+
32
+ # TODO finish tests for OAuth class
33
+ # unless $testing_data["cookie_hash"] && $testing_data["app_id"] && $testing_data["secret"]
34
+ # puts "Cookie tests will fail until you store valid data for the cookie hash, app_id, and app secret in facebook_data.yml"
35
+ # end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cbaclig-koala
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 5
8
+ - 0
9
+ - 1
10
+ version: 0.5.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Alex Koppel, Rafi Jacoby, Context Optional
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-11 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ 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.
23
+ email: alex@alexkoppel.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - CHANGELOG
30
+ - lib/http_services.rb
31
+ - lib/koala.rb
32
+ files:
33
+ - CHANGELOG
34
+ - Manifest
35
+ - Rakefile
36
+ - init.rb
37
+ - lib/http_services.rb
38
+ - lib/koala.rb
39
+ - readme.md
40
+ - test/facebook_data.yml
41
+ - test/koala/facebook_no_access_token_tests.rb
42
+ - test/koala/facebook_oauth_tests.rb
43
+ - test/koala/facebook_with_access_token_tests.rb
44
+ - test/koala_tests.rb
45
+ - cbaclig-koala.gemspec
46
+ has_rdoc: true
47
+ homepage: http://github.com/arsduo/koala
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --line-numbers
53
+ - --inline-source
54
+ - --title
55
+ - Cbaclig-koala
56
+ - --main
57
+ - readme.md
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 1
73
+ - 2
74
+ version: "1.2"
75
+ requirements: []
76
+
77
+ rubyforge_project: cbaclig-koala
78
+ rubygems_version: 1.3.6
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: A lightweight, flexible library for Facebook's new Graph API
82
+ test_files: []
83
+