koala 1.4.0 → 1.5.0rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +21 -1
- data/koala.gemspec +22 -43
- data/lib/koala/api/graph_api.rb +11 -12
- data/lib/koala/api/graph_batch_api.rb +10 -4
- data/lib/koala/api/graph_collection.rb +5 -4
- data/lib/koala/api/rest_api.rb +1 -1
- data/lib/koala/api.rb +13 -11
- data/lib/koala/http_service.rb +32 -25
- data/lib/koala/oauth.rb +4 -3
- data/lib/koala/utils.rb +17 -2
- data/lib/koala/version.rb +1 -1
- data/readme.md +10 -10
- data/spec/cases/api_spec.rb +2 -2
- data/spec/cases/error_spec.rb +15 -12
- data/spec/cases/graph_api_batch_spec.rb +8 -1
- data/spec/cases/graph_collection_spec.rb +20 -14
- data/spec/cases/http_service_spec.rb +70 -30
- data/spec/cases/oauth_spec.rb +7 -1
- data/spec/cases/utils_spec.rb +21 -0
- data/spec/fixtures/mock_facebook_responses.yml +20 -20
- data/spec/support/graph_api_shared_examples.rb +6 -3
- data/spec/support/json_testing_fix.rb +9 -9
- data/spec/support/mock_http_service.rb +1 -1
- data/spec/support/ordered_hash.rb +1 -5
- data/spec/support/rest_api_shared_examples.rb +1 -1
- metadata +39 -19
data/CHANGELOG
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
|
+
v1.5
|
|
2
|
+
New methods:
|
|
3
|
+
-- Added Koala::Utils.logger to enable debugging (thanks, KentonWhite!)
|
|
4
|
+
-- Expose fb_error_message and fb_error_code directly in APIError
|
|
5
|
+
Updated methods:
|
|
6
|
+
-- GraphCollection.parse_page_url now uses the URI library and can parse any address (thanks, bnorton!)
|
|
7
|
+
Internal improvements:
|
|
8
|
+
-- Update MultiJson dependency to support the Oj library (thanks, zinenko!)
|
|
9
|
+
-- Loosened Faraday dependency (thanks, rewritten and romanbsd!)
|
|
10
|
+
-- Fixed typos (thanks, nathanbertram!)
|
|
11
|
+
-- Switched uses of put_object to the more semantically accurate put_connections
|
|
12
|
+
-- Cleaned up gemspec
|
|
13
|
+
-- Handle invalid batch API responses better
|
|
14
|
+
Documentation:
|
|
15
|
+
-- Added HTTP Services description for Faraday 0.8/persistent connections (thanks, romanbsd!)
|
|
16
|
+
-- Remove documentation of the old pre-1.2 HTTP Service options
|
|
17
|
+
|
|
18
|
+
v.1.4.1
|
|
19
|
+
-- Update MultiJson to 1.3 and change syntax to silence warnings (thanks, eckz and masterkain!)
|
|
20
|
+
|
|
1
21
|
v1.4
|
|
2
22
|
New methods:
|
|
3
|
-
-- OAuth#exchange_access_token(_info) allows you to extend access tokens you receive (thanks,
|
|
23
|
+
-- OAuth#exchange_access_token(_info) allows you to extend access tokens you receive (thanks, etiennebarrie!)
|
|
4
24
|
Updated methods:
|
|
5
25
|
-- HTTPServices#encode_params sorts parameters to aid in URL comparison (thanks, sholden!)
|
|
6
26
|
-- get_connections is now aliased as get_connection (use whichever makes sense to you)
|
data/koala.gemspec
CHANGED
|
@@ -2,47 +2,26 @@
|
|
|
2
2
|
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
|
3
3
|
require 'koala/version'
|
|
4
4
|
|
|
5
|
-
Gem::Specification.new do |
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if s.respond_to? :specification_version then
|
|
29
|
-
s.specification_version = 3
|
|
30
|
-
|
|
31
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
32
|
-
s.add_runtime_dependency(%q<multi_json>, ["~> 1.0"])
|
|
33
|
-
s.add_runtime_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
34
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.8.0rc1"])
|
|
35
|
-
s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
|
|
36
|
-
else
|
|
37
|
-
s.add_dependency(%q<multi_json>, ["~> 1.0"])
|
|
38
|
-
s.add_dependency(%q<rspec>, ["~> 2.8.0rc1"])
|
|
39
|
-
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
|
40
|
-
s.add_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
41
|
-
end
|
|
42
|
-
else
|
|
43
|
-
s.add_dependency(%q<multi_json>, ["~> 1.0"])
|
|
44
|
-
s.add_dependency(%q<rspec>, ["~> 2.8.0rc1"])
|
|
45
|
-
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
|
46
|
-
s.add_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
47
|
-
end
|
|
5
|
+
Gem::Specification.new do |gem|
|
|
6
|
+
gem.name = "koala"
|
|
7
|
+
gem.summary = "A lightweight, flexible library for Facebook with support for the Graph API, the REST API, realtime updates, and OAuth authentication."
|
|
8
|
+
gem.description = "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."
|
|
9
|
+
gem.homepage = "http://github.com/arsduo/koala"
|
|
10
|
+
gem.version = Koala::VERSION
|
|
11
|
+
|
|
12
|
+
gem.authors = ["Alex Koppel"]
|
|
13
|
+
gem.email = "alex@alexkoppel.com"
|
|
14
|
+
|
|
15
|
+
gem.require_paths = ["lib"]
|
|
16
|
+
gem.files = `git ls-files`.split("\n")
|
|
17
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
18
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
|
+
|
|
20
|
+
gem.extra_rdoc_files = ["readme.md", "CHANGELOG"]
|
|
21
|
+
gem.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Koala"]
|
|
22
|
+
|
|
23
|
+
gem.add_runtime_dependency(%q<multi_json>, ["~> 1.3"])
|
|
24
|
+
gem.add_runtime_dependency(%q<faraday>, ["~> 0.7"])
|
|
25
|
+
gem.add_development_dependency(%q<rspec>, ["~> 2.8"])
|
|
26
|
+
gem.add_development_dependency(%q<rake>, ["~> 0.8"])
|
|
48
27
|
end
|
data/lib/koala/api/graph_api.rb
CHANGED
|
@@ -72,8 +72,7 @@ module Koala
|
|
|
72
72
|
# Please use put_connections; in a future version of Koala (2.0?),
|
|
73
73
|
# put_object will issue a POST directly to an individual object, not to a connection.
|
|
74
74
|
def put_object(parent_object, connection_name, args = {}, options = {})
|
|
75
|
-
|
|
76
|
-
graph_call("#{parent_object}/#{connection_name}", args, "post", options)
|
|
75
|
+
put_connections(parent_object, connection_name, args, options)
|
|
77
76
|
end
|
|
78
77
|
|
|
79
78
|
# Delete an object from the Graph if you have appropriate permissions.
|
|
@@ -115,7 +114,7 @@ module Koala
|
|
|
115
114
|
# @note (see #get_connection)
|
|
116
115
|
#
|
|
117
116
|
# @example
|
|
118
|
-
# graph.
|
|
117
|
+
# graph.put_connections("me", "feed", :message => "Hello, world")
|
|
119
118
|
# => writes "Hello, world" to the active user's wall
|
|
120
119
|
#
|
|
121
120
|
# Most write operations require extended permissions. For example,
|
|
@@ -192,7 +191,7 @@ module Koala
|
|
|
192
191
|
#
|
|
193
192
|
# @return (see #put_connections)
|
|
194
193
|
def put_picture(*picture_args)
|
|
195
|
-
|
|
194
|
+
put_connections(*parse_media_args(picture_args, "photos"))
|
|
196
195
|
end
|
|
197
196
|
|
|
198
197
|
# Upload a video. Functions exactly the same as put_picture.
|
|
@@ -200,11 +199,11 @@ module Koala
|
|
|
200
199
|
def put_video(*video_args)
|
|
201
200
|
args = parse_media_args(video_args, "videos")
|
|
202
201
|
args.last[:video] = true
|
|
203
|
-
|
|
202
|
+
put_connections(*args)
|
|
204
203
|
end
|
|
205
204
|
|
|
206
205
|
# Write directly to the user's wall.
|
|
207
|
-
# Convenience method equivalent to
|
|
206
|
+
# Convenience method equivalent to put_connections(id, "feed").
|
|
208
207
|
#
|
|
209
208
|
# To get wall posts, use get_connections(user, "feed")
|
|
210
209
|
# To delete a wall post, use delete_object(post_id)
|
|
@@ -227,7 +226,7 @@ module Koala
|
|
|
227
226
|
# @see #put_connections
|
|
228
227
|
# @return (see #put_connections)
|
|
229
228
|
def put_wall_post(message, attachment = {}, target_id = "me", options = {})
|
|
230
|
-
|
|
229
|
+
put_connections(target_id, "feed", attachment.merge({:message => message}), options)
|
|
231
230
|
end
|
|
232
231
|
|
|
233
232
|
# Comment on a given object.
|
|
@@ -243,7 +242,7 @@ module Koala
|
|
|
243
242
|
# @return (see #put_connections)
|
|
244
243
|
def put_comment(id, message, options = {})
|
|
245
244
|
# Writes the given comment on the given post.
|
|
246
|
-
|
|
245
|
+
put_connections(id, "comments", {:message => message}, options)
|
|
247
246
|
end
|
|
248
247
|
|
|
249
248
|
# Like a given object.
|
|
@@ -257,7 +256,7 @@ module Koala
|
|
|
257
256
|
# @return (see #put_connections)
|
|
258
257
|
def put_like(id, options = {})
|
|
259
258
|
# Likes the given post.
|
|
260
|
-
|
|
259
|
+
put_connections(id, "likes", {}, options)
|
|
261
260
|
end
|
|
262
261
|
|
|
263
262
|
# Unlike a given object.
|
|
@@ -318,7 +317,7 @@ module Koala
|
|
|
318
317
|
#
|
|
319
318
|
# @return a hash of FQL results keyed to the appropriate query
|
|
320
319
|
def fql_multiquery(queries = {}, args = {}, options = {})
|
|
321
|
-
if results = get_object("fql", args.merge(:q => MultiJson.
|
|
320
|
+
if results = get_object("fql", args.merge(:q => MultiJson.dump(queries)), options)
|
|
322
321
|
# simplify the multiquery result format
|
|
323
322
|
results.inject({}) {|outcome, data| outcome[data["name"]] = data["fql_result_set"]; outcome}
|
|
324
323
|
end
|
|
@@ -353,7 +352,7 @@ module Koala
|
|
|
353
352
|
end
|
|
354
353
|
|
|
355
354
|
def set_app_restrictions(app_id, restrictions_hash, args = {}, options = {})
|
|
356
|
-
graph_call(app_id, args.merge(:restrictions => MultiJson.
|
|
355
|
+
graph_call(app_id, args.merge(:restrictions => MultiJson.dump(restrictions_hash)), "post", options)
|
|
357
356
|
end
|
|
358
357
|
|
|
359
358
|
# Certain calls such as {#get_connections} return an array of results which you can page through
|
|
@@ -447,7 +446,7 @@ module Koala
|
|
|
447
446
|
|
|
448
447
|
def parse_media_args(media_args, method)
|
|
449
448
|
# photo and video uploads can accept different types of arguments (see above)
|
|
450
|
-
# so here, we parse the arguments into a form directly usable in
|
|
449
|
+
# so here, we parse the arguments into a form directly usable in put_connections
|
|
451
450
|
raise KoalaError.new("Wrong number of arguments for put_#{method == "photos" ? "picture" : "video"}") unless media_args.size.between?(1, 5)
|
|
452
451
|
|
|
453
452
|
args_offset = media_args[1].kind_of?(Hash) || media_args.size == 1 ? 0 : 1
|
|
@@ -41,7 +41,7 @@ module Koala
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# redefine the graph_call and check_response methods
|
|
44
|
-
# so we can use this API inside the batch block just like any regular Graph API
|
|
44
|
+
# so we can use this API inside the batch block just like any regular Graph API
|
|
45
45
|
alias_method :graph_call_outside_batch, :graph_call
|
|
46
46
|
alias_method :graph_call, :graph_call_in_batch
|
|
47
47
|
|
|
@@ -53,12 +53,18 @@ module Koala
|
|
|
53
53
|
return [] unless batch_calls.length > 0
|
|
54
54
|
# Turn the call args collected into what facebook expects
|
|
55
55
|
args = {}
|
|
56
|
-
args["batch"] = MultiJson.
|
|
56
|
+
args["batch"] = MultiJson.dump(batch_calls.map { |batch_op|
|
|
57
57
|
args.merge!(batch_op.files) if batch_op.files
|
|
58
58
|
batch_op.to_batch_params(access_token)
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
batch_result = graph_call_outside_batch('/', args, 'post', http_options) do |response|
|
|
62
|
+
unless response
|
|
63
|
+
# Facebook sometimes reportedly returns an empty body at times
|
|
64
|
+
# see https://github.com/arsduo/koala/issues/184
|
|
65
|
+
raise APIError.new({"type" => "BadFacebookResponse", "message" => "Facebook returned invalid batch response: #{response.inspect}"})
|
|
66
|
+
end
|
|
67
|
+
|
|
62
68
|
# map the results with post-processing included
|
|
63
69
|
index = 0 # keep compat with ruby 1.8 - no with_index for map
|
|
64
70
|
response.map do |call_result|
|
|
@@ -68,7 +74,7 @@ module Koala
|
|
|
68
74
|
|
|
69
75
|
if call_result
|
|
70
76
|
# (see note in regular api method about JSON parsing)
|
|
71
|
-
body = MultiJson.
|
|
77
|
+
body = MultiJson.load("[#{call_result['body'].to_s}]")[0]
|
|
72
78
|
|
|
73
79
|
unless call_result["code"].to_i >= 500 || error = check_response(body)
|
|
74
80
|
# Get the HTTP component they want
|
|
@@ -92,7 +98,7 @@ module Koala
|
|
|
92
98
|
end
|
|
93
99
|
end
|
|
94
100
|
end
|
|
95
|
-
|
|
101
|
+
|
|
96
102
|
# turn any results that are pageable into GraphCollections
|
|
97
103
|
batch_result.inject([]) {|processed_results, raw| processed_results << GraphCollection.evaluate(raw, @original_api)}
|
|
98
104
|
end
|
|
@@ -87,10 +87,11 @@ module Koala
|
|
|
87
87
|
#
|
|
88
88
|
# @return an array of parameters that can be provided via graph_call(*parsed_params)
|
|
89
89
|
def self.parse_page_url(url)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
params = CGI.parse(
|
|
90
|
+
uri = URI.parse(url)
|
|
91
|
+
|
|
92
|
+
base = uri.path.sub(/^\//, '')
|
|
93
|
+
params = CGI.parse(uri.query)
|
|
94
|
+
|
|
94
95
|
new_params = {}
|
|
95
96
|
params.each_pair do |key,value|
|
|
96
97
|
new_params[key] = value.join ","
|
data/lib/koala/api/rest_api.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Koala
|
|
|
22
22
|
# @return true if successful, false if not. (This call currently doesn't give useful feedback on failure.)
|
|
23
23
|
def set_app_properties(properties, args = {}, options = {})
|
|
24
24
|
raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "setAppProperties requires an access token"}) unless @access_token
|
|
25
|
-
rest_call("admin.setAppProperties", args.merge(:properties => MultiJson.
|
|
25
|
+
rest_call("admin.setAppProperties", args.merge(:properties => MultiJson.dump(properties)), options, "post")
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Make a call to the REST API.
|
data/lib/koala/api.rb
CHANGED
|
@@ -12,12 +12,12 @@ module Koala
|
|
|
12
12
|
def initialize(access_token = nil)
|
|
13
13
|
@access_token = access_token
|
|
14
14
|
end
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
attr_reader :access_token
|
|
17
17
|
|
|
18
18
|
include GraphAPIMethods
|
|
19
19
|
include RestAPIMethods
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
# Makes a request to the appropriate Facebook API.
|
|
22
22
|
# @note You'll rarely need to call this method directly.
|
|
23
23
|
#
|
|
@@ -27,17 +27,17 @@ module Koala
|
|
|
27
27
|
# @param path the server path for this request (leading / is prepended if not present)
|
|
28
28
|
# @param args arguments to be sent to Facebook
|
|
29
29
|
# @param verb the HTTP method to use
|
|
30
|
-
# @param options request-related options for Koala and Faraday.
|
|
30
|
+
# @param options request-related options for Koala and Faraday.
|
|
31
31
|
# See https://github.com/arsduo/koala/wiki/HTTP-Services for additional options.
|
|
32
32
|
# @option options [Symbol] :http_component which part of the response (headers, body, or status) to return
|
|
33
33
|
# @option options [Boolean] :beta use Facebook's beta tier
|
|
34
|
-
# @option options [Boolean] :use_ssl force SSL for this request, even if it's tokenless.
|
|
34
|
+
# @option options [Boolean] :use_ssl force SSL for this request, even if it's tokenless.
|
|
35
35
|
# (All API requests with access tokens use SSL.)
|
|
36
|
-
# @param error_checking_block a block to evaluate the response status for additional JSON-encoded errors
|
|
36
|
+
# @param error_checking_block a block to evaluate the response status for additional JSON-encoded errors
|
|
37
37
|
#
|
|
38
38
|
# @yield The response body for evaluation
|
|
39
39
|
#
|
|
40
|
-
# @raise [Koala::Facebook::APIError] if Facebook returns an error (response status >= 500)
|
|
40
|
+
# @raise [Koala::Facebook::APIError] if Facebook returns an error (response status >= 500)
|
|
41
41
|
#
|
|
42
42
|
# @return the body of the response from Facebook (unless another http_component is requested)
|
|
43
43
|
def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
|
|
@@ -57,8 +57,8 @@ module Koala
|
|
|
57
57
|
|
|
58
58
|
# parse the body as JSON and run it through the error checker (if provided)
|
|
59
59
|
# Note: Facebook sometimes sends results like "true" and "false", which aren't strictly objects
|
|
60
|
-
# and cause MultiJson.
|
|
61
|
-
body = MultiJson.
|
|
60
|
+
# and cause MultiJson.load to fail -- so we account for that by wrapping the result in []
|
|
61
|
+
body = MultiJson.load("[#{result.body.to_s}]")[0]
|
|
62
62
|
yield body if error_checking_block
|
|
63
63
|
|
|
64
64
|
# if we want a component other than the body (e.g. redirect header for images), return that
|
|
@@ -69,11 +69,11 @@ module Koala
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
class APIError < StandardError
|
|
74
|
-
attr_accessor :fb_error_type, :raw_response
|
|
74
|
+
attr_accessor :fb_error_type, :fb_error_code, :fb_error_message, :raw_response
|
|
75
75
|
|
|
76
|
-
# Creates a new APIError.
|
|
76
|
+
# Creates a new APIError.
|
|
77
77
|
#
|
|
78
78
|
# Assigns the error type (as reported by Facebook) to #fb_error_type
|
|
79
79
|
# and the raw error response available to #raw_response.
|
|
@@ -82,6 +82,8 @@ module Koala
|
|
|
82
82
|
def initialize(details = {})
|
|
83
83
|
self.raw_response = details
|
|
84
84
|
self.fb_error_type = details["type"]
|
|
85
|
+
self.fb_error_code = details["code"]
|
|
86
|
+
self.fb_error_message = details["message"]
|
|
85
87
|
super("#{fb_error_type}: #{details["message"]}")
|
|
86
88
|
end
|
|
87
89
|
end
|
data/lib/koala/http_service.rb
CHANGED
|
@@ -4,7 +4,7 @@ require 'koala/http_service/uploadable_io'
|
|
|
4
4
|
require 'koala/http_service/response'
|
|
5
5
|
|
|
6
6
|
module Koala
|
|
7
|
-
module HTTPService
|
|
7
|
+
module HTTPService
|
|
8
8
|
class << self
|
|
9
9
|
# A customized stack of Faraday middleware that will be used to make each request.
|
|
10
10
|
attr_accessor :faraday_middleware
|
|
@@ -13,9 +13,9 @@ module Koala
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
@http_options ||= {}
|
|
16
|
-
|
|
17
|
-
# Koala's default middleware stack.
|
|
18
|
-
# We encode requests in a Facebook-compatible multipart request,
|
|
16
|
+
|
|
17
|
+
# Koala's default middleware stack.
|
|
18
|
+
# We encode requests in a Facebook-compatible multipart request,
|
|
19
19
|
# and use whichever adapter has been configured for this application.
|
|
20
20
|
DEFAULT_MIDDLEWARE = Proc.new do |builder|
|
|
21
21
|
builder.use Koala::HTTPService::MultipartRequest
|
|
@@ -30,7 +30,7 @@ module Koala
|
|
|
30
30
|
# @option options :video use the server designated for video uploads
|
|
31
31
|
# @option options :beta use the beta tier
|
|
32
32
|
# @option options :use_ssl force https, even if not needed
|
|
33
|
-
#
|
|
33
|
+
#
|
|
34
34
|
# @return a complete server address with protocol
|
|
35
35
|
def self.server(options = {})
|
|
36
36
|
server = "#{options[:rest_api] ? Facebook::REST_SERVER : Facebook::GRAPH_SERVER}"
|
|
@@ -48,9 +48,9 @@ module Koala
|
|
|
48
48
|
#
|
|
49
49
|
# @param path the server path for this request
|
|
50
50
|
# @param args (see Koala::Facebook::API#api)
|
|
51
|
-
# @param verb the HTTP method to use.
|
|
51
|
+
# @param verb the HTTP method to use.
|
|
52
52
|
# If not get or post, this will be turned into a POST request with the appropriate :method
|
|
53
|
-
# specified in the arguments.
|
|
53
|
+
# specified in the arguments.
|
|
54
54
|
# @param options (see Koala::Facebook::API#api)
|
|
55
55
|
#
|
|
56
56
|
# @raise an appropriate connection error if unable to make the request to Facebook
|
|
@@ -63,23 +63,30 @@ module Koala
|
|
|
63
63
|
# turn all the keys to strings (Faraday has issues with symbols under 1.8.7) and resolve UploadableIOs
|
|
64
64
|
params = args.inject({}) {|hash, kv| hash[kv.first.to_s] = kv.last.is_a?(UploadableIO) ? kv.last.to_upload_io : kv.last; hash}
|
|
65
65
|
|
|
66
|
-
# figure out our options for this request
|
|
66
|
+
# figure out our options for this request
|
|
67
67
|
request_options = {:params => (verb == "get" ? params : {})}.merge(http_options || {}).merge(process_options(options))
|
|
68
68
|
request_options[:use_ssl] = true if args["access_token"] # require https if there's a token
|
|
69
|
+
if request_options[:use_ssl]
|
|
70
|
+
ssl = (request_options[:ssl] ||= {})
|
|
71
|
+
ssl[:verify] = true unless ssl.has_key?(:verify)
|
|
72
|
+
end
|
|
69
73
|
|
|
70
74
|
# set up our Faraday connection
|
|
71
75
|
# we have to manually assign params to the URL or the
|
|
72
76
|
conn = Faraday.new(server(request_options), request_options, &(faraday_middleware || DEFAULT_MIDDLEWARE))
|
|
73
77
|
|
|
74
78
|
response = conn.send(verb, path, (verb == "post" ? params : {}))
|
|
79
|
+
|
|
80
|
+
# Log URL information
|
|
81
|
+
Koala::Utils.debug "#{verb.upcase}: #{path} params: #{params.inspect}"
|
|
75
82
|
Koala::HTTPService::Response.new(response.status.to_i, response.body, response.headers)
|
|
76
83
|
end
|
|
77
84
|
|
|
78
|
-
# Encodes a given hash into a query string.
|
|
85
|
+
# Encodes a given hash into a query string.
|
|
79
86
|
# This is used mainly by the Batch API nowadays, since Faraday handles this for regular cases.
|
|
80
|
-
#
|
|
87
|
+
#
|
|
81
88
|
# @param params_hash a hash of values to CGI-encode and appropriately join
|
|
82
|
-
#
|
|
89
|
+
#
|
|
83
90
|
# @example
|
|
84
91
|
# Koala.http_service.encode_params({:a => 2, :b => "My String"})
|
|
85
92
|
# => "a=2&b=My+String"
|
|
@@ -87,14 +94,14 @@ module Koala
|
|
|
87
94
|
# @return the appropriately-encoded string
|
|
88
95
|
def self.encode_params(param_hash)
|
|
89
96
|
((param_hash || {}).sort_by{|k, v| k.to_s}.collect do |key_and_value|
|
|
90
|
-
key_and_value[1] = MultiJson.
|
|
97
|
+
key_and_value[1] = MultiJson.dump(key_and_value[1]) unless key_and_value[1].is_a? String
|
|
91
98
|
"#{key_and_value[0].to_s}=#{CGI.escape key_and_value[1]}"
|
|
92
99
|
end).join("&")
|
|
93
100
|
end
|
|
94
|
-
|
|
101
|
+
|
|
95
102
|
# deprecations
|
|
96
103
|
# not elegant or compact code, but temporary
|
|
97
|
-
|
|
104
|
+
|
|
98
105
|
# @private
|
|
99
106
|
def self.always_use_ssl
|
|
100
107
|
Koala::Utils.deprecate("HTTPService.always_use_ssl is now HTTPService.http_options[:use_ssl]; always_use_ssl will be removed in a future version.")
|
|
@@ -106,7 +113,7 @@ module Koala
|
|
|
106
113
|
Koala::Utils.deprecate("HTTPService.always_use_ssl is now HTTPService.http_options[:use_ssl]; always_use_ssl will be removed in a future version.")
|
|
107
114
|
http_options[:use_ssl] = value
|
|
108
115
|
end
|
|
109
|
-
|
|
116
|
+
|
|
110
117
|
# @private
|
|
111
118
|
def self.timeout
|
|
112
119
|
Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
|
|
@@ -118,7 +125,7 @@ module Koala
|
|
|
118
125
|
Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
|
|
119
126
|
http_options[:timeout] = value
|
|
120
127
|
end
|
|
121
|
-
|
|
128
|
+
|
|
122
129
|
# @private
|
|
123
130
|
def self.timeout
|
|
124
131
|
Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
|
|
@@ -130,7 +137,7 @@ module Koala
|
|
|
130
137
|
Koala::Utils.deprecate("HTTPService.timeout is now HTTPService.http_options[:timeout]; .timeout will be removed in a future version.")
|
|
131
138
|
http_options[:timeout] = value
|
|
132
139
|
end
|
|
133
|
-
|
|
140
|
+
|
|
134
141
|
# @private
|
|
135
142
|
def self.proxy
|
|
136
143
|
Koala::Utils.deprecate("HTTPService.proxy is now HTTPService.http_options[:proxy]; .proxy will be removed in a future version.")
|
|
@@ -142,7 +149,7 @@ module Koala
|
|
|
142
149
|
Koala::Utils.deprecate("HTTPService.proxy is now HTTPService.http_options[:proxy]; .proxy will be removed in a future version.")
|
|
143
150
|
http_options[:proxy] = value
|
|
144
151
|
end
|
|
145
|
-
|
|
152
|
+
|
|
146
153
|
# @private
|
|
147
154
|
def self.ca_path
|
|
148
155
|
Koala::Utils.deprecate("HTTPService.ca_path is now (HTTPService.http_options[:ssl] ||= {})[:ca_path]; .ca_path will be removed in a future version.")
|
|
@@ -154,7 +161,7 @@ module Koala
|
|
|
154
161
|
Koala::Utils.deprecate("HTTPService.ca_path is now (HTTPService.http_options[:ssl] ||= {})[:ca_path]; .ca_path will be removed in a future version.")
|
|
155
162
|
(http_options[:ssl] ||= {})[:ca_path] = value
|
|
156
163
|
end
|
|
157
|
-
|
|
164
|
+
|
|
158
165
|
# @private
|
|
159
166
|
def self.ca_file
|
|
160
167
|
Koala::Utils.deprecate("HTTPService.ca_file is now (HTTPService.http_options[:ssl] ||= {})[:ca_file]; .ca_file will be removed in a future version.")
|
|
@@ -179,14 +186,14 @@ module Koala
|
|
|
179
186
|
(http_options[:ssl] ||= {})[:verify_mode] = value
|
|
180
187
|
end
|
|
181
188
|
|
|
182
|
-
private
|
|
183
|
-
|
|
189
|
+
private
|
|
190
|
+
|
|
184
191
|
def self.process_options(options)
|
|
185
192
|
if typhoeus_options = options.delete(:typhoeus_options)
|
|
186
193
|
Koala::Utils.deprecate("typhoeus_options should now be included directly in the http_options hash. Support for this key will be removed in a future version.")
|
|
187
194
|
options = options.merge(typhoeus_options)
|
|
188
195
|
end
|
|
189
|
-
|
|
196
|
+
|
|
190
197
|
if ca_file = options.delete(:ca_file)
|
|
191
198
|
Koala::Utils.deprecate("http_options[:ca_file] should now be passed inside (http_options[:ssl] = {}) -- that is, http_options[:ssl][:ca_file]. Support for this key will be removed in a future version.")
|
|
192
199
|
(options[:ssl] ||= {})[:ca_file] = ca_file
|
|
@@ -201,11 +208,11 @@ module Koala
|
|
|
201
208
|
Koala::Utils.deprecate("http_options[:verify_mode] should now be passed inside (http_options[:ssl] = {}) -- that is, http_options[:ssl][:verify_mode]. Support for this key will be removed in a future version.")
|
|
202
209
|
(options[:ssl] ||= {})[:verify_mode] = verify_mode
|
|
203
210
|
end
|
|
204
|
-
|
|
211
|
+
|
|
205
212
|
options
|
|
206
|
-
end
|
|
213
|
+
end
|
|
207
214
|
end
|
|
208
|
-
|
|
215
|
+
|
|
209
216
|
# @private
|
|
210
217
|
module TyphoeusService
|
|
211
218
|
def self.deprecated_interface
|
data/lib/koala/oauth.rb
CHANGED
|
@@ -229,7 +229,7 @@ module Koala
|
|
|
229
229
|
raise 'SignedRequest: Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope
|
|
230
230
|
|
|
231
231
|
signature = base64_url_decode(encoded_sig).unpack("H*").first
|
|
232
|
-
envelope = MultiJson.
|
|
232
|
+
envelope = MultiJson.load(base64_url_decode(encoded_envelope))
|
|
233
233
|
|
|
234
234
|
raise "SignedRequest: Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
|
|
235
235
|
|
|
@@ -260,7 +260,7 @@ module Koala
|
|
|
260
260
|
})
|
|
261
261
|
end
|
|
262
262
|
|
|
263
|
-
MultiJson.
|
|
263
|
+
MultiJson.load(response)
|
|
264
264
|
end
|
|
265
265
|
|
|
266
266
|
# @deprecated (see #get_token_info_from_session_keys)
|
|
@@ -285,7 +285,7 @@ module Koala
|
|
|
285
285
|
result = fetch_token_string(args, post, "access_token", options)
|
|
286
286
|
|
|
287
287
|
# if we have an error, parse the error JSON and raise an error
|
|
288
|
-
raise APIError.new((MultiJson.
|
|
288
|
+
raise APIError.new((MultiJson.load(result)["error"] rescue nil) || {}) if result =~ /error/
|
|
289
289
|
|
|
290
290
|
# otherwise, parse the access token
|
|
291
291
|
parse_access_token(result)
|
|
@@ -325,6 +325,7 @@ module Koala
|
|
|
325
325
|
|
|
326
326
|
components.merge(token_info) if token_info
|
|
327
327
|
else
|
|
328
|
+
Koala::Utils.logger.warn("Signed cookie didn't contain Facebook OAuth code! Components: #{components}")
|
|
328
329
|
nil
|
|
329
330
|
end
|
|
330
331
|
end
|
data/lib/koala/utils.rb
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
module Koala
|
|
2
2
|
module Utils
|
|
3
3
|
|
|
4
|
+
# Utility methods used by Koala.
|
|
5
|
+
require 'logger'
|
|
6
|
+
require 'forwardable'
|
|
7
|
+
|
|
8
|
+
extend Forwardable
|
|
9
|
+
extend self
|
|
10
|
+
|
|
11
|
+
def_delegators :logger, :debug, :info, :warn, :error, :fatal, :level, :level=
|
|
12
|
+
|
|
13
|
+
# The Koala logger, an instance of the standard Ruby logger, pointing to STDOUT by default.
|
|
14
|
+
# In Rails projects, you can set this to Rails.logger.
|
|
15
|
+
attr_accessor :logger
|
|
16
|
+
self.logger = Logger.new(STDOUT)
|
|
17
|
+
self.logger.level = Logger::ERROR
|
|
18
|
+
|
|
4
19
|
# @private
|
|
5
20
|
DEPRECATION_PREFIX = "KOALA: Deprecation warning: "
|
|
6
21
|
|
|
7
|
-
# Prints a deprecation message.
|
|
22
|
+
# Prints a deprecation message.
|
|
8
23
|
# Each individual message will only be printed once to avoid spamming.
|
|
9
|
-
def
|
|
24
|
+
def deprecate(message)
|
|
10
25
|
@posted_deprecations ||= []
|
|
11
26
|
unless @posted_deprecations.include?(message)
|
|
12
27
|
# only include each message once
|
data/lib/koala/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -23,17 +23,17 @@ Or in Bundler:
|
|
|
23
23
|
Graph API
|
|
24
24
|
----
|
|
25
25
|
The Graph API is the simple, slick new interface to Facebook's data. Using it with Koala is quite straightforward:
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
@graph = Koala::Facebook::API.new(oauth_access_token)
|
|
28
28
|
# in 1.1 or earlier, use GraphAPI instead of API
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
profile = @graph.get_object("me")
|
|
31
31
|
friends = @graph.get_connections("me", "friends")
|
|
32
|
-
@graph.
|
|
33
|
-
|
|
32
|
+
@graph.put_connections("me", "feed", :message => "I am writing on my wall!")
|
|
33
|
+
|
|
34
34
|
# three-part queries are easy too!
|
|
35
35
|
@graph.get_connections("me", "mutualfriends/#{friend_id}")
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
# you can even use the new Timeline API
|
|
38
38
|
# see https://developers.facebook.com/docs/beta/opengraph/tutorial/
|
|
39
39
|
@graph.put_connections("me", "namespace:action", :object => object_url)
|
|
@@ -70,7 +70,7 @@ Fortunately, Koala supports the REST API using the very same interface; to use t
|
|
|
70
70
|
|
|
71
71
|
@rest = Koala::Facebook::API.new(oauth_access_token)
|
|
72
72
|
# in 1.1 or earlier, use RestAPI instead of API
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
@rest.fql_query(my_fql_query) # convenience method
|
|
75
75
|
@rest.fql_multiquery(fql_query_hash) # convenience method
|
|
76
76
|
@rest.rest_call("stream.publish", arguments_hash) # generic version
|
|
@@ -79,11 +79,11 @@ Of course, you can use the Graph API methods on the same object -- the power of
|
|
|
79
79
|
|
|
80
80
|
@api = Koala::Facebook::API.new(oauth_access_token)
|
|
81
81
|
# in 1.1 or earlier, use GraphAndRestAPI instead of API
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
@api = Koala::Facebook::API.new(oauth_access_token)
|
|
84
84
|
fql = @api.fql_query(my_fql_query)
|
|
85
85
|
@api.put_wall_post(process_result(fql))
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
|
|
88
88
|
OAuth
|
|
89
89
|
-----
|
|
@@ -91,7 +91,7 @@ You can use the Graph and REST APIs without an OAuth access token, but the real
|
|
|
91
91
|
|
|
92
92
|
@oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
|
|
93
93
|
|
|
94
|
-
If your application uses Koala and the Facebook [JavaScript SDK](http://github.com/facebook/
|
|
94
|
+
If your application uses Koala and the Facebook [JavaScript SDK](http://github.com/facebook/facebook-js-sdk) (formerly Facebook Connect), you can use the OAuth class to parse the cookies:
|
|
95
95
|
|
|
96
96
|
@oauth.get_user_from_cookies(cookies) # gets the user's ID
|
|
97
97
|
@oauth.get_user_info_from_cookies(cookies) # parses and returns the entire hash
|
|
@@ -166,7 +166,7 @@ Koala uses Faraday to make HTTP requests, which means you have complete control
|
|
|
166
166
|
}
|
|
167
167
|
# or on a per-request basis
|
|
168
168
|
@api.get_object(id, args_hash, { :timeout => 10 })
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
The <a href="https://github.com/arsduo/koala/wiki/HTTP-Services">HTTP Services wiki page</a> has more information on what options are available, as well as on how to configure your own Faraday middleware stack (for instance, to implement request logging).
|
|
171
171
|
|
|
172
172
|
See examples, ask questions
|