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/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