fgraph 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +5 -0
- data/README.rdoc +80 -3
- data/Rakefile +1 -1
- data/VERSION.yml +2 -2
- data/lib/fgraph/rails/fgraph_helper.rb +8 -3
- data/lib/fgraph/rails/fgraph_tag_helper.rb +90 -0
- data/lib/fgraph.rb +341 -271
- data/lib/tasks/fgraph.rake +15 -0
- data/lib/tasks/fgraph.rb +1 -0
- data/rails/init.rb +15 -0
- data/rails/install.rb +9 -0
- data/templates/fgraph.yml +16 -0
- data/test/fgraph_test.rb +77 -2
- metadata +9 -4
data/History
CHANGED
data/README.rdoc
CHANGED
@@ -9,7 +9,26 @@ Facebook Graph Ruby API implementation with Ruby magic (http://graph.facebook.co
|
|
9
9
|
== Installation
|
10
10
|
|
11
11
|
sudo gem install fgraph
|
12
|
+
|
13
|
+
== Rails Plugin Installation
|
14
|
+
|
15
|
+
Gem Plugin installation:
|
16
|
+
|
17
|
+
sudo gem install fgraph
|
12
18
|
|
19
|
+
# Edit [RAILS_ROOT]/config/environment.rb
|
20
|
+
config.gem 'fgraph', :version => ">=0.2.0"
|
21
|
+
|
22
|
+
# Edit [RAILS_ROOT]/Rakefile
|
23
|
+
require 'tasks/fgraph'
|
24
|
+
|
25
|
+
# Create fgraph.yml config in [RAILS_ROOT]/config
|
26
|
+
rake fgraph:setup
|
27
|
+
|
28
|
+
Normal Plugin Installation:
|
29
|
+
|
30
|
+
script/plugin install http://github.com/jugend/fgraph.git
|
31
|
+
|
13
32
|
=== Facebook Graph Cheat Sheet
|
14
33
|
|
15
34
|
sudo gem install cheat
|
@@ -29,6 +48,10 @@ Facebook Graph Ruby API implementation with Ruby magic (http://graph.facebook.co
|
|
29
48
|
# Page photos
|
30
49
|
FGraph.object('/cocacola/photos')
|
31
50
|
FGraph.object_photos('cocacola')
|
51
|
+
|
52
|
+
# Passing object hash as id
|
53
|
+
friend = { 'name' => 'Mark Zuckerberg', 'id' => '4'}
|
54
|
+
friend_details = FGraph.object(friend)
|
32
55
|
|
33
56
|
# Current user: https://graph.facebook.com/me?access_token=...
|
34
57
|
FGraph.me(:access_token => '...')
|
@@ -40,11 +63,27 @@ Facebook Graph Ruby API implementation with Ruby magic (http://graph.facebook.co
|
|
40
63
|
=== Multiple objects query
|
41
64
|
|
42
65
|
# Multiple users select: https://graph.facebook.com?ids=arjun,vernal
|
43
|
-
FGraph.objects('arjun', '
|
66
|
+
FGraph.objects('arjun', 'herryanto')
|
44
67
|
|
45
68
|
# Filter fields: https://graph.facebook.com?ids=arjun,vernal&fields=id,name,picture
|
46
|
-
FGraph.objects('arjun', '
|
69
|
+
FGraph.objects('arjun', 'herryanto', :fields => 'id,name,picture')
|
70
|
+
|
71
|
+
# Passing hash objects
|
72
|
+
FGraph.objects([{:name => 'Arjun Banker', :id => 'arjun'}, {:name => 'Herryanto Siatono', :id => 'herryanto'}])
|
47
73
|
|
74
|
+
=== Collection Response
|
75
|
+
|
76
|
+
friends = FGraph.me_friends(:limit => 5, :access_token => '...')
|
77
|
+
friends.each do |friend|
|
78
|
+
puts friend['name']
|
79
|
+
puts friend['id']
|
80
|
+
end
|
81
|
+
|
82
|
+
# Other convenient methods
|
83
|
+
friends.next?
|
84
|
+
friends.next_url
|
85
|
+
friends.next_options
|
86
|
+
|
48
87
|
=== OAuth
|
49
88
|
|
50
89
|
OAuth authorization URL:
|
@@ -115,13 +154,51 @@ OAuth Application Access Token, required to access application anlytics data:
|
|
115
154
|
fg_client = FGraph::Client.new(:access_token => '...')
|
116
155
|
fg_client.me
|
117
156
|
fg.client.publish_feed('herryanto', :message => 'Cool!')
|
118
|
-
|
157
|
+
|
119
158
|
=== Pagination Options
|
120
159
|
|
121
160
|
* <tt>limit</tt> - max no of records
|
122
161
|
* <tt>offset</tt> - offset
|
123
162
|
* <tt>until</tt> - since (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
124
163
|
|
164
|
+
=== Rails Helper
|
165
|
+
|
166
|
+
Sample codes:
|
167
|
+
|
168
|
+
<%= fgraph_javascript_init_tag %>
|
169
|
+
<script type="text/javascript">
|
170
|
+
FB.Event.subscribe('auth.sessionChange', function(response) {
|
171
|
+
if (response.session) {
|
172
|
+
// A user has logged in, and a new cookie has been saved
|
173
|
+
} else {
|
174
|
+
// The user has logged out, and the cookie has been cleared
|
175
|
+
}
|
176
|
+
});
|
177
|
+
</script>
|
178
|
+
|
179
|
+
<!-- Facebook Login Button -->
|
180
|
+
<fb:login-button autologoutlink="true" perms="email,publish_stream"></fb:login-button>
|
181
|
+
|
182
|
+
<% if fgraph_logged_in? %>
|
183
|
+
<br>Hello <%= fgraph_current_user['name'] %>,
|
184
|
+
<br><%= fgraph_image_tag(fgraph_current_user, 'large') %>
|
185
|
+
<% end %>
|
186
|
+
|
187
|
+
For Asynchronous load, use <tt>window.afterFbAsyncInit</tt>:
|
188
|
+
|
189
|
+
<%= fgraph_javascript_init_tag :async => true %>
|
190
|
+
<script type="text/javascript">
|
191
|
+
window.afterFbAsyncInit = function() {
|
192
|
+
FB.Event.subscribe('auth.sessionChange', function(response) {
|
193
|
+
if (response.session) {
|
194
|
+
// A user has logged in, and a new cookie has been saved
|
195
|
+
} else {
|
196
|
+
// The user has logged out, and the cookie has been cleared
|
197
|
+
}
|
198
|
+
});
|
199
|
+
}
|
200
|
+
</script>
|
201
|
+
|
125
202
|
== License
|
126
203
|
|
127
204
|
(The MIT License)
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ begin
|
|
9
9
|
gem.email = "herryanto@gmail.com"
|
10
10
|
gem.homepage = "http://github.com/jugend/fgraph"
|
11
11
|
gem.authors = ["Herryanto Siatono"]
|
12
|
-
gem.files = FileList["[A-Z]*", "{examples,lib,test}/**/*"]
|
12
|
+
gem.files = FileList["[A-Z]*", "{examples,lib,test,rails,tasks,templates}/**/*"]
|
13
13
|
|
14
14
|
gem.add_dependency("httparty", "~> 0.5.0")
|
15
15
|
# gem.add_dependency("hashie", "~> 0.2.0")
|
data/VERSION.yml
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
module FGraph
|
2
2
|
module Rails
|
3
3
|
module FGraphHelper
|
4
|
+
|
5
|
+
# Access FGraph.config initialized with values set in <tt>[RAILS_ROOT]/config/fgraph.yml</tt>.
|
4
6
|
def fgraph_config
|
5
7
|
FGraph.config || {}
|
6
8
|
end
|
7
|
-
|
9
|
+
|
10
|
+
# Return Facebook session, default to retrieve session from cookies
|
8
11
|
def fgraph_session(app_id = fgraph_config['app_id'],
|
9
12
|
app_secret = fgraph_config['app_secret'])
|
10
13
|
|
@@ -12,6 +15,7 @@ module FGraph
|
|
12
15
|
@fgraph_session = fgraph_session_cookies(app_id, app_secret)
|
13
16
|
end
|
14
17
|
|
18
|
+
# Return Facebook_session cookies
|
15
19
|
def fgraph_session_cookies(app_id = fgraph_config['app_id'],
|
16
20
|
app_secret = fgraph_config['app_secret'])
|
17
21
|
|
@@ -60,10 +64,11 @@ module FGraph
|
|
60
64
|
@fgraph_current_user = fgraph_client.me
|
61
65
|
end
|
62
66
|
|
67
|
+
# Return FGraph::Client.instance initialized with settings set in <tt>fgraph.yml</tt>.
|
68
|
+
# Initialized with <tt>:access_token</tt> as well if Facebook session exists.
|
63
69
|
def fgraph_client
|
64
70
|
return @fgraph_client if @fgraph_client
|
65
|
-
|
66
|
-
|
71
|
+
|
67
72
|
@fgraph_client = FGraph::Client.new(
|
68
73
|
:client_id => fgraph_config['app_id'],
|
69
74
|
:client_secret => fgraph_config['app_secret'],
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module FGraph
|
2
|
+
module Rails
|
3
|
+
module FGraphTagHelper
|
4
|
+
def fgraph_javascript_include_tag
|
5
|
+
%{<script src="http://connect.facebook.net/en_US/all.js"></script>}
|
6
|
+
end
|
7
|
+
|
8
|
+
# Inititalize XFBML Javascript include and initialization script.
|
9
|
+
#
|
10
|
+
# ==== Options
|
11
|
+
# * <tt>app_id</tt> - overrride Fgraph.config['app_id'] value.
|
12
|
+
# * <tt>async</tt> - asynchronous javascript include & initialization.
|
13
|
+
# for other Facebook JS initialization codes please wrap under:
|
14
|
+
#
|
15
|
+
# window.afterFbAsyncInit = function() {
|
16
|
+
# ....
|
17
|
+
# }
|
18
|
+
#
|
19
|
+
def fgraph_javascript_init_tag(options={})
|
20
|
+
options = { :app_id => FGraph.config['app_id'] }.merge(options || {})
|
21
|
+
|
22
|
+
if options[:async]
|
23
|
+
%{
|
24
|
+
<div id="fb-root"></div>
|
25
|
+
<script>
|
26
|
+
window.fbAsyncInit = function() {
|
27
|
+
FB.init({appId: '#{options[:app_id]}', status: true, cookie: true,
|
28
|
+
xfbml: true});
|
29
|
+
|
30
|
+
if (window.afterFbAsyncInit) {
|
31
|
+
window.afterFbAsyncInit();
|
32
|
+
}
|
33
|
+
};
|
34
|
+
(function() {
|
35
|
+
var e = document.createElement('script'); e.async = true;
|
36
|
+
e.src = document.location.protocol +
|
37
|
+
'//connect.facebook.net/en_US/all.js';
|
38
|
+
document.getElementById('fb-root').appendChild(e);
|
39
|
+
}());
|
40
|
+
</script>
|
41
|
+
}
|
42
|
+
else
|
43
|
+
tag = fgraph_javascript_include_tag
|
44
|
+
tag << %{
|
45
|
+
<div id="fb-root"></div>
|
46
|
+
<script>
|
47
|
+
FB.init({appId: '#{options[:app_id]}', status: true, cookie: true, xfbml: true});
|
48
|
+
</script>
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return Facebook object picture url: http://graph.facebook.com/[id]/picture
|
54
|
+
#
|
55
|
+
# ==== Type Options
|
56
|
+
# * <tt>square</tt> - 50x50 (default)
|
57
|
+
# * <tt>small</tt> - 50 pixels wide, variable height
|
58
|
+
# * <tt>normal</tt> - 100 pixels wide, variable height
|
59
|
+
# * <tt>large</tt> - 200 pixels wide, variable height
|
60
|
+
#
|
61
|
+
def fgraph_picture_url(id, type=nil)
|
62
|
+
id = FGraph.get_id(id)
|
63
|
+
url = "http://graph.facebook.com/#{id}/picture"
|
64
|
+
url += "?type=#{type}" if type
|
65
|
+
url
|
66
|
+
end
|
67
|
+
|
68
|
+
def fgraph_image_tag(id, type=nil, options={})
|
69
|
+
default_options = fgraph_image_options(type)
|
70
|
+
default_options[:alt] = id['name'] if id.is_a?(Hash)
|
71
|
+
image_tag(fgraph_picture_url(id, type), default_options.merge(options || {}))
|
72
|
+
end
|
73
|
+
|
74
|
+
def fgraph_image_options(type)
|
75
|
+
case type
|
76
|
+
when 'square'
|
77
|
+
{:width => 50, :height => 50}
|
78
|
+
when 'small'
|
79
|
+
{:width => 50}
|
80
|
+
when 'normal'
|
81
|
+
{:width => 100}
|
82
|
+
when 'large'
|
83
|
+
{:width => 200}
|
84
|
+
else
|
85
|
+
{:width => 50, :height => 50}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/fgraph.rb
CHANGED
@@ -8,14 +8,6 @@ module FGraph
|
|
8
8
|
base_uri 'https://graph.facebook.com'
|
9
9
|
format :json
|
10
10
|
|
11
|
-
def self.config
|
12
|
-
@@config
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.config=(config)
|
16
|
-
@@config = config
|
17
|
-
end
|
18
|
-
|
19
11
|
# Facebook Error
|
20
12
|
class FacebookError < StandardError
|
21
13
|
attr_reader :data
|
@@ -31,299 +23,377 @@ module FGraph
|
|
31
23
|
class OAuthError < FacebookError; end
|
32
24
|
class OAuthAccessTokenError < OAuthError; end
|
33
25
|
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# # Users: https://graph.facebook.com/btaylor (Bret Taylor)
|
37
|
-
# FGraph.object('btaylor')
|
38
|
-
#
|
39
|
-
# # Pages: https://graph.facebook.com/cocacola (Coca-Cola page)
|
40
|
-
# FGraph.object('cocacola')
|
41
|
-
#
|
42
|
-
# # Fields selection with metadata
|
43
|
-
# FGraph.object('btaylor', :fields => 'id,name,picture', :metadata => 1)
|
26
|
+
# Collection objects for Graph response with array data.
|
44
27
|
#
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
28
|
+
class Collection < Array
|
29
|
+
attr_reader :next_url, :previous_url, :next_options, :previous_options
|
30
|
+
|
31
|
+
# Initialize Facebook response object with 'data' array value.
|
32
|
+
def initialize(response)
|
33
|
+
return super unless response
|
34
|
+
|
35
|
+
super(response['data'])
|
36
|
+
paging = response['paging'] || {}
|
37
|
+
self.next_url = paging['next']
|
38
|
+
self.previous_url = paging['previous']
|
39
|
+
end
|
40
|
+
|
41
|
+
def next_url=(url)
|
42
|
+
@next_url = url
|
43
|
+
@next_options = self.url_options(url)
|
44
|
+
end
|
45
|
+
|
46
|
+
def previous_url=(url)
|
47
|
+
@previous_url = url
|
48
|
+
@previous_options = self.url_options(url)
|
49
|
+
end
|
50
|
+
|
51
|
+
def first?
|
52
|
+
@previous_url.blank? and not @next_url.blank?
|
53
|
+
end
|
54
|
+
|
55
|
+
def next?
|
56
|
+
not @next_url.blank?
|
57
|
+
end
|
58
|
+
|
59
|
+
def previous?
|
60
|
+
not @previous_url.blank?
|
61
|
+
end
|
62
|
+
|
63
|
+
def url_options(url)
|
64
|
+
return unless url
|
65
|
+
|
66
|
+
uri = URI.parse(url)
|
67
|
+
options = {}
|
68
|
+
uri.query.split('&').each do |param_set|
|
69
|
+
param_set = param_set.split('=')
|
70
|
+
options[param_set[0]] = CGI.unescape(param_set[1])
|
71
|
+
end
|
72
|
+
options
|
73
|
+
end
|
51
74
|
end
|
52
75
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
76
|
+
class << self
|
77
|
+
attr_accessor :config
|
78
|
+
|
79
|
+
# Single object query.
|
80
|
+
#
|
81
|
+
# # Users: https://graph.facebook.com/btaylor (Bret Taylor)
|
82
|
+
# FGraph.object('btaylor')
|
83
|
+
#
|
84
|
+
# # Pages: https://graph.facebook.com/cocacola (Coca-Cola page)
|
85
|
+
# FGraph.object('cocacola')
|
86
|
+
#
|
87
|
+
# # Fields selection with metadata
|
88
|
+
# FGraph.object('btaylor', :fields => 'id,name,picture', :metadata => 1)
|
89
|
+
#
|
90
|
+
# # Page photos
|
91
|
+
# FGraph.object('/cocacola/photos')
|
92
|
+
# photos = FGraph.object_photos('cocacola')
|
93
|
+
#
|
94
|
+
# # Support id from object hash
|
95
|
+
# friend = { 'name' => 'Mark Zuckerberg', 'id' => '4'}
|
96
|
+
# friend_details = FGraph.object(friend)
|
97
|
+
def object(id, options={})
|
98
|
+
id = self.get_id(id)
|
99
|
+
perform_get("/#{id}", options)
|
100
|
+
end
|
70
101
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
#
|
86
|
-
def self.me(*args)
|
87
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
88
|
-
category = args.shift
|
102
|
+
# call-seq:
|
103
|
+
# FGraph.objects(id, id)
|
104
|
+
# FGraph.objects(id, id, options_hash)
|
105
|
+
#
|
106
|
+
# Multiple objects query.
|
107
|
+
#
|
108
|
+
# # Multiple users select: https://graph.facebook.com?ids=arjun,vernal
|
109
|
+
# FGraph.objects('arjun', 'vernel')
|
110
|
+
#
|
111
|
+
# # Filter fields: https://graph.facebook.com?ids=arjun,vernal&fields=id,name,picture
|
112
|
+
# FGraph.objects('arjun', 'vernel', :fields => 'id,name,picture')
|
113
|
+
#
|
114
|
+
def objects(*args)
|
115
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
89
116
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
117
|
+
# If first input before option is an array
|
118
|
+
if args.count == 1 and args.first.is_a?(Array)
|
119
|
+
args = args.first.map do |arg|
|
120
|
+
self.get_id(arg)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
options = options.merge(:ids => args.join(','))
|
125
|
+
perform_get("/", options)
|
126
|
+
end
|
94
127
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
self.format_url('/oauth/authorize', {
|
119
|
-
:client_id => client_id,
|
120
|
-
:redirect_uri => redirect_uri
|
121
|
-
}.merge(options))
|
122
|
-
end
|
128
|
+
# call-seq:
|
129
|
+
# FGraph.me(category)
|
130
|
+
# FGraph.me(category, options_hash)
|
131
|
+
#
|
132
|
+
# Returns current user object details.
|
133
|
+
#
|
134
|
+
# <tt>category</tt> - <tt>friends|home|feed|likes|movies|books|notes|photos|videos|events|groups</tt>
|
135
|
+
#
|
136
|
+
# # Current user: https://graph.facebook.com/me?access_token=...
|
137
|
+
# FGraph.me(:access_token => '...')
|
138
|
+
#
|
139
|
+
# # Current user's friends: https://graph.facebook.com/me/friends?access_token=...
|
140
|
+
# FGraph.me('friends', :access_token => '...')
|
141
|
+
# FGraph.me_friends(:access_token => '...')
|
142
|
+
#
|
143
|
+
def me(*args)
|
144
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
145
|
+
category = args.shift
|
146
|
+
|
147
|
+
path = "me"
|
148
|
+
path += "/#{category}" unless category.blank?
|
149
|
+
self.object(path, options)
|
150
|
+
end
|
123
151
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
152
|
+
# Request authorization from Facebok to fetch private data in the profile or permission to publish on a
|
153
|
+
# user's behalf. Returns Oauth Authorization URL, redirect to this URL to allow user to authorize your
|
154
|
+
# application from Facebook.
|
155
|
+
#
|
156
|
+
# <tt>client_id</tt> - Application ID
|
157
|
+
# <tt>redirect_uri</tt> - Needs to begin with your app's Connect URL. For instance, if your Connect URL
|
158
|
+
# is http://www.example.com then your redirect URI could be http://www.example.com/oauth_redirect.
|
159
|
+
# <tt>scope (optional)</tt> -
|
160
|
+
#
|
161
|
+
# ==== Options
|
162
|
+
# * <tt>scope</tt> - Extended permission required to fetch private data or request permision to
|
163
|
+
# publish to Facebook on a user's behalf.
|
164
|
+
# * <tt>display</tt> - Other display type for authentication/authorization form, i.e. popup, touch.
|
165
|
+
#
|
166
|
+
# # https://graph.facebook.com/oauth/authorize?
|
167
|
+
# # client_id=...&
|
168
|
+
# # redirect_uri=http://www.example.com/oauth_redirect&
|
169
|
+
# # scope=publish_stream
|
170
|
+
#
|
171
|
+
# FGraph.oauth_authorize_url('[client id]', 'http://www.example.com/oauth_redirect', :scope =>
|
172
|
+
# 'publish_stream')
|
173
|
+
#
|
174
|
+
def oauth_authorize_url(client_id, redirect_uri, options={})
|
175
|
+
self.format_url('/oauth/authorize', {
|
176
|
+
:client_id => client_id,
|
177
|
+
:redirect_uri => redirect_uri
|
178
|
+
}.merge(options))
|
179
|
+
end
|
180
|
+
|
181
|
+
# Return OAuth access_token. There are two types of access token, user access token and application
|
182
|
+
# access token.
|
183
|
+
#
|
184
|
+
# User access_token requires <tt>code</tt> and and <tt>redirect_uri</tt> options. <tt>code</tt> is
|
185
|
+
# the autorization code appended as query string to redirect URI when accessing oauth authorization URL.
|
186
|
+
#
|
187
|
+
# # https://graph.facebook.com/oauth/access_token?
|
188
|
+
# # client_id=...&
|
189
|
+
# # client_secret=...&
|
190
|
+
# # redirect_uri=http://www.example.com/oauth_redirect&
|
191
|
+
# # code=...
|
192
|
+
# FGraph.oauth_access_token('[client id]', '[client secret]',
|
193
|
+
# :redirect_uri => ''http://www.example.com/oauth_redirect',
|
194
|
+
# :code => '[authorization code]')
|
195
|
+
#
|
196
|
+
# Application access token requires <tt>:type => 'client_cred'</td> option. Used to access application
|
197
|
+
# insights data.
|
198
|
+
#
|
199
|
+
# # https://graph.facebook.com/oauth/access_token?
|
200
|
+
# # client_id=...&
|
201
|
+
# # client_secret=...&
|
202
|
+
# # type=client_cred
|
203
|
+
# FGraph.oauth_access_token('[client id]', '[client secret]', :type => 'client_cred')
|
204
|
+
#
|
205
|
+
def oauth_access_token(client_id, client_secret, options={})
|
206
|
+
url = self.format_url('/oauth/access_token', {
|
207
|
+
:client_id => client_id,
|
208
|
+
:client_secret => client_secret
|
209
|
+
}.merge(options || {}))
|
153
210
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
211
|
+
response = self.perform_get(url)
|
212
|
+
response_hash = {}
|
213
|
+
response.split('&').each do |value|
|
214
|
+
value_pair = value.split('=')
|
215
|
+
response_hash[value_pair[0]] = value_pair[1]
|
216
|
+
end
|
217
|
+
response_hash
|
159
218
|
end
|
160
|
-
response_hash
|
161
|
-
end
|
162
219
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
220
|
+
# Shortcut to retrieve application access token.
|
221
|
+
def oauth_app_access_token(client_id, client_secret)
|
222
|
+
self.oauth_access_token(client_id, client_secret, :type => 'client_cred')
|
223
|
+
end
|
167
224
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
225
|
+
# Publish to Facebook, you would need to be authorized and provide access token.
|
226
|
+
#
|
227
|
+
# # Post to user's feed.
|
228
|
+
# # curl -F 'access_token=...' \
|
229
|
+
# # -F 'message=Hello, Arjun. I like this new API.' \
|
230
|
+
# # https://graph.facebook.com/arjun/feed
|
231
|
+
# FGraph.publish('arjun/feed', :message => 'Hello, Arjun. I like this new API.',
|
232
|
+
# :access_token => '...')
|
233
|
+
# FGraph.publish_feed('arjun', :message => '...', :access_token => '...')
|
234
|
+
# FGraph.publish_feed('me', ':message => '...', :access_token => '...')
|
235
|
+
#
|
236
|
+
# ==== Options
|
237
|
+
#
|
238
|
+
# Method Description Options
|
239
|
+
# -------------------------------------------------------------------------------------
|
240
|
+
# /PROFILE_ID/feed write to the given profile's feed/wall :message, :picture,
|
241
|
+
# :link, :name, description
|
242
|
+
# /POST_ID/comments comment on the given post :message
|
243
|
+
# /POST_ID/likes like the given post none
|
244
|
+
# /PROFILE_ID/notes write a note on the given profile :message, :subject
|
245
|
+
# /PROFILE_ID/links write a link on the given profile :link, :message
|
246
|
+
# /EVENT_ID/attending attend the given event none
|
247
|
+
# /EVENT_ID/maybe maybe attend the given event none
|
248
|
+
# /EVENT_ID/declined decline the given event none
|
249
|
+
#
|
250
|
+
def publish(id, options={})
|
251
|
+
id = self.get_id(id)
|
252
|
+
self.perform_post("/#{id}", options)
|
253
|
+
end
|
196
254
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
255
|
+
# Delete objects in the graph.
|
256
|
+
#
|
257
|
+
# # DELETE https://graph.facebook.com/ID?access_token=... HTTP/1.1
|
258
|
+
#
|
259
|
+
# FGraph.remove('[ID]')
|
260
|
+
# FGraph.remove('[ID]/likes')
|
261
|
+
# FGraph.remove_likes('[ID]')
|
262
|
+
#
|
263
|
+
def remove(id, options={})
|
264
|
+
id = self.get_id(id)
|
265
|
+
self.perform_delete("/#{id}", options)
|
266
|
+
end
|
208
267
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
268
|
+
# Search over all public objects in the social graph.
|
269
|
+
#
|
270
|
+
# # https://graph.facebook.com/search?q=watermelon&type=post
|
271
|
+
# FGraph.search('watermelon', :type => 'post')
|
272
|
+
# FGraph.search_post('watermelon')
|
273
|
+
#
|
274
|
+
# ==== Options
|
275
|
+
# * <tt>type</tt> - <tt>album|event|group|link|note|page|photo|post|status|user|video</tt>
|
276
|
+
# * <tt>limit</tt> - max no of records
|
277
|
+
# * <tt>offset</tt> - offset
|
278
|
+
# * <tt>until</tt> - since (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
279
|
+
def search(query, options={})
|
280
|
+
self.perform_get("/search", {
|
281
|
+
:q => query
|
282
|
+
}.merge(options|| {}))
|
283
|
+
end
|
225
284
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
285
|
+
# Download insights data for your application.
|
286
|
+
#
|
287
|
+
# # https://graph.facebook.com/[client_id]/insights?access_token=...
|
288
|
+
# FGraph.insights('[client_id]', '[app_access_token]')
|
289
|
+
#
|
290
|
+
# # https://graph.facebook.com/[client_id]/insights/application_api_call/day?access_token=...
|
291
|
+
# FGraph.insights('[client_id]', '[app_access_token]', :metric_path => 'application_api_call/day')
|
292
|
+
#
|
293
|
+
# ==== Options
|
294
|
+
# * <tt>metric_path</tt> - e.g. application_api_calls/day
|
295
|
+
# * <tt>since</tt> - since (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
296
|
+
# * <tt>until</tt> - until (a unix timestamp or any date accepted by strtotime, e.g. yesterday)
|
297
|
+
def insights(client_id, app_access_token, options={})
|
298
|
+
metric_path = options.delete(:metric_path)
|
240
299
|
|
241
|
-
|
242
|
-
|
300
|
+
path = "/#{client_id}/insights"
|
301
|
+
path += "/#{metric_path}" if metric_path
|
243
302
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
303
|
+
self.perform_get(path, {
|
304
|
+
:access_token => app_access_token
|
305
|
+
}.merge(options || {}))
|
306
|
+
end
|
248
307
|
|
249
|
-
|
250
|
-
|
251
|
-
|
308
|
+
def perform_get(uri, options = {})
|
309
|
+
handle_response(get(uri, {:query => options}))
|
310
|
+
end
|
252
311
|
|
253
|
-
|
254
|
-
|
255
|
-
|
312
|
+
def perform_post(uri, options = {})
|
313
|
+
handle_response(post(uri, {:body => options}))
|
314
|
+
end
|
256
315
|
|
257
|
-
|
258
|
-
|
259
|
-
|
316
|
+
def perform_delete(uri, options = {})
|
317
|
+
handle_response(delete(uri, {:body => options}))
|
318
|
+
end
|
260
319
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
case response['error']['type']
|
266
|
-
when 'QueryParseException'
|
267
|
-
raise QueryParseError, response['error']
|
268
|
-
when 'GraphMethodException'
|
269
|
-
raise GraphMethodError, response['error']
|
270
|
-
when 'OAuthException'
|
271
|
-
raise OAuthError, response['error']
|
272
|
-
when 'OAuthAccessTokenException'
|
273
|
-
raise OAuthAccessTokenError, response['error']
|
320
|
+
def handle_response(response)
|
321
|
+
unless response['error']
|
322
|
+
return FGraph::Collection.new(response) if response['data']
|
323
|
+
response
|
274
324
|
else
|
275
|
-
|
325
|
+
case response['error']['type']
|
326
|
+
when 'QueryParseException'
|
327
|
+
raise QueryParseError, response['error']
|
328
|
+
when 'GraphMethodException'
|
329
|
+
raise GraphMethodError, response['error']
|
330
|
+
when 'OAuthException'
|
331
|
+
raise OAuthError, response['error']
|
332
|
+
when 'OAuthAccessTokenException'
|
333
|
+
raise OAuthAccessTokenError, response['error']
|
334
|
+
else
|
335
|
+
raise FacebookError, response['error']
|
336
|
+
end
|
337
|
+
end
|
276
338
|
end
|
277
|
-
end
|
278
339
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
340
|
+
def format_url(path, options={})
|
341
|
+
url = self.base_uri.dup
|
342
|
+
url << path
|
343
|
+
unless options.blank?
|
344
|
+
url << "?"
|
284
345
|
|
285
|
-
|
346
|
+
option_count = 0
|
286
347
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
348
|
+
stringified_options = {}
|
349
|
+
options.each do |key, value|
|
350
|
+
stringified_options[key.to_s] = value
|
351
|
+
end
|
352
|
+
options = stringified_options
|
292
353
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
354
|
+
options.each do |option|
|
355
|
+
next if option[1].blank?
|
356
|
+
url << "&" if option_count > 0
|
357
|
+
url << "#{option[0]}=#{CGI.escape(option[1].to_s)}"
|
358
|
+
option_count += 1
|
359
|
+
end
|
298
360
|
end
|
361
|
+
url
|
299
362
|
end
|
300
|
-
url
|
301
|
-
end
|
302
363
|
|
303
|
-
|
304
|
-
|
305
|
-
|
364
|
+
def method_missing(name, *args, &block)
|
365
|
+
names = name.to_s.split('_')
|
366
|
+
super unless names.length > 1
|
306
367
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
368
|
+
case names.shift
|
369
|
+
when 'object'
|
370
|
+
# object_photos
|
371
|
+
self.object("#{args[0]}/#{names[0]}", args[1])
|
372
|
+
when 'me'
|
373
|
+
# me_photos
|
374
|
+
self.me(names[0], args[0])
|
375
|
+
when 'publish'
|
376
|
+
# publish_feed(id)
|
377
|
+
self.publish("#{args[0]}/#{names[0]}", args[1])
|
378
|
+
when 'remove'
|
379
|
+
# remove_feed(id)
|
380
|
+
self.remove("#{args[0]}/#{names[0]}", args[1])
|
381
|
+
when 'search'
|
382
|
+
# search_user(query)
|
383
|
+
options = args[1] || {}
|
384
|
+
options[:type] = names[0]
|
385
|
+
self.search(args[0], options)
|
386
|
+
else
|
387
|
+
super
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# Return ID['id'] if ID is a hash object
|
392
|
+
#
|
393
|
+
def get_id(id)
|
394
|
+
return unless id
|
395
|
+
id = id['id'] if id.is_a?(Hash)
|
396
|
+
id
|
327
397
|
end
|
328
398
|
end
|
329
399
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
namespace :fgraph do
|
4
|
+
desc "Create fgraph.yml configuration file in Rails config folder"
|
5
|
+
task :setup => :environment do
|
6
|
+
fgraph_config = File.join(RAILS_ROOT, "config", "fgraph.yml")
|
7
|
+
unless File.exist?(fgraph_config)
|
8
|
+
fgraph_config_template = File.join(RAILS_ROOT, "vendor", "plugins", "fgraph", "templates", "fgraph.yml")
|
9
|
+
FileUtils.cp fgraph_config_teirmplate, fgraph_config
|
10
|
+
puts "#{RAILS_ROOT}/config/fgraph.yml created, please update your app_id and app_secret."
|
11
|
+
else
|
12
|
+
puts "#{RAILS_ROOT}/config/fgraph.yml already exists."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/tasks/fgraph.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
load 'tasks/fgraph.rake'
|
data/rails/init.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fgraph'
|
2
|
+
require 'fgraph/rails/fgraph_helper'
|
3
|
+
require 'fgraph/rails/fgraph_tag_helper'
|
4
|
+
|
5
|
+
FGRAPH_CONFIG = "#{RAILS_ROOT}/config/fgraph.yml"
|
6
|
+
|
7
|
+
# Load configuration file
|
8
|
+
if File.exists?(FGRAPH_CONFIG)
|
9
|
+
fgraph_config = YAML.load(ERB.new(File.read(FGRAPH_CONFIG)).result)
|
10
|
+
FGraph.config = fgraph_config[RAILS_ENV]
|
11
|
+
end
|
12
|
+
|
13
|
+
ActionController::Base.send :include, FGraph::Rails::FGraphHelper
|
14
|
+
ActionView::Base.send :include, FGraph::Rails::FGraphHelper
|
15
|
+
ActionView::Base.send :include, FGraph::Rails::FGraphTagHelper
|
data/rails/install.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fileutil'
|
3
|
+
|
4
|
+
fgraph_config = File.join(RAILS_ROOT, "config", "fgraph.yml")
|
5
|
+
unless File.exist?(fgraph_config)
|
6
|
+
fgraph_config_template = File.join(RAILS_ROOT, "vendor", "plugins", "fgraph", "templates", "fgraph.yml")
|
7
|
+
FileUtils.cp fgraph_config_template, fgraph_config
|
8
|
+
puts "#{RAILS_ROOT}/config/fgraph.yml created, please update your app_id and app_secret."
|
9
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# FGraph Facebook Configuration File
|
2
|
+
#
|
3
|
+
# Register your application to get your app_id and app_secret:
|
4
|
+
# http://developers.facebook.com/setup/
|
5
|
+
#
|
6
|
+
development:
|
7
|
+
app_id:
|
8
|
+
app_secret:
|
9
|
+
|
10
|
+
test:
|
11
|
+
app_id:
|
12
|
+
app_secret:
|
13
|
+
|
14
|
+
production:
|
15
|
+
app_id:
|
16
|
+
app_secret:
|
data/test/fgraph_test.rb
CHANGED
@@ -8,6 +8,20 @@ class FGraphTest < Test::Unit::TestCase
|
|
8
8
|
FACEBOOK_OAUTH_ACCESS_TOKEN = "115187085478818|rDIv_5zgjCSM_fWBv5Z-lQr5gFk."
|
9
9
|
FACEBOOK_OAUTH_APP_ACCESS_TOKEN = "112167085478818|rDIv_5zgjCSM_fWBv5Z-lQr5gFk."
|
10
10
|
|
11
|
+
context "FGraph.get_id" do
|
12
|
+
should "return 'id' if input 'id' is not a Hash" do
|
13
|
+
test_id = '123'
|
14
|
+
id = FGraph.get_id(test_id)
|
15
|
+
id.should == test_id
|
16
|
+
end
|
17
|
+
|
18
|
+
should "return 'id' value from hash object if input 'id' is a Hash" do
|
19
|
+
test_id = { 'name' => 'Anthony', 'id' => '123' }
|
20
|
+
id = FGraph.get_id(test_id)
|
21
|
+
id.should == test_id['id']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
11
25
|
context "FGraph.object" do
|
12
26
|
should "return object hash" do
|
13
27
|
stub_get('/cocacola', 'object_cocacola.json')
|
@@ -28,13 +42,29 @@ class FGraphTest < Test::Unit::TestCase
|
|
28
42
|
FGraph.expects(:perform_get).with('/cocacola', options)
|
29
43
|
FGraph.object('cocacola', options)
|
30
44
|
end
|
45
|
+
|
46
|
+
should "call FGraph.get_id" do
|
47
|
+
stub_get('/cocacola', 'object_cocacola.json')
|
48
|
+
FGraph.expects(:get_id).with('cocacola')
|
49
|
+
FGraph.expects(:perform_get)
|
50
|
+
object = FGraph.object('cocacola')
|
51
|
+
end
|
31
52
|
end
|
32
53
|
|
33
54
|
context "FGraph.objects" do
|
34
55
|
should "call perform_get with ids and query options" do
|
35
56
|
options = {:fields => 'id,name'}
|
36
|
-
FGraph.expects(:perform_get).with('/', options.merge(:ids => '
|
37
|
-
FGraph.objects('
|
57
|
+
FGraph.expects(:perform_get).with('/', options.merge(:ids => '1,2'))
|
58
|
+
FGraph.objects('1', '2', options)
|
59
|
+
end
|
60
|
+
|
61
|
+
should "collect id values if input is an array of hash values" do
|
62
|
+
test_ids = [
|
63
|
+
{ 'name' => 'Herry', 'id' => '1'},
|
64
|
+
{ 'name' => 'John', 'id' => '2'}
|
65
|
+
]
|
66
|
+
FGraph.expects(:perform_get).with('/', :ids => '1,2')
|
67
|
+
FGraph.objects(test_ids)
|
38
68
|
end
|
39
69
|
end
|
40
70
|
|
@@ -204,6 +234,28 @@ class FGraphTest < Test::Unit::TestCase
|
|
204
234
|
end
|
205
235
|
|
206
236
|
context "FGraph.handle_response" do
|
237
|
+
should "return response object if there's no error" do
|
238
|
+
fb_response = {'name' => 'test'}
|
239
|
+
response = FGraph.handle_response(fb_response)
|
240
|
+
response.should == fb_response
|
241
|
+
end
|
242
|
+
|
243
|
+
should "convert to FGraph::Collection object if response contain 'data' value" do
|
244
|
+
fb_response = {
|
245
|
+
"data" => [
|
246
|
+
{ "name" =>"Belle Clara", "id" => "100000133774483" },
|
247
|
+
{ "name" =>"Rosemary Schapira", "id" => "100000237306697" }
|
248
|
+
],
|
249
|
+
"paging" => {
|
250
|
+
"next" => "https://graph.facebook.com/756314021/friends?offset=4&limit=2&access_token=101507589896698"
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
collection = FGraph.handle_response(fb_response)
|
255
|
+
collection.class.should == FGraph::Collection
|
256
|
+
collection.count.should == fb_response['data'].count
|
257
|
+
end
|
258
|
+
|
207
259
|
should "raise QueryParseError" do
|
208
260
|
lambda do
|
209
261
|
object = FGraph.handle_response(response_error('QueryParseException'))
|
@@ -229,6 +281,29 @@ class FGraphTest < Test::Unit::TestCase
|
|
229
281
|
end
|
230
282
|
end
|
231
283
|
|
284
|
+
context "FGraph::Collection" do
|
285
|
+
should "should convert response object to Collection" do
|
286
|
+
response = {
|
287
|
+
"data" => [
|
288
|
+
{"name"=>"Belle Clara", "id"=>"100000133774483"},
|
289
|
+
{"name"=>"Rosemary Schapira", "id"=>"100000237306697"}
|
290
|
+
],
|
291
|
+
"paging"=> {
|
292
|
+
"previous"=> "https://graph.facebook.com/756314021/friends?offset=0&limit=2&access_token=101507589896698",
|
293
|
+
"next"=> "https://graph.facebook.com/756314021/friends?offset=4&limit=2&access_token=101507589896698"
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
collection = FGraph::Collection.new(response)
|
298
|
+
collection.count.should == response['data'].count
|
299
|
+
collection.first.should == response['data'].first
|
300
|
+
collection.next_url.should == response['paging']['next']
|
301
|
+
collection.previous_url.should == response['paging']['previous']
|
302
|
+
collection.previous_options.should == {'offset' => '0', 'limit' => '2', 'access_token' => '101507589896698'}
|
303
|
+
collection.next_options.should == {'offset' => '4', 'limit' => '2', 'access_token' => '101507589896698'}
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
232
307
|
def response_error(type, msg=nil)
|
233
308
|
{'error' => { 'type' => type, 'message' => msg}}
|
234
309
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Herryanto Siatono
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-26 00:00:00 +08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -107,6 +107,11 @@ files:
|
|
107
107
|
- lib/fgraph/client.rb
|
108
108
|
- lib/fgraph/rails/fgraph_helper.rb
|
109
109
|
- lib/fgraph/rails/fgraph_tag_helper.rb
|
110
|
+
- lib/tasks/fgraph.rake
|
111
|
+
- lib/tasks/fgraph.rb
|
112
|
+
- rails/init.rb
|
113
|
+
- rails/install.rb
|
114
|
+
- templates/fgraph.yml
|
110
115
|
- test/fgraph/client_test.rb
|
111
116
|
- test/fgraph_test.rb
|
112
117
|
- test/fixtures/access_token.txt
|