fgraph 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +2 -0
- data/License +20 -0
- data/README.rdoc +136 -0
- data/Rakefile +37 -0
- data/VERSION.yml +5 -0
- data/examples/get_access_token.rb +19 -0
- data/examples/publish_feed.rb +16 -0
- data/lib/fgraph.rb +321 -0
- data/lib/fgraph/client.rb +111 -0
- data/test/fgraph/client_test.rb +155 -0
- data/test/fgraph_test.rb +236 -0
- data/test/fixtures/access_token.txt +1 -0
- data/test/fixtures/object_cocacola.json +10 -0
- data/test/test_helper.rb +34 -0
- metadata +148 -0
data/History
ADDED
data/License
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Herryanto Siatono
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
= fgraph
|
2
|
+
|
3
|
+
* http://github.com/jugend/fgraph
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
Facebook Graph Ruby API implementation with Ruby magic (http://graph.facebook.com).
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
sudo gem install fgraph
|
12
|
+
|
13
|
+
=== Facebook Graph Cheat Sheet
|
14
|
+
|
15
|
+
sudo gem install cheat
|
16
|
+
cheat fbgraph
|
17
|
+
|
18
|
+
=== Single object query
|
19
|
+
|
20
|
+
# Users: https://graph.facebook.com/btaylor (Bret Taylor)
|
21
|
+
FGraph.object('btaylor')
|
22
|
+
|
23
|
+
# Pages: https://graph.facebook.com/cocacola (Coca-Cola page)
|
24
|
+
FGraph.object('cocacola')
|
25
|
+
|
26
|
+
# Fields selection with metadata
|
27
|
+
FGraph.object('btaylor', :fields => 'id,name,picture', :metadata => 1)
|
28
|
+
|
29
|
+
# Page photos
|
30
|
+
FGraph.object('/cocacola/photos')
|
31
|
+
FGraph.object_photos('cocacola')
|
32
|
+
|
33
|
+
# Current user: https://graph.facebook.com/me?access_token=...
|
34
|
+
FGraph.me(:access_token => '...')
|
35
|
+
|
36
|
+
# Current user's friends: https://graph.facebook.com/me/friends?access_token=...
|
37
|
+
FGraph.me('friends', :access_token => '...')
|
38
|
+
FGraph.me_friends(:access_token => '...')
|
39
|
+
|
40
|
+
=== Multiple objects query
|
41
|
+
|
42
|
+
# Multiple users select: https://graph.facebook.com?ids=arjun,vernal
|
43
|
+
FGraph.objects('arjun', 'vernel')
|
44
|
+
|
45
|
+
# Filter fields: https://graph.facebook.com?ids=arjun,vernal&fields=id,name,picture
|
46
|
+
FGraph.objects('arjun', 'vernel', :fields => 'id,name,picture')
|
47
|
+
|
48
|
+
=== OAuth
|
49
|
+
|
50
|
+
OAuth authorization URL:
|
51
|
+
# https://graph.facebook.com/oauth/authorize?
|
52
|
+
# client_id=...&
|
53
|
+
# redirect_uri=http://www.example.com/oauth_redirect&
|
54
|
+
# scope=publish_stream
|
55
|
+
FGraph.oauth_authorize_url('[client id]', 'http://www.example.com/oauth_redirect', :scope =>
|
56
|
+
'publish_stream')
|
57
|
+
|
58
|
+
OAuth Access Token:
|
59
|
+
# https://graph.facebook.com/oauth/access_token?
|
60
|
+
# client_id=...&
|
61
|
+
# client_secret=...&
|
62
|
+
# redirect_uri=http://www.example.com/oauth_redirect&
|
63
|
+
# code=...
|
64
|
+
FGraph.oauth_access_token('[client id]', '[client secret]',
|
65
|
+
:redirect_uri => ''http://www.example.com/oauth_redirect',
|
66
|
+
:code => '[authorization code]')
|
67
|
+
|
68
|
+
OAuth Application Access Token, required to access application anlytics data:
|
69
|
+
# https://graph.facebook.com/oauth/access_token?
|
70
|
+
# client_id=...&
|
71
|
+
# client_secret=...&
|
72
|
+
# type=client_cred
|
73
|
+
FGraph.oauth_access_token('[client id]', '[client secret]', :type => 'client_cred')
|
74
|
+
|
75
|
+
=== Publish to Facebook Graph
|
76
|
+
|
77
|
+
# Post to user's feed.
|
78
|
+
# curl -F 'access_token=...' \
|
79
|
+
# -F 'message=Hello, Arjun. I like this new API.' \
|
80
|
+
# https://graph.facebook.com/arjun/feed
|
81
|
+
FGraph.publish('arjun/feed', :message => 'Hello, Arjun. I like this n ew API.',
|
82
|
+
:access_token => '...')
|
83
|
+
FGraph.publish_feed('arjun', :message => '...', :access_token => '... ')
|
84
|
+
FGraph.publish_feed('me', ':message => '...', :access_token => '...')
|
85
|
+
|
86
|
+
=== Remove from Facebook Graph
|
87
|
+
|
88
|
+
# DELETE https://graph.facebook.com/ID?access_token=... HTTP/1.1
|
89
|
+
FGraph.remove('[ID]')
|
90
|
+
FGraph.remove('[ID]/likes')
|
91
|
+
FGraph.remove_likes('[ID]')
|
92
|
+
|
93
|
+
=== Search
|
94
|
+
|
95
|
+
# https://graph.facebook.com/search?q=watermelon&type=post
|
96
|
+
FGraph.search('watermelon', :type => 'post')
|
97
|
+
FGraph.search_post('watermelon')
|
98
|
+
|
99
|
+
=== Insights
|
100
|
+
|
101
|
+
# https://graph.facebook.com/app_id/insights?access_token=...
|
102
|
+
FGraph.insights('[app_id]', '[app_access_token]')
|
103
|
+
|
104
|
+
# https://graph.facebook.com/app_id/insights/application_api_call/day?access_token=...
|
105
|
+
FGraph.insights('[app_id]', '[app_access_token]', :metric_path => 'application_api_call/day')
|
106
|
+
|
107
|
+
=== Pagination Options
|
108
|
+
|
109
|
+
* <tt>limit</tt> - max no of records
|
110
|
+
* <tt>offset</tt> - offset
|
111
|
+
* <tt>until</tt> - since (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
112
|
+
|
113
|
+
== License
|
114
|
+
|
115
|
+
(The MIT License)
|
116
|
+
|
117
|
+
Copyright (c) 2010 Herryanto Siatono http://www.pluitsolutions.com
|
118
|
+
|
119
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
120
|
+
a copy of this software and associated documentation files (the
|
121
|
+
'Software'), to deal in the Software without restriction, including
|
122
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
123
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
124
|
+
permit persons to whom the Software is furnished to do so, subject to
|
125
|
+
the following conditions:
|
126
|
+
|
127
|
+
The above copyright notice and this permission notice shall be
|
128
|
+
included in all copies or substantial portions of the Software.
|
129
|
+
|
130
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
131
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
132
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
133
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
134
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
135
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
136
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'jeweler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Jeweler::Tasks.new do |gem|
|
6
|
+
gem.name = "fgraph"
|
7
|
+
gem.summary = "Ruby Facebook Graph API"
|
8
|
+
gem.description = "Ruby Facebook Graph API"
|
9
|
+
gem.email = "herryanto@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/jugend/fgraph"
|
11
|
+
gem.authors = ["Herryanto Siatono"]
|
12
|
+
gem.files = FileList["[A-Z]*", "{examples,lib,test}/**/*"]
|
13
|
+
|
14
|
+
gem.add_dependency("httparty", "~> 0.5.0")
|
15
|
+
# gem.add_dependency("hashie", "~> 0.2.0")
|
16
|
+
|
17
|
+
gem.add_development_dependency("shoulda", "~> 2.10.0")
|
18
|
+
gem.add_development_dependency("jnunemaker-matchy", "~> 0.4.0")
|
19
|
+
gem.add_development_dependency("mocha", "~> 0.9.0")
|
20
|
+
gem.add_development_dependency("fakeweb", "~> 1.2.0")
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
|
28
|
+
require "rake/testtask"
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << "test"
|
31
|
+
test.ruby_opts << "-rubygems"
|
32
|
+
test.pattern = "test/**/*_test.rb"
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
task :default => :test
|
37
|
+
task :test => :check_dependencies
|
data/VERSION.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Retrieve your code by accessing Authorization URL From your browser,
|
2
|
+
# it should redirect to 'redirect_uri' with 'code' param.
|
3
|
+
# Replace the FACEBOOK_XXX constants accordingly.
|
4
|
+
#
|
5
|
+
# Authorization URL, access from your browser to get Authorization Code
|
6
|
+
# https://graph.facebook.com/oauth/authorize?client_id=878116c4a4a76f25e4beb97ab096cc92&redirect_uri=http%3A%2F%2Fbookjetty.pluitsolutions.com%2F&scope=publish_stream
|
7
|
+
|
8
|
+
FACEBOOK_APP_ID = '878116c4a4a76f25e4beb97ab096cc92'
|
9
|
+
FACEBOOK_APP_SECRET = '41f0e7ee8b6409dce1610de9926477c4'
|
10
|
+
FACEBOOK_OAUTH_REDIRECT_URI = 'http://bookjetty.pluitsolutions.com/'
|
11
|
+
FACEBOOK_OAUTH_CODE = '2.Dq5RDPHxhsgaeScc_fiigg__.3600.1273831200-756314021|4Eew6iuIg0x69N1d3Cr99gdVGwU.'
|
12
|
+
FACEBOOK_OAUTH_SCOPE = ''
|
13
|
+
|
14
|
+
require 'pp'
|
15
|
+
require 'rubygems'
|
16
|
+
require File.dirname(__FILE__) + '/../lib/fgraph'
|
17
|
+
|
18
|
+
pp FGraph.oauth_access_token(FACEBOOK_APP_ID, FACEBOOK_OAUTH_REDIRECT_URI,
|
19
|
+
FACEBOOK_APP_SECRET, FACEBOOK_OAUTH_CODE)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Replace FACEBOOK_ACCESS_TOKEN with valid token with publish_stream extended permission
|
2
|
+
#
|
3
|
+
# Post to user's feed.
|
4
|
+
# curl -F 'access_token=...' -F 'message=Hello, Arjun. I like this new API.' https://graph.facebook.com/me/feed
|
5
|
+
# curl -F 'access_token=112157085478818|2.AlP5TBjZ9F6wOXPX_V0GTg__.3600.1273777200-756314021|NV7Dnuol59KbQr6W1axv6ZmytaI.' -F 'message=Hello, Arjun. I like this new API.' https://graph.facebook.com/me/feed
|
6
|
+
FACEBOOK_ACCESS_TOKEN = '112157085478818|2.Dq5RDPHxhsgaeScc_fiigg__.3600.1273831200-756314021|Tvm41skDwOdGR2O3Lz4owMVz1lM.'
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'pp'
|
10
|
+
require File.dirname(__FILE__) + '/../lib/fgraph'
|
11
|
+
|
12
|
+
# Post to current user's f eed
|
13
|
+
pp FGraph.publish_feed('me', :message => 'Hello. I like this new API.',
|
14
|
+
:access_token => FACEBOOK_ACCESS_TOKEN)
|
15
|
+
|
16
|
+
puts "Message successfully posted."
|
data/lib/fgraph.rb
ADDED
@@ -0,0 +1,321 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'cgi'
|
3
|
+
require 'oauth2'
|
4
|
+
require 'fgraph/client'
|
5
|
+
|
6
|
+
module FGraph
|
7
|
+
include HTTParty
|
8
|
+
base_uri 'https://graph.facebook.com'
|
9
|
+
format :json
|
10
|
+
|
11
|
+
# Facebook Error
|
12
|
+
class FacebookError < StandardError
|
13
|
+
attr_reader :data
|
14
|
+
|
15
|
+
def initialize(data)
|
16
|
+
@data = data
|
17
|
+
super("(#{data['type']}) #{data['message']}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class QueryParseError < FacebookError; end
|
22
|
+
class GraphMethodError < FacebookError; end
|
23
|
+
class OAuthError < FacebookError; end
|
24
|
+
class OAuthAccessTokenError < OAuthError; end
|
25
|
+
|
26
|
+
# Single object query.
|
27
|
+
#
|
28
|
+
# # Users: https://graph.facebook.com/btaylor (Bret Taylor)
|
29
|
+
# FGraph.object('btaylor')
|
30
|
+
#
|
31
|
+
# # Pages: https://graph.facebook.com/cocacola (Coca-Cola page)
|
32
|
+
# FGraph.object('cocacola')
|
33
|
+
#
|
34
|
+
# # Fields selection with metadata
|
35
|
+
# FGraph.object('btaylor', :fields => 'id,name,picture', :metadata => 1)
|
36
|
+
#
|
37
|
+
# # Page photos
|
38
|
+
# FGraph.object('/cocacola/photos')
|
39
|
+
# FGraph.object_photos('cocacola')
|
40
|
+
#
|
41
|
+
def self.object(id, options={})
|
42
|
+
perform_get("/#{id}", options)
|
43
|
+
end
|
44
|
+
|
45
|
+
# call-seq:
|
46
|
+
# FGraph.objects(id, id)
|
47
|
+
# FGraph.objects(id, id, options_hash)
|
48
|
+
#
|
49
|
+
# Multiple objects query.
|
50
|
+
#
|
51
|
+
# # Multiple users select: https://graph.facebook.com?ids=arjun,vernal
|
52
|
+
# FGraph.objects('arjun', 'vernel')
|
53
|
+
#
|
54
|
+
# # Filter fields: https://graph.facebook.com?ids=arjun,vernal&fields=id,name,picture
|
55
|
+
# FGraph.objects('arjun', 'vernel', :fields => 'id,name,picture')
|
56
|
+
#
|
57
|
+
def self.objects(*args)
|
58
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
59
|
+
options = options.merge(:ids => args.join(','))
|
60
|
+
perform_get("/", options)
|
61
|
+
end
|
62
|
+
|
63
|
+
# call-seq:
|
64
|
+
# FGraph.me(category)
|
65
|
+
# FGraph.me(category, options_hash)
|
66
|
+
#
|
67
|
+
# Returns current user object details.
|
68
|
+
#
|
69
|
+
# <tt>category</tt> - <tt>friends|home|feed|likes|movies|books|notes|photos|videos|events|groups</tt>
|
70
|
+
#
|
71
|
+
# # Current user: https://graph.facebook.com/me?access_token=...
|
72
|
+
# FGraph.me(:access_token => '...')
|
73
|
+
#
|
74
|
+
# # Current user's friends: https://graph.facebook.com/me/friends?access_token=...
|
75
|
+
# FGraph.me('friends', :access_token => '...')
|
76
|
+
# FGraph.me_friends(:access_token => '...')
|
77
|
+
#
|
78
|
+
def self.me(*args)
|
79
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
80
|
+
category = args.shift
|
81
|
+
|
82
|
+
path = "me"
|
83
|
+
path += "/#{category}" unless category.blank?
|
84
|
+
self.object(path, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Request authorization from Facebok to fetch private data in the profile or permission to publish on a
|
88
|
+
# user's behalf. Returns Oauth Authorization URL, redirect to this URL to allow user to authorize your
|
89
|
+
# application from Facebook.
|
90
|
+
#
|
91
|
+
# <tt>client_id</tt> - Facebook API Key
|
92
|
+
# <tt>redirect_uri</tt> - Needs to begin with your app's Connect URL. For instance, if your Connect URL
|
93
|
+
# is http://www.example.com then your redirect URI could be http://www.example.com/oauth_redirect.
|
94
|
+
# <tt>scope (optional)</tt> -
|
95
|
+
#
|
96
|
+
# ==== Options
|
97
|
+
# * <tt>scope</tt> - Extended permission required to fetch private data or request permision to
|
98
|
+
# publish to Facebook on a user's behalf.
|
99
|
+
# * <tt>display</tt> - Other display type for authentication/authorization form, i.e. popup, touch.
|
100
|
+
#
|
101
|
+
# # https://graph.facebook.com/oauth/authorize?
|
102
|
+
# # client_id=...&
|
103
|
+
# # redirect_uri=http://www.example.com/oauth_redirect&
|
104
|
+
# # scope=publish_stream
|
105
|
+
#
|
106
|
+
# FGraph.oauth_authorize_url('[client id]', 'http://www.example.com/oauth_redirect', :scope =>
|
107
|
+
# 'publish_stream')
|
108
|
+
#
|
109
|
+
def self.oauth_authorize_url(client_id, redirect_uri, options={})
|
110
|
+
self.format_url('/oauth/authorize', {
|
111
|
+
:client_id => client_id,
|
112
|
+
:redirect_uri => redirect_uri
|
113
|
+
}.merge(options))
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return OAuth access_token. There are two types of access token, user access token and application
|
117
|
+
# access token.
|
118
|
+
#
|
119
|
+
# User access_token requires <tt>code</tt> and and <tt>redirect_uri</tt> options. <tt>code</tt> is
|
120
|
+
# the autorization code appended as query string to redirect URI when accessing oauth authorization URL.
|
121
|
+
#
|
122
|
+
# # https://graph.facebook.com/oauth/access_token?
|
123
|
+
# # client_id=...&
|
124
|
+
# # client_secret=...&
|
125
|
+
# # redirect_uri=http://www.example.com/oauth_redirect&
|
126
|
+
# # code=...
|
127
|
+
# FGraph.oauth_access_token('[client id]', '[client secret]',
|
128
|
+
# :redirect_uri => ''http://www.example.com/oauth_redirect',
|
129
|
+
# :code => '[authorization code]')
|
130
|
+
#
|
131
|
+
# Application access token requires <tt>:type => 'client_cred'</td> option. Used to access application
|
132
|
+
# insights data.
|
133
|
+
#
|
134
|
+
# # https://graph.facebook.com/oauth/access_token?
|
135
|
+
# # client_id=...&
|
136
|
+
# # client_secret=...&
|
137
|
+
# # type=client_cred
|
138
|
+
# FGraph.oauth_access_token('[client id]', '[client secret]', :type => 'client_cred')
|
139
|
+
#
|
140
|
+
def self.oauth_access_token(client_id, client_secret, options={})
|
141
|
+
url = self.format_url('/oauth/access_token', {
|
142
|
+
:client_id => client_id,
|
143
|
+
:client_secret => client_secret
|
144
|
+
}.merge(options || {}))
|
145
|
+
|
146
|
+
response = self.perform_get(url)
|
147
|
+
response_hash = {}
|
148
|
+
response.split('&').each do |value|
|
149
|
+
value_pair = value.split('=')
|
150
|
+
response_hash[value_pair[0]] = value_pair[1]
|
151
|
+
end
|
152
|
+
response_hash
|
153
|
+
end
|
154
|
+
|
155
|
+
# Shortcut to retrieve application access token.
|
156
|
+
def self.oauth_app_access_token(client_id, client_secret)
|
157
|
+
self.oauth_access_token(client_id, client_secret, :type => 'client_cred')
|
158
|
+
end
|
159
|
+
|
160
|
+
# Publish to Facebook, you would need to be authorized and provide access token.
|
161
|
+
#
|
162
|
+
# # Post to user's feed.
|
163
|
+
# # curl -F 'access_token=...' \
|
164
|
+
# # -F 'message=Hello, Arjun. I like this new API.' \
|
165
|
+
# # https://graph.facebook.com/arjun/feed
|
166
|
+
# FGraph.publish('arjun/feed', :message => 'Hello, Arjun. I like this new API.',
|
167
|
+
# :access_token => '...')
|
168
|
+
# FGraph.publish_feed('arjun', :message => '...', :access_token => '...')
|
169
|
+
# FGraph.publish_feed('me', ':message => '...', :access_token => '...')
|
170
|
+
#
|
171
|
+
# ==== Options
|
172
|
+
#
|
173
|
+
# Method Description Options
|
174
|
+
# -------------------------------------------------------------------------------------
|
175
|
+
# /PROFILE_ID/feed write to the given profile's feed/wall :message, :picture,
|
176
|
+
# :link, :name, description
|
177
|
+
# /POST_ID/comments comment on the given post :message
|
178
|
+
# /POST_ID/likes like the given post none
|
179
|
+
# /PROFILE_ID/notes write a note on the given profile :message, :subject
|
180
|
+
# /PROFILE_ID/links write a link on the given profile :link, :message
|
181
|
+
# /EVENT_ID/attending attend the given event none
|
182
|
+
# /EVENT_ID/maybe maybe attend the given event none
|
183
|
+
# /EVENT_ID/declined decline the given event none
|
184
|
+
#
|
185
|
+
def self.publish(id, options={})
|
186
|
+
self.perform_post("/#{id}", options)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Delete objects in the graph.
|
190
|
+
#
|
191
|
+
# # DELETE https://graph.facebook.com/ID?access_token=... HTTP/1.1
|
192
|
+
#
|
193
|
+
# FGraph.remove('[ID]')
|
194
|
+
# FGraph.remove('[ID]/likes')
|
195
|
+
# FGraph.remove_likes('[ID]')
|
196
|
+
#
|
197
|
+
def self.remove(id, options={})
|
198
|
+
self.perform_delete("/#{id}", options)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Search over all public objects in the social graph.
|
202
|
+
#
|
203
|
+
# # https://graph.facebook.com/search?q=watermelon&type=post
|
204
|
+
# FGraph.search('watermelon', :type => 'post')
|
205
|
+
# FGraph.search_post('watermelon')
|
206
|
+
#
|
207
|
+
# ==== Options
|
208
|
+
# * <tt>type</tt> - <tt>album|event|group|link|note|page|photo|post|status|user|video</tt>
|
209
|
+
# * <tt>limit</tt> - max no of records
|
210
|
+
# * <tt>offset</tt> - offset
|
211
|
+
# * <tt>until</tt> - since (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
212
|
+
def self.search(query, options={})
|
213
|
+
self.perform_get("/search", {
|
214
|
+
:q => query
|
215
|
+
}.merge(options|| {}))
|
216
|
+
end
|
217
|
+
|
218
|
+
# Download insights data for your application.
|
219
|
+
#
|
220
|
+
# # https://graph.facebook.com/app_id/insights?access_token=...
|
221
|
+
# FGraph.insights('[app_id]', '[app_access_token]')
|
222
|
+
#
|
223
|
+
# # https://graph.facebook.com/app_id/insights/application_api_call/day?access_token=...
|
224
|
+
# FGraph.insights('[app_id]', '[app_access_token]', :metric_path => 'application_api_call/day')
|
225
|
+
#
|
226
|
+
# ==== Options
|
227
|
+
# * <tt>metric_path</tt> - e.g. application_api_calls/day
|
228
|
+
# * <tt>since</tt> - since (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
229
|
+
# * <tt>until</tt> - until (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
230
|
+
def self.insights(app_id, app_access_token, options={})
|
231
|
+
metric_path = options.delete(:metric_path)
|
232
|
+
|
233
|
+
path = "/#{app_id}/insights"
|
234
|
+
path += "/#{metric_path}" if metric_path
|
235
|
+
|
236
|
+
self.perform_get(path, {
|
237
|
+
:access_token => app_access_token
|
238
|
+
}.merge(options || {}))
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.perform_get(uri, options = {})
|
242
|
+
handle_response(get(uri, {:query => options}))
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.perform_post(uri, options = {})
|
246
|
+
handle_response(post(uri, {:body => options}))
|
247
|
+
end
|
248
|
+
|
249
|
+
def self.perform_delete(uri, options = {})
|
250
|
+
handle_response(delete(uri, {:body => options}))
|
251
|
+
end
|
252
|
+
|
253
|
+
def self.handle_response(response)
|
254
|
+
# Check for error
|
255
|
+
return response unless response['error']
|
256
|
+
|
257
|
+
case response['error']['type']
|
258
|
+
when 'QueryParseException'
|
259
|
+
raise QueryParseError, response['error']
|
260
|
+
when 'GraphMethodException'
|
261
|
+
raise GraphMethodError, response['error']
|
262
|
+
when 'OAuthException'
|
263
|
+
raise OAuthError, response['error']
|
264
|
+
when 'OAuthAccessTokenException'
|
265
|
+
raise OAuthAccessTokenError, response['error']
|
266
|
+
else
|
267
|
+
raise FacebookError, response['error']
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.format_url(path, options={})
|
272
|
+
url = self.base_uri.dup
|
273
|
+
url << path
|
274
|
+
unless options.blank?
|
275
|
+
url << "?"
|
276
|
+
|
277
|
+
option_count = 0
|
278
|
+
|
279
|
+
stringified_options = {}
|
280
|
+
options.each do |key, value|
|
281
|
+
stringified_options[key.to_s] = value
|
282
|
+
end
|
283
|
+
options = stringified_options
|
284
|
+
|
285
|
+
options.each do |option|
|
286
|
+
next if option[1].blank?
|
287
|
+
url << "&" if option_count > 0
|
288
|
+
url << "#{option[0]}=#{CGI.escape(option[1].to_s)}"
|
289
|
+
option_count += 1
|
290
|
+
end
|
291
|
+
end
|
292
|
+
url
|
293
|
+
end
|
294
|
+
|
295
|
+
def self.method_missing(name, *args, &block)
|
296
|
+
names = name.to_s.split('_')
|
297
|
+
super unless names.length > 1
|
298
|
+
|
299
|
+
case names.shift
|
300
|
+
when 'object'
|
301
|
+
# object_photos
|
302
|
+
self.object("#{args[0]}/#{names[0]}", args[1])
|
303
|
+
when 'me'
|
304
|
+
# me_photos
|
305
|
+
self.me(names[0], args[0])
|
306
|
+
when 'publish'
|
307
|
+
# publish_feed(id)
|
308
|
+
self.publish("#{args[0]}/#{names[0]}", args[1])
|
309
|
+
when 'remove'
|
310
|
+
# remove_feed(id)
|
311
|
+
self.remove("#{args[0]}/#{names[0]}", args[1])
|
312
|
+
when 'search'
|
313
|
+
# search_user(query)
|
314
|
+
options = args[1] || {}
|
315
|
+
options[:type] = names[0]
|
316
|
+
self.search(args[0], options)
|
317
|
+
else
|
318
|
+
super
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'oauth2'
|
2
|
+
|
3
|
+
module FGraph
|
4
|
+
|
5
|
+
# Facebook proxy class to call Facebook Graph API methods with default options.
|
6
|
+
# Please refer to FGraph method documentation for more information.
|
7
|
+
class Client
|
8
|
+
attr_reader :oauth_client, :client_id, :client_secret, :options
|
9
|
+
|
10
|
+
# Initialize Client with default options, so options are not required to be passed
|
11
|
+
# when calling respective Facebook Graph API methods.
|
12
|
+
#
|
13
|
+
# ==== Options
|
14
|
+
# * <tt>client_id</tt> - Application API key
|
15
|
+
# * <tt>client_secret</tt> - Application Secret
|
16
|
+
# * <tt>app_id</tt> - Application ID
|
17
|
+
# * <tt>access_token</tt> - Access token, required to publish to Facebook Graph or access
|
18
|
+
# current user profile.
|
19
|
+
# * <tt>app_access_token</tt> - Application access token, required to access Facebook insights.
|
20
|
+
# Auto generated if client_id and client_secret option are provided.
|
21
|
+
def initialize(options={})
|
22
|
+
@options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
def oauth_authorize_url(redirect_uri)
|
26
|
+
FGraph.oauth_authorize_url(self.options[:client_id], redirect_uri)
|
27
|
+
end
|
28
|
+
|
29
|
+
def oauth_access_token(redirect_uri, code)
|
30
|
+
FGraph.oauth_access_token(self.options[:client_id], self.options[:client_secret],
|
31
|
+
:redirect_uri => redirect_uri, :code => code)
|
32
|
+
end
|
33
|
+
|
34
|
+
def oauth_app_access_token
|
35
|
+
FGraph.oauth_app_access_token(self.options[:client_id], self.options[:client_secret])
|
36
|
+
end
|
37
|
+
|
38
|
+
def object(id, options={})
|
39
|
+
FGraph.object(id, {:access_token => self.options[:access_token]}.merge(options || {}))
|
40
|
+
end
|
41
|
+
|
42
|
+
def objects(*args)
|
43
|
+
return if args.blank?
|
44
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
45
|
+
options[:access_token] = self.options[:access_token]
|
46
|
+
args << options
|
47
|
+
|
48
|
+
FGraph.objects(args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def me(*args)
|
52
|
+
return if args.blank?
|
53
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
54
|
+
options[:access_token] = self.options[:access_token]
|
55
|
+
args << options
|
56
|
+
|
57
|
+
FGraph.me(args)
|
58
|
+
end
|
59
|
+
|
60
|
+
def publish(id, options={})
|
61
|
+
FGraph.publish(id, {
|
62
|
+
:access_token => self.options[:access_token]
|
63
|
+
}.merge(options || {}))
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove(id, options={})
|
67
|
+
FGraph.remove(id, {
|
68
|
+
:access_token => self.options[:access_token]
|
69
|
+
}.merge(options || {}))
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def search(query, options={})
|
74
|
+
FGraph.search(query, options)
|
75
|
+
end
|
76
|
+
|
77
|
+
def insights(options={})
|
78
|
+
unless self.options[:app_access_token]
|
79
|
+
self.options[:app_access_token] = self.oauth_app_access_token
|
80
|
+
end
|
81
|
+
FGraph.insights(self.options[:app_id], self.options[:app_access_token], options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def method_missing(name, *args, &block)
|
85
|
+
names = name.to_s.split('_')
|
86
|
+
super unless names.length > 1
|
87
|
+
|
88
|
+
case names.shift
|
89
|
+
when 'object'
|
90
|
+
# object_photos
|
91
|
+
self.object("#{args[0]}/#{names[0]}", args[1])
|
92
|
+
when 'me'
|
93
|
+
# me_photos
|
94
|
+
self.me(names[0], args[0])
|
95
|
+
when 'publish'
|
96
|
+
# publish_feed(id)
|
97
|
+
self.publish("#{args[0]}/#{names[0]}", args[1])
|
98
|
+
when 'remove'
|
99
|
+
# remove_feed(id)
|
100
|
+
self.remove("#{args[0]}/#{names[0]}", args[1])
|
101
|
+
when 'search'
|
102
|
+
# search_user(query)
|
103
|
+
options = args[1] || {}
|
104
|
+
options[:type] = names[0]
|
105
|
+
self.search(args[0], options)
|
106
|
+
else
|
107
|
+
super
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ClientTest < Test::Unit::TestCase
|
4
|
+
FACEBOOK_API_KEY = '878116c4a4b79f25e4beb97ab096cc92'
|
5
|
+
FACEBOOK_APP_SECRET = '41f0e7ee8b6501dca1610de9926477c4'
|
6
|
+
FACEBOOK_APP_ID = '112157085578818'
|
7
|
+
FACEBOOK_OAUTH_REDIRECT_URI = 'http://www.example.com/oauth_redirect'
|
8
|
+
FACEBOOK_OAUTH_CODE = '2.0eXhebBSDTpoe08qIaocNQ__.3600.1273748400-503153225|caqygNb5Gobz6lpj3HXjlthDxds.'
|
9
|
+
FACEBOOK_OAUTH_ACCESS_TOKEN = "115187085478818|rDIv_5zgjCSM_fWBv5Z-lQr5gFk."
|
10
|
+
FACEBOOK_OAUTH_APP_ACCESS_TOKEN = "112167085478818|rDIv_5zgjCSM_fWBv5Z-lQr5gFk."
|
11
|
+
|
12
|
+
def fb_client
|
13
|
+
FGraph::Client.new(
|
14
|
+
:client_id => FACEBOOK_API_KEY,
|
15
|
+
:client_secret => FACEBOOK_APP_SECRET,
|
16
|
+
:access_token => FACEBOOK_OAUTH_ACCESS_TOKEN
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
context "FGraph::Client#oauth_authorize_url" do
|
21
|
+
should "call FGraph.oauth_authorize_url with :client_id option" do
|
22
|
+
FGraph.expects(:oauth_authorize_url).with(FACEBOOK_API_KEY, FACEBOOK_OAUTH_REDIRECT_URI)
|
23
|
+
fb_client.oauth_authorize_url(FACEBOOK_OAUTH_REDIRECT_URI)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "FGraph::Client#oauth_access_token" do
|
28
|
+
should "call FGraph.oauth_access_token with :client_id and :client_secret options" do
|
29
|
+
FGraph.expects(:oauth_access_token).with(FACEBOOK_API_KEY, FACEBOOK_APP_SECRET,
|
30
|
+
:redirect_uri => FACEBOOK_OAUTH_REDIRECT_URI, :code => FACEBOOK_OAUTH_CODE)
|
31
|
+
|
32
|
+
fb_client.oauth_access_token(FACEBOOK_OAUTH_REDIRECT_URI, FACEBOOK_OAUTH_CODE)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "FGraph::Client#object" do
|
37
|
+
should "call FGraph.object with :access_token option" do
|
38
|
+
object_id = '12345'
|
39
|
+
FGraph.expects(:object).with(object_id,
|
40
|
+
:access_token => FACEBOOK_OAUTH_ACCESS_TOKEN,
|
41
|
+
:fields => 'user_photos'
|
42
|
+
)
|
43
|
+
|
44
|
+
fb_client.object(object_id, :fields => 'user_photos')
|
45
|
+
end
|
46
|
+
|
47
|
+
should "support #object_[category] method" do
|
48
|
+
client = fb_client
|
49
|
+
client.expects(:object).with('arun/photos', {:limit => 5})
|
50
|
+
client.object_photos('arun', {:limit => 5})
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "FGraph::Client#objects" do
|
55
|
+
should "call FGraph.objects with :access_token option" do
|
56
|
+
FGraph.expects(:objects).with(['1', '2', {
|
57
|
+
:access_token => FACEBOOK_OAUTH_ACCESS_TOKEN,
|
58
|
+
:fields => 'user_photos'
|
59
|
+
}])
|
60
|
+
|
61
|
+
fb_client.objects('1', '2', :fields => 'user_photos')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "FGraph::Client#me" do
|
66
|
+
should "call FGraph.me with :access_token option" do
|
67
|
+
FGraph.expects(:me).with([{
|
68
|
+
:access_token => FACEBOOK_OAUTH_ACCESS_TOKEN,
|
69
|
+
:fields => 'user_photos'
|
70
|
+
}])
|
71
|
+
|
72
|
+
fb_client.me(:fields => 'user_photos')
|
73
|
+
end
|
74
|
+
|
75
|
+
should "support #me_[category] method" do
|
76
|
+
client = fb_client
|
77
|
+
client.expects(:me).with('photos', {:limit => 5})
|
78
|
+
client.me_photos(:limit => 5)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "FGraph::Client#publish" do
|
83
|
+
should "call FGraph.publish with :access_token option" do
|
84
|
+
id = '1'
|
85
|
+
FGraph.expects(:publish).with(id, {
|
86
|
+
:access_token => FACEBOOK_OAUTH_ACCESS_TOKEN,
|
87
|
+
:message => 'hello'
|
88
|
+
})
|
89
|
+
|
90
|
+
fb_client.publish(id, :message => 'hello')
|
91
|
+
end
|
92
|
+
|
93
|
+
should "support publish_[category] method" do
|
94
|
+
client = fb_client
|
95
|
+
client.expects(:publish).with('me/feed', {:limit => 5})
|
96
|
+
client.publish_feed('me', {:limit => 5})
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "FGraph::Client#remove" do
|
101
|
+
should "call FGraph.remove with :access_token option" do
|
102
|
+
id = '1'
|
103
|
+
FGraph.expects(:remove).with(id, {
|
104
|
+
:access_token => FACEBOOK_OAUTH_ACCESS_TOKEN
|
105
|
+
})
|
106
|
+
|
107
|
+
fb_client.remove(id)
|
108
|
+
end
|
109
|
+
|
110
|
+
should "support remove_[category] method" do
|
111
|
+
client = fb_client
|
112
|
+
client.expects(:remove).with('12345/likes', {:limit => 5})
|
113
|
+
client.remove_likes('12345', :limit => 5)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "FGraph::Client#search" do
|
118
|
+
should "call FGraph.search with options" do
|
119
|
+
query = 'watermelon'
|
120
|
+
options = {:limit => 5}
|
121
|
+
FGraph.expects(:search).with(query, options)
|
122
|
+
|
123
|
+
fb_client.search(query, options)
|
124
|
+
end
|
125
|
+
|
126
|
+
should "support dynamic method search_[type] method" do
|
127
|
+
client = fb_client
|
128
|
+
client.expects(:search).with('watermelon', {
|
129
|
+
:type => 'post'
|
130
|
+
})
|
131
|
+
client.search_post('watermelon')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "FGraph::Client#insights" do
|
136
|
+
should "auto populate :app_id and :oauth_app_access_token" do
|
137
|
+
client = fb_client
|
138
|
+
client.options[:app_id] = FACEBOOK_APP_ID
|
139
|
+
client.options[:app_access_token] = FACEBOOK_OAUTH_APP_ACCESS_TOKEN
|
140
|
+
|
141
|
+
FGraph.expects(:insights).with(FACEBOOK_APP_ID, FACEBOOK_OAUTH_APP_ACCESS_TOKEN, {})
|
142
|
+
client.insights
|
143
|
+
end
|
144
|
+
|
145
|
+
should "auto retrieve :oauth_app_access_token option" do
|
146
|
+
client = fb_client
|
147
|
+
|
148
|
+
client.expects(:oauth_app_access_token).returns(FACEBOOK_OAUTH_APP_ACCESS_TOKEN)
|
149
|
+
FGraph.expects(:insights).with(nil, FACEBOOK_OAUTH_APP_ACCESS_TOKEN, {
|
150
|
+
:metric_path => 'application_api_calls/day'
|
151
|
+
})
|
152
|
+
client.insights(:metric_path => 'application_api_calls/day')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/test/fgraph_test.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FGraphTest < Test::Unit::TestCase
|
4
|
+
FACEBOOK_API_KEY = '878116c4a4b79f25e4beb97ab096cc92'
|
5
|
+
FACEBOOK_APP_SECRET = '41f0e7ee8b6501dca1610de9926477c4'
|
6
|
+
FACEBOOK_APP_ID = '112157085578818'
|
7
|
+
FACEBOOK_OAUTH_REDIRECT_URI = 'http://www.example.com/oauth_redirect'
|
8
|
+
FACEBOOK_OAUTH_CODE = '2.0eXhebBSDTpoe08qIaocNQ__.3600.1273748400-503153225|caqygNb5Gobz6lpj3HXjlthDxds.'
|
9
|
+
FACEBOOK_OAUTH_ACCESS_TOKEN = "115187085478818|rDIv_5zgjCSM_fWBv5Z-lQr5gFk."
|
10
|
+
FACEBOOK_OAUTH_APP_ACCESS_TOKEN = "112167085478818|rDIv_5zgjCSM_fWBv5Z-lQr5gFk."
|
11
|
+
|
12
|
+
context "FGraph.object" do
|
13
|
+
should "return object hash" do
|
14
|
+
stub_get('/cocacola', 'object_cocacola.json')
|
15
|
+
object = FGraph.object('cocacola')
|
16
|
+
|
17
|
+
object.should_not be_nil
|
18
|
+
object['name'].should == 'Coca-Cola'
|
19
|
+
end
|
20
|
+
|
21
|
+
should "call handle_response" do
|
22
|
+
stub_get('/cocacola', 'object_cocacola.json')
|
23
|
+
FGraph.expects(:handle_response).once
|
24
|
+
object = FGraph.object('cocacola')
|
25
|
+
end
|
26
|
+
|
27
|
+
should "parse options into get options" do
|
28
|
+
options = {:fields => 'id,name,picture'}
|
29
|
+
FGraph.expects(:perform_get).with('/cocacola', options)
|
30
|
+
FGraph.object('cocacola', options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "FGraph.objects" do
|
35
|
+
should "call perform_get with ids and query options" do
|
36
|
+
options = {:fields => 'id,name'}
|
37
|
+
FGraph.expects(:perform_get).with('/', options.merge(:ids => 'herry,john'))
|
38
|
+
FGraph.objects('herry', 'john', options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "FGraph.me" do
|
43
|
+
access_token = {:access_token => FACEBOOK_OAUTH_ACCESS_TOKEN}
|
44
|
+
|
45
|
+
should "get object with /me path" do
|
46
|
+
FGraph.expects(:object).with('me', access_token)
|
47
|
+
FGraph.me(access_token)
|
48
|
+
end
|
49
|
+
|
50
|
+
should "get object with /me/likes path" do
|
51
|
+
FGraph.expects(:object).with('me/likes', access_token)
|
52
|
+
FGraph.me('likes', access_token)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "FGraph.oauth_authorize_url" do
|
57
|
+
should "should call format_url with appropriate hash" do
|
58
|
+
FGraph.expects(:format_url).with('/oauth/authorize', {
|
59
|
+
:client_id => FACEBOOK_API_KEY,
|
60
|
+
:redirect_uri => FACEBOOK_OAUTH_REDIRECT_URI
|
61
|
+
})
|
62
|
+
|
63
|
+
FGraph.oauth_authorize_url(FACEBOOK_API_KEY, FACEBOOK_OAUTH_REDIRECT_URI)
|
64
|
+
end
|
65
|
+
|
66
|
+
should "should call format_url with options" do
|
67
|
+
FGraph.expects(:format_url).with('/oauth/authorize', {
|
68
|
+
:client_id => FACEBOOK_API_KEY,
|
69
|
+
:redirect_uri => FACEBOOK_OAUTH_REDIRECT_URI,
|
70
|
+
:scope => 'user_photos'
|
71
|
+
})
|
72
|
+
|
73
|
+
FGraph.oauth_authorize_url(FACEBOOK_API_KEY, FACEBOOK_OAUTH_REDIRECT_URI,
|
74
|
+
:scope => 'user_photos')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "FGraph.oauth_access_token" do
|
79
|
+
should "return user access token and expires" do
|
80
|
+
stub_get(FGraph.format_url('/oauth/access_token', {
|
81
|
+
:client_id => FACEBOOK_API_KEY,
|
82
|
+
:client_secret => FACEBOOK_APP_SECRET,
|
83
|
+
:redirect_uri => FACEBOOK_OAUTH_REDIRECT_URI,
|
84
|
+
:code => FACEBOOK_OAUTH_CODE
|
85
|
+
}), 'access_token.txt')
|
86
|
+
|
87
|
+
token = FGraph.oauth_access_token(FACEBOOK_API_KEY, FACEBOOK_APP_SECRET,
|
88
|
+
:redirect_uri => FACEBOOK_OAUTH_REDIRECT_URI,
|
89
|
+
:code => FACEBOOK_OAUTH_CODE)
|
90
|
+
|
91
|
+
token['access_token'].should == 'thisisanaccesstoken'
|
92
|
+
token['expires'].should == '4000'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "FGraph.publish" do
|
97
|
+
options = { :message => 'test message'}
|
98
|
+
|
99
|
+
should "call perform_post" do
|
100
|
+
FGraph.expects(:perform_post).with("/me/feed", options)
|
101
|
+
FGraph.publish('me/feed', options)
|
102
|
+
end
|
103
|
+
|
104
|
+
should "have publish_[category] method" do
|
105
|
+
FGraph.expects(:publish).with('me/feed', options)
|
106
|
+
FGraph.publish_feed('me', options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "FGraph.delete" do
|
111
|
+
options = {}
|
112
|
+
|
113
|
+
should "call perform_delete" do
|
114
|
+
FGraph.expects(:perform_delete).with('/12345', options)
|
115
|
+
FGraph.remove('12345', options)
|
116
|
+
end
|
117
|
+
|
118
|
+
should "support remove_[category] method" do
|
119
|
+
FGraph.expects(:remove).with('12345/likes', options)
|
120
|
+
FGraph.remove_likes('12345', options)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "FGraph.search" do
|
125
|
+
should "call perform_get('/search')" do
|
126
|
+
FGraph.expects(:perform_get).with('/search', {
|
127
|
+
:q => 'watermelon',
|
128
|
+
:type => 'post'
|
129
|
+
})
|
130
|
+
|
131
|
+
FGraph.search('watermelon', :type => 'post')
|
132
|
+
end
|
133
|
+
|
134
|
+
should "support dynamic method search_[type] method" do
|
135
|
+
FGraph.expects(:search).with('watermelon', {
|
136
|
+
:type => 'post'
|
137
|
+
})
|
138
|
+
|
139
|
+
FGraph.search_post('watermelon')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "Facebook.insights" do
|
144
|
+
should "call perform_get('/[app_id]/insights')" do
|
145
|
+
FGraph.expects(:perform_get).with("/#{FACEBOOK_APP_ID}/insights", {
|
146
|
+
:access_token => FACEBOOK_OAUTH_APP_ACCESS_TOKEN
|
147
|
+
})
|
148
|
+
|
149
|
+
FGraph.insights(FACEBOOK_APP_ID, FACEBOOK_OAUTH_APP_ACCESS_TOKEN)
|
150
|
+
end
|
151
|
+
|
152
|
+
should "process :metric_path option" do
|
153
|
+
FGraph.expects(:perform_get).with("/#{FACEBOOK_APP_ID}/insights/application_api_call/day", {
|
154
|
+
:access_token => FACEBOOK_OAUTH_APP_ACCESS_TOKEN
|
155
|
+
})
|
156
|
+
|
157
|
+
FGraph.insights(FACEBOOK_APP_ID, FACEBOOK_OAUTH_APP_ACCESS_TOKEN, {
|
158
|
+
:metric_path => 'application_api_call/day'
|
159
|
+
})
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "FGraph.method_missing" do
|
164
|
+
options = options = {:filter => 'id,name,picture'}
|
165
|
+
|
166
|
+
should "auto map object_[category] method" do
|
167
|
+
FGraph.expects(:object).with('arun/photos', options)
|
168
|
+
FGraph.object_photos('arun', options)
|
169
|
+
end
|
170
|
+
|
171
|
+
should "auto map me_[category] method" do
|
172
|
+
FGraph.expects(:me).with('photos', options)
|
173
|
+
FGraph.me_photos(options)
|
174
|
+
end
|
175
|
+
|
176
|
+
should "raise no method error if missing method name does not start with object_ or me_" do
|
177
|
+
lambda do
|
178
|
+
FGraph.xyz_photos
|
179
|
+
end.should raise_error(NoMethodError)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "FGraph.format_url" do
|
184
|
+
should "return URL without query string" do
|
185
|
+
formatted_url = FGraph.format_url('/test')
|
186
|
+
formatted_url.should == "https://graph.facebook.com/test"
|
187
|
+
end
|
188
|
+
|
189
|
+
should "return URL with query string with escaped value" do
|
190
|
+
formatted_url = FGraph.format_url('/test', {:username => 'john lim'})
|
191
|
+
formatted_url.should == "https://graph.facebook.com/test?username=john+lim"
|
192
|
+
end
|
193
|
+
|
194
|
+
should "return URL with multiple options" do
|
195
|
+
formatted_url = FGraph.format_url('/test', {:username => 'john', :age => 20})
|
196
|
+
formatted_url.should =~ /username=john/
|
197
|
+
formatted_url.should =~ /age=20/
|
198
|
+
formatted_url.should =~ /&/
|
199
|
+
end
|
200
|
+
|
201
|
+
should "return URL without empty options" do
|
202
|
+
formatted_url = FGraph.format_url('/test', {:username => 'john', :age => nil})
|
203
|
+
formatted_url.should == "https://graph.facebook.com/test?username=john"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context "FGraph.handle_response" do
|
208
|
+
should "raise QueryParseError" do
|
209
|
+
lambda do
|
210
|
+
object = FGraph.handle_response(response_error('QueryParseException'))
|
211
|
+
end.should raise_error(FGraph::QueryParseError)
|
212
|
+
end
|
213
|
+
|
214
|
+
should "raise GraphMethodError" do
|
215
|
+
lambda do
|
216
|
+
object = FGraph.handle_response(response_error('GraphMethodException'))
|
217
|
+
end.should raise_error(FGraph::GraphMethodError)
|
218
|
+
end
|
219
|
+
|
220
|
+
should "raise OAuthError" do
|
221
|
+
lambda do
|
222
|
+
object = FGraph.handle_response(response_error('OAuthException'))
|
223
|
+
end.should raise_error(FGraph::OAuthError)
|
224
|
+
end
|
225
|
+
|
226
|
+
should "raise OAuthAccessTokenError" do
|
227
|
+
lambda do
|
228
|
+
object = FGraph.handle_response(response_error('OAuthAccessTokenException'))
|
229
|
+
end.should raise_error(FGraph::OAuthAccessTokenError)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def response_error(type, msg=nil)
|
234
|
+
{'error' => { 'type' => type, 'message' => msg}}
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
access_token=thisisanaccesstoken&expires=4000
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"id": "40796308305",
|
3
|
+
"name": "Coca-Cola",
|
4
|
+
"picture": "http://profile.ak.fbcdn.net/object3/1853/100/s40796308305_2334.jpg",
|
5
|
+
"link": "http://www.facebook.com/coca-cola",
|
6
|
+
"category": "Consumer_products",
|
7
|
+
"username": "coca-cola",
|
8
|
+
"products": "Coca-Cola is the most popular and biggest-selling soft drink in history, as well as the best-known product in the world.\n\nCreated in Atlanta, Georgia, by Dr. John S. Pemberton, Coca-Cola was first offered as a fountain beverage by mixing Coca-Cola syrup with carbonated water. Coca-Cola was introduced in 1886, patented in 1887, registered as a trademark in 1893 and by 1895 it was being sold in every state and territory in the United States. In 1899, The Coca-Cola Company began franchised bottling operations in the United States.\n\nCoca-Cola might owe its origins to the United States, but its popularity has made it truly universal. Today, you can find Coca-Cola in virtually every part of the world.",
|
9
|
+
"fan_count": 5445797
|
10
|
+
}
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'shoulda'
|
3
|
+
require 'matchy'
|
4
|
+
require 'mocha'
|
5
|
+
require 'fakeweb'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
# FakeWeb.allow_net_connect = true
|
9
|
+
# FakeWeb.allow_net_connect = false
|
10
|
+
|
11
|
+
require File.dirname(__FILE__) + '/../lib/fgraph'
|
12
|
+
|
13
|
+
def stub_get(url, filename, status=nil)
|
14
|
+
options = {:body => read_fixture(filename)}
|
15
|
+
options.merge!({:status => status}) unless status.nil?
|
16
|
+
FakeWeb.register_uri(:get, graph_url(url), options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def stub_post(url, filename)
|
20
|
+
FakeWeb.register_uri(:post, graph_url(url), :body => read_fixture(filename))
|
21
|
+
end
|
22
|
+
|
23
|
+
def stub_put(url, filename)
|
24
|
+
FakeWeb.register_uri(:put, graph_url(url), :body => read_fixture(filename))
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_fixture(filename)
|
28
|
+
return "" if filename == ""
|
29
|
+
File.read(File.dirname(__FILE__) + "/fixtures/" + filename)
|
30
|
+
end
|
31
|
+
|
32
|
+
def graph_url(url)
|
33
|
+
url =~ /^http/ ? url : "http://graph.facebook.com#{url}"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fgraph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Herryanto Siatono
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-20 00:00:00 +08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: httparty
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 5
|
30
|
+
- 0
|
31
|
+
version: 0.5.0
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: shoulda
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 2
|
43
|
+
- 10
|
44
|
+
- 0
|
45
|
+
version: 2.10.0
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: jnunemaker-matchy
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
- 4
|
58
|
+
- 0
|
59
|
+
version: 0.4.0
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mocha
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
- 9
|
72
|
+
- 0
|
73
|
+
version: 0.9.0
|
74
|
+
type: :development
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: fakeweb
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 1
|
85
|
+
- 2
|
86
|
+
- 0
|
87
|
+
version: 1.2.0
|
88
|
+
type: :development
|
89
|
+
version_requirements: *id005
|
90
|
+
description: Ruby Facebook Graph API
|
91
|
+
email: herryanto@gmail.com
|
92
|
+
executables: []
|
93
|
+
|
94
|
+
extensions: []
|
95
|
+
|
96
|
+
extra_rdoc_files:
|
97
|
+
- README.rdoc
|
98
|
+
files:
|
99
|
+
- History
|
100
|
+
- License
|
101
|
+
- README.rdoc
|
102
|
+
- Rakefile
|
103
|
+
- VERSION.yml
|
104
|
+
- examples/get_access_token.rb
|
105
|
+
- examples/publish_feed.rb
|
106
|
+
- lib/fgraph.rb
|
107
|
+
- lib/fgraph/client.rb
|
108
|
+
- test/fgraph/client_test.rb
|
109
|
+
- test/fgraph_test.rb
|
110
|
+
- test/fixtures/access_token.txt
|
111
|
+
- test/fixtures/object_cocacola.json
|
112
|
+
- test/test_helper.rb
|
113
|
+
has_rdoc: true
|
114
|
+
homepage: http://github.com/jugend/fgraph
|
115
|
+
licenses: []
|
116
|
+
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options:
|
119
|
+
- --charset=UTF-8
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
version: "0"
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
segments:
|
134
|
+
- 0
|
135
|
+
version: "0"
|
136
|
+
requirements: []
|
137
|
+
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 1.3.6
|
140
|
+
signing_key:
|
141
|
+
specification_version: 3
|
142
|
+
summary: Ruby Facebook Graph API
|
143
|
+
test_files:
|
144
|
+
- test/fgraph/client_test.rb
|
145
|
+
- test/fgraph_test.rb
|
146
|
+
- test/test_helper.rb
|
147
|
+
- examples/get_access_token.rb
|
148
|
+
- examples/publish_feed.rb
|