rest-graph 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = rest-graph 1.5.0
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.0
24
- * gem install rest-client
25
- * gem install yajl-ruby (optional)
26
- * gem install json (optional)
27
- * gem install json_pure (optional)
28
- * gem install rack (optional, to parse access_token in HTTP_COOKIE)
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
- * {Sample}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
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 => false , # default false
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.5.0
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.0
24
- * gem install rest-client
25
- * gem install yajl-ruby (optional)
26
- * gem install json (optional)
27
- * gem install json_pure (optional)
28
- * gem install rack (optional, to parse access_token in HTTP_COOKIE)
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
- * {Sample}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
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 => false , # default false
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
@@ -15,7 +15,8 @@ Bones{
15
15
 
16
16
  version RestGraph::VERSION
17
17
 
18
- depend_on 'rest-client'
18
+ depend_on 'rest-client' , :development => true
19
+ depend_on 'em-http-request', :development => true
19
20
 
20
21
  depend_on 'rack' , :development => true
21
22
 
data/TODO CHANGED
@@ -1,5 +1,7 @@
1
1
  = rest-graph todo list
2
2
 
3
+ * error_handler can't be turned off
4
+
3
5
  * test for rails util for writing cookie
4
6
 
5
7
  * more docs?
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ Rainbows! do
4
+ use :EventMachine
5
+ end
@@ -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
- # gem
3
- require 'rest_client'
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
- :error_handler,
23
- :log_handler) unless defined?(::RestGraphStruct)
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; end
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, :error_handler, :log_handler].each{ |obj| send("#{obj}=", nil) }
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+)=([^,]+)/){ |match|
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), opts)
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 delete path, query={}, opts={}
241
- request(:delete, url(path, query, graph_server), opts)
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 post path, payload, query={}, opts={}
245
- request(:post , url(path, query, graph_server), opts, payload)
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 put path, payload, query={}, opts={}
249
- request(:put , url(path, query, graph_server), opts, payload)
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
- def next_page hash, opts={}
253
- return unless hash['paging'].kind_of?(Hash) && hash['paging']['next']
254
- request(:get , hash['paging']['next'] , opts)
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
- return unless hash['paging'].kind_of?(Hash) && hash['paging']['previous']
259
- request(:get , hash['paging']['previous'] , opts)
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, opts={}
264
- return hash if pages <= 1
265
- if result = send(kind, hash, opts)
266
- for_pages(merge_data(result, hash), pages - 1, kind, opts)
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(:get, url('oauth/access_token', query),
330
- :suppress_decode => true))
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
- :get,
342
- url("method/#{path}", {:format => 'json'}.merge(query), old_server),
343
- opts)
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
- query = {:client_id => app_id, :client_secret => secret,
353
- :type => 'client_cred'}.merge(opts)
354
- request(:post, url('oauth/exchange_sessions', query))
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 meth, uri, opts={}, payload=nil
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
- log_handler.call(Event::Requested.new(Time.now - start_time, uri))
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
- log_handler.call(Event::CacheHit.new(Time.now - start_time, uri)) if
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