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.
- data/CHANGES +67 -0
- data/CONTRIBUTORS +1 -0
- data/Gemfile +9 -1
- data/Gemfile.lock +13 -3
- data/README +10 -3
- data/README.rdoc +10 -3
- data/Rakefile +5 -2
- data/example/{rails → rails2}/README +0 -0
- data/example/{rails → rails2}/Rakefile +0 -0
- data/example/{rails → rails2}/app/controllers/application_controller.rb +4 -0
- data/example/{rails → rails2}/config/boot.rb +6 -2
- data/example/{rails → rails2}/config/environment.rb +2 -2
- data/example/{rails → rails2}/config/environments/development.rb +0 -0
- data/example/{rails → rails2}/config/environments/production.rb +0 -0
- data/example/{rails → rails2}/config/environments/test.rb +0 -0
- data/example/{rails → rails2}/config/initializers/cookie_verification_secret.rb +0 -0
- data/example/{rails → rails2}/config/initializers/new_rails_defaults.rb +0 -0
- data/example/{rails → rails2}/config/initializers/session_store.rb +1 -1
- data/example/{rails → rails2}/config/rest-graph.yaml +0 -0
- data/example/{rails → rails2}/config/routes.rb +0 -0
- data/example/{rails → rails2}/log +0 -0
- data/example/{rails → rails2}/script/console +0 -0
- data/example/{rails → rails2}/script/server +0 -0
- data/example/{rails → rails2}/test/functional/application_controller_test.rb +10 -1
- data/example/{rails → rails2}/test/test_helper.rb +0 -0
- data/example/{rails → rails2}/test/unit/rails_util_test.rb +0 -0
- data/lib/rest-graph.rb +150 -49
- data/lib/rest-graph/rails_util.rb +31 -27
- data/lib/rest-graph/version.rb +1 -1
- data/rest-graph.gemspec +16 -13
- data/test/test_api.rb +1 -1
- data/test/test_error.rb +51 -0
- data/test/test_handler.rb +4 -2
- data/test/test_misc.rb +29 -0
- data/test/test_old.rb +33 -0
- data/test/test_page.rb +54 -0
- data/test/test_parse.rb +7 -0
- data/test/test_rest-graph.rb +10 -0
- metadata +69 -50
- 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,
|
data/CONTRIBUTORS
CHANGED
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
|
-
|
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'
|
data/Gemfile.lock
CHANGED
@@ -1,30 +1,40 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
addressable (2.2.
|
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
|
-
|
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.
|
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.
|
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
|
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
|
-
===
|
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/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= rest-graph 1.
|
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
|
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
|
-
===
|
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 '
|
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/
|
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
|
-
|
66
|
-
|
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.
|
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.
|
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"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -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 => '
|
8
|
+
:key => '_rails2_session',
|
9
9
|
:secret => 'c99a19ce0dc4ed1809e32b6b43bd9229c3a504c456230119dd445fdcb63c0ce06b436f1bf1eace27ebbe0da6041ff2b65cbb4ae4beadc3077e3e6ae07ea75118'
|
10
10
|
}
|
11
11
|
|
File without changes
|
File without changes
|
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 =>
|
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
|
File without changes
|
File without changes
|
data/lib/rest-graph.rb
CHANGED
@@ -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 &&
|
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
|
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,
|
308
|
-
if auto_decode && !suppress_decode
|
309
|
-
|
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
|