rest-graph 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.specification +309 -0
- data/CHANGES +78 -0
- data/Gemfile +4 -2
- data/Gemfile.lock +12 -7
- data/README +20 -9
- data/README.rdoc +20 -9
- data/Rakefile +2 -1
- data/TODO +2 -0
- data/example/multi/config.ru +44 -0
- data/example/multi/rainbows.rb +5 -0
- data/example/rails2/test/functional/application_controller_test.rb +6 -6
- data/lib/rest-graph.rb +179 -75
- data/lib/rest-graph/rails_util.rb +2 -13
- data/lib/rest-graph/test_util.rb +95 -0
- data/lib/rest-graph/version.rb +1 -1
- data/rest-graph.gemspec +305 -57
- data/test/common.rb +3 -3
- data/test/test_api.rb +12 -11
- data/test/test_cache.rb +3 -3
- data/test/test_default.rb +2 -2
- data/test/test_error.rb +3 -3
- data/test/test_handler.rb +13 -10
- data/test/test_load_config.rb +1 -1
- data/test/test_misc.rb +14 -12
- data/test/test_multi.rb +126 -0
- data/test/test_oauth.rb +4 -4
- data/test/test_old.rb +8 -8
- data/test/test_page.rb +70 -14
- data/test/test_parse.rb +9 -9
- data/test/test_serialize.rb +13 -4
- data/test/test_test_util.rb +74 -0
- data/test/test_timeout.rb +20 -0
- metadata +56 -30
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= rest-graph 1.
|
1
|
+
= rest-graph 1.6.0
|
2
2
|
by Cardinal Blue ( http://cardinalblue.com )
|
3
3
|
|
4
4
|
== LINKS:
|
@@ -20,12 +20,19 @@ A super simple Facebook Open Graph API client
|
|
20
20
|
|
21
21
|
== REQUIREMENTS:
|
22
22
|
|
23
|
-
* Tested with MRI 1.8.7 and 1.9.2 and Rubinius 1.1.
|
24
|
-
|
25
|
-
*
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
* Tested with MRI 1.8.7 and 1.9.2 and Rubinius 1.1.1
|
24
|
+
|
25
|
+
* (must) pick one HTTP client:
|
26
|
+
- gem install rest-client
|
27
|
+
- gem install em-http-request
|
28
|
+
|
29
|
+
* (optional) pick one JSON parser/generator:
|
30
|
+
- gem install yajl-ruby
|
31
|
+
- gem install json
|
32
|
+
- gem install json_pure
|
33
|
+
|
34
|
+
* (optional) parse access_token in HTTP_COOKIE
|
35
|
+
- gem install rack
|
29
36
|
|
30
37
|
== INSTALL:
|
31
38
|
|
@@ -77,7 +84,7 @@ adds the following two methods to your Controllers:
|
|
77
84
|
New RestGraph objects can read their default setup configuration from a
|
78
85
|
YAML configuration file.
|
79
86
|
|
80
|
-
* {
|
87
|
+
* {Example}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
|
81
88
|
|
82
89
|
To enable, just require anywhere:
|
83
90
|
|
@@ -119,6 +126,10 @@ Here are ALL the available options for new instance of RestGraph.
|
|
119
126
|
# auto_authorize. That way, RailsUtil would do redirect instead
|
120
127
|
# of raising an exception.
|
121
128
|
|
129
|
+
:log_method => method(:puts),
|
130
|
+
# This way, any log message would be output by puts. If you want to
|
131
|
+
# change the log format, use log_handler instead. See below:
|
132
|
+
|
122
133
|
:log_handler => lambda{ |event|
|
123
134
|
Rails.logger.
|
124
135
|
debug("Spent #{event.duration} requesting #{event.url}")})
|
@@ -146,7 +157,7 @@ options for RestGraph instance are also valid options for rest_graph_setup.
|
|
146
157
|
# This means if the access_token is not there,
|
147
158
|
# then do auto_authorize.
|
148
159
|
|
149
|
-
:write_session =>
|
160
|
+
:write_session => true , # default false
|
150
161
|
:write_cookies => false , # default false
|
151
162
|
:write_handler =>
|
152
163
|
lambda{ |fbs| @cache[uid] = fbs } , # default nil
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= rest-graph 1.
|
1
|
+
= rest-graph 1.6.0
|
2
2
|
by Cardinal Blue ( http://cardinalblue.com )
|
3
3
|
|
4
4
|
== LINKS:
|
@@ -20,12 +20,19 @@ A super simple Facebook Open Graph API client
|
|
20
20
|
|
21
21
|
== REQUIREMENTS:
|
22
22
|
|
23
|
-
* Tested with MRI 1.8.7 and 1.9.2 and Rubinius 1.1.
|
24
|
-
|
25
|
-
*
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
* Tested with MRI 1.8.7 and 1.9.2 and Rubinius 1.1.1
|
24
|
+
|
25
|
+
* (must) pick one HTTP client:
|
26
|
+
- gem install rest-client
|
27
|
+
- gem install em-http-request
|
28
|
+
|
29
|
+
* (optional) pick one JSON parser/generator:
|
30
|
+
- gem install yajl-ruby
|
31
|
+
- gem install json
|
32
|
+
- gem install json_pure
|
33
|
+
|
34
|
+
* (optional) parse access_token in HTTP_COOKIE
|
35
|
+
- gem install rack
|
29
36
|
|
30
37
|
== INSTALL:
|
31
38
|
|
@@ -77,7 +84,7 @@ adds the following two methods to your Controllers:
|
|
77
84
|
New RestGraph objects can read their default setup configuration from a
|
78
85
|
YAML configuration file.
|
79
86
|
|
80
|
-
* {
|
87
|
+
* {Example}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
|
81
88
|
|
82
89
|
To enable, just require anywhere:
|
83
90
|
|
@@ -119,6 +126,10 @@ Here are ALL the available options for new instance of RestGraph.
|
|
119
126
|
# auto_authorize. That way, RailsUtil would do redirect instead
|
120
127
|
# of raising an exception.
|
121
128
|
|
129
|
+
:log_method => method(:puts),
|
130
|
+
# This way, any log message would be output by puts. If you want to
|
131
|
+
# change the log format, use log_handler instead. See below:
|
132
|
+
|
122
133
|
:log_handler => lambda{ |event|
|
123
134
|
Rails.logger.
|
124
135
|
debug("Spent #{event.duration} requesting #{event.url}")})
|
@@ -146,7 +157,7 @@ options for RestGraph instance are also valid options for rest_graph_setup.
|
|
146
157
|
# This means if the access_token is not there,
|
147
158
|
# then do auto_authorize.
|
148
159
|
|
149
|
-
:write_session =>
|
160
|
+
:write_session => true , # default false
|
150
161
|
:write_cookies => false , # default false
|
151
162
|
:write_handler =>
|
152
163
|
lambda{ |fbs| @cache[uid] = fbs } , # default nil
|
data/Rakefile
CHANGED
data/TODO
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
id = '4'
|
3
|
+
times = 10
|
4
|
+
|
5
|
+
require 'async-rack'
|
6
|
+
require 'rest-graph'
|
7
|
+
|
8
|
+
use Rack::ContentType
|
9
|
+
use Rack::Reloader
|
10
|
+
|
11
|
+
module RG
|
12
|
+
module_function
|
13
|
+
def create env
|
14
|
+
RestGraph.new(:log_method => env['rack.logger'].method(:debug))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
run Rack::Builder.new{
|
19
|
+
map('/async'){
|
20
|
+
run lambda{ |env|
|
21
|
+
RG.create(env).multi([[:get, id]] * times){ |r|
|
22
|
+
env['async.callback'].call [200, {}, r.map(&:inspect)]
|
23
|
+
}
|
24
|
+
throw :async
|
25
|
+
}
|
26
|
+
}
|
27
|
+
map('/sync'){
|
28
|
+
run lambda{ |env|
|
29
|
+
[200, {}, (0...times).map{ RG.create(env).get(id) }.map(&:inspect)]
|
30
|
+
}
|
31
|
+
}
|
32
|
+
map('/'){
|
33
|
+
run lambda{ |env|
|
34
|
+
[200, {'Content-Type' => 'text/html'},
|
35
|
+
[<<-HTML
|
36
|
+
<html><body>
|
37
|
+
go to <a href="/async">/async</a> for em-http-request (multi) result,<br/>
|
38
|
+
go to <a href="/sync">/sync</a> for rest-client result.<br/>
|
39
|
+
</body></html>
|
40
|
+
HTML
|
41
|
+
]]
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
@@ -5,7 +5,7 @@ require 'webmock'
|
|
5
5
|
WebMock.disable_net_connect!
|
6
6
|
|
7
7
|
class ApplicationControllerTest < ActionController::TestCase
|
8
|
-
include WebMock
|
8
|
+
include WebMock::API
|
9
9
|
|
10
10
|
def setup
|
11
11
|
body = rand(2) == 0 ? '{"error":{"type":"OAuthException"}}' :
|
@@ -16,7 +16,7 @@ class ApplicationControllerTest < ActionController::TestCase
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def teardown
|
19
|
-
reset_webmock
|
19
|
+
WebMock.reset_webmock
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_index
|
@@ -87,7 +87,7 @@ class ApplicationControllerTest < ActionController::TestCase
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def test_cache
|
90
|
-
reset_webmock
|
90
|
+
WebMock.reset_webmock
|
91
91
|
stub_request(:get, 'https://graph.facebook.com/cache').
|
92
92
|
to_return(:body => '{"message":"ok"}')
|
93
93
|
|
@@ -97,7 +97,7 @@ class ApplicationControllerTest < ActionController::TestCase
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def test_handler
|
100
|
-
reset_webmock
|
100
|
+
WebMock.reset_webmock
|
101
101
|
stub_request(:get, 'https://graph.facebook.com/me?access_token=aloha').
|
102
102
|
to_return(:body => '["snowman"]')
|
103
103
|
|
@@ -110,7 +110,7 @@ class ApplicationControllerTest < ActionController::TestCase
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def test_session
|
113
|
-
reset_webmock
|
113
|
+
WebMock.reset_webmock
|
114
114
|
stub_request(:get, 'https://graph.facebook.com/me?access_token=wozilla').
|
115
115
|
to_return(:body => '["fireball"]')
|
116
116
|
|
@@ -123,7 +123,7 @@ class ApplicationControllerTest < ActionController::TestCase
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def test_cookies
|
126
|
-
reset_webmock
|
126
|
+
WebMock.reset_webmock
|
127
127
|
stub_request(:get, 'https://graph.facebook.com/me?access_token=blizzard').
|
128
128
|
to_return(:body => '["yeti"]')
|
129
129
|
|
data/lib/rest-graph.rb
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
|
2
|
-
#
|
3
|
-
require '
|
2
|
+
# optional http client
|
3
|
+
begin; require 'restclient' ; rescue LoadError; end
|
4
|
+
begin; require 'em-http-request'; rescue LoadError; end
|
5
|
+
|
6
|
+
# optional gem
|
7
|
+
begin; require 'rack' ; rescue LoadError; end
|
4
8
|
|
5
9
|
# stdlib
|
6
10
|
require 'digest/md5'
|
7
11
|
require 'openssl'
|
8
12
|
|
9
13
|
require 'cgi'
|
10
|
-
|
11
|
-
# optional gem
|
12
|
-
begin
|
13
|
-
require 'rack'
|
14
|
-
rescue LoadError; end
|
14
|
+
require 'timeout'
|
15
15
|
|
16
16
|
# the data structure used in RestGraph
|
17
|
-
RestGraphStruct = Struct.new(:auto_decode, :strict,
|
17
|
+
RestGraphStruct = Struct.new(:auto_decode, :strict, :timeout,
|
18
18
|
:graph_server, :old_server,
|
19
19
|
:accept, :lang,
|
20
20
|
:app_id, :secret,
|
21
21
|
:data, :cache,
|
22
|
-
:
|
23
|
-
:log_handler
|
22
|
+
:log_method,
|
23
|
+
:log_handler,
|
24
|
+
:error_handler) unless defined?(RestGraphStruct)
|
24
25
|
|
25
26
|
class RestGraph < RestGraphStruct
|
26
27
|
EventStruct = Struct.new(:duration, :url) unless
|
@@ -29,31 +30,37 @@ class RestGraph < RestGraphStruct
|
|
29
30
|
Attributes = RestGraphStruct.members.map(&:to_sym) unless
|
30
31
|
defined?(::RestGraph::Attributes)
|
31
32
|
|
32
|
-
class Event < EventStruct
|
33
|
+
class Event < EventStruct
|
34
|
+
# self.class.name[/(?<=::)\w+$/] if RUBY_VERSION >= '1.9.2'
|
35
|
+
def name; self.class.name[/::\w+$/].tr(':', ''); end
|
36
|
+
def to_s; "RestGraph: spent #{sprintf('%f', duration)} #{name} #{url}";end
|
37
|
+
end
|
38
|
+
class Event::MultiDone < Event; end
|
33
39
|
class Event::Requested < Event; end
|
34
40
|
class Event::CacheHit < Event; end
|
41
|
+
class Event::Failed < Event; end
|
35
42
|
|
36
43
|
class Error < RuntimeError
|
37
44
|
class AccessToken < Error; end
|
38
45
|
class InvalidAccessToken < AccessToken; end
|
39
46
|
class MissingAccessToken < AccessToken; end
|
40
47
|
|
41
|
-
attr_reader :error
|
42
|
-
def initialize error
|
43
|
-
@error = error
|
44
|
-
super(error.inspect)
|
48
|
+
attr_reader :error, :url
|
49
|
+
def initialize error, url=''
|
50
|
+
@error, @url = error, url
|
51
|
+
super("#{error.inspect} from #{url}")
|
45
52
|
end
|
46
53
|
|
47
54
|
module Util
|
48
55
|
extend self
|
49
|
-
def parse error
|
50
|
-
return Error.new(error) unless error.kind_of?(Hash)
|
56
|
+
def parse error, url=''
|
57
|
+
return Error.new(error, url) unless error.kind_of?(Hash)
|
51
58
|
if invalid_token?(error)
|
52
|
-
InvalidAccessToken.new(error)
|
59
|
+
InvalidAccessToken.new(error, url)
|
53
60
|
elsif missing_token?(error)
|
54
|
-
MissingAccessToken.new(error)
|
61
|
+
MissingAccessToken.new(error, url)
|
55
62
|
else
|
56
|
-
Error.new(error)
|
63
|
+
Error.new(error, url)
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
@@ -85,6 +92,7 @@ class RestGraph < RestGraphStruct
|
|
85
92
|
extend self
|
86
93
|
def default_auto_decode ; true ; end
|
87
94
|
def default_strict ; false ; end
|
95
|
+
def default_timeout ; 10 ; end
|
88
96
|
def default_graph_server; 'https://graph.facebook.com/'; end
|
89
97
|
def default_old_server ; 'https://api.facebook.com/' ; end
|
90
98
|
def default_accept ; 'text/javascript' ; end
|
@@ -93,11 +101,10 @@ class RestGraph < RestGraphStruct
|
|
93
101
|
def default_secret ; nil ; end
|
94
102
|
def default_data ; {} ; end
|
95
103
|
def default_cache ; nil ; end
|
104
|
+
def default_log_method ; nil ; end
|
105
|
+
def default_log_handler ; nil ; end
|
96
106
|
def default_error_handler
|
97
|
-
lambda{ |error| raise ::RestGraph::Error.parse(error) }
|
98
|
-
end
|
99
|
-
def default_log_handler
|
100
|
-
lambda{ |event| }
|
107
|
+
lambda{ |error, url| raise ::RestGraph::Error.parse(error, url) }
|
101
108
|
end
|
102
109
|
end
|
103
110
|
extend DefaultAttributes
|
@@ -208,7 +215,8 @@ class RestGraph < RestGraphStruct
|
|
208
215
|
end
|
209
216
|
|
210
217
|
def lighten!
|
211
|
-
[:cache, :
|
218
|
+
[:cache, :log_method, :log_handler, :error_handler].each{ |obj|
|
219
|
+
send("#{obj}=", nil) }
|
212
220
|
self
|
213
221
|
end
|
214
222
|
|
@@ -217,7 +225,7 @@ class RestGraph < RestGraphStruct
|
|
217
225
|
end
|
218
226
|
|
219
227
|
def inspect
|
220
|
-
super.gsub(/(\w+)=([
|
228
|
+
super.gsub(/(\w+)=([^,>]+)/){ |match|
|
221
229
|
value = $2 == 'nil' ? self.class.send("default_#{$1}").inspect : $2
|
222
230
|
"#{$1}=#{value}"
|
223
231
|
}
|
@@ -233,38 +241,76 @@ class RestGraph < RestGraphStruct
|
|
233
241
|
"#{server}#{path}#{build_query_string(query)}"
|
234
242
|
end
|
235
243
|
|
236
|
-
def get path, query={}, opts={}
|
237
|
-
request(:get , url(path, query, graph_server),
|
244
|
+
def get path, query={}, opts={}, &cb
|
245
|
+
request(opts, [:get , url(path, query, graph_server)], &cb)
|
246
|
+
end
|
247
|
+
|
248
|
+
def delete path, query={}, opts={}, &cb
|
249
|
+
request(opts, [:delete, url(path, query, graph_server)], &cb)
|
250
|
+
end
|
251
|
+
|
252
|
+
def post path, payload={}, query={}, opts={}, &cb
|
253
|
+
request(opts, [:post , url(path, query, graph_server), payload], &cb)
|
254
|
+
end
|
255
|
+
|
256
|
+
def put path, payload={}, query={}, opts={}, &cb
|
257
|
+
request(opts, [:put , url(path, query, graph_server), payload], &cb)
|
258
|
+
end
|
259
|
+
|
260
|
+
# request by eventmachine (em-http)
|
261
|
+
|
262
|
+
def aget path, query={}, opts={}, &cb
|
263
|
+
get(path, query, {:async => true}.merge(opts), &cb)
|
264
|
+
end
|
265
|
+
|
266
|
+
def adelete path, query={}, opts={}, &cb
|
267
|
+
delete(path, query, {:async => true}.merge(opts), &cb)
|
238
268
|
end
|
239
269
|
|
240
|
-
def
|
241
|
-
|
270
|
+
def apost path, payload={}, query={}, opts={}, &cb
|
271
|
+
post(path, payload, query, {:async => true}.merge(opts), &cb)
|
242
272
|
end
|
243
273
|
|
244
|
-
def
|
245
|
-
|
274
|
+
def aput path, payload={}, query={}, opts={}, &cb
|
275
|
+
put(path, payload, query, {:async => true}.merge(opts), &cb)
|
246
276
|
end
|
247
277
|
|
248
|
-
def
|
249
|
-
request(:
|
278
|
+
def multi reqs, opts={}, &cb
|
279
|
+
request({:async => true}.merge(opts),
|
280
|
+
*reqs.map{ |(meth, path, query, payload)|
|
281
|
+
[meth, url(path, query || {}, graph_server), payload]
|
282
|
+
}, &cb)
|
250
283
|
end
|
251
284
|
|
252
|
-
|
253
|
-
|
254
|
-
|
285
|
+
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
def next_page hash, opts={}, &cb
|
290
|
+
if hash['paging'].kind_of?(Hash) && hash['paging']['next']
|
291
|
+
request(opts, [:get, hash['paging']['next']], &cb)
|
292
|
+
else
|
293
|
+
yield(nil) if block_given?
|
294
|
+
end
|
255
295
|
end
|
256
296
|
|
257
|
-
def prev_page hash, opts={}
|
258
|
-
|
259
|
-
|
297
|
+
def prev_page hash, opts={}, &cb
|
298
|
+
if hash['paging'].kind_of?(Hash) && hash['paging']['previous']
|
299
|
+
request(opts, [:get, hash['paging']['previous']], &cb)
|
300
|
+
else
|
301
|
+
yield(nil) if block_given?
|
302
|
+
end
|
260
303
|
end
|
261
304
|
alias_method :previous_page, :prev_page
|
262
305
|
|
263
|
-
def for_pages hash, pages=1, kind=:next_page,
|
264
|
-
|
265
|
-
|
266
|
-
|
306
|
+
def for_pages hash, pages=1, opts={}, kind=:next_page, &cb
|
307
|
+
if pages > 1
|
308
|
+
merge_data(send(kind, hash, opts){ |result|
|
309
|
+
yield(result.freeze) if block_given?
|
310
|
+
for_pages(result, pages - 1, opts, kind, &cb) if result
|
311
|
+
}, hash)
|
267
312
|
else
|
313
|
+
yield(nil) if block_given?
|
268
314
|
hash
|
269
315
|
end
|
270
316
|
end
|
@@ -326,8 +372,8 @@ class RestGraph < RestGraphStruct
|
|
326
372
|
def authorize! opts={}
|
327
373
|
query = {:client_id => app_id, :client_secret => secret}.merge(opts)
|
328
374
|
self.data = Rack::Utils.parse_query(
|
329
|
-
request(:
|
330
|
-
:
|
375
|
+
request({:suppress_decode => true}.merge(opts),
|
376
|
+
[:get, url('oauth/access_token', query)]))
|
331
377
|
end
|
332
378
|
|
333
379
|
|
@@ -336,31 +382,33 @@ class RestGraph < RestGraphStruct
|
|
336
382
|
|
337
383
|
# old rest facebook api, i will definitely love to remove them someday
|
338
384
|
|
339
|
-
def old_rest path, query={}, opts={}
|
385
|
+
def old_rest path, query={}, opts={}, &cb
|
340
386
|
request(
|
341
|
-
|
342
|
-
|
343
|
-
|
387
|
+
opts,
|
388
|
+
[:get,
|
389
|
+
url("method/#{path}", {:format => 'json'}.merge(query), old_server)],
|
390
|
+
&cb)
|
344
391
|
end
|
345
392
|
|
346
|
-
def secret_old_rest path, query={}, opts={}
|
347
|
-
old_rest(path, {:access_token => secret_access_token}.merge(query), opts
|
393
|
+
def secret_old_rest path, query={}, opts={}, &cb
|
394
|
+
old_rest(path, {:access_token => secret_access_token}.merge(query), opts,
|
395
|
+
&cb)
|
348
396
|
end
|
349
397
|
alias_method :broken_old_rest, :secret_old_rest
|
350
398
|
|
351
|
-
def exchange_sessions opts={}
|
352
|
-
|
353
|
-
|
354
|
-
request(:post, url('oauth/exchange_sessions',
|
399
|
+
def exchange_sessions query={}, opts={}, &cb
|
400
|
+
q = {:client_id => app_id, :client_secret => secret,
|
401
|
+
:type => 'client_cred'}.merge(query)
|
402
|
+
request(opts, [:post, url('oauth/exchange_sessions', q)], &cb)
|
355
403
|
end
|
356
404
|
|
357
|
-
def fql code, query={}, opts={}
|
358
|
-
old_rest('fql.query', {:query => code}.merge(query), opts)
|
405
|
+
def fql code, query={}, opts={}, &cb
|
406
|
+
old_rest('fql.query', {:query => code}.merge(query), opts, &cb)
|
359
407
|
end
|
360
408
|
|
361
|
-
def fql_multi codes, query={}, opts={}
|
409
|
+
def fql_multi codes, query={}, opts={}, &cb
|
362
410
|
old_rest('fql.multiquery',
|
363
|
-
{:queries => self.class.json_encode(codes)}.merge(query), opts)
|
411
|
+
{:queries => self.class.json_encode(codes)}.merge(query), opts, &cb)
|
364
412
|
end
|
365
413
|
|
366
414
|
|
@@ -368,13 +416,61 @@ class RestGraph < RestGraphStruct
|
|
368
416
|
|
369
417
|
|
370
418
|
private
|
371
|
-
def request
|
419
|
+
def request opts, *reqs, &cb
|
420
|
+
Timeout.timeout(timeout){
|
421
|
+
if opts[:async]
|
422
|
+
request_em(opts, reqs, &cb)
|
423
|
+
else
|
424
|
+
request_rc(opts, *reqs.first, &cb)
|
425
|
+
end
|
426
|
+
}
|
427
|
+
end
|
428
|
+
|
429
|
+
def request_em opts, reqs
|
430
|
+
start_time = Time.now
|
431
|
+
rs = reqs.map{ |(meth, uri, payload)|
|
432
|
+
r = EM::HttpRequest.new(uri).send(meth, :body => payload)
|
433
|
+
if cached = cache_get(uri)
|
434
|
+
# TODO: this is hack!!
|
435
|
+
r.instance_variable_set('@response', cached)
|
436
|
+
r.instance_variable_set('@state' , :finish)
|
437
|
+
r.on_request_complete
|
438
|
+
r.succeed(r)
|
439
|
+
else
|
440
|
+
r.callback{
|
441
|
+
cache_for(uri, r.response, meth)
|
442
|
+
log(Event::Requested.new(Time.now - start_time, uri))
|
443
|
+
}
|
444
|
+
r.error{
|
445
|
+
log(Event::Failed.new(Time.now - start_time, uri))
|
446
|
+
}
|
447
|
+
end
|
448
|
+
r
|
449
|
+
}
|
450
|
+
EM::MultiRequest.new(rs){ |m|
|
451
|
+
# TODO: how to deal with the failed?
|
452
|
+
clients = m.responses[:succeeded]
|
453
|
+
results = clients.map{ |client|
|
454
|
+
post_request(client.response, client.uri)
|
455
|
+
}
|
456
|
+
|
457
|
+
if reqs.size == 1
|
458
|
+
yield(results.first)
|
459
|
+
else
|
460
|
+
log(Event::MultiDone.new(Time.now - start_time,
|
461
|
+
clients.map(&:uri).join(', ')))
|
462
|
+
yield(results)
|
463
|
+
end
|
464
|
+
}
|
465
|
+
end
|
466
|
+
|
467
|
+
def request_rc opts, meth, uri, payload=nil, &cb
|
372
468
|
start_time = Time.now
|
373
|
-
post_request(cache_get(uri) || fetch(meth, uri, payload), opts)
|
469
|
+
post_request(cache_get(uri) || fetch(meth, uri, payload), uri, opts, &cb)
|
374
470
|
rescue RestClient::Exception => e
|
375
|
-
post_request(e.http_body, opts)
|
471
|
+
post_request(e.http_body, uri, opts, &cb)
|
376
472
|
ensure
|
377
|
-
|
473
|
+
log(Event::Requested.new(Time.now - start_time, uri))
|
378
474
|
end
|
379
475
|
|
380
476
|
def build_query_string query={}
|
@@ -391,30 +487,32 @@ class RestGraph < RestGraphStruct
|
|
391
487
|
headers
|
392
488
|
end
|
393
489
|
|
394
|
-
def post_request result, opts={}
|
490
|
+
def post_request result, uri='', opts={}, &cb
|
395
491
|
if auto_decode && !opts[:suppress_decode]
|
396
492
|
decoded = self.class.json_decode("[#{result}]").first
|
397
493
|
check_error(if strict || !decoded.kind_of?(String)
|
398
494
|
decoded
|
399
495
|
else
|
400
496
|
self.class.json_decode(decoded)
|
401
|
-
end)
|
497
|
+
end, uri, &cb)
|
402
498
|
else
|
403
|
-
result
|
499
|
+
block_given? ? yield(result) : result
|
404
500
|
end
|
501
|
+
rescue ParseError => error
|
502
|
+
error_handler.call(error, uri) if error_handler
|
405
503
|
end
|
406
504
|
|
407
505
|
def check_sig_and_return_data cookies
|
408
506
|
cookies if secret && calculate_sig(cookies) == cookies['sig']
|
409
507
|
end
|
410
508
|
|
411
|
-
def check_error hash
|
509
|
+
def check_error hash, uri
|
412
510
|
if error_handler && hash.kind_of?(Hash) &&
|
413
511
|
(hash['error'] || # from graph api
|
414
512
|
hash['error_code']) # from fql
|
415
|
-
error_handler.call(hash)
|
513
|
+
error_handler.call(hash, uri)
|
416
514
|
else
|
417
|
-
hash
|
515
|
+
block_given? ? yield(hash) : hash
|
418
516
|
end
|
419
517
|
end
|
420
518
|
|
@@ -434,18 +532,19 @@ class RestGraph < RestGraphStruct
|
|
434
532
|
return unless cache
|
435
533
|
start_time = Time.now
|
436
534
|
cache[cache_key(uri)].tap{ |result|
|
437
|
-
|
438
|
-
result
|
535
|
+
log(Event::CacheHit.new(Time.now - start_time, uri)) if result
|
439
536
|
}
|
440
537
|
end
|
441
538
|
|
539
|
+
def cache_for uri, result, meth
|
540
|
+
cache[cache_key(uri)] = result if cache && meth == :get
|
541
|
+
end
|
542
|
+
|
442
543
|
def fetch meth, uri, payload
|
443
544
|
RestClient::Request.execute(:method => meth, :url => uri,
|
444
545
|
:headers => build_headers,
|
445
546
|
:payload => payload).body.
|
446
|
-
tap{ |result|
|
447
|
-
cache[cache_key(uri)] = result if cache && meth == :get
|
448
|
-
}
|
547
|
+
tap{ |result| cache_for(uri, result, meth) }
|
449
548
|
end
|
450
549
|
|
451
550
|
def merge_data lhs, rhs
|
@@ -456,4 +555,9 @@ class RestGraph < RestGraphStruct
|
|
456
555
|
lhs['data'].unshift(*rhs['data'])
|
457
556
|
lhs
|
458
557
|
end
|
558
|
+
|
559
|
+
def log event
|
560
|
+
log_handler.call(event) if log_handler
|
561
|
+
log_method .call("DEBUG: #{event}") if log_method
|
562
|
+
end
|
459
563
|
end
|