koala 1.0.0 → 1.2.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.
- data/.autotest +12 -0
- data/.gitignore +3 -1
- data/.travis.yml +9 -0
- data/CHANGELOG +62 -2
- data/Gemfile +8 -0
- data/Rakefile +0 -1
- data/autotest/discover.rb +1 -0
- data/koala.gemspec +13 -14
- data/lib/koala/batch_operation.rb +74 -0
- data/lib/koala/graph_api.rb +145 -132
- data/lib/koala/graph_batch_api.rb +97 -0
- data/lib/koala/graph_collection.rb +59 -0
- data/lib/koala/http_service.rb +176 -0
- data/lib/koala/oauth.rb +191 -0
- data/lib/koala/realtime_updates.rb +23 -29
- data/lib/koala/rest_api.rb +13 -8
- data/lib/koala/test_users.rb +33 -17
- data/lib/koala/uploadable_io.rb +153 -87
- data/lib/koala/utils.rb +11 -0
- data/lib/koala/version.rb +3 -0
- data/lib/koala.rb +59 -217
- data/readme.md +92 -53
- data/spec/cases/{api_base_spec.rb → api_spec.rb} +31 -6
- data/spec/cases/error_spec.rb +32 -0
- data/spec/cases/graph_and_rest_api_spec.rb +12 -21
- data/spec/cases/graph_api_batch_spec.rb +582 -0
- data/spec/cases/graph_api_spec.rb +11 -14
- data/spec/cases/graph_collection_spec.rb +116 -0
- data/spec/cases/http_service_spec.rb +446 -0
- data/spec/cases/koala_spec.rb +54 -0
- data/spec/cases/oauth_spec.rb +319 -213
- data/spec/cases/realtime_updates_spec.rb +45 -31
- data/spec/cases/rest_api_spec.rb +23 -7
- data/spec/cases/test_users_spec.rb +123 -75
- data/spec/cases/uploadable_io_spec.rb +120 -37
- data/spec/cases/utils_spec.rb +10 -0
- data/spec/fixtures/cat.m4v +0 -0
- data/spec/fixtures/facebook_data.yml +26 -24
- data/spec/fixtures/mock_facebook_responses.yml +203 -78
- data/spec/spec_helper.rb +30 -5
- data/spec/support/graph_api_shared_examples.rb +149 -118
- data/spec/support/json_testing_fix.rb +42 -0
- data/spec/support/koala_test.rb +187 -0
- data/spec/support/mock_http_service.rb +62 -58
- data/spec/support/ordered_hash.rb +205 -0
- data/spec/support/rest_api_shared_examples.rb +139 -15
- data/spec/support/uploadable_io_shared_examples.rb +2 -8
- metadata +90 -114
- data/lib/koala/http_services.rb +0 -146
- data/spec/cases/http_services/http_service_spec.rb +0 -54
- data/spec/cases/http_services/net_http_service_spec.rb +0 -350
- data/spec/cases/http_services/typhoeus_service_spec.rb +0 -144
- data/spec/support/live_testing_data_helper.rb +0 -40
- data/spec/support/setup_mocks_or_live.rb +0 -52
data/.autotest
ADDED
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,62 @@
|
|
|
1
|
+
v1.2
|
|
2
|
+
New methods:
|
|
3
|
+
-- API is now the main API class, contains both Graph and REST methods
|
|
4
|
+
-- Old classes are aliased with deprecation warnings (non-breaking change)
|
|
5
|
+
-- TestUsers#update lets you update the name or password of an existing test user
|
|
6
|
+
-- API.get_page_access_token lets you easily fetch the access token for a page you manage (thanks, marcgg!)
|
|
7
|
+
-- Added version.rb (Koala::VERSION)
|
|
8
|
+
Updated methods:
|
|
9
|
+
-- OAuth now parses Facebook's new signed cookie format
|
|
10
|
+
-- API.put_picture now accepts URLs to images (thanks, marcgg!)
|
|
11
|
+
-- Bug fixes to put_picture, parse_signed_request, and the test suite (thanks, johnbhall and Will S.!)
|
|
12
|
+
-- Smarter GraphCollection use
|
|
13
|
+
-- Any pageable result will now become a GraphCollection
|
|
14
|
+
-- Non-pageable results from get_connections no longer error
|
|
15
|
+
-- GraphCollection.raw_results allows access to original result data
|
|
16
|
+
-- Koala no longer enforces any limits on the number of test users you create at once
|
|
17
|
+
Internal improvements:
|
|
18
|
+
-- Koala now uses Faraday to make requests, replacing the HTTPServices (see wiki)
|
|
19
|
+
-- Koala::HTTPService.http_options allows specification of default Faraday connection options
|
|
20
|
+
-- Koala::HTTPService.faraday_middleware allows custom middleware configurations
|
|
21
|
+
-- Koala now defaults to Net::HTTP rather than Typhoeus
|
|
22
|
+
-- Koala::NetHTTPService and Koala::TyphoeusService modules no longer exist
|
|
23
|
+
-- Koala no longer automatically switches to Net::HTTP when uploading IO objects to Facebook
|
|
24
|
+
-- RealTimeUpdates and TestUsers are no longer subclasses of API, but have their own .api objects
|
|
25
|
+
-- The old .graph_api accessor is aliases to .api with a deprecation warning
|
|
26
|
+
-- Removed deprecation warnings for pre-1.1 batch interface
|
|
27
|
+
Testing improvements:
|
|
28
|
+
-- Live test suites now run against test users by default
|
|
29
|
+
-- Test suite can be repeatedly run live without having to update facebook_data.yml
|
|
30
|
+
-- OAuth code and session key tests cannot be run against test users
|
|
31
|
+
-- Faraday adapter for live tests can be specified with ADAPTER=[your adapter] in the rspec command
|
|
32
|
+
-- Live tests can be run against the beta server by specifying BETA=true in the rspec command
|
|
33
|
+
-- Tests now pass against all rubies on Travis CI
|
|
34
|
+
-- Expanded and refactored test coverage
|
|
35
|
+
-- Fixed bug with YAML parsing in Ruby 1.9
|
|
36
|
+
|
|
37
|
+
v1.1
|
|
38
|
+
New methods:
|
|
39
|
+
-- Added Batch API support (thanks, seejohnrun and spiegela!)
|
|
40
|
+
-- includes file uploads, error handling, and FQL
|
|
41
|
+
-- Added GraphAPI#put_video
|
|
42
|
+
-- Added GraphAPI#get_comments_for_urls (thanks, amrnt!)
|
|
43
|
+
-- Added RestAPI#fql_multiquery, which simplifies the results (thanks, amrnt!)
|
|
44
|
+
-- HTTP services support global proxy and timeout settings (thanks, itchy!)
|
|
45
|
+
-- Net::HTTP supports global ca_path, ca_file, and verify_mode settings (thanks, spiegela!)
|
|
46
|
+
Updated methods:
|
|
47
|
+
-- RealtimeUpdates now uses a GraphAPI object instead of its own API
|
|
48
|
+
-- RestAPI#rest_call now has an optional last argument for method, for calls requiring POST, DELETE, etc. (thanks, sshilo!)
|
|
49
|
+
-- Filename can now be specified when uploading (e.g. for Ads API) (thanks, sshilo!)
|
|
50
|
+
-- get_objects([]) returns [] instead of a Facebook error in non-batch mode (thanks, aselder!)
|
|
51
|
+
Internal improvements:
|
|
52
|
+
-- Koala is now more compatible with other Rubies (JRuby, Rubinius, etc.)
|
|
53
|
+
-- HTTP services are more modular and can be changed on the fly (thanks, chadk!)
|
|
54
|
+
-- Includes support for uploading StringIOs and other non-files via Net::HTTP even when using TyphoeusService
|
|
55
|
+
-- Koala now uses multi_json to improve compatibility with Rubinius and other Ruby versions
|
|
56
|
+
-- Koala now uses the modern Typhoeus API (thanks, aselder!)
|
|
57
|
+
-- Koala now uses the current modern Net::HTTP interface (thanks, romanbsd!)
|
|
58
|
+
-- Fixed bugs and typos (thanks, waynn, mokevnin, and tikh!)
|
|
59
|
+
|
|
1
60
|
v1.0
|
|
2
61
|
New methods:
|
|
3
62
|
-- Photo and file upload now supported through #put_picture
|
|
@@ -6,13 +65,13 @@ New methods:
|
|
|
6
65
|
-- Added put_connection and delete_connection convenience methods
|
|
7
66
|
Updated methods:
|
|
8
67
|
-- 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
|
|
68
|
+
-- You can now pass :beta => true in the http options to use Facebook's beta tier
|
|
10
69
|
-- TestUser#befriend now requires user info hashes (id and access token) due to Facebook API changes (thanks, pulsd and kbighorse!)
|
|
11
70
|
-- All methods now accept an http_options hash as their optional last parameter (thanks, spiegela!)
|
|
12
71
|
-- url_for_oauth_code can now take a :display option (thanks, netbe!)
|
|
13
72
|
-- Net::HTTP can now accept :timeout and :proxy options (thanks, gilles!)
|
|
14
73
|
-- Test users now supports using test accounts across multiple apps
|
|
15
|
-
Internal improvements:
|
|
74
|
+
Internal improvements:
|
|
16
75
|
-- For public requests, Koala now uses http by default (instead of https) to improve speed
|
|
17
76
|
-- This can be overridden through Koala.always_use_ssl= or by passing :use_ssl => true in the options hash for an api call
|
|
18
77
|
-- Read-only REST API requests now go through the faster api-read server
|
|
@@ -22,6 +81,7 @@ Internal improvements:
|
|
|
22
81
|
-- Updated parse_signed_request to match Facebook's current implementation (thanks, imajes!)
|
|
23
82
|
-- APIError is now < StandardError, not Exception
|
|
24
83
|
-- Added KoalaError for non-API errors
|
|
84
|
+
-- Net::HTTP's SSL verification is no longer disabled by default
|
|
25
85
|
Test improvements:
|
|
26
86
|
-- Incorporated joshk's awesome rewrite of the entire Koala test suite (thanks, joshk!)
|
|
27
87
|
-- Expanded HTTP service tests (added Typhoeus test suite and additional Net::HTTP test cases)
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/koala.gemspec
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
|
3
|
+
require 'koala/version'
|
|
2
4
|
|
|
3
5
|
Gem::Specification.new do |s|
|
|
4
6
|
s.name = %q{koala}
|
|
5
|
-
s.version =
|
|
6
|
-
s.date = %q{2011-
|
|
7
|
+
s.version = Koala::VERSION
|
|
8
|
+
s.date = %q{2011-09-27}
|
|
7
9
|
|
|
8
10
|
s.summary = %q{A lightweight, flexible library for Facebook with support for the Graph API, the REST API, realtime updates, and OAuth authentication.}
|
|
9
11
|
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.}
|
|
@@ -28,23 +30,20 @@ Gem::Specification.new do |s|
|
|
|
28
30
|
s.specification_version = 3
|
|
29
31
|
|
|
30
32
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
31
|
-
s.add_runtime_dependency(%q<
|
|
32
|
-
s.add_runtime_dependency(%q<
|
|
33
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.5
|
|
33
|
+
s.add_runtime_dependency(%q<multi_json>, ["~> 1.0"])
|
|
34
|
+
s.add_runtime_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
35
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.5"])
|
|
34
36
|
s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
|
|
35
|
-
s.add_development_dependency(%q<typhoeus>, ["~> 0.2.4"])
|
|
36
37
|
else
|
|
37
|
-
s.add_dependency(%q<
|
|
38
|
-
s.add_dependency(%q<
|
|
39
|
-
s.add_dependency(%q<rspec>, ["~> 2.5.0"])
|
|
38
|
+
s.add_dependency(%q<multi_json>, ["~> 1.0"])
|
|
39
|
+
s.add_dependency(%q<rspec>, ["~> 2.5"])
|
|
40
40
|
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
|
41
|
-
s.add_dependency(%q<
|
|
41
|
+
s.add_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
42
42
|
end
|
|
43
43
|
else
|
|
44
|
-
s.add_dependency(%q<
|
|
45
|
-
s.add_dependency(%q<
|
|
46
|
-
s.add_dependency(%q<rspec>, ["~> 2.5.0"])
|
|
44
|
+
s.add_dependency(%q<multi_json>, ["~> 1.0"])
|
|
45
|
+
s.add_dependency(%q<rspec>, ["~> 2.5"])
|
|
47
46
|
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
|
48
|
-
s.add_dependency(%q<
|
|
47
|
+
s.add_dependency(%q<faraday>, ["~> 0.7.0"])
|
|
49
48
|
end
|
|
50
49
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Koala
|
|
2
|
+
module Facebook
|
|
3
|
+
class BatchOperation
|
|
4
|
+
attr_reader :access_token, :http_options, :post_processing, :files, :batch_api, :identifier
|
|
5
|
+
|
|
6
|
+
@identifier = 0
|
|
7
|
+
|
|
8
|
+
def self.next_identifier
|
|
9
|
+
@identifier += 1
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(options = {})
|
|
13
|
+
@identifier = self.class.next_identifier
|
|
14
|
+
@args = (options[:args] || {}).dup # because we modify it below
|
|
15
|
+
@access_token = options[:access_token]
|
|
16
|
+
@http_options = (options[:http_options] || {}).dup # dup because we modify it below
|
|
17
|
+
@batch_args = @http_options.delete(:batch_args) || {}
|
|
18
|
+
@url = options[:url]
|
|
19
|
+
@method = options[:method].to_sym
|
|
20
|
+
@post_processing = options[:post_processing]
|
|
21
|
+
|
|
22
|
+
process_binary_args
|
|
23
|
+
|
|
24
|
+
raise Koala::KoalaError, "Batch operations require an access token, none provided." unless @access_token
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_batch_params(main_access_token)
|
|
28
|
+
# set up the arguments
|
|
29
|
+
args_string = Koala.http_service.encode_params(@access_token == main_access_token ? @args : @args.merge(:access_token => @access_token))
|
|
30
|
+
|
|
31
|
+
response = {
|
|
32
|
+
:method => @method.to_s,
|
|
33
|
+
:relative_url => @url,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# handle batch-level arguments, such as name, depends_on, and attached_files
|
|
37
|
+
@batch_args[:attached_files] = @files.keys.join(",") if @files
|
|
38
|
+
response.merge!(@batch_args) if @batch_args
|
|
39
|
+
|
|
40
|
+
# for get and delete, we append args to the URL string
|
|
41
|
+
# otherwise, they go in the body
|
|
42
|
+
if args_string.length > 0
|
|
43
|
+
if args_in_url?
|
|
44
|
+
response[:relative_url] += (@url =~ /\?/ ? "&" : "?") + args_string if args_string.length > 0
|
|
45
|
+
else
|
|
46
|
+
response[:body] = args_string if args_string.length > 0
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
response
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def process_binary_args
|
|
56
|
+
# collect binary files
|
|
57
|
+
@args.each_pair do |key, value|
|
|
58
|
+
if UploadableIO.binary_content?(value)
|
|
59
|
+
@files ||= {}
|
|
60
|
+
# we use a class-level counter to ensure unique file identifiers across multiple batch operations
|
|
61
|
+
# (this is thread safe, since we just care about uniqueness)
|
|
62
|
+
# so remove the file from the original hash and add it to the file store
|
|
63
|
+
id = "op#{identifier}_file#{@files.keys.length}"
|
|
64
|
+
@files[id] = @args.delete(key).is_a?(UploadableIO) ? value : UploadableIO.new(value)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def args_in_url?
|
|
70
|
+
@method == :get || @method == :delete
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
data/lib/koala/graph_api.rb
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
module Koala
|
|
2
2
|
module Facebook
|
|
3
|
-
GRAPH_SERVER = "graph.facebook.com"
|
|
3
|
+
GRAPH_SERVER = "graph.facebook.com"
|
|
4
4
|
|
|
5
5
|
module GraphAPIMethods
|
|
6
6
|
# A client for the Facebook Graph API.
|
|
7
|
-
#
|
|
7
|
+
#
|
|
8
8
|
# See http://github.com/arsduo/koala for Ruby/Koala documentation
|
|
9
9
|
# and http://developers.facebook.com/docs/api for Facebook API documentation
|
|
10
|
-
#
|
|
10
|
+
#
|
|
11
11
|
# The Graph API is made up of the objects in Facebook (e.g., people, pages,
|
|
12
12
|
# events, photos) and the connections between them (e.g., friends,
|
|
13
13
|
# photo tags, and event RSVPs). This client provides access to those
|
|
14
14
|
# primitive types in a generic way. For example, given an OAuth access
|
|
15
15
|
# token, this will fetch the profile of the active user and the list
|
|
16
16
|
# of the user's friends:
|
|
17
|
-
#
|
|
18
|
-
# graph = Koala::Facebook::
|
|
17
|
+
#
|
|
18
|
+
# graph = Koala::Facebook::API.new(access_token)
|
|
19
19
|
# user = graph.get_object("me")
|
|
20
20
|
# friends = graph.get_connections(user["id"], "friends")
|
|
21
|
-
#
|
|
21
|
+
#
|
|
22
22
|
# You can see a list of all of the objects and connections supported
|
|
23
23
|
# by the API at http://developers.facebook.com/docs/reference/api/.
|
|
24
|
-
#
|
|
24
|
+
#
|
|
25
25
|
# You can obtain an access token via OAuth or by using the Facebook
|
|
26
26
|
# JavaScript SDK. See the Koala and Facebook documentation for more information.
|
|
27
|
-
#
|
|
27
|
+
#
|
|
28
28
|
# If you are using the JavaScript SDK, you can use the
|
|
29
29
|
# Koala::Facebook::OAuth.get_user_from_cookie() method below to get the OAuth access token
|
|
30
30
|
# for the active user from the cookie saved by the SDK.
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
# Objects
|
|
33
33
|
|
|
34
34
|
def get_object(id, args = {}, options = {})
|
|
35
35
|
# Fetchs the given object from the graph.
|
|
36
36
|
graph_call(id, args, "get", options)
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
def get_objects(ids, args = {}, options = {})
|
|
40
|
-
# Fetchs all of the given
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
graph_call("", args.merge("ids" => ids.join(",")), "get", options)
|
|
40
|
+
# Fetchs all of the given objects from the graph.
|
|
41
|
+
# If any of the IDs are invalid, they'll raise an exception.
|
|
42
|
+
return [] if ids.empty?
|
|
43
|
+
graph_call("", args.merge("ids" => ids.respond_to?(:join) ? ids.join(",") : ids), "get", options)
|
|
44
44
|
end
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
def put_object(parent_object, connection_name, args = {}, options = {})
|
|
47
47
|
# Writes the given object to the graph, connected to the given parent.
|
|
48
48
|
# See http://developers.facebook.com/docs/api#publishing for all of
|
|
49
49
|
# the supported writeable objects.
|
|
50
|
-
#
|
|
50
|
+
#
|
|
51
51
|
# For example,
|
|
52
52
|
# graph.put_object("me", "feed", :message => "Hello, world")
|
|
53
53
|
# writes "Hello, world" to the active user's wall.
|
|
@@ -56,7 +56,7 @@ module Koala
|
|
|
56
56
|
# publishing wall posts requires the "publish_stream" permission. See
|
|
57
57
|
# http://developers.facebook.com/docs/authentication/ for details about
|
|
58
58
|
# extended permissions.
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "Write operations require an access token"}) unless @access_token
|
|
61
61
|
graph_call("#{parent_object}/#{connection_name}", args, "post", options)
|
|
62
62
|
end
|
|
@@ -66,13 +66,12 @@ module Koala
|
|
|
66
66
|
raise APIError.new({"type" => "KoalaMissingAccessToken", "message" => "Delete requires an access token"}) unless @access_token
|
|
67
67
|
graph_call(id, {}, "delete", options)
|
|
68
68
|
end
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
# Connections
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
def get_connections(id, connection_name, args = {}, options = {})
|
|
73
73
|
# Fetchs the connections for given object.
|
|
74
|
-
|
|
75
|
-
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
|
74
|
+
graph_call("#{id}/#{connection_name}", args, "get", options)
|
|
76
75
|
end
|
|
77
76
|
|
|
78
77
|
def put_connections(id, connection_name, args = {}, options = {})
|
|
@@ -87,55 +86,56 @@ module Koala
|
|
|
87
86
|
graph_call("#{id}/#{connection_name}", args, "delete", options)
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
#
|
|
91
|
-
# to delete
|
|
92
|
-
# note: you'll need the user_photos
|
|
93
|
-
|
|
89
|
+
# Media (photos and videos)
|
|
90
|
+
# to delete photos or videos, use delete_object(object_id)
|
|
91
|
+
# note: you'll need the user_photos or user_videos permissions to actually access media after upload
|
|
92
|
+
|
|
94
93
|
def get_picture(object, args = {}, options = {})
|
|
95
94
|
# Gets a picture object, returning the URL (which Facebook sends as a header)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
graph_call("#{object}/picture", args, "get", options.merge(:http_component => :headers)) do |result|
|
|
96
|
+
result["Location"]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Can be called in multiple ways:
|
|
101
|
+
#
|
|
102
|
+
# put_picture(file, [content_type], ...)
|
|
103
|
+
# put_picture(path_to_file, [content_type], ...)
|
|
104
|
+
# put_picture(picture_url, ...)
|
|
105
|
+
#
|
|
106
|
+
# You can pass in uploaded files directly from Rails or Sinatra.
|
|
107
|
+
# (See lib/koala/uploadable_io.rb for supported frameworks)
|
|
108
|
+
#
|
|
109
|
+
# Optional parameters can be added to the end of the argument list:
|
|
110
|
+
# - args: a hash of request parameters (default: {})
|
|
111
|
+
# - target_id: ID of the target where to post the picture (default: "me")
|
|
112
|
+
# - options: a hash of http options passed to the HTTPService module
|
|
113
|
+
#
|
|
114
|
+
# put_picture(file, content_type, {:message => "Message"}, 01234560)
|
|
115
|
+
# put_picture(params[:file], {:message => "Message"})
|
|
116
|
+
#
|
|
117
|
+
# (Note that with URLs, there's no optional content type field)
|
|
118
|
+
# put_picture(picture_url, {:message => "Message"}, my_page_id)
|
|
119
|
+
|
|
100
120
|
def put_picture(*picture_args)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
#
|
|
109
|
-
# Optional parameters can be added to the end of the argument list:
|
|
110
|
-
# - args: a hash of request parameters (default: {})
|
|
111
|
-
# - target_id: ID of the target where to post the picture (default: "me")
|
|
112
|
-
# - options: a hash of http options passed to the HTTPService module
|
|
113
|
-
#
|
|
114
|
-
# put_picture(file, content_type, {:message => "Message"}, 01234560)
|
|
115
|
-
# put_picture(params[:file], {:message => "Message"})
|
|
116
|
-
|
|
117
|
-
raise KoalaError.new("Wrong number of arguments for put_picture") unless picture_args.size.between?(1, 5)
|
|
118
|
-
|
|
119
|
-
args_offset = picture_args[1].kind_of?(Hash) || picture_args.size == 1 ? 0 : 1
|
|
120
|
-
|
|
121
|
-
args = picture_args[1 + args_offset] || {}
|
|
122
|
-
target_id = picture_args[2 + args_offset] || "me"
|
|
123
|
-
options = picture_args[3 + args_offset] || {}
|
|
124
|
-
|
|
125
|
-
args["source"] = Koala::UploadableIO.new(*picture_args.slice(0, 1 + args_offset))
|
|
126
|
-
|
|
127
|
-
self.put_object(target_id, "photos", args, options)
|
|
121
|
+
put_object(*parse_media_args(picture_args, "photos"))
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def put_video(*video_args)
|
|
125
|
+
args = parse_media_args(video_args, "videos")
|
|
126
|
+
args.last[:video] = true
|
|
127
|
+
put_object(*args)
|
|
128
128
|
end
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
# Wall posts
|
|
131
131
|
# To get wall posts, use get_connections(user, "feed")
|
|
132
132
|
# To delete a wall post, just use delete_object(post_id)
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
def put_wall_post(message, attachment = {}, profile_id = "me", options = {})
|
|
135
135
|
# attachment is a hash describing the wall post
|
|
136
136
|
# (see X for more details)
|
|
137
|
-
# For instance,
|
|
138
|
-
#
|
|
137
|
+
# For instance,
|
|
138
|
+
#
|
|
139
139
|
# {"name" => "Link name"
|
|
140
140
|
# "link" => "http://www.example.com/",
|
|
141
141
|
# "caption" => "{*actor*} posted a new review",
|
|
@@ -144,19 +144,19 @@ module Koala
|
|
|
144
144
|
|
|
145
145
|
self.put_object(profile_id, "feed", attachment.merge({:message => message}), options)
|
|
146
146
|
end
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
# Comments
|
|
149
149
|
# to delete comments, use delete_object(comment_id)
|
|
150
150
|
# to get comments, use get_connections(object, "likes")
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
def put_comment(object_id, message, options = {})
|
|
153
153
|
# Writes the given comment on the given post.
|
|
154
154
|
self.put_object(object_id, "comments", {:message => message}, options)
|
|
155
155
|
end
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
# Likes
|
|
158
158
|
# to get likes, use get_connections(user, "likes")
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
def put_like(object_id, options = {})
|
|
161
161
|
# Likes the given post.
|
|
162
162
|
self.put_object(object_id, "likes", {}, options)
|
|
@@ -169,89 +169,102 @@ module Koala
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
# Search
|
|
172
|
-
|
|
172
|
+
|
|
173
173
|
def search(search_terms, args = {}, options = {})
|
|
174
174
|
args.merge!({:q => search_terms}) unless search_terms.nil?
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
# see any of the above methods for example invocations
|
|
184
|
-
response = api(*args) do |response|
|
|
185
|
-
# check for Graph API-specific errors
|
|
186
|
-
if response.is_a?(Hash) && error_details = response["error"]
|
|
187
|
-
raise APIError.new(error_details)
|
|
188
|
-
end
|
|
175
|
+
graph_call("search", args, "get", options)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Convenience Methods
|
|
179
|
+
|
|
180
|
+
def get_page_access_token(object_id)
|
|
181
|
+
result = get_object(object_id, :fields => "access_token") do
|
|
182
|
+
result ? result["access_token"] : nil
|
|
189
183
|
end
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def get_comments_for_urls(urls = [], args = {}, options = {})
|
|
187
|
+
# Fetchs the comments for given URLs (array or comma-separated string)
|
|
188
|
+
# see https://developers.facebook.com/blog/post/490
|
|
189
|
+
return [] if urls.empty?
|
|
190
|
+
args.merge!(:ids => urls.respond_to?(:join) ? urls.join(",") : urls)
|
|
191
|
+
get_object("comments", args, options)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
194
|
# GraphCollection support
|
|
195
|
-
|
|
196
195
|
def get_page(params)
|
|
197
196
|
# Pages through a set of results stored in a GraphCollection
|
|
198
197
|
# Used for connections and search results
|
|
199
|
-
|
|
200
|
-
result ? GraphCollection.new(result, self) : nil # when facebook is down nil can be returned
|
|
198
|
+
graph_call(*params)
|
|
201
199
|
end
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
#
|
|
213
|
-
#It also allows access to paging information and the
|
|
214
|
-
#ability to get the next/previous page in the collection
|
|
215
|
-
#by calling next_page or previous_page.
|
|
216
|
-
attr_reader :paging
|
|
217
|
-
attr_reader :api
|
|
218
|
-
|
|
219
|
-
def initialize(response, api)
|
|
220
|
-
super response["data"]
|
|
221
|
-
@paging = response["paging"]
|
|
222
|
-
@api = api
|
|
200
|
+
|
|
201
|
+
# Batch API
|
|
202
|
+
def batch(http_options = {}, &block)
|
|
203
|
+
batch_client = GraphBatchAPI.new(access_token, self)
|
|
204
|
+
if block
|
|
205
|
+
yield batch_client
|
|
206
|
+
batch_client.execute(http_options)
|
|
207
|
+
else
|
|
208
|
+
batch_client
|
|
209
|
+
end
|
|
223
210
|
end
|
|
224
|
-
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
base, args = send("#{this}_page_params")
|
|
232
|
-
base ? @api.get_page([base, args]) : nil
|
|
211
|
+
|
|
212
|
+
# Direct access to the Facebook API
|
|
213
|
+
# see any of the above methods for example invocations
|
|
214
|
+
def graph_call(path, args = {}, verb = "get", options = {}, &post_processing)
|
|
215
|
+
result = api(path, args, verb, options) do |response|
|
|
216
|
+
error = check_response(response)
|
|
217
|
+
raise error if error
|
|
233
218
|
end
|
|
219
|
+
|
|
220
|
+
# turn this into a GraphCollection if it's pageable
|
|
221
|
+
result = GraphCollection.evaluate(result, self)
|
|
234
222
|
|
|
235
|
-
#
|
|
236
|
-
|
|
237
|
-
define_method "#{this.to_sym}_page_params" do
|
|
238
|
-
return nil unless @paging and @paging[this]
|
|
239
|
-
parse_page_url(@paging[this])
|
|
240
|
-
end
|
|
223
|
+
# now process as appropriate for the given call (get picture header, etc.)
|
|
224
|
+
post_processing ? post_processing.call(result) : result
|
|
241
225
|
end
|
|
226
|
+
|
|
227
|
+
private
|
|
242
228
|
|
|
243
|
-
def
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
229
|
+
def check_response(response)
|
|
230
|
+
# check for Graph API-specific errors
|
|
231
|
+
# this returns an error, which is immediately raised (non-batch)
|
|
232
|
+
# or added to the list of batch results (batch)
|
|
233
|
+
if response.is_a?(Hash) && error_details = response["error"]
|
|
234
|
+
APIError.new(error_details)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def parse_media_args(media_args, method)
|
|
239
|
+
# photo and video uploads can accept different types of arguments (see above)
|
|
240
|
+
# so here, we parse the arguments into a form directly usable in put_object
|
|
241
|
+
raise KoalaError.new("Wrong number of arguments for put_#{method == "photos" ? "picture" : "video"}") unless media_args.size.between?(1, 5)
|
|
242
|
+
|
|
243
|
+
args_offset = media_args[1].kind_of?(Hash) || media_args.size == 1 ? 0 : 1
|
|
244
|
+
|
|
245
|
+
args = media_args[1 + args_offset] || {}
|
|
246
|
+
target_id = media_args[2 + args_offset] || "me"
|
|
247
|
+
options = media_args[3 + args_offset] || {}
|
|
248
|
+
|
|
249
|
+
if url?(media_args.first)
|
|
250
|
+
# If media_args is a URL, we can upload without UploadableIO
|
|
251
|
+
args.merge!(:url => media_args.first)
|
|
252
|
+
else
|
|
253
|
+
args["source"] = Koala::UploadableIO.new(*media_args.slice(0, 1 + args_offset))
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
[target_id, method, args, options]
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def url?(data)
|
|
260
|
+
return false unless data.is_a? String
|
|
261
|
+
begin
|
|
262
|
+
uri = URI.parse(data)
|
|
263
|
+
%w( http https ).include?(uri.scheme)
|
|
264
|
+
rescue URI::BadURIError
|
|
265
|
+
false
|
|
251
266
|
end
|
|
252
|
-
[base,new_params]
|
|
253
267
|
end
|
|
254
|
-
|
|
255
268
|
end
|
|
256
269
|
end
|
|
257
270
|
end
|