rest-graph 1.4.6 → 1.5.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.
Files changed (40) hide show
  1. data/CHANGES +67 -0
  2. data/CONTRIBUTORS +1 -0
  3. data/Gemfile +9 -1
  4. data/Gemfile.lock +13 -3
  5. data/README +10 -3
  6. data/README.rdoc +10 -3
  7. data/Rakefile +5 -2
  8. data/example/{rails → rails2}/README +0 -0
  9. data/example/{rails → rails2}/Rakefile +0 -0
  10. data/example/{rails → rails2}/app/controllers/application_controller.rb +4 -0
  11. data/example/{rails → rails2}/config/boot.rb +6 -2
  12. data/example/{rails → rails2}/config/environment.rb +2 -2
  13. data/example/{rails → rails2}/config/environments/development.rb +0 -0
  14. data/example/{rails → rails2}/config/environments/production.rb +0 -0
  15. data/example/{rails → rails2}/config/environments/test.rb +0 -0
  16. data/example/{rails → rails2}/config/initializers/cookie_verification_secret.rb +0 -0
  17. data/example/{rails → rails2}/config/initializers/new_rails_defaults.rb +0 -0
  18. data/example/{rails → rails2}/config/initializers/session_store.rb +1 -1
  19. data/example/{rails → rails2}/config/rest-graph.yaml +0 -0
  20. data/example/{rails → rails2}/config/routes.rb +0 -0
  21. data/example/{rails → rails2}/log +0 -0
  22. data/example/{rails → rails2}/script/console +0 -0
  23. data/example/{rails → rails2}/script/server +0 -0
  24. data/example/{rails → rails2}/test/functional/application_controller_test.rb +10 -1
  25. data/example/{rails → rails2}/test/test_helper.rb +0 -0
  26. data/example/{rails → rails2}/test/unit/rails_util_test.rb +0 -0
  27. data/lib/rest-graph.rb +150 -49
  28. data/lib/rest-graph/rails_util.rb +31 -27
  29. data/lib/rest-graph/version.rb +1 -1
  30. data/rest-graph.gemspec +16 -13
  31. data/test/test_api.rb +1 -1
  32. data/test/test_error.rb +51 -0
  33. data/test/test_handler.rb +4 -2
  34. data/test/test_misc.rb +29 -0
  35. data/test/test_old.rb +33 -0
  36. data/test/test_page.rb +54 -0
  37. data/test/test_parse.rb +7 -0
  38. data/test/test_rest-graph.rb +10 -0
  39. metadata +69 -50
  40. data/test/test_access_token.rb +0 -26
data/CHANGES CHANGED
@@ -1,5 +1,72 @@
1
1
  = rest-graph changes history
2
2
 
3
+ == rest-graph 1.5.0 -- 2010-10-11
4
+
5
+ * [RestGraph] Make sure RestGraph::Error#message is string, that way,
6
+ irb could print out error message correctly. Introduced
7
+ RestGraph::Error#error for original error hash. Thanks Bluce.
8
+
9
+ * [RestGraph] Make RestGraph#inspect honor default attributes, see:
10
+ http://groups.google.com/group/rest-graph/browse_thread/thread/7ad5c81fbb0334e8
11
+
12
+ * [RestGraph] Introduced RestGraph::Error::AccessToken,
13
+ RestGraph::Error::InvalidAccessToken,
14
+ RestGraph::Error::MissingAccessToken.
15
+ RestGraph::Error::AccessToken is the parent of the others,
16
+ and RestGraph::Error is the parent of all above.
17
+
18
+ * [RestGraph] Add RestGraph#next_page and RestGraph#prev_page.
19
+ To get next page for a result from Facebook, example:
20
+
21
+ rg.next_page(rg.get('me/feed'))
22
+
23
+ * [RestGraph] Add RestGraph#for_pages that would crawl from page 1 to
24
+ a number of pages you specified. For example, this might
25
+ crawl down all the feeds:
26
+
27
+ rg.for_pages(rg.get('me/feed'), 1000)
28
+
29
+ * [RestGraph] Added RestGraph#secret_old_rest, see:
30
+ http://www.nivas.hr/blog/2010/09/03/facebook-php-sdk-access-token-signing-bug/
31
+
32
+ If you're getting this error from calling old_rest:
33
+
34
+ The method you are calling or the FQL table you are querying cannot be
35
+ called using a session secret or by a desktop application.
36
+
37
+ Then try secret_old_rest instead. The problem is that the access_token
38
+ should be formatted by "#{app_id}|#{secret}" instead of the usual one.
39
+
40
+ * [RestGraph] Added RestGraph#strict.
41
+ In some API, e.g. admin.getAppProperties, Facebook returns
42
+ broken JSON, which is double encoded, and is not a well-formed
43
+ JSON. That case, we'll need to double parse the JSON to get
44
+ the correct result. For example, Facebook might return this:
45
+
46
+ "{\"app_id\":\"123\"}"
47
+
48
+ instead of the correct one:
49
+
50
+ {"app_id":"123"}
51
+
52
+ P.S. This doesn't matter for people who don't use :auto_decode.
53
+ So under non-strict mode, which is the default, rest-graph
54
+ would handle this for you.
55
+
56
+ * [RestGraph] Fallback to ruby-hmac gem if we have an old openssl lib when
57
+ parsing signed_request which requires HMAC SHA256.
58
+ (e.g. Mac OS 10.5) Thanks Barnabas Debreczeni!
59
+
60
+ * [RailsUtil] Only rescue RestGraph::Error::AccessToken in controller,
61
+ make other errors raise through.
62
+
63
+ * [RailsUtil] Remove bad fbs in cookies when doing new authorization.
64
+ * [RailsUtil] Make signed_request and session higher priority than
65
+ fbs in cookies. Since Facebook is not deleting bad fbs,
66
+ I guess? Thanks Barnabas Debreczeni!
67
+
68
+ * [RailsUtil] URI.encode before URI.parse for broken URL Facebook passed.
69
+
3
70
  == rest-graph 1.4.6 -- 2010-09-01
4
71
 
5
72
  * [RestGraph] Now it will try to pick yajl-ruby or json gem from memory first,
@@ -2,6 +2,7 @@ Lin Jen-Shin (godfat)
2
2
  Jaime Cham (jcham)
3
3
  Andrew Liu (eggegg)
4
4
  John Fan (johnfan)
5
+ Barnabas Debreczeni (keo)
5
6
  Florent Vaucelle (florent)
6
7
  Ethan Czahor (ethanz5)
7
8
  Nicolas Fouché (nfo)
data/Gemfile CHANGED
@@ -4,11 +4,19 @@ source 'http://rubygems.org'
4
4
  gem 'rest-client'
5
5
 
6
6
  group :test do
7
+ # optional
8
+ gem 'rack'
9
+
10
+ # optional, pick one json backend
7
11
  gem 'yajl-ruby'
8
12
  gem 'json'
9
13
  gem 'json_pure'
10
14
 
11
- gem 'rack'
15
+ # falls back from openssl
16
+ gem 'ruby-hmac'
17
+
18
+ # for testing
19
+ gem 'bones'
12
20
  gem 'rr'
13
21
  gem 'webmock'
14
22
  gem 'bacon'
@@ -1,30 +1,40 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- addressable (2.2.0)
4
+ addressable (2.2.1)
5
5
  bacon (1.1.0)
6
+ bones (3.4.7)
7
+ little-plugger (>= 1.1.2)
8
+ loquacious (>= 1.6.4)
9
+ rake (>= 0.8.7)
6
10
  crack (0.1.8)
7
11
  json (1.4.6)
8
12
  json_pure (1.4.6)
13
+ little-plugger (1.1.2)
14
+ loquacious (1.7.0)
9
15
  mime-types (1.16)
10
16
  rack (1.2.1)
17
+ rake (0.8.7)
11
18
  rest-client (1.6.1)
12
19
  mime-types (>= 1.16)
13
20
  rr (1.0.0)
14
- webmock (1.3.4)
21
+ ruby-hmac (0.4.0)
22
+ webmock (1.3.5)
15
23
  addressable (>= 2.1.1)
16
24
  crack (>= 0.1.7)
17
- yajl-ruby (0.7.7)
25
+ yajl-ruby (0.7.8)
18
26
 
19
27
  PLATFORMS
20
28
  ruby
21
29
 
22
30
  DEPENDENCIES
23
31
  bacon
32
+ bones
24
33
  json
25
34
  json_pure
26
35
  rack
27
36
  rest-client
28
37
  rr
38
+ ruby-hmac
29
39
  webmock
30
40
  yajl-ruby
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = rest-graph 1.4.6
1
+ = rest-graph 1.5.0
2
2
  by Cardinal Blue ( http://cardinalblue.com )
3
3
 
4
4
  == LINKS:
@@ -20,8 +20,9 @@ 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 HEAD
23
+ * Tested with MRI 1.8.7 and 1.9.2 and Rubinius 1.1.0
24
24
  * gem install rest-client
25
+ * gem install yajl-ruby (optional)
25
26
  * gem install json (optional)
26
27
  * gem install json_pure (optional)
27
28
  * gem install rack (optional, to parse access_token in HTTP_COOKIE)
@@ -97,6 +98,7 @@ Here are ALL the available options for new instance of RestGraph.
97
98
 
98
99
  rg = RestGraph.new(
99
100
  :access_token => TOKEN , # default nil
101
+ :strict => false , # this is the default
100
102
  :graph_server => 'https://graph.facebook.com/', # this is the default
101
103
  :old_server => 'https://api.facebook.com/' , # this is the default
102
104
  :accept => 'text/javascript' , # this is the default
@@ -159,7 +161,7 @@ options for RestGraph instance are also valid options for rest_graph_setup.
159
161
  # site or iframe canvas application, you might want
160
162
  # to just use the Rails (or other framework) session.
161
163
 
162
- === Setup procedures:
164
+ === Alternate ways to setup RestGraph:
163
165
 
164
166
  1. Set upon RestGraph object creation:
165
167
 
@@ -239,6 +241,11 @@ Call functionality from Facebook's old REST API:
239
241
  # if Facebook is not returning a proper JSON
240
242
  # response. Otherwise, this could be omitted.
241
243
 
244
+ # Some Old Rest API requires a special access token with app secret
245
+ # inside of it. For those methods, use secret_old_rest instead of the
246
+ # usual old_rest with common access token.
247
+ rg.secret_old_rest('admin.getAppProperties', :properties => 'app_id')
248
+
242
249
  === Utility Methods:
243
250
 
244
251
  ==== parse_xxxx
@@ -1,4 +1,4 @@
1
- = rest-graph 1.4.6
1
+ = rest-graph 1.5.0
2
2
  by Cardinal Blue ( http://cardinalblue.com )
3
3
 
4
4
  == LINKS:
@@ -20,8 +20,9 @@ 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 HEAD
23
+ * Tested with MRI 1.8.7 and 1.9.2 and Rubinius 1.1.0
24
24
  * gem install rest-client
25
+ * gem install yajl-ruby (optional)
25
26
  * gem install json (optional)
26
27
  * gem install json_pure (optional)
27
28
  * gem install rack (optional, to parse access_token in HTTP_COOKIE)
@@ -97,6 +98,7 @@ Here are ALL the available options for new instance of RestGraph.
97
98
 
98
99
  rg = RestGraph.new(
99
100
  :access_token => TOKEN , # default nil
101
+ :strict => false , # this is the default
100
102
  :graph_server => 'https://graph.facebook.com/', # this is the default
101
103
  :old_server => 'https://api.facebook.com/' , # this is the default
102
104
  :accept => 'text/javascript' , # this is the default
@@ -159,7 +161,7 @@ options for RestGraph instance are also valid options for rest_graph_setup.
159
161
  # site or iframe canvas application, you might want
160
162
  # to just use the Rails (or other framework) session.
161
163
 
162
- === Setup procedures:
164
+ === Alternate ways to setup RestGraph:
163
165
 
164
166
  1. Set upon RestGraph object creation:
165
167
 
@@ -239,6 +241,11 @@ Call functionality from Facebook's old REST API:
239
241
  # if Facebook is not returning a proper JSON
240
242
  # response. Otherwise, this could be omitted.
241
243
 
244
+ # Some Old Rest API requires a special access token with app secret
245
+ # inside of it. For those methods, use secret_old_rest instead of the
246
+ # usual old_rest with common access token.
247
+ rg.secret_old_rest('admin.getAppProperties', :properties => 'app_id')
248
+
242
249
  === Utility Methods:
243
250
 
244
251
  ==== parse_xxxx
data/Rakefile CHANGED
@@ -17,11 +17,14 @@ Bones{
17
17
 
18
18
  depend_on 'rest-client'
19
19
 
20
+ depend_on 'rack' , :development => true
21
+
20
22
  depend_on 'yajl-ruby', :development => true
21
23
  depend_on 'json' , :development => true
22
24
  depend_on 'json_pure', :development => true
23
25
 
24
- depend_on 'rack' , :development => true
26
+ depend_on 'ruby-hmac', :development => true
27
+
25
28
  depend_on 'rr' , :development => true
26
29
  depend_on 'webmock' , :development => true
27
30
  depend_on 'bacon' , :development => true
@@ -47,7 +50,7 @@ end
47
50
 
48
51
  desc 'Run example tests'
49
52
  task 'test:example' => ['gem:install'] do
50
- sh "cd example/rails; #{Gem.ruby} -S rake test"
53
+ sh "cd example/rails2; #{Gem.ruby} -S rake test"
51
54
  end
52
55
 
53
56
  desc 'Run all tests'
File without changes
File without changes
@@ -50,6 +50,10 @@ class ApplicationController < ActionController::Base
50
50
  render :text => Rails.cache.read(Digest::MD5.hexdigest(url))
51
51
  end
52
52
 
53
+ def error
54
+ raise RestGraph::Error.new("don't rescue me")
55
+ end
56
+
53
57
  private
54
58
  def filter_common
55
59
  rest_graph_setup(:auto_authorize => true, :canvas => '')
@@ -62,8 +62,12 @@ module Rails
62
62
  gem 'rails'
63
63
  end
64
64
  rescue Gem::LoadError => load_error
65
- $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
66
- exit 1
65
+ if load_error.message =~ /Could not find RubyGem rails/
66
+ STDERR.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
67
+ exit 1
68
+ else
69
+ raise
70
+ end
67
71
  end
68
72
 
69
73
  class << self
@@ -1,7 +1,7 @@
1
1
  # Be sure to restart your server when you modify this file
2
2
 
3
3
  # Specifies gem version of Rails to use when vendor/rails is not present
4
- RAILS_GEM_VERSION = '2.3.8' unless defined? RAILS_GEM_VERSION
4
+ RAILS_GEM_VERSION = '2.3.9' unless defined? RAILS_GEM_VERSION
5
5
 
6
6
  # Bootstrap the Rails environment, frameworks, and default configuration
7
7
  require File.join(File.dirname(__FILE__), 'boot')
@@ -12,7 +12,7 @@ Rails::Initializer.run do |config|
12
12
  # -- all .rb files in that directory are automatically loaded.
13
13
 
14
14
  # Add additional load paths for your own custom dirs
15
- # config.load_paths += %W( #{RAILS_ROOT}/extras )
15
+ # config.autoload_paths += %W( #{RAILS_ROOT}/extras )
16
16
 
17
17
  # Specify gems that this application depends on and have them installed with rake gems:install
18
18
  # config.gem "bj"
@@ -5,7 +5,7 @@
5
5
  # Make sure the secret is at least 30 characters and all random,
6
6
  # no regular words or you'll be exposed to dictionary attacks.
7
7
  ActionController::Base.session = {
8
- :key => '_rails_session',
8
+ :key => '_rails2_session',
9
9
  :secret => 'c99a19ce0dc4ed1809e32b6b43bd9229c3a504c456230119dd445fdcb63c0ce06b436f1bf1eace27ebbe0da6041ff2b65cbb4ae4beadc3077e3e6ae07ea75118'
10
10
  }
11
11
 
File without changes
File without changes
File without changes
@@ -8,8 +8,11 @@ class ApplicationControllerTest < ActionController::TestCase
8
8
  include WebMock
9
9
 
10
10
  def setup
11
+ body = rand(2) == 0 ? '{"error":{"type":"OAuthException"}}' :
12
+ '{"error_code":104}'
13
+
11
14
  stub_request(:get, 'https://graph.facebook.com/me').
12
- to_return(:body => '{"error":"not authorized"}')
15
+ to_return(:body => body)
13
16
  end
14
17
 
15
18
  def teardown
@@ -131,4 +134,10 @@ class ApplicationControllerTest < ActionController::TestCase
131
134
  assert_response :success
132
135
  assert_equal '["yeti"]', @response.body
133
136
  end
137
+
138
+ def test_error
139
+ get(:error)
140
+ rescue => e
141
+ assert_equal RestGraph::Error, e.class
142
+ end
134
143
  end
@@ -14,15 +14,105 @@ begin
14
14
  rescue LoadError; end
15
15
 
16
16
  # the data structure used in RestGraph
17
- RestGraphStruct = Struct.new(:auto_decode,
17
+ RestGraphStruct = Struct.new(:auto_decode, :strict,
18
18
  :graph_server, :old_server,
19
19
  :accept, :lang,
20
20
  :app_id, :secret,
21
21
  :data, :cache,
22
22
  :error_handler,
23
- :log_handler) unless defined?(RestGraphStruct)
23
+ :log_handler) unless defined?(::RestGraphStruct)
24
24
 
25
25
  class RestGraph < RestGraphStruct
26
+ EventStruct = Struct.new(:duration, :url) unless
27
+ defined?(::RestGraph::EventStruct)
28
+
29
+ Attributes = RestGraphStruct.members.map(&:to_sym) unless
30
+ defined?(::RestGraph::Attributes)
31
+
32
+ class Event < EventStruct; end
33
+ class Event::Requested < Event; end
34
+ class Event::CacheHit < Event; end
35
+
36
+ class Error < RuntimeError
37
+ class AccessToken < Error; end
38
+ class InvalidAccessToken < AccessToken; end
39
+ class MissingAccessToken < AccessToken; end
40
+
41
+ attr_reader :error
42
+ def initialize error
43
+ @error = error
44
+ super(error.inspect)
45
+ end
46
+
47
+ module Util
48
+ extend self
49
+ def parse error
50
+ return Error.new(error) unless error.kind_of?(Hash)
51
+ if invalid_token?(error)
52
+ InvalidAccessToken.new(error)
53
+ elsif missing_token?(error)
54
+ MissingAccessToken.new(error)
55
+ else
56
+ Error.new(error)
57
+ end
58
+ end
59
+
60
+ def invalid_token? error
61
+ (%w[OAuthInvalidTokenException
62
+ OAuthException].include?((error['error'] || {})['type'])) ||
63
+ (error['error_code'] == 190) # Invalid OAuth 2.0 Access Token
64
+ end
65
+
66
+ def missing_token? error
67
+ (error['error'] || {})['message'] =~ /^An active access token/ ||
68
+ (error['error_code'] == 104) # Requires valid signature
69
+ end
70
+ end
71
+ extend Util
72
+ end
73
+
74
+ # honor default attributes
75
+ Attributes.each{ |name|
76
+ module_eval <<-RUBY
77
+ def #{name}
78
+ (r = super).nil? ? (self.#{name} = self.class.default_#{name}) : r
79
+ end
80
+ RUBY
81
+ }
82
+
83
+ # setup defaults
84
+ module DefaultAttributes
85
+ extend self
86
+ def default_auto_decode ; true ; end
87
+ def default_strict ; false ; end
88
+ def default_graph_server; 'https://graph.facebook.com/'; end
89
+ def default_old_server ; 'https://api.facebook.com/' ; end
90
+ def default_accept ; 'text/javascript' ; end
91
+ def default_lang ; 'en-us' ; end
92
+ def default_app_id ; nil ; end
93
+ def default_secret ; nil ; end
94
+ def default_data ; {} ; end
95
+ def default_cache ; nil ; end
96
+ def default_error_handler
97
+ lambda{ |error| raise ::RestGraph::Error.parse(error) }
98
+ end
99
+ def default_log_handler
100
+ lambda{ |event| }
101
+ end
102
+ end
103
+ extend DefaultAttributes
104
+
105
+ # Fallback to ruby-hmac gem in case system openssl
106
+ # lib doesn't support SHA256 (OSX 10.5)
107
+ def self.hmac_sha256 key, data
108
+ # for ruby version >= 1.8.7, we can simply pass sha256,
109
+ # instead of OpenSSL::Digest::Digest.new('sha256')
110
+ # i'll go back to original implementation once all old systems died
111
+ OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, data)
112
+ rescue RuntimeError
113
+ require 'hmac-sha2'
114
+ HMAC::SHA256.digest(key, data)
115
+ end
26
116
 
27
117
  # begin json backend adapter
28
118
  module YajlRuby
@@ -54,7 +144,6 @@ class RestGraph < RestGraphStruct
54
144
  def self.extended mod
55
145
  mod.const_set(:ParseError, Gsub::ParseError)
56
146
  end
57
-
58
147
  # only works for flat hash
59
148
  def json_encode hash
60
149
  middle = hash.inject([]){ |r, (k, v)|
@@ -62,7 +151,7 @@ class RestGraph < RestGraphStruct
62
151
  }.join(',')
63
152
  "{#{middle}}"
64
153
  end
65
- def json_decode
154
+ def json_decode json
66
155
  raise NotImplementedError.new(
67
156
  'You need to install either yajl-ruby, json, or json_pure gem')
68
157
  end
@@ -87,46 +176,9 @@ class RestGraph < RestGraphStruct
87
176
  select_json!(true)
88
177
  end
89
178
  end
90
- select_json!
179
+ select_json! unless respond_to?(:json_decode)
91
180
  # end json backend adapter
92
181
 
93
- class Error < RuntimeError; end
94
- class Event < Struct.new(:duration, :url); end
95
- class Event::Requested < Event; end
96
- class Event::CacheHit < Event; end
97
-
98
- Attributes = RestGraphStruct.members.map(&:to_sym)
99
-
100
- # honor default attributes
101
- Attributes.each{ |name|
102
- module_eval <<-RUBY
103
- def #{name}
104
- (r = super).nil? ? (self.#{name} = self.class.default_#{name}) : r
105
- end
106
- RUBY
107
- }
108
-
109
- # setup defaults
110
- module DefaultAttributes
111
- extend self
112
- def default_auto_decode ; true ; end
113
- def default_graph_server; 'https://graph.facebook.com/'; end
114
- def default_old_server ; 'https://api.facebook.com/' ; end
115
- def default_accept ; 'text/javascript' ; end
116
- def default_lang ; 'en-us' ; end
117
- def default_app_id ; nil ; end
118
- def default_secret ; nil ; end
119
- def default_data ; {} ; end
120
- def default_cache ; nil ; end
121
- def default_error_handler
122
- lambda{ |error| raise ::RestGraph::Error.new(error) }
123
- end
124
- def default_log_handler
125
- lambda{ |event| }
126
- end
127
- end
128
- extend DefaultAttributes
129
-
130
182
 
131
183
 
132
184
 
@@ -151,6 +203,10 @@ class RestGraph < RestGraphStruct
151
203
  !!access_token
152
204
  end
153
205
 
206
+ def secret_access_token
207
+ "#{app_id}|#{secret}"
208
+ end
209
+
154
210
  def lighten!
155
211
  [:cache, :error_handler, :log_handler].each{ |obj| send("#{obj}=", nil) }
156
212
  self
@@ -160,6 +216,13 @@ class RestGraph < RestGraphStruct
160
216
  dup.lighten!
161
217
  end
162
218
 
219
+ def inspect
220
+ super.gsub(/(\w+)=([^,]+)/){ |match|
221
+ value = $2 == 'nil' ? self.class.send("default_#{$1}").inspect : $2
222
+ "#{$1}=#{value}"
223
+ }
224
+ end
225
+
163
226
 
164
227
 
165
228
 
@@ -186,6 +249,26 @@ class RestGraph < RestGraphStruct
186
249
  request(:put , url(path, query, graph_server), opts, payload)
187
250
  end
188
251
 
252
+ def next_page hash, opts={}
253
+ return unless hash['paging'].kind_of?(Hash) && hash['paging']['next']
254
+ request(:get , hash['paging']['next'] , opts)
255
+ end
256
+
257
+ def prev_page hash, opts={}
258
+ return unless hash['paging'].kind_of?(Hash) && hash['paging']['previous']
259
+ request(:get , hash['paging']['previous'] , opts)
260
+ end
261
+ alias_method :previous_page, :prev_page
262
+
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)
267
+ else
268
+ hash
269
+ end
270
+ end
271
+
189
272
 
190
273
 
191
274
 
@@ -225,7 +308,7 @@ class RestGraph < RestGraphStruct
225
308
  "#{str.tr('-_', '+/')}==".unpack('m').first
226
309
  }
227
310
  self.data = self.class.json_decode(json) if
228
- secret && OpenSSL::HMAC.digest('sha256', secret, json_encoded) == sig
311
+ secret && self.class.hmac_sha256(secret, json_encoded) == sig
229
312
  rescue ParseError
230
313
  end
231
314
 
@@ -260,6 +343,11 @@ class RestGraph < RestGraphStruct
260
343
  opts)
261
344
  end
262
345
 
346
+ def secret_old_rest path, query={}, opts={}
347
+ old_rest(path, {:access_token => secret_access_token}.merge(query), opts)
348
+ end
349
+ alias_method :broken_old_rest, :secret_old_rest
350
+
263
351
  def exchange_sessions opts={}
264
352
  query = {:client_id => app_id, :client_secret => secret,
265
353
  :type => 'client_cred'}.merge(opts)
@@ -282,10 +370,9 @@ class RestGraph < RestGraphStruct
282
370
  private
283
371
  def request meth, uri, opts={}, payload=nil
284
372
  start_time = Time.now
285
- post_request(cache_get(uri) || fetch(meth, uri, payload),
286
- opts[:suppress_decode])
373
+ post_request(cache_get(uri) || fetch(meth, uri, payload), opts)
287
374
  rescue RestClient::Exception => e
288
- post_request(e.http_body, opts[:suppress_decode])
375
+ post_request(e.http_body, opts)
289
376
  ensure
290
377
  log_handler.call(Event::Requested.new(Time.now - start_time, uri))
291
378
  end
@@ -304,9 +391,14 @@ class RestGraph < RestGraphStruct
304
391
  headers
305
392
  end
306
393
 
307
- def post_request result, suppress_decode=nil
308
- if auto_decode && !suppress_decode
309
- check_error(self.class.json_decode(result))
394
+ def post_request result, opts={}
395
+ if auto_decode && !opts[:suppress_decode]
396
+ decoded = self.class.json_decode("[#{result}]").first
397
+ check_error(if strict || !decoded.kind_of?(String)
398
+ decoded
399
+ else
400
+ self.class.json_decode(decoded)
401
+ end)
310
402
  else
311
403
  result
312
404
  end
@@ -355,4 +447,13 @@ class RestGraph < RestGraphStruct
355
447
  cache[cache_key(uri)] = result if cache && meth == :get
356
448
  }
357
449
  end
450
+
451
+ def merge_data lhs, rhs
452
+ [lhs, rhs].each{ |hash|
453
+ return rhs.reject{ |k, v| k == 'paging' } if
454
+ !hash.kind_of?(Hash) || !hash['data'].kind_of?(Array)
455
+ }
456
+ lhs['data'].unshift(*rhs['data'])
457
+ lhs
458
+ end
358
459
  end