rest-graph 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +21 -1
- data/Gemfile +12 -0
- data/Gemfile.lock +49 -0
- data/README +5 -4
- data/README.rdoc +5 -4
- data/example/rails/app/controllers/application_controller.rb +15 -2
- data/example/rails/config/environments/test.rb +2 -0
- data/example/rails/test/functional/application_controller_test.rb +9 -0
- data/lib/rest-graph.rb +67 -18
- data/lib/rest-graph/rails_util.rb +74 -46
- data/lib/rest-graph/version.rb +1 -1
- data/rest-graph.gemspec +7 -7
- data/test/config/rest-graph.yaml +1 -0
- data/test/test_handler.rb +63 -31
- data/test/test_oauth.rb +4 -1
- data/test/test_old.rb +1 -4
- data/test/test_parse.rb +19 -0
- data/test/test_rest-graph.rb +21 -4
- metadata +11 -8
- data/lib/rest-graph/facebook_util.rb +0 -27
data/CHANGES
CHANGED
@@ -1,6 +1,26 @@
|
|
1
1
|
= rest-graph changes history
|
2
2
|
|
3
|
-
== rest-graph 1.4.
|
3
|
+
== rest-graph 1.4.1 -- 2010-08-04
|
4
|
+
|
5
|
+
* [RestGraph] Call error_handler when response contains error_code as well,
|
6
|
+
which came from FQL response. Thanks Florent.
|
7
|
+
|
8
|
+
* [RestGraph] Added RestGraph#parse_signed_request!
|
9
|
+
|
10
|
+
* [RestGraph] Added RestGraph#url to generate desired API request URL,
|
11
|
+
in case you'll want to use different HTTP client, such as em-http-request,
|
12
|
+
or pass the API request to different process of data fetcher.
|
13
|
+
|
14
|
+
* [RestGraph] Added an :cache option that allow you to pass a cache
|
15
|
+
object, which should respond to [] and []= for reading and writing.
|
16
|
+
The cache key would be MD5 hexdigest from the URL being called.
|
17
|
+
pass :cache => Rails.cache to rest_graph_setup when using RailsUtil.
|
18
|
+
|
19
|
+
* [RailsUtil] Pass :cache => Rails.cache to rest_graph_setup to enable caching.
|
20
|
+
* [RailsUtil] Favor signed_request over session in rest_graph_setup
|
21
|
+
* [RailsUtil] Now it's possible to setup all options in rest-graph.yaml.
|
22
|
+
|
23
|
+
== rest-graph 1.4.0 -- 2010-07-15
|
4
24
|
|
5
25
|
Changes only for RailsUtil, the core (rest-graph.rb) is pretty stable for now.
|
6
26
|
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
---
|
2
|
+
hash: 1abfe240bd774ad21e7a187351da9cabacb74d58
|
3
|
+
sources:
|
4
|
+
- Rubygems:
|
5
|
+
uri: http://rubygems.org
|
6
|
+
specs:
|
7
|
+
- addressable:
|
8
|
+
version: 2.1.2
|
9
|
+
- bacon:
|
10
|
+
version: 1.1.0
|
11
|
+
- crack:
|
12
|
+
version: 0.1.8
|
13
|
+
- mime-types:
|
14
|
+
version: "1.16"
|
15
|
+
- rack:
|
16
|
+
version: 1.2.1
|
17
|
+
- rest-client:
|
18
|
+
version: 1.6.0
|
19
|
+
- rr:
|
20
|
+
version: 0.10.11
|
21
|
+
- webmock:
|
22
|
+
version: 1.3.2
|
23
|
+
- yajl-ruby:
|
24
|
+
version: 0.7.7
|
25
|
+
dependencies:
|
26
|
+
rest-client:
|
27
|
+
version: ">= 0"
|
28
|
+
group:
|
29
|
+
- :default
|
30
|
+
yajl-ruby:
|
31
|
+
version: ">= 0"
|
32
|
+
group:
|
33
|
+
- :test
|
34
|
+
rack:
|
35
|
+
version: ">= 0"
|
36
|
+
group:
|
37
|
+
- :test
|
38
|
+
rr:
|
39
|
+
version: ">= 0"
|
40
|
+
group:
|
41
|
+
- :test
|
42
|
+
webmock:
|
43
|
+
version: ">= 0"
|
44
|
+
group:
|
45
|
+
- :test
|
46
|
+
bacon:
|
47
|
+
version: ">= 0"
|
48
|
+
group:
|
49
|
+
- :test
|
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= rest-graph 1.
|
1
|
+
= rest-graph 1.4.1
|
2
2
|
by Cardinal Blue ( http://cardinalblue.com )
|
3
3
|
|
4
4
|
== LINKS:
|
@@ -98,6 +98,7 @@ by Cardinal Blue ( http://cardinalblue.com )
|
|
98
98
|
:auto_decode => true , # decode by json
|
99
99
|
:app_id => '123' ,
|
100
100
|
:secret => '1829' ,
|
101
|
+
:cache => {} , # a cache for the same API call
|
101
102
|
|
102
103
|
# This handler callback is only called if auto_decode is set to true,
|
103
104
|
# otherwise, it's ignored.
|
@@ -215,13 +216,13 @@ by Cardinal Blue ( http://cardinalblue.com )
|
|
215
216
|
# or (3) Load config automatically
|
216
217
|
require 'rest-graph/auto_load' # under Rails, load config/rest-graph.yaml
|
217
218
|
|
218
|
-
# Please read:
|
219
|
+
# Please read: for an example of config file.
|
220
|
+
# Note that :auto_authorize_scope and friends is only for RailsUtil.
|
219
221
|
{rest-graph.yaml}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
|
220
|
-
# for an example of config file.
|
221
222
|
|
222
223
|
== REQUIREMENTS:
|
223
224
|
|
224
|
-
* Tested with MRI 1.8.7 and 1.9.1
|
225
|
+
* Tested with MRI 1.8.7 and 1.9.1 and Rubinius HEAD
|
225
226
|
* gem install rest-client
|
226
227
|
* gem install json (optional)
|
227
228
|
* gem install json_pure (optional)
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= rest-graph 1.
|
1
|
+
= rest-graph 1.4.1
|
2
2
|
by Cardinal Blue ( http://cardinalblue.com )
|
3
3
|
|
4
4
|
== LINKS:
|
@@ -98,6 +98,7 @@ by Cardinal Blue ( http://cardinalblue.com )
|
|
98
98
|
:auto_decode => true , # decode by json
|
99
99
|
:app_id => '123' ,
|
100
100
|
:secret => '1829' ,
|
101
|
+
:cache => {} , # a cache for the same API call
|
101
102
|
|
102
103
|
# This handler callback is only called if auto_decode is set to true,
|
103
104
|
# otherwise, it's ignored.
|
@@ -215,13 +216,13 @@ by Cardinal Blue ( http://cardinalblue.com )
|
|
215
216
|
# or (3) Load config automatically
|
216
217
|
require 'rest-graph/auto_load' # under Rails, load config/rest-graph.yaml
|
217
218
|
|
218
|
-
# Please read:
|
219
|
+
# Please read: for an example of config file.
|
220
|
+
# Note that :auto_authorize_scope and friends is only for RailsUtil.
|
219
221
|
{rest-graph.yaml}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
|
220
|
-
# for an example of config file.
|
221
222
|
|
222
223
|
== REQUIREMENTS:
|
223
224
|
|
224
|
-
* Tested with MRI 1.8.7 and 1.9.1
|
225
|
+
* Tested with MRI 1.8.7 and 1.9.1 and Rubinius HEAD
|
225
226
|
* gem install rest-client
|
226
227
|
* gem install json (optional)
|
227
228
|
* gem install json_pure (optional)
|
@@ -16,6 +16,7 @@ class ApplicationController < ActionController::Base
|
|
16
16
|
before_filter :filter_no_auto, :only => [:no_auto]
|
17
17
|
before_filter :filter_diff_app_id, :only => [:diff_app_id]
|
18
18
|
before_filter :filter_diff_canvas, :only => [:diff_canvas]
|
19
|
+
before_filter :filter_cache, :only => [:cache]
|
19
20
|
|
20
21
|
def index
|
21
22
|
render :text => rest_graph.get('me').to_json
|
@@ -34,9 +35,16 @@ class ApplicationController < ActionController::Base
|
|
34
35
|
render :text => rest_graph.app_id
|
35
36
|
end
|
36
37
|
|
38
|
+
def cache
|
39
|
+
url = rest_graph.url('cache')
|
40
|
+
rest_graph.get('cache')
|
41
|
+
rest_graph.get('cache')
|
42
|
+
render :text => Rails.cache.read(Digest::MD5.hexdigest(url))
|
43
|
+
end
|
44
|
+
|
37
45
|
private
|
38
46
|
def filter_common
|
39
|
-
rest_graph_setup(:auto_authorize => true)
|
47
|
+
rest_graph_setup(:auto_authorize => true, :canvas => '')
|
40
48
|
end
|
41
49
|
|
42
50
|
def filter_canvas
|
@@ -59,6 +67,11 @@ class ApplicationController < ActionController::Base
|
|
59
67
|
end
|
60
68
|
|
61
69
|
def filter_options
|
62
|
-
rest_graph_setup(:auto_authorize_options => {:scope => 'bogus'}
|
70
|
+
rest_graph_setup(:auto_authorize_options => {:scope => 'bogus'},
|
71
|
+
:canvas => nil)
|
72
|
+
end
|
73
|
+
|
74
|
+
def filter_cache
|
75
|
+
rest_graph_setup(:cache => Rails.cache)
|
63
76
|
end
|
64
77
|
end
|
@@ -26,3 +26,5 @@ config.action_mailer.delivery_method = :test
|
|
26
26
|
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
27
27
|
# like if you have constraints or database-specific column types
|
28
28
|
# config.active_record.schema_format = :sql
|
29
|
+
|
30
|
+
config.logger = Logger.new($stdout, :debug)
|
@@ -71,4 +71,13 @@ class ApplicationControllerTest < ActionController::TestCase
|
|
71
71
|
assert_response :success
|
72
72
|
assert_equal 'zzz', @response.body
|
73
73
|
end
|
74
|
+
|
75
|
+
def test_cache
|
76
|
+
stub_request(:get, 'https://graph.facebook.com/cache').
|
77
|
+
to_return(:body => '{"message":"ok"}')
|
78
|
+
|
79
|
+
get(:cache)
|
80
|
+
assert_response :success
|
81
|
+
assert_equal '{"message":"ok"}', @response.body
|
82
|
+
end
|
74
83
|
end
|
data/lib/rest-graph.rb
CHANGED
@@ -4,6 +4,8 @@ require 'rest_client'
|
|
4
4
|
|
5
5
|
# stdlib
|
6
6
|
require 'digest/md5'
|
7
|
+
require 'openssl'
|
8
|
+
|
7
9
|
require 'cgi'
|
8
10
|
|
9
11
|
# optional gem
|
@@ -21,15 +23,19 @@ rescue LoadError; end
|
|
21
23
|
}
|
22
24
|
|
23
25
|
# the data structure used in RestGraph
|
24
|
-
RestGraphStruct = Struct.new(:
|
26
|
+
RestGraphStruct = Struct.new(:auto_decode,
|
25
27
|
:graph_server, :old_server,
|
26
28
|
:accept, :lang,
|
27
29
|
:app_id, :secret,
|
30
|
+
:data, :cache,
|
28
31
|
:error_handler,
|
29
32
|
:log_handler) unless defined?(RestGraphStruct)
|
30
33
|
|
31
34
|
class RestGraph < RestGraphStruct
|
32
35
|
class Error < RuntimeError; end
|
36
|
+
class Event < Struct.new(:duration, :url); end
|
37
|
+
class Event::Requested < Event; end
|
38
|
+
class Event::CacheHit < Event; end
|
33
39
|
|
34
40
|
Attributes = RestGraphStruct.members.map(&:to_sym)
|
35
41
|
|
@@ -45,7 +51,6 @@ class RestGraph < RestGraphStruct
|
|
45
51
|
# setup defaults
|
46
52
|
module DefaultAttributes
|
47
53
|
extend self
|
48
|
-
def default_data ; {} ; end
|
49
54
|
def default_auto_decode ; true ; end
|
50
55
|
def default_graph_server; 'https://graph.facebook.com/'; end
|
51
56
|
def default_old_server ; 'https://api.facebook.com/' ; end
|
@@ -53,11 +58,13 @@ class RestGraph < RestGraphStruct
|
|
53
58
|
def default_lang ; 'en-us' ; end
|
54
59
|
def default_app_id ; nil ; end
|
55
60
|
def default_secret ; nil ; end
|
61
|
+
def default_data ; {} ; end
|
62
|
+
def default_cache ; nil ; end
|
56
63
|
def default_error_handler
|
57
64
|
lambda{ |error| raise ::RestGraph::Error.new(error) }
|
58
65
|
end
|
59
66
|
def default_log_handler
|
60
|
-
lambda{ |
|
67
|
+
lambda{ |event| }
|
61
68
|
end
|
62
69
|
end
|
63
70
|
extend DefaultAttributes
|
@@ -80,20 +87,24 @@ class RestGraph < RestGraphStruct
|
|
80
87
|
!!access_token
|
81
88
|
end
|
82
89
|
|
90
|
+
def url path, query={}, server=graph_server
|
91
|
+
"#{server}#{path}#{build_query_string(query)}"
|
92
|
+
end
|
93
|
+
|
83
94
|
def get path, query={}, opts={}
|
84
|
-
request(
|
95
|
+
request(:get , url(path, query, graph_server), opts)
|
85
96
|
end
|
86
97
|
|
87
98
|
def delete path, query={}, opts={}
|
88
|
-
request(
|
99
|
+
request(:delete, url(path, query, graph_server), opts)
|
89
100
|
end
|
90
101
|
|
91
102
|
def post path, payload, query={}, opts={}
|
92
|
-
request(
|
103
|
+
request(:post , url(path, query, graph_server), opts, payload)
|
93
104
|
end
|
94
105
|
|
95
106
|
def put path, payload, query={}, opts={}
|
96
|
-
request(
|
107
|
+
request(:put , url(path, query, graph_server), opts, payload)
|
97
108
|
end
|
98
109
|
|
99
110
|
# cookies, app_id, secrect related below
|
@@ -119,6 +130,18 @@ class RestGraph < RestGraphStruct
|
|
119
130
|
rescue JSON::ParserError
|
120
131
|
end
|
121
132
|
|
133
|
+
# facebook's new signed_request...
|
134
|
+
|
135
|
+
def parse_signed_request! request
|
136
|
+
sig_encoded, json_encoded = request.split('.')
|
137
|
+
sig, json = [sig_encoded, json_encoded].map{ |str|
|
138
|
+
"#{str.tr('-_', '+/')}==".unpack('m').first
|
139
|
+
}
|
140
|
+
self.data = JSON.parse(json) if
|
141
|
+
secret && OpenSSL::HMAC.digest('sha256', secret, json_encoded) == sig
|
142
|
+
rescue JSON::ParserError
|
143
|
+
end
|
144
|
+
|
122
145
|
# oauth related
|
123
146
|
|
124
147
|
def authorize_url opts={}
|
@@ -129,20 +152,23 @@ class RestGraph < RestGraphStruct
|
|
129
152
|
def authorize! opts={}
|
130
153
|
query = {:client_id => app_id, :client_secret => secret}.merge(opts)
|
131
154
|
self.data = Rack::Utils.parse_query(
|
132
|
-
|
155
|
+
request(:get, url('oauth/access_token', query),
|
156
|
+
:suppress_decode => true))
|
133
157
|
end
|
134
158
|
|
135
159
|
# old rest facebook api, i will definitely love to remove them someday
|
136
160
|
|
137
161
|
def old_rest path, query={}, opts={}
|
138
|
-
request(
|
139
|
-
|
162
|
+
request(
|
163
|
+
:get,
|
164
|
+
url("method/#{path}", {:format => 'json'}.merge(query), old_server),
|
165
|
+
opts)
|
140
166
|
end
|
141
167
|
|
142
168
|
def exchange_sessions opts={}
|
143
169
|
query = {:client_id => app_id, :client_secret => secret,
|
144
170
|
:type => 'client_cred'}.merge(opts)
|
145
|
-
request(
|
171
|
+
request(:post, url('oauth/exchange_sessions', query))
|
146
172
|
end
|
147
173
|
|
148
174
|
def fql code, query={}, opts={}
|
@@ -162,15 +188,14 @@ class RestGraph < RestGraphStruct
|
|
162
188
|
end
|
163
189
|
|
164
190
|
private
|
165
|
-
def request
|
191
|
+
def request meth, uri, opts={}, payload=nil
|
166
192
|
start_time = Time.now
|
167
|
-
|
168
|
-
|
169
|
-
res.send(method, *[payload, build_headers].compact), suppress_decode)
|
193
|
+
post_request(cache_get(uri) || fetch(meth, uri, payload),
|
194
|
+
opts[:suppress_decode])
|
170
195
|
rescue RestClient::Exception => e
|
171
|
-
post_request(e.http_body, suppress_decode)
|
196
|
+
post_request(e.http_body, opts[:suppress_decode])
|
172
197
|
ensure
|
173
|
-
log_handler.call(Time.now - start_time,
|
198
|
+
log_handler.call(Event::Requested.new(Time.now - start_time, uri))
|
174
199
|
end
|
175
200
|
|
176
201
|
def build_query_string query={}
|
@@ -200,7 +225,9 @@ class RestGraph < RestGraphStruct
|
|
200
225
|
end
|
201
226
|
|
202
227
|
def check_error hash
|
203
|
-
if error_handler && hash.kind_of?(Hash) &&
|
228
|
+
if error_handler && hash.kind_of?(Hash) &&
|
229
|
+
(hash['error'] || # from graph api
|
230
|
+
hash['error_code']) # from fql
|
204
231
|
error_handler.call(hash)
|
205
232
|
else
|
206
233
|
hash
|
@@ -213,4 +240,26 @@ class RestGraph < RestGraphStruct
|
|
213
240
|
|
214
241
|
Digest::MD5.hexdigest(args + secret)
|
215
242
|
end
|
243
|
+
|
244
|
+
def cache_key uri
|
245
|
+
Digest::MD5.hexdigest(uri)
|
246
|
+
end
|
247
|
+
|
248
|
+
def cache_get uri
|
249
|
+
return unless cache
|
250
|
+
start_time = Time.now
|
251
|
+
cache[cache_key(uri)].tap{ |result|
|
252
|
+
log_handler.call(Event::CacheHit.new(Time.now - start_time, uri)) if
|
253
|
+
result
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
def fetch meth, uri, payload
|
258
|
+
RestClient::Request.execute(:method => meth, :url => uri,
|
259
|
+
:headers => build_headers,
|
260
|
+
:payload => payload).
|
261
|
+
tap{ |result|
|
262
|
+
cache[cache_key(uri)] = result if cache
|
263
|
+
}
|
264
|
+
end
|
216
265
|
end
|
@@ -1,12 +1,23 @@
|
|
1
1
|
|
2
2
|
require 'rest-graph'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
''
|
4
|
+
class RestGraph
|
5
|
+
module DefaultAttributes
|
6
|
+
def default_canvas ; '' ; end
|
7
|
+
def default_auto_authorize ; false; end
|
8
|
+
def default_auto_authorize_options; {} ; end
|
9
|
+
def default_auto_authorize_scope ; '' ; end
|
10
|
+
def default_write_session ; false; end
|
11
|
+
end
|
12
|
+
|
13
|
+
module RailsCache
|
14
|
+
def [] key ; read(key) ; end
|
15
|
+
def []= key, value; write(key, value); end
|
7
16
|
end
|
8
17
|
end
|
9
18
|
|
19
|
+
::ActiveSupport::Cache::Store.send(:include, ::RestGraph::RailsCache)
|
20
|
+
|
10
21
|
module RestGraph::RailsUtil
|
11
22
|
module Helper
|
12
23
|
def rest_graph
|
@@ -21,30 +32,16 @@ module RestGraph::RailsUtil
|
|
21
32
|
controller.helper(::RestGraph::RailsUtil::Helper)
|
22
33
|
end
|
23
34
|
|
24
|
-
def rest_graph_options
|
25
|
-
@rest_graph_options ||=
|
26
|
-
{:canvas => '',
|
27
|
-
:auto_authorize => false,
|
28
|
-
:auto_authorize_options => {},
|
29
|
-
:auto_authorize_scope => '',
|
30
|
-
:write_session => false}
|
31
|
-
end
|
32
|
-
|
33
|
-
def rest_graph_options_new
|
34
|
-
@rest_graph_options_new ||=
|
35
|
-
{:error_handler => method(:rest_graph_authorize),
|
36
|
-
:log_handler => method(:rest_graph_log)}
|
37
|
-
end
|
38
|
-
|
39
35
|
def rest_graph_setup options={}
|
40
|
-
|
36
|
+
rest_graph_options_ctl.merge!(rest_graph_extract_options(options, :reject))
|
41
37
|
rest_graph_options_new.merge!(rest_graph_extract_options(options, :select))
|
42
38
|
|
43
39
|
rest_graph_check_cookie
|
40
|
+
rest_graph_check_params_signed_request
|
44
41
|
rest_graph_check_params_session
|
45
42
|
rest_graph_check_code
|
46
43
|
|
47
|
-
# there are above
|
44
|
+
# there are above 4 ways to check the user identity!
|
48
45
|
# if nor of them passed, then we can suppose the user
|
49
46
|
# didn't authorize for us, but we can check if user has authorized
|
50
47
|
# before, in that case, the fbs would be inside session,
|
@@ -64,8 +61,8 @@ module RestGraph::RailsUtil
|
|
64
61
|
if redirect || rest_graph_auto_authorize?
|
65
62
|
@rest_graph_authorize_url = rest_graph.authorize_url(
|
66
63
|
{:redirect_uri => rest_graph_normalized_request_uri,
|
67
|
-
:scope =>
|
68
|
-
merge(
|
64
|
+
:scope => rest_graph_oget(:auto_authorize_scope)}.
|
65
|
+
merge(rest_graph_oget(:auto_authorize_options)))
|
69
66
|
|
70
67
|
logger.debug("DEBUG: RestGraph: redirect to #{@rest_graph_authorize_url}")
|
71
68
|
|
@@ -79,7 +76,6 @@ module RestGraph::RailsUtil
|
|
79
76
|
def rest_graph_authorize_redirect
|
80
77
|
if !rest_graph_in_canvas?
|
81
78
|
redirect_to @rest_graph_authorize_url
|
82
|
-
|
83
79
|
else
|
84
80
|
render :inline => <<-HTML
|
85
81
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
@@ -104,7 +100,27 @@ module RestGraph::RailsUtil
|
|
104
100
|
|
105
101
|
module_function
|
106
102
|
|
107
|
-
# ====================
|
103
|
+
# ==================== options utility =======================
|
104
|
+
|
105
|
+
def rest_graph_oget key
|
106
|
+
if rest_graph_options_ctl.has_key?(key)
|
107
|
+
rest_graph_options_ctl[key]
|
108
|
+
else
|
109
|
+
RestGraph.send("default_#{key}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def rest_graph_options_ctl
|
114
|
+
@rest_graph_options_ctl ||= {}
|
115
|
+
end
|
116
|
+
|
117
|
+
def rest_graph_options_new
|
118
|
+
@rest_graph_options_new ||=
|
119
|
+
{:error_handler => method(:rest_graph_authorize),
|
120
|
+
:log_handler => method(:rest_graph_log)}
|
121
|
+
end
|
122
|
+
|
123
|
+
# ==================== checking utility ======================
|
108
124
|
|
109
125
|
# if we're not in canvas nor code passed,
|
110
126
|
# we could check out cookies as well.
|
@@ -117,24 +133,37 @@ module RestGraph::RailsUtil
|
|
117
133
|
" #{rest_graph.data.inspect}")
|
118
134
|
end
|
119
135
|
|
136
|
+
def rest_graph_check_params_signed_request
|
137
|
+
return if rest_graph.authorized? || !params[:signed_request]
|
138
|
+
|
139
|
+
rest_graph.parse_signed_request!(params[:signed_request])
|
140
|
+
logger.debug("DEBUG: RestGraph: detected signed_request, parsed:" \
|
141
|
+
" #{rest_graph.data.inspect}")
|
142
|
+
|
143
|
+
if rest_graph.authorized?
|
144
|
+
rest_graph_write_session
|
145
|
+
else
|
146
|
+
logger.warn(
|
147
|
+
"WARN: RestGraph: bad signed_request: #{params[:signed_request]}")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
120
151
|
# if the code is bad or not existed,
|
121
152
|
# check if there's one in session,
|
122
153
|
# meanwhile, there the sig and access_token is correct,
|
123
154
|
# that means we're in the context of canvas
|
124
155
|
def rest_graph_check_params_session
|
125
|
-
|
156
|
+
return if rest_graph.authorized? || !params[:session]
|
126
157
|
|
127
158
|
rest_graph.parse_json!(params[:session])
|
128
159
|
logger.debug("DEBUG: RestGraph: detected session, parsed:" \
|
129
160
|
" #{rest_graph.data.inspect}")
|
130
161
|
|
131
162
|
if rest_graph.authorized?
|
132
|
-
|
163
|
+
rest_graph_write_session
|
133
164
|
else
|
134
165
|
logger.warn("WARN: RestGraph: bad session: #{params[:session]}")
|
135
166
|
end
|
136
|
-
|
137
|
-
rest_graph_write_session
|
138
167
|
end
|
139
168
|
|
140
169
|
# exchange the code with access_token
|
@@ -148,7 +177,7 @@ module RestGraph::RailsUtil
|
|
148
177
|
"#{rest_graph_normalized_request_uri}, " \
|
149
178
|
"parsed: #{rest_graph.data.inspect}")
|
150
179
|
|
151
|
-
rest_graph_write_session
|
180
|
+
rest_graph_write_session if rest_graph.authorized?
|
152
181
|
end
|
153
182
|
|
154
183
|
def rest_graph_check_rails_session
|
@@ -159,24 +188,31 @@ module RestGraph::RailsUtil
|
|
159
188
|
" #{rest_graph.data.inspect}")
|
160
189
|
end
|
161
190
|
|
162
|
-
# ==================== others
|
191
|
+
# ==================== others ================================
|
163
192
|
|
164
193
|
def rest_graph_write_session
|
165
|
-
return if !
|
194
|
+
return if !rest_graph_oget(:write_session)
|
166
195
|
|
167
196
|
fbs = rest_graph.data.to_a.map{ |k_v| k_v.join('=') }.join('&')
|
168
197
|
session['fbs'] = fbs
|
169
198
|
logger.debug("DEBUG: RestGraph: wrote session: fbs => #{fbs}")
|
170
199
|
end
|
171
200
|
|
172
|
-
def rest_graph_log
|
173
|
-
|
201
|
+
def rest_graph_log event
|
202
|
+
message = "DEBUG: RestGraph: spent #{sprintf('%f', event.duration)} "
|
203
|
+
case event
|
204
|
+
when RestGraph::Event::Requested
|
205
|
+
logger.debug(message + "requesting #{event.url}")
|
206
|
+
|
207
|
+
when RestGraph::Event::CacheHit
|
208
|
+
logger.debug(message + "cache hit' #{event.url}")
|
209
|
+
end
|
174
210
|
end
|
175
211
|
|
176
212
|
def rest_graph_normalized_request_uri
|
177
213
|
if rest_graph_in_canvas?
|
178
214
|
"http://apps.facebook.com/" \
|
179
|
-
"#{
|
215
|
+
"#{rest_graph_oget(:canvas)}#{request.request_uri}"
|
180
216
|
else
|
181
217
|
request.url
|
182
218
|
end.sub(/[\&\?]session=[^\&]+/, '').
|
@@ -184,21 +220,13 @@ module RestGraph::RailsUtil
|
|
184
220
|
end
|
185
221
|
|
186
222
|
def rest_graph_in_canvas?
|
187
|
-
!
|
188
|
-
end
|
189
|
-
|
190
|
-
def rest_graph_canvas
|
191
|
-
if rest_graph_options[:canvas].empty?
|
192
|
-
RestGraph.default_canvas
|
193
|
-
else
|
194
|
-
rest_graph_options[:canvas]
|
195
|
-
end
|
223
|
+
!rest_graph_oget(:canvas).blank?
|
196
224
|
end
|
197
225
|
|
198
226
|
def rest_graph_auto_authorize?
|
199
|
-
!
|
200
|
-
!
|
201
|
-
|
227
|
+
!rest_graph_oget(:auto_authorize_scope) .blank? ||
|
228
|
+
!rest_graph_oget(:auto_authorize_options).blank? ||
|
229
|
+
rest_graph_oget(:auto_authorize)
|
202
230
|
end
|
203
231
|
|
204
232
|
def rest_graph_extract_options options, method
|
data/lib/rest-graph/version.rb
CHANGED
data/rest-graph.gemspec
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{rest-graph}
|
5
|
-
s.version = "1.4.
|
5
|
+
s.version = "1.4.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Cardinal Blue", "Lin Jen-Shin (aka godfat 真常)"]
|
9
|
-
s.date = %q{2010-07-
|
9
|
+
s.date = %q{2010-07-26}
|
10
10
|
s.description = %q{ A super simple Facebook Open Graph API client}
|
11
11
|
s.email = %q{dev (XD) cardinalblue.com}
|
12
|
-
s.extra_rdoc_files = ["CHANGES", "LICENSE", "README", "TODO", "example/rails/README", "example/rails/config/rest-graph.yaml", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "rest-graph.gemspec"]
|
13
|
-
s.files = ["CHANGES", "LICENSE", "README", "README.rdoc", "Rakefile", "TODO", "example/rails/README", "example/rails/Rakefile", "example/rails/app/controllers/application_controller.rb", "example/rails/config/boot.rb", "example/rails/config/environment.rb", "example/rails/config/environments/development.rb", "example/rails/config/environments/production.rb", "example/rails/config/environments/test.rb", "example/rails/config/initializers/cookie_verification_secret.rb", "example/rails/config/initializers/new_rails_defaults.rb", "example/rails/config/initializers/session_store.rb", "example/rails/config/rest-graph.yaml", "example/rails/config/routes.rb", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "example/rails/test/functional/application_controller_test.rb", "example/rails/test/test_helper.rb", "init.rb", "lib/rest-graph.rb", "lib/rest-graph/auto_load.rb", "lib/rest-graph/facebook_util.rb", "lib/rest-graph/load_config.rb", "lib/rest-graph/rails_util.rb", "lib/rest-graph/version.rb", "rest-graph.gemspec", "test/common.rb", "test/config/rest-graph.yaml", "test/test_default.rb", "test/test_handler.rb", "test/test_load_config.rb", "test/test_oauth.rb", "test/test_old.rb", "test/test_parse.rb", "test/test_rest-graph.rb"]
|
12
|
+
s.extra_rdoc_files = ["CHANGES", "Gemfile", "Gemfile.lock", "LICENSE", "README", "TODO", "example/rails/README", "example/rails/config/rest-graph.yaml", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "rest-graph.gemspec"]
|
13
|
+
s.files = ["CHANGES", "Gemfile", "Gemfile.lock", "LICENSE", "README", "README.rdoc", "Rakefile", "TODO", "example/rails/README", "example/rails/Rakefile", "example/rails/app/controllers/application_controller.rb", "example/rails/config/boot.rb", "example/rails/config/environment.rb", "example/rails/config/environments/development.rb", "example/rails/config/environments/production.rb", "example/rails/config/environments/test.rb", "example/rails/config/initializers/cookie_verification_secret.rb", "example/rails/config/initializers/new_rails_defaults.rb", "example/rails/config/initializers/session_store.rb", "example/rails/config/rest-graph.yaml", "example/rails/config/routes.rb", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "example/rails/test/functional/application_controller_test.rb", "example/rails/test/test_helper.rb", "init.rb", "lib/rest-graph.rb", "lib/rest-graph/auto_load.rb", "lib/rest-graph/facebook_util.rb", "lib/rest-graph/load_config.rb", "lib/rest-graph/rails_util.rb", "lib/rest-graph/version.rb", "rest-graph.gemspec", "test/common.rb", "test/config/rest-graph.yaml", "test/test_default.rb", "test/test_handler.rb", "test/test_load_config.rb", "test/test_oauth.rb", "test/test_old.rb", "test/test_parse.rb", "test/test_rest-graph.rb"]
|
14
14
|
s.homepage = %q{http://github.com/cardinalblue/rest-graph}
|
15
15
|
s.rdoc_options = ["--main", "README.rdoc"]
|
16
16
|
s.require_paths = ["lib"]
|
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.add_development_dependency(%q<json>, [">= 1.4.3"])
|
29
29
|
s.add_development_dependency(%q<rack>, [">= 1.2.1"])
|
30
30
|
s.add_development_dependency(%q<rr>, [">= 0.10.11"])
|
31
|
-
s.add_development_dependency(%q<webmock>, [">= 1.3.
|
31
|
+
s.add_development_dependency(%q<webmock>, [">= 1.3.2"])
|
32
32
|
s.add_development_dependency(%q<bacon>, [">= 1.1.0"])
|
33
33
|
s.add_development_dependency(%q<bones>, [">= 3.4.7"])
|
34
34
|
else
|
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
|
|
36
36
|
s.add_dependency(%q<json>, [">= 1.4.3"])
|
37
37
|
s.add_dependency(%q<rack>, [">= 1.2.1"])
|
38
38
|
s.add_dependency(%q<rr>, [">= 0.10.11"])
|
39
|
-
s.add_dependency(%q<webmock>, [">= 1.3.
|
39
|
+
s.add_dependency(%q<webmock>, [">= 1.3.2"])
|
40
40
|
s.add_dependency(%q<bacon>, [">= 1.1.0"])
|
41
41
|
s.add_dependency(%q<bones>, [">= 3.4.7"])
|
42
42
|
end
|
@@ -45,7 +45,7 @@ Gem::Specification.new do |s|
|
|
45
45
|
s.add_dependency(%q<json>, [">= 1.4.3"])
|
46
46
|
s.add_dependency(%q<rack>, [">= 1.2.1"])
|
47
47
|
s.add_dependency(%q<rr>, [">= 0.10.11"])
|
48
|
-
s.add_dependency(%q<webmock>, [">= 1.3.
|
48
|
+
s.add_dependency(%q<webmock>, [">= 1.3.2"])
|
49
49
|
s.add_dependency(%q<bacon>, [">= 1.1.0"])
|
50
50
|
s.add_dependency(%q<bones>, [">= 3.4.7"])
|
51
51
|
end
|
data/test/config/rest-graph.yaml
CHANGED
data/test/test_handler.rb
CHANGED
@@ -8,49 +8,81 @@ end
|
|
8
8
|
require 'json'
|
9
9
|
|
10
10
|
describe RestGraph do
|
11
|
-
|
12
|
-
@id = lambda{ |obj| obj }
|
13
|
-
@error = '{"error":{"type":"Exception","message":"(#2500)"}}'
|
14
|
-
@error_hash = JSON.parse(@error)
|
15
|
-
|
11
|
+
after do
|
16
12
|
reset_webmock
|
17
|
-
|
18
|
-
to_return(:body => @error)
|
13
|
+
RR.verify
|
19
14
|
end
|
20
15
|
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
describe 'log handler' do
|
17
|
+
it 'would log whenever doing network request' do
|
18
|
+
stub_request(:get, 'https://graph.facebook.com/me').
|
19
|
+
to_return(:body => '{}')
|
20
|
+
|
21
|
+
mock(Time).now{ 666 }
|
22
|
+
mock(Time).now{ 999 }
|
23
|
+
|
24
|
+
logger = []
|
25
|
+
rg = RestGraph.new(:log_handler => lambda{ |e|
|
26
|
+
logger << [e.duration, e.url] })
|
27
|
+
rg.get('me')
|
24
28
|
|
25
|
-
|
26
|
-
begin
|
27
|
-
RestGraph.new.get('me')
|
28
|
-
rescue ::RestGraph::Error => e
|
29
|
-
e.message.should == @error_hash
|
29
|
+
logger.last.should == [333, 'https://graph.facebook.com/me']
|
30
30
|
end
|
31
31
|
end
|
32
|
-
end
|
33
32
|
|
34
|
-
describe
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
describe 'with Graph API' do
|
34
|
+
before do
|
35
|
+
@id = lambda{ |obj| obj }
|
36
|
+
@error = '{"error":{"type":"Exception","message":"(#2500)"}}'
|
37
|
+
@error_hash = JSON.parse(@error)
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
stub_request(:get, 'https://graph.facebook.com/me').
|
40
|
+
to_return(:body => @error)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'would call error_handler if error occurred' do
|
44
|
+
RestGraph.new(:error_handler => @id).get('me').should == @error_hash
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'would raise ::RestGraph::Error in default error_handler' do
|
48
|
+
begin
|
49
|
+
RestGraph.new.get('me')
|
50
|
+
rescue ::RestGraph::Error => e
|
51
|
+
e.message.should == @error_hash
|
52
|
+
end
|
53
|
+
end
|
41
54
|
end
|
42
55
|
|
43
|
-
|
44
|
-
|
45
|
-
|
56
|
+
describe 'with FQL API' do
|
57
|
+
# Example of an actual response (without newline)
|
58
|
+
# {"error_code":603,"error_msg":"Unknown table: bad_table",
|
59
|
+
# "request_args":[{"key":"method","value":"fql.query"},
|
60
|
+
# {"key":"format","value":"json"},
|
61
|
+
# {"key":"query","value":
|
62
|
+
# "SELECT name FROM bad_table WHERE uid=12345"}]}
|
63
|
+
before do
|
64
|
+
@id = lambda{ |obj| obj }
|
65
|
+
@fql_error = '{"error_code":603,"error_msg":"Unknown table: bad"}'
|
66
|
+
@fql_error_hash = JSON.parse(@fql_error)
|
46
67
|
|
47
|
-
|
48
|
-
|
68
|
+
@bad_fql_query = 'SELECT name FROM bad_table WHERE uid="12345"'
|
69
|
+
bad_fql_request = "https://api.facebook.com/method/fql.query?" \
|
70
|
+
"format=json&query=#{CGI.escape(@bad_fql_query)}"
|
49
71
|
|
50
|
-
|
51
|
-
|
52
|
-
rg.get('me')
|
72
|
+
stub_request(:get, bad_fql_request).to_return(:body => @fql_error)
|
73
|
+
end
|
53
74
|
|
54
|
-
|
75
|
+
it 'would call error_handler if error occurred' do
|
76
|
+
RestGraph.new(:error_handler => @id).fql(@bad_fql_query).
|
77
|
+
should == @fql_error_hash
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'would raise ::RestGraph::Error in default error_handler' do
|
81
|
+
begin
|
82
|
+
RestGraph.new.fql(@bad_fql_query)
|
83
|
+
rescue ::RestGraph::Error => e
|
84
|
+
e.message.should == @fql_error_hash
|
85
|
+
end
|
86
|
+
end
|
55
87
|
end
|
56
88
|
end
|
data/test/test_oauth.rb
CHANGED
@@ -7,11 +7,14 @@ end
|
|
7
7
|
|
8
8
|
describe RestGraph do
|
9
9
|
before do
|
10
|
-
reset_webmock
|
11
10
|
@rg = RestGraph.new(:app_id => '29', :secret => '18')
|
12
11
|
@uri = 'http://zzz.tw'
|
13
12
|
end
|
14
13
|
|
14
|
+
after do
|
15
|
+
reset_webmock
|
16
|
+
end
|
17
|
+
|
15
18
|
it 'would return correct oauth url' do
|
16
19
|
TestHelper.normalize_url(@rg.authorize_url(:redirect_uri => @uri)).
|
17
20
|
should == 'https://graph.facebook.com/oauth/authorize?' \
|
data/test/test_old.rb
CHANGED
data/test/test_parse.rb
CHANGED
@@ -68,4 +68,23 @@ describe RestGraph do
|
|
68
68
|
should == {'feed' => 'me', 'sig' => "20393e7823730308938a86ecf1c88b14"}
|
69
69
|
end
|
70
70
|
|
71
|
+
it 'would parse signed_request' do
|
72
|
+
secret = 'aloha'
|
73
|
+
json = {'ooh' => 'dir', 'moo' => 'bar'}.to_json
|
74
|
+
encode = lambda{ |str|
|
75
|
+
[str].pack('m').tr("\n=", '').tr('+/', '-_')
|
76
|
+
}
|
77
|
+
json_encoded = encode[json]
|
78
|
+
sig = OpenSSL::HMAC.digest('sha256', secret, json_encoded)
|
79
|
+
signed_request = "#{encode[sig]}.#{json_encoded}"
|
80
|
+
|
81
|
+
rg = RestGraph.new(:secret => secret)
|
82
|
+
rg.parse_signed_request!(signed_request)
|
83
|
+
rg.data['ooh'].should == 'dir'
|
84
|
+
rg.data['moo'].should == 'bar'
|
85
|
+
|
86
|
+
signed_request = "#{encode[sig[0..-4]+'bad']}.#{json_encoded}"
|
87
|
+
rg.parse_signed_request!(signed_request).should == nil
|
88
|
+
end
|
89
|
+
|
71
90
|
end
|
data/test/test_rest-graph.rb
CHANGED
@@ -6,11 +6,8 @@ else
|
|
6
6
|
end
|
7
7
|
|
8
8
|
describe RestGraph do
|
9
|
-
before do
|
10
|
-
reset_webmock
|
11
|
-
end
|
12
|
-
|
13
9
|
after do
|
10
|
+
reset_webmock
|
14
11
|
RR.verify
|
15
12
|
end
|
16
13
|
|
@@ -42,6 +39,13 @@ describe RestGraph do
|
|
42
39
|
should == '?message=hi%21%21&subject=%28%26oh%26%29'
|
43
40
|
end
|
44
41
|
|
42
|
+
it 'would generate correct url' do
|
43
|
+
TestHelper.normalize_url(
|
44
|
+
RestGraph.new(:access_token => 'awesome').url('path', :query => 'string')).
|
45
|
+
should ==
|
46
|
+
'https://graph.facebook.com/path?access_token=awesome&query=string'
|
47
|
+
end
|
48
|
+
|
45
49
|
it 'would request to correct server' do
|
46
50
|
stub_request(:get, 'http://nothing.godfat.org/me').with(
|
47
51
|
:headers => {'Accept' => 'text/plain',
|
@@ -113,4 +117,17 @@ describe RestGraph do
|
|
113
117
|
to_return(:body => 'ok')
|
114
118
|
RestGraph.new(:auto_decode => false).get('search', :q => o).should == 'ok'
|
115
119
|
end
|
120
|
+
|
121
|
+
it 'would enable cache if passing cache' do
|
122
|
+
url, body = "https://graph.facebook.com/cache", '{"message":"ok"}'
|
123
|
+
stub_request(:get, url).to_return(:body => body)
|
124
|
+
|
125
|
+
cache = {}
|
126
|
+
rg = RestGraph.new(:cache => cache, :auto_decode => false)
|
127
|
+
3.times{
|
128
|
+
rg.get('cache').should == body
|
129
|
+
reset_webmock
|
130
|
+
}
|
131
|
+
cache.should == {rg.send(:cache_key, url) => body}
|
132
|
+
end
|
116
133
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 1.4.
|
9
|
+
- 1
|
10
|
+
version: 1.4.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Cardinal Blue
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-08-04 00:00:00 +08:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -91,12 +91,12 @@ dependencies:
|
|
91
91
|
requirements:
|
92
92
|
- - ">="
|
93
93
|
- !ruby/object:Gem::Version
|
94
|
-
hash:
|
94
|
+
hash: 31
|
95
95
|
segments:
|
96
96
|
- 1
|
97
97
|
- 3
|
98
|
-
-
|
99
|
-
version: 1.3.
|
98
|
+
- 2
|
99
|
+
version: 1.3.2
|
100
100
|
type: :development
|
101
101
|
version_requirements: *id005
|
102
102
|
- !ruby/object:Gem::Dependency
|
@@ -139,6 +139,8 @@ extensions: []
|
|
139
139
|
|
140
140
|
extra_rdoc_files:
|
141
141
|
- CHANGES
|
142
|
+
- Gemfile
|
143
|
+
- Gemfile.lock
|
142
144
|
- LICENSE
|
143
145
|
- README
|
144
146
|
- TODO
|
@@ -150,6 +152,8 @@ extra_rdoc_files:
|
|
150
152
|
- rest-graph.gemspec
|
151
153
|
files:
|
152
154
|
- CHANGES
|
155
|
+
- Gemfile
|
156
|
+
- Gemfile.lock
|
153
157
|
- LICENSE
|
154
158
|
- README
|
155
159
|
- README.rdoc
|
@@ -176,7 +180,6 @@ files:
|
|
176
180
|
- init.rb
|
177
181
|
- lib/rest-graph.rb
|
178
182
|
- lib/rest-graph/auto_load.rb
|
179
|
-
- lib/rest-graph/facebook_util.rb
|
180
183
|
- lib/rest-graph/load_config.rb
|
181
184
|
- lib/rest-graph/rails_util.rb
|
182
185
|
- lib/rest-graph/version.rb
|
@@ -1,27 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'rest-graph'
|
3
|
-
|
4
|
-
module RestGraph::FacebookUtil
|
5
|
-
module_function
|
6
|
-
def ext_perm
|
7
|
-
%w[publish_stream create_event rsvp_event sms offline_access manage_pages
|
8
|
-
email read_insights read_stream read_mailbox ads_management xmpp_login
|
9
|
-
user_about_me user_activities user_birthday user_education_history
|
10
|
-
user_events user_groups user_hometown user_interests user_likes
|
11
|
-
user_location user_notes user_online_presence user_photo_video_tags
|
12
|
-
user_photos user_relationships user_religion_politics user_status
|
13
|
-
user_videos user_videos user_work_history read_friendlists read_requests
|
14
|
-
friends_about_me friends_activities friends_birthday
|
15
|
-
friends_education_history friends_events friends_groups friends_hometown
|
16
|
-
friends_interests friends_likes friends_location friends_notes
|
17
|
-
friends_online_presence friends_photo_video_tags friends_photos
|
18
|
-
friends_relationships friends_religion_politics friends_status
|
19
|
-
friends_videos friends_website friends_work_history]
|
20
|
-
end
|
21
|
-
|
22
|
-
def method_name
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
RestGraph.send(:include, RestGraph::FacebookUtil)
|