koala 1.0.0.beta2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG +15 -12
  3. data/Gemfile +3 -0
  4. data/LICENSE +1 -1
  5. data/Rakefile +13 -14
  6. data/koala.gemspec +35 -20
  7. data/lib/koala/graph_api.rb +2 -2
  8. data/lib/koala/http_services.rb +32 -27
  9. data/lib/koala/test_users.rb +4 -4
  10. data/lib/koala/uploadable_io.rb +1 -1
  11. data/lib/koala.rb +29 -54
  12. data/readme.md +15 -8
  13. data/spec/cases/api_base_spec.rb +101 -0
  14. data/spec/cases/graph_and_rest_api_spec.rb +31 -0
  15. data/spec/cases/graph_api_spec.rb +25 -0
  16. data/spec/{koala/http_services/http_service_tests.rb → cases/http_services/http_service_spec.rb} +8 -5
  17. data/spec/cases/http_services/net_http_service_spec.rb +350 -0
  18. data/spec/cases/http_services/typhoeus_service_spec.rb +144 -0
  19. data/spec/cases/oauth_spec.rb +409 -0
  20. data/spec/cases/realtime_updates_spec.rb +184 -0
  21. data/spec/cases/rest_api_spec.rb +25 -0
  22. data/spec/{koala/test_users/test_users_tests.rb → cases/test_users_spec.rb} +34 -29
  23. data/spec/cases/uploadable_io_spec.rb +151 -0
  24. data/spec/{facebook_data.yml → fixtures/facebook_data.yml} +5 -5
  25. data/spec/{mock_facebook_responses.yml → fixtures/mock_facebook_responses.yml} +313 -311
  26. data/spec/spec_helper.rb +18 -0
  27. data/spec/support/graph_api_shared_examples.rb +424 -0
  28. data/spec/{koala → support}/live_testing_data_helper.rb +39 -42
  29. data/spec/{mock_http_service.rb → support/mock_http_service.rb} +94 -94
  30. data/spec/{koala/rest_api/rest_api_tests.rb → support/rest_api_shared_examples.rb} +43 -0
  31. data/spec/support/setup_mocks_or_live.rb +52 -0
  32. data/spec/support/uploadable_io_shared_examples.rb +76 -0
  33. metadata +106 -53
  34. data/init.rb +0 -2
  35. data/spec/koala/api_base_tests.rb +0 -102
  36. data/spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb +0 -14
  37. data/spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb +0 -16
  38. data/spec/koala/graph_api/graph_api_no_access_token_tests.rb +0 -65
  39. data/spec/koala/graph_api/graph_api_tests.rb +0 -85
  40. data/spec/koala/graph_api/graph_api_with_access_token_tests.rb +0 -194
  41. data/spec/koala/graph_api/graph_collection_tests.rb +0 -104
  42. data/spec/koala/http_services/net_http_service_tests.rb +0 -339
  43. data/spec/koala/http_services/typhoeus_service_tests.rb +0 -162
  44. data/spec/koala/oauth/oauth_tests.rb +0 -372
  45. data/spec/koala/realtime_updates/realtime_updates_tests.rb +0 -187
  46. data/spec/koala/rest_api/rest_api_no_access_token_tests.rb +0 -25
  47. data/spec/koala/rest_api/rest_api_with_access_token_tests.rb +0 -38
  48. data/spec/koala/uploadable_io/uploadable_io_tests.rb +0 -246
  49. data/spec/koala_spec.rb +0 -18
  50. data/spec/koala_spec_helper.rb +0 -74
  51. data/spec/koala_spec_without_mocks.rb +0 -19
  52. /data/spec/{koala/assets → fixtures}/beach.jpg +0 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg
2
+ .project
3
+ Gemfile.lock
data/CHANGELOG CHANGED
@@ -1,26 +1,29 @@
1
1
  v1.0
2
2
  New methods:
3
3
  -- Photo and file upload now supported through #put_picture
4
- -- Beta 2: support for Rails 3, Sinatra, File objects, and paths (see documentation)
5
- -- Beta 2: Added UploadableIO class to manage file uploads
6
- -- Added a delete_like method (thanks to wassem)
7
- -- Beta 2: Added put_connection and delete_connection convenience methods
4
+ -- Added UploadableIO class to manage file uploads
5
+ -- Added a delete_like method (thanks to waseem)
6
+ -- Added put_connection and delete_connection convenience methods
8
7
  Updated methods:
9
- -- Beta 2: TestUser#befriend now requires user info hashes (id and access token) due to Facebook API changes (thanks to pulsd/kbighorse)
10
- -- Beta 2: all methods now accept an http_options hash as their optional last parameter
11
- -- Beta 2: You can now pass :beta => true in the http options to use Facebook's beta tier.
8
+ -- Search can now search places, checkins, etc. (thanks, rickyc!)
9
+ -- You can now pass :beta => true in the http options to use Facebook's beta tier.
10
+ -- TestUser#befriend now requires user info hashes (id and access token) due to Facebook API changes (thanks, pulsd and kbighorse!)
11
+ -- All methods now accept an http_options hash as their optional last parameter (thanks, spiegela!)
12
+ -- url_for_oauth_code can now take a :display option (thanks, netbe!)
13
+ -- Net::HTTP can now accept :timeout and :proxy options (thanks, gilles!)
14
+ -- Test users now supports using test accounts across multiple apps
12
15
  Internal improvements:
13
16
  -- For public requests, Koala now uses http by default (instead of https) to improve speed
14
17
  -- This can be overridden through Koala.always_use_ssl= or by passing :use_ssl => true in the options hash for an api call
15
18
  -- Read-only REST API requests now go through the faster api-read server
16
19
  -- Replaced parse_signed_request with a version from Facebook that also supports the new signed params proposal
17
20
  -- Note: invalid requests will now raise exceptions rather than return nil, in keeping with other SDKs
18
- -- delete_object will now raise an error if there's no access token (like put_object and delete_like)
19
- -- Beta 2: photo and file uploads now accept symbols as well as strings overall
20
- -- Beta 2: APIError is now < StandardError, not Exception
21
- -- Beta 2: Added KoalaError for non-API errors
22
- -- Beta 3: Updated parse_signed_request to match Facebook's current implementation (thanks, imajes!)
21
+ -- Delete methods will now raise an error if there's no access token (like put_object and delete_like)
22
+ -- Updated parse_signed_request to match Facebook's current implementation (thanks, imajes!)
23
+ -- APIError is now < StandardError, not Exception
24
+ -- Added KoalaError for non-API errors
23
25
  Test improvements:
26
+ -- Incorporated joshk's awesome rewrite of the entire Koala test suite (thanks, joshk!)
24
27
  -- Expanded HTTP service tests (added Typhoeus test suite and additional Net::HTTP test cases)
25
28
  -- Live tests now verify that the access token has the necessary permissions before starting
26
29
  -- Replaced the 50-person network test, which often took 15+ minutes to run live, with a 5-person test
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright (c) 2010 Alex Koppel
3
+ Copyright (c) 2010-2011 Alex Koppel
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,17 +1,16 @@
1
- # Rakefile
2
- require 'rubygems'
3
1
  require 'rake'
4
- require 'echoe'
5
2
 
6
- # gem management
7
- Echoe.new('koala', '1.0.0.beta2.1') do |p|
8
- p.summary = "A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation."
9
- p.description = "Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services."
10
- p.url = "http://github.com/arsduo/koala"
11
- p.author = ["Alex Koppel", "Chris Baclig", "Rafi Jacoby", "Context Optional"]
12
- p.email = "alex@alexkoppel.com"
13
- p.ignore_pattern = ["tmp/*", "script/*", "pkg/*"]
14
- p.runtime_dependencies = ["json >=1.0", "multipart-post >=1.0"]
15
- p.development_dependencies = []
16
- p.retain_gemspec = true
3
+ begin
4
+ require 'bundler/setup'
5
+ Bundler::GemHelper.install_tasks
6
+ rescue LoadError
7
+ puts 'although not required, bundler is recommened for running the tests'
8
+ end
9
+
10
+
11
+ task :default => :spec
12
+
13
+ require 'rspec/core/rake_task'
14
+ RSpec::Core::RakeTask.new do |t|
15
+ t.rspec_opts = ["--color", '--format doc']
17
16
  end
data/koala.gemspec CHANGED
@@ -1,35 +1,50 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  Gem::Specification.new do |s|
4
- s.name = %q{koala}
5
- s.version = "1.0.0.beta2.1"
4
+ s.name = %q{koala}
5
+ s.version = "1.0.0"
6
+ s.date = %q{2011-05-01}
7
+
8
+ s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the REST API, realtime updates, and OAuth authentication.}
9
+ s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph and REST APIs, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
10
+ s.homepage = %q{http://github.com/arsduo/koala}
6
11
 
7
- s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
12
  s.authors = ["Alex Koppel, Chris Baclig, Rafi Jacoby, Context Optional"]
9
- s.date = %q{2011-04-06}
10
- s.description = %q{Koala is a lightweight, flexible Ruby SDK for Facebook. It allows read/write access to the social graph via the Graph API and the older REST API, as well as support for realtime updates and OAuth and Facebook Connect authentication. Koala is fully tested and supports Net::HTTP and Typhoeus connections out of the box and can accept custom modules for other services.}
11
- s.email = %q{alex@alexkoppel.com}
12
- s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb", "lib/koala/uploadable_io.rb"]
13
- s.files = ["CHANGELOG", "LICENSE", "Manifest", "Rakefile", "init.rb", "koala.gemspec", "lib/koala.rb", "lib/koala/graph_api.rb", "lib/koala/http_services.rb", "lib/koala/realtime_updates.rb", "lib/koala/rest_api.rb", "lib/koala/test_users.rb", "lib/koala/uploadable_io.rb", "readme.md", "spec/facebook_data.yml", "spec/koala/api_base_tests.rb", "spec/koala/assets/beach.jpg", "spec/koala/graph_and_rest_api/graph_and_rest_api_no_token_tests.rb", "spec/koala/graph_and_rest_api/graph_and_rest_api_with_token_tests.rb", "spec/koala/graph_api/graph_api_no_access_token_tests.rb", "spec/koala/graph_api/graph_api_tests.rb", "spec/koala/graph_api/graph_api_with_access_token_tests.rb", "spec/koala/graph_api/graph_collection_tests.rb", "spec/koala/http_services/http_service_tests.rb", "spec/koala/http_services/net_http_service_tests.rb", "spec/koala/http_services/typhoeus_service_tests.rb", "spec/koala/live_testing_data_helper.rb", "spec/koala/oauth/oauth_tests.rb", "spec/koala/realtime_updates/realtime_updates_tests.rb", "spec/koala/rest_api/rest_api_no_access_token_tests.rb", "spec/koala/rest_api/rest_api_tests.rb", "spec/koala/rest_api/rest_api_with_access_token_tests.rb", "spec/koala/test_users/test_users_tests.rb", "spec/koala/uploadable_io/uploadable_io_tests.rb", "spec/koala_spec.rb", "spec/koala_spec_helper.rb", "spec/koala_spec_without_mocks.rb", "spec/mock_facebook_responses.yml", "spec/mock_http_service.rb"]
14
- s.homepage = %q{http://github.com/arsduo/koala}
15
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala"]
16
- s.require_paths = ["lib"]
17
- s.rubyforge_project = %q{koala}
18
- s.rubygems_version = %q{1.4.2}
19
- s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the old REST API, realtime updates, and OAuth validation.}
13
+ s.email = %q{alex@alexkoppel.com}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+
18
+ s.extra_rdoc_files = ["readme.md", "CHANGELOG"]
19
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala"]
20
+
21
+ s.require_paths = ["lib"]
22
+
23
+ s.rubygems_version = %q{1.4.2}
24
+
25
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
20
26
 
21
27
  if s.respond_to? :specification_version then
22
28
  s.specification_version = 3
23
29
 
24
30
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
25
- s.add_runtime_dependency(%q<json>, [">= 1.0"])
26
- s.add_runtime_dependency(%q<multipart-post>, [">= 1.0"])
31
+ s.add_runtime_dependency(%q<json>, ["~> 1.0"])
32
+ s.add_runtime_dependency(%q<multipart-post>, ["~> 1.0"])
33
+ s.add_development_dependency(%q<rspec>, ["~> 2.5.0"])
34
+ s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
35
+ s.add_development_dependency(%q<typhoeus>, ["~> 0.2.4"])
27
36
  else
28
- s.add_dependency(%q<json>, [">= 1.0"])
29
- s.add_dependency(%q<multipart-post>, [">= 1.0"])
37
+ s.add_dependency(%q<json>, ["~> 1.0"])
38
+ s.add_dependency(%q<multipart-post>, ["~> 1.0"])
39
+ s.add_dependency(%q<rspec>, ["~> 2.5.0"])
40
+ s.add_dependency(%q<rake>, ["~> 0.8.7"])
41
+ s.add_dependency(%q<typhoeus>, ["~> 0.2.4"])
30
42
  end
31
43
  else
32
- s.add_dependency(%q<json>, [">= 1.0"])
33
- s.add_dependency(%q<multipart-post>, [">= 1.0"])
44
+ s.add_dependency(%q<json>, ["~> 1.0"])
45
+ s.add_dependency(%q<multipart-post>, ["~> 1.0"])
46
+ s.add_dependency(%q<rspec>, ["~> 2.5.0"])
47
+ s.add_dependency(%q<rake>, ["~> 0.8.7"])
48
+ s.add_dependency(%q<typhoeus>, ["~> 0.2.4"])
34
49
  end
35
50
  end
@@ -171,8 +171,8 @@ module Koala
171
171
  # Search
172
172
 
173
173
  def search(search_terms, args = {}, options = {})
174
- # Searches for a given term among posts visible to the current user (or public posts if no token)
175
- result = graph_call("search", args.merge(:q => search_terms), "get", options)
174
+ args.merge!({:q => search_terms}) unless search_terms.nil?
175
+ result = graph_call("search", args, "get", options)
176
176
  result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
177
177
  end
178
178
 
@@ -54,7 +54,7 @@ module Koala
54
54
  # if the verb isn't get or post, send it as a post argument
55
55
  args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
56
56
 
57
- http = Net::HTTP.new(server(options), private_request ? 443 : nil)
57
+ http = create_http(server(options), private_request, options)
58
58
  http.use_ssl = true if private_request
59
59
 
60
60
  result = http.start do |http|
@@ -87,6 +87,22 @@ module Koala
87
87
  [key, value.kind_of?(Koala::UploadableIO) ? value.to_upload_io : value]
88
88
  end.flatten]
89
89
  end
90
+
91
+ def self.create_http(server, private_request, options)
92
+ if options[:proxy]
93
+ proxy = URI.parse(options[:proxy])
94
+ http = Net::HTTP.new(server, private_request ? 443 : nil,
95
+ proxy.host, proxy.port, proxy.user, proxy.password)
96
+ else
97
+ http = Net::HTTP.new(server, private_request ? 443 : nil)
98
+ end
99
+ if options[:timeout]
100
+ http.open_timeout = options[:timeout]
101
+ http.read_timeout = options[:timeout]
102
+ end
103
+ http
104
+ end
105
+
90
106
  end
91
107
  end
92
108
  end
@@ -94,11 +110,6 @@ module Koala
94
110
  module TyphoeusService
95
111
  # this service uses Typhoeus to send requests to the graph
96
112
 
97
- # used for multipart file uploads (see below)
98
- class NetHTTPInterface
99
- include NetHTTPService
100
- end
101
-
102
113
  def self.included(base)
103
114
  base.class_eval do
104
115
  require "typhoeus" unless defined?(Typhoeus)
@@ -107,34 +118,28 @@ module Koala
107
118
  include Koala::HTTPService
108
119
 
109
120
  def self.make_request(path, args, verb, options = {})
110
- unless params_require_multipart?(args)
111
- # if the verb isn't get or post, send it as a post argument
112
- args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
121
+ # if the verb isn't get or post, send it as a post argument
122
+ args.merge!({:method => verb}) && verb = "post" if verb != "get" && verb != "post"
113
123
 
114
- # switch any UploadableIOs to the files Typhoeus expects
115
- # args.each_pair {|key, value| args[key] = value.to_file if value.is_a?(UploadableIO)}
124
+ # switch any UploadableIOs to the files Typhoeus expects
125
+ args.each_pair {|key, value| args[key] = value.to_file if value.is_a?(UploadableIO)}
116
126
 
117
- # you can pass arguments directly to Typhoeus using the :typhoeus_options key
118
- typhoeus_options = {:params => args}.merge(options[:typhoeus_options] || {})
127
+ # you can pass arguments directly to Typhoeus using the :typhoeus_options key
128
+ typhoeus_options = {:params => args}.merge(options[:typhoeus_options] || {})
119
129
 
120
- # by default, we use SSL only for private requests (e.g. with access token)
121
- # this makes public requests faster
122
- prefix = (args["access_token"] || @always_use_ssl || options[:use_ssl]) ? "https" : "http"
130
+ # by default, we use SSL only for private requests (e.g. with access token)
131
+ # this makes public requests faster
132
+ prefix = (args["access_token"] || @always_use_ssl || options[:use_ssl]) ? "https" : "http"
123
133
 
124
- response = self.send(verb, "#{prefix}://#{server(options)}#{path}", typhoeus_options)
125
- Koala::Response.new(response.code, response.body, response.headers_hash)
126
- else
127
- # we have to use NetHTTPService for multipart for file uploads
128
- # until Typhoeus integrates support
129
- Koala::TyphoeusService::NetHTTPInterface.make_request(path, args, verb, options)
130
- end
134
+ response = self.send(verb, "#{prefix}://#{server(options)}#{path}", typhoeus_options)
135
+ Koala::Response.new(response.code, response.body, response.headers_hash)
131
136
  end
132
137
 
133
138
  private
134
- # Typhoeus file uploads are not currently supported; this will be added when we can get them working
135
- #def self.multipart_requires_content_type?
136
- # false # Typhoeus handles multipart file types, we don't have to require it
137
- #end
139
+
140
+ def self.multipart_requires_content_type?
141
+ false # Typhoeus handles multipart file types, we don't have to require it
142
+ end
138
143
  end # class_eval
139
144
  end
140
145
  end
@@ -20,11 +20,11 @@ module Koala
20
20
  @graph_api = GraphAPI.new(@app_access_token)
21
21
  end
22
22
 
23
- def create(installed, permissions = nil)
23
+ def create(installed, permissions = nil, args = {}, options = {})
24
24
  # Creates and returns a test user
25
- args = {'installed' => installed}
25
+ args['installed'] = installed
26
26
  args['permissions'] = (permissions.is_a?(Array) ? permissions.join(",") : permissions) if installed
27
- result = @graph_api.graph_call(accounts_path, args, "post")
27
+ result = @graph_api.graph_call(accounts_path, args, "post", options)
28
28
  end
29
29
 
30
30
  def list
@@ -49,7 +49,7 @@ module Koala
49
49
  # we explicitly raise an error here to minimize the risk of confusing output
50
50
  # if you pass in a string (as was previously supported) no local exception would be raised
51
51
  # but the Facebook call would fail
52
- raise ArgumentError, "TestUsers#befriend requires hash arguments with id and access_token"
52
+ raise ArgumentError, "TestUsers#befriend requires hash arguments for both users with id and access_token"
53
53
  end
54
54
 
55
55
  u1_graph_api = GraphAPI.new(user1_token)
@@ -42,7 +42,7 @@ module Koala
42
42
 
43
43
  # Expects a parameter of type ActionDispatch::Http::UploadedFile
44
44
  def parse_rails_3_param(uploaded_file)
45
- if uploaded_file.respond_to?(:content_type) and uploaded_file.respond_to?(:tempfile) and uploaded_file.respond_to?(:path)
45
+ if uploaded_file.respond_to?(:content_type) and uploaded_file.respond_to?(:tempfile) and uploaded_file.tempfile.respond_to?(:path)
46
46
  @io_or_path = uploaded_file.tempfile.path
47
47
  @content_type = uploaded_file.content_type
48
48
  end
data/lib/koala.rb CHANGED
@@ -1,27 +1,17 @@
1
1
  require 'cgi'
2
2
  require 'digest/md5'
3
3
 
4
- # rubygems is required to support json, how facebook returns data
5
- require 'rubygems'
6
4
  require 'json'
7
5
 
8
6
  # OpenSSL and Base64 are required to support signed_request
9
7
  require 'openssl'
10
8
  require 'base64'
11
9
 
12
- # include default http services
10
+ # include koala modules
13
11
  require 'koala/http_services'
14
-
15
- # add Graph API methods
16
12
  require 'koala/graph_api'
17
-
18
- # add REST API methods
19
13
  require 'koala/rest_api'
20
-
21
- # add realtime update methods
22
14
  require 'koala/realtime_updates'
23
-
24
- # add test user methods
25
15
  require 'koala/test_users'
26
16
 
27
17
  # add KoalaIO class
@@ -31,28 +21,12 @@ module Koala
31
21
 
32
22
  module Facebook
33
23
  # Ruby client library for the Facebook Platform.
34
- # Copyright 2010 Facebook
35
- # Adapted from the Python library by Alex Koppel, Rafi Jacoby, and the team at Context Optional
36
- #
37
- # Licensed under the Apache License, Version 2.0 (the "License"); you may
38
- # not use this file except in compliance with the License. You may obtain
39
- # a copy of the License at
40
- # http://www.apache.org/licenses/LICENSE-2.0
41
- #
42
- # Unless required by applicable law or agreed to in writing, software
43
- # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
44
- # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
45
- # License for the specific language governing permissions and limitations
46
- # under the License.
47
- #
48
- # This client library is designed to support the Graph API and the official
49
- # Facebook JavaScript SDK, which is the canonical way to implement
50
- # Facebook authentication. Read more about the Graph API at
51
- # http://developers.facebook.com/docs/api. You can download the Facebook
52
- # JavaScript SDK at http://github.com/facebook/connect-js/.
24
+ # Copyright 2010-2011 Alex Koppel
25
+ # Contributors: Alex Koppel, Chris Baclig, Rafi Jacoby, and the team at Context Optional
26
+ # http://github.com/arsduo/koala
53
27
 
54
28
  class API
55
- # initialize with an access token
29
+ # initialize with an access token
56
30
  def initialize(access_token = nil)
57
31
  @access_token = access_token
58
32
  end
@@ -61,7 +35,7 @@ module Koala
61
35
  def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
62
36
  # Fetches the given path in the Graph API.
63
37
  args["access_token"] = @access_token || @app_access_token if @access_token || @app_access_token
64
-
38
+
65
39
  # add a leading /
66
40
  path = "/#{path}" unless path =~ /^\//
67
41
 
@@ -173,12 +147,13 @@ module Koala
173
147
  # for permissions, see http://developers.facebook.com/docs/authentication/permissions
174
148
  permissions = options[:permissions]
175
149
  scope = permissions ? "&scope=#{permissions.is_a?(Array) ? permissions.join(",") : permissions}" : ""
176
-
150
+ display = options.has_key?(:display) ? "&display=#{options[:display]}" : ""
151
+
177
152
  callback = options[:callback] || @oauth_callback_url
178
153
  raise ArgumentError, "url_for_oauth_code must get a callback either from the OAuth object or in the options!" unless callback
179
154
 
180
155
  # Creates the URL for oauth authorization for a given callback and optional set of permissions
181
- "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}"
156
+ "https://#{GRAPH_SERVER}/oauth/authorize?client_id=#{@app_id}&redirect_uri=#{callback}#{scope}#{display}"
182
157
  end
183
158
 
184
159
  def url_for_access_token(code, options = {})
@@ -188,26 +163,26 @@ module Koala
188
163
  "https://#{GRAPH_SERVER}/oauth/access_token?client_id=#{@app_id}&redirect_uri=#{callback}&client_secret=#{@app_secret}&code=#{code}"
189
164
  end
190
165
 
191
- def get_access_token_info(code)
166
+ def get_access_token_info(code, options = {})
192
167
  # convenience method to get a parsed token from Facebook for a given code
193
168
  # should this require an OAuth callback URL?
194
- get_token_from_server(:code => code, :redirect_uri => @oauth_callback_url)
169
+ get_token_from_server({:code => code, :redirect_uri => @oauth_callback_url}, false, options)
195
170
  end
196
171
 
197
- def get_access_token(code)
172
+ def get_access_token(code, options = {})
198
173
  # upstream methods will throw errors if needed
199
- if info = get_access_token_info(code)
174
+ if info = get_access_token_info(code, options)
200
175
  string = info["access_token"]
201
176
  end
202
177
  end
203
178
 
204
- def get_app_access_token_info
179
+ def get_app_access_token_info(options = {})
205
180
  # convenience method to get a the application's sessionless access token
206
- get_token_from_server({:type => 'client_cred'}, true)
181
+ get_token_from_server({:type => 'client_cred'}, true, options)
207
182
  end
208
183
 
209
- def get_app_access_token
210
- if info = get_app_access_token_info
184
+ def get_app_access_token(options = {})
185
+ if info = get_app_access_token_info(options)
211
186
  string = info["access_token"]
212
187
  end
213
188
  end
@@ -225,19 +200,19 @@ module Koala
225
200
  raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
226
201
 
227
202
  # now see if the signature is valid (digest, key, data)
228
- hmac = OpenSSL::HMAC.hexdigest('sha256', @app_secret, encoded_envelope.tr("-_", "+/"))
203
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope.tr("-_", "+/"))
229
204
  raise 'SignedRequest: Invalid signature' if (signature != hmac)
230
205
 
231
206
  return envelope
232
207
  end
233
208
 
234
209
  # from session keys
235
- def get_token_info_from_session_keys(sessions)
210
+ def get_token_info_from_session_keys(sessions, options = {})
236
211
  # fetch the OAuth tokens from Facebook
237
212
  response = fetch_token_string({
238
213
  :type => 'client_cred',
239
214
  :sessions => sessions.join(",")
240
- }, true, "exchange_sessions")
215
+ }, true, "exchange_sessions", options)
241
216
 
242
217
  # Facebook returns an empty body in certain error conditions
243
218
  if response == ""
@@ -250,24 +225,24 @@ module Koala
250
225
  JSON.parse(response)
251
226
  end
252
227
 
253
- def get_tokens_from_session_keys(sessions)
228
+ def get_tokens_from_session_keys(sessions, options = {})
254
229
  # get the original hash results
255
- results = get_token_info_from_session_keys(sessions)
230
+ results = get_token_info_from_session_keys(sessions, options)
256
231
  # now recollect them as just the access tokens
257
232
  results.collect { |r| r ? r["access_token"] : nil }
258
233
  end
259
234
 
260
- def get_token_from_session_key(session)
235
+ def get_token_from_session_key(session, options = {})
261
236
  # convenience method for a single key
262
237
  # gets the overlaoded strings automatically
263
- get_tokens_from_session_keys([session])[0]
238
+ get_tokens_from_session_keys([session], options)[0]
264
239
  end
265
240
 
266
241
  protected
267
242
 
268
- def get_token_from_server(args, post = false)
243
+ def get_token_from_server(args, post = false, options = {})
269
244
  # fetch the result from Facebook's servers
270
- result = fetch_token_string(args, post)
245
+ result = fetch_token_string(args, post, "access_token", options)
271
246
 
272
247
  # if we have an error, parse the error JSON and raise an error
273
248
  raise APIError.new((JSON.parse(result)["error"] rescue nil) || {}) if result =~ /error/
@@ -284,11 +259,11 @@ module Koala
284
259
  components
285
260
  end
286
261
 
287
- def fetch_token_string(args, post = false, endpoint = "access_token")
262
+ def fetch_token_string(args, post = false, endpoint = "access_token", options = {})
288
263
  Koala.make_request("/oauth/#{endpoint}", {
289
264
  :client_id => @app_id,
290
265
  :client_secret => @app_secret
291
- }.merge!(args), post ? "post" : "get", :use_ssl => true).body
266
+ }.merge!(args), post ? "post" : "get", {:use_ssl => true}.merge!(options)).body
292
267
  end
293
268
 
294
269
  # base 64
@@ -299,7 +274,7 @@ module Koala
299
274
  end
300
275
  end
301
276
  end
302
-
277
+
303
278
  class KoalaError< StandardError; end
304
279
 
305
280
  # finally, set up the http service Koala methods used to make requests
data/readme.md CHANGED
@@ -7,12 +7,16 @@ Koala (<a href="http://github.com/arsduo/koala" target="_blank">http://github.co
7
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
8
  * Tested: Koala should have complete test coverage, so you can rely on it. (Our complete test coverage can be run against either mocked responses or the live Facebook servers.)
9
9
 
10
- 1.0 beta
10
+ 1.0
11
11
  ---
12
- Version 1.0 is currently in beta, chock-full of great new features. To download the beta, just add --pre when installing the gem:
12
+ Version 1.0 is due out on May 1st, 2011 with a ton of great features.
13
13
 
14
- sudo gem install koala --pre
14
+ sudo gem install koala
15
15
 
16
+ Until then, you can install the release candidate like so:
17
+
18
+ sudo gem install koala --pre
19
+
16
20
  Graph API
17
21
  ----
18
22
  The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
@@ -126,11 +130,14 @@ Testing
126
130
  -----
127
131
 
128
132
  Unit tests are provided for all of Koala's methods. By default, these tests run against mock responses and hence are ready out of the box:
129
- # From the spec directory
130
- spec koala_spec.rb
133
+
134
+ # From anywhere in the project directory:
135
+ rake spec
136
+
131
137
 
132
138
  You can also run live tests against Facebook's servers:
133
- # Again from the spec directory
134
- spec koala_spec_without_mocks.rb
139
+
140
+ # Again from anywhere in the project directory:
141
+ LIVE=true rake spec
135
142
 
136
- Important Note: to run the live tests, you have to provide some of your own data: a valid OAuth access token with publish\_stream, read\_stream, and user\_photos permissions and an OAuth code that can be used to generate an access token. You can get these data at the OAuth Playground; if you want to use your own app, remember to swap out the app ID, secret, and other values. (The file also provides valid values for other tests, which you're welcome to swap out for data specific to your own application.)
143
+ Important Note: to run the live tests, you have to provide some of your own data in spec/fixtures/facebook_data.yml: a valid OAuth access token with publish\_stream, read\_stream, and user\_photos permissions and an OAuth code that can be used to generate an access token. You can get thisdata at the OAuth Playground; if you want to use your own app, remember to swap out the app ID, secret, and other values. (The file also provides valid values for other tests, which you're welcome to swap out for data specific to your own application.)
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Koala::Facebook::API" do
4
+ before(:each) do
5
+ @service = Koala::Facebook::API.new
6
+ end
7
+
8
+ it "should not include an access token if none was given" do
9
+ Koala.should_receive(:make_request).with(
10
+ anything,
11
+ hash_not_including('access_token' => 1),
12
+ anything,
13
+ anything
14
+ ).and_return(Koala::Response.new(200, "", ""))
15
+
16
+ @service.api('anything')
17
+ end
18
+
19
+ it "should include an access token if given" do
20
+ token = 'adfadf'
21
+ service = Koala::Facebook::API.new token
22
+
23
+ Koala.should_receive(:make_request).with(
24
+ anything,
25
+ hash_including('access_token' => token),
26
+ anything,
27
+ anything
28
+ ).and_return(Koala::Response.new(200, "", ""))
29
+
30
+ service.api('anything')
31
+ end
32
+
33
+ it "should have an attr_reader for access token" do
34
+ token = 'adfadf'
35
+ service = Koala::Facebook::API.new token
36
+ service.access_token.should == token
37
+ end
38
+
39
+ it "should get the attribute of a Koala::Response given by the http_component parameter" do
40
+ http_component = :method_name
41
+
42
+ response = mock('Mock KoalaResponse', :body => '', :status => 200)
43
+ response.should_receive(http_component).and_return('')
44
+
45
+ Koala.stub(:make_request).and_return(response)
46
+
47
+ @service.api('anything', 'get', {}, :http_component => http_component)
48
+ end
49
+
50
+ it "should return the body of the request as JSON if no http_component is given" do
51
+ response = stub('response', :body => 'body', :status => 200)
52
+ Koala.stub(:make_request).and_return(response)
53
+
54
+ json_body = mock('JSON body')
55
+ JSON.stub(:parse).and_return([json_body])
56
+
57
+ @service.api('anything').should == json_body
58
+ end
59
+
60
+ it "should execute a block with the response body if passed one" do
61
+ body = '{}'
62
+ Koala.stub(:make_request).and_return(Koala::Response.new(200, body, {}))
63
+
64
+ yield_test = mock('Yield Tester')
65
+ yield_test.should_receive(:pass)
66
+
67
+ @service.api('anything') do |arg|
68
+ yield_test.pass
69
+ arg.should == JSON.parse(body)
70
+ end
71
+ end
72
+
73
+ it "should raise an API error if the HTTP response code is greater than or equal to 500" do
74
+ Koala.stub(:make_request).and_return(Koala::Response.new(500, 'response body', {}))
75
+
76
+ lambda { @service.api('anything') }.should raise_exception(Koala::Facebook::APIError)
77
+ end
78
+
79
+ it "should handle rogue true/false as responses" do
80
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'true', {}))
81
+ @service.api('anything').should be_true
82
+
83
+ Koala.should_receive(:make_request).and_return(Koala::Response.new(200, 'false', {}))
84
+ @service.api('anything').should be_false
85
+ end
86
+
87
+ describe "with regard to leading slashes" do
88
+ it "should add a leading / to the path if not present" do
89
+ path = "anything"
90
+ Koala.should_receive(:make_request).with("/#{path}", anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
91
+ @service.api(path)
92
+ end
93
+
94
+ it "shouldn't change the path if a leading / is present" do
95
+ path = "/anything"
96
+ Koala.should_receive(:make_request).with(path, anything, anything, anything).and_return(Koala::Response.new(200, 'true', {}))
97
+ @service.api(path)
98
+ end
99
+ end
100
+
101
+ end