rest-graph 1.4.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,6 +1,26 @@
1
1
  = rest-graph changes history
2
2
 
3
- == rest-graph 1.4.0 -- ?
3
+ == rest-graph 1.4.1 -- 2010-08-04
4
+
5
+ * [RestGraph] Call error_handler when response contains error_code as well,
6
+ which came from FQL response. Thanks Florent.
7
+
8
+ * [RestGraph] Added RestGraph#parse_signed_request!
9
+
10
+ * [RestGraph] Added RestGraph#url to generate desired API request URL,
11
+ in case you'll want to use different HTTP client, such as em-http-request,
12
+ or pass the API request to different process of data fetcher.
13
+
14
+ * [RestGraph] Added an :cache option that allow you to pass a cache
15
+ object, which should respond to [] and []= for reading and writing.
16
+ The cache key would be MD5 hexdigest from the URL being called.
17
+ pass :cache => Rails.cache to rest_graph_setup when using RailsUtil.
18
+
19
+ * [RailsUtil] Pass :cache => Rails.cache to rest_graph_setup to enable caching.
20
+ * [RailsUtil] Favor signed_request over session in rest_graph_setup
21
+ * [RailsUtil] Now it's possible to setup all options in rest-graph.yaml.
22
+
23
+ == rest-graph 1.4.0 -- 2010-07-15
4
24
 
5
25
  Changes only for RailsUtil, the core (rest-graph.rb) is pretty stable for now.
6
26
 
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+
2
+ source 'http://rubygems.org'
3
+
4
+ gem 'rest-client'
5
+
6
+ group :test do
7
+ gem 'yajl-ruby'
8
+ gem 'rack'
9
+ gem 'rr'
10
+ gem 'webmock'
11
+ gem 'bacon'
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,49 @@
1
+ ---
2
+ hash: 1abfe240bd774ad21e7a187351da9cabacb74d58
3
+ sources:
4
+ - Rubygems:
5
+ uri: http://rubygems.org
6
+ specs:
7
+ - addressable:
8
+ version: 2.1.2
9
+ - bacon:
10
+ version: 1.1.0
11
+ - crack:
12
+ version: 0.1.8
13
+ - mime-types:
14
+ version: "1.16"
15
+ - rack:
16
+ version: 1.2.1
17
+ - rest-client:
18
+ version: 1.6.0
19
+ - rr:
20
+ version: 0.10.11
21
+ - webmock:
22
+ version: 1.3.2
23
+ - yajl-ruby:
24
+ version: 0.7.7
25
+ dependencies:
26
+ rest-client:
27
+ version: ">= 0"
28
+ group:
29
+ - :default
30
+ yajl-ruby:
31
+ version: ">= 0"
32
+ group:
33
+ - :test
34
+ rack:
35
+ version: ">= 0"
36
+ group:
37
+ - :test
38
+ rr:
39
+ version: ">= 0"
40
+ group:
41
+ - :test
42
+ webmock:
43
+ version: ">= 0"
44
+ group:
45
+ - :test
46
+ bacon:
47
+ version: ">= 0"
48
+ group:
49
+ - :test
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = rest-graph 1.3.0
1
+ = rest-graph 1.4.1
2
2
  by Cardinal Blue ( http://cardinalblue.com )
3
3
 
4
4
  == LINKS:
@@ -98,6 +98,7 @@ by Cardinal Blue ( http://cardinalblue.com )
98
98
  :auto_decode => true , # decode by json
99
99
  :app_id => '123' ,
100
100
  :secret => '1829' ,
101
+ :cache => {} , # a cache for the same API call
101
102
 
102
103
  # This handler callback is only called if auto_decode is set to true,
103
104
  # otherwise, it's ignored.
@@ -215,13 +216,13 @@ by Cardinal Blue ( http://cardinalblue.com )
215
216
  # or (3) Load config automatically
216
217
  require 'rest-graph/auto_load' # under Rails, load config/rest-graph.yaml
217
218
 
218
- # Please read:
219
+ # Please read: for an example of config file.
220
+ # Note that :auto_authorize_scope and friends is only for RailsUtil.
219
221
  {rest-graph.yaml}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
220
- # for an example of config file.
221
222
 
222
223
  == REQUIREMENTS:
223
224
 
224
- * Tested with MRI 1.8.7 and 1.9.1
225
+ * Tested with MRI 1.8.7 and 1.9.1 and Rubinius HEAD
225
226
  * gem install rest-client
226
227
  * gem install json (optional)
227
228
  * gem install json_pure (optional)
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = rest-graph 1.3.0
1
+ = rest-graph 1.4.1
2
2
  by Cardinal Blue ( http://cardinalblue.com )
3
3
 
4
4
  == LINKS:
@@ -98,6 +98,7 @@ by Cardinal Blue ( http://cardinalblue.com )
98
98
  :auto_decode => true , # decode by json
99
99
  :app_id => '123' ,
100
100
  :secret => '1829' ,
101
+ :cache => {} , # a cache for the same API call
101
102
 
102
103
  # This handler callback is only called if auto_decode is set to true,
103
104
  # otherwise, it's ignored.
@@ -215,13 +216,13 @@ by Cardinal Blue ( http://cardinalblue.com )
215
216
  # or (3) Load config automatically
216
217
  require 'rest-graph/auto_load' # under Rails, load config/rest-graph.yaml
217
218
 
218
- # Please read:
219
+ # Please read: for an example of config file.
220
+ # Note that :auto_authorize_scope and friends is only for RailsUtil.
219
221
  {rest-graph.yaml}[http://github.com/cardinalblue/rest-graph/blob/master/test/config/rest-graph.yaml]
220
- # for an example of config file.
221
222
 
222
223
  == REQUIREMENTS:
223
224
 
224
- * Tested with MRI 1.8.7 and 1.9.1
225
+ * Tested with MRI 1.8.7 and 1.9.1 and Rubinius HEAD
225
226
  * gem install rest-client
226
227
  * gem install json (optional)
227
228
  * gem install json_pure (optional)
@@ -16,6 +16,7 @@ class ApplicationController < ActionController::Base
16
16
  before_filter :filter_no_auto, :only => [:no_auto]
17
17
  before_filter :filter_diff_app_id, :only => [:diff_app_id]
18
18
  before_filter :filter_diff_canvas, :only => [:diff_canvas]
19
+ before_filter :filter_cache, :only => [:cache]
19
20
 
20
21
  def index
21
22
  render :text => rest_graph.get('me').to_json
@@ -34,9 +35,16 @@ class ApplicationController < ActionController::Base
34
35
  render :text => rest_graph.app_id
35
36
  end
36
37
 
38
+ def cache
39
+ url = rest_graph.url('cache')
40
+ rest_graph.get('cache')
41
+ rest_graph.get('cache')
42
+ render :text => Rails.cache.read(Digest::MD5.hexdigest(url))
43
+ end
44
+
37
45
  private
38
46
  def filter_common
39
- rest_graph_setup(:auto_authorize => true)
47
+ rest_graph_setup(:auto_authorize => true, :canvas => '')
40
48
  end
41
49
 
42
50
  def filter_canvas
@@ -59,6 +67,11 @@ class ApplicationController < ActionController::Base
59
67
  end
60
68
 
61
69
  def filter_options
62
- rest_graph_setup(:auto_authorize_options => {:scope => 'bogus'})
70
+ rest_graph_setup(:auto_authorize_options => {:scope => 'bogus'},
71
+ :canvas => nil)
72
+ end
73
+
74
+ def filter_cache
75
+ rest_graph_setup(:cache => Rails.cache)
63
76
  end
64
77
  end
@@ -26,3 +26,5 @@ config.action_mailer.delivery_method = :test
26
26
  # This is necessary if your schema can't be completely dumped by the schema dumper,
27
27
  # like if you have constraints or database-specific column types
28
28
  # config.active_record.schema_format = :sql
29
+
30
+ config.logger = Logger.new($stdout, :debug)
@@ -71,4 +71,13 @@ class ApplicationControllerTest < ActionController::TestCase
71
71
  assert_response :success
72
72
  assert_equal 'zzz', @response.body
73
73
  end
74
+
75
+ def test_cache
76
+ stub_request(:get, 'https://graph.facebook.com/cache').
77
+ to_return(:body => '{"message":"ok"}')
78
+
79
+ get(:cache)
80
+ assert_response :success
81
+ assert_equal '{"message":"ok"}', @response.body
82
+ end
74
83
  end
data/lib/rest-graph.rb CHANGED
@@ -4,6 +4,8 @@ require 'rest_client'
4
4
 
5
5
  # stdlib
6
6
  require 'digest/md5'
7
+ require 'openssl'
8
+
7
9
  require 'cgi'
8
10
 
9
11
  # optional gem
@@ -21,15 +23,19 @@ rescue LoadError; end
21
23
  }
22
24
 
23
25
  # the data structure used in RestGraph
24
- RestGraphStruct = Struct.new(:data, :auto_decode,
26
+ RestGraphStruct = Struct.new(:auto_decode,
25
27
  :graph_server, :old_server,
26
28
  :accept, :lang,
27
29
  :app_id, :secret,
30
+ :data, :cache,
28
31
  :error_handler,
29
32
  :log_handler) unless defined?(RestGraphStruct)
30
33
 
31
34
  class RestGraph < RestGraphStruct
32
35
  class Error < RuntimeError; end
36
+ class Event < Struct.new(:duration, :url); end
37
+ class Event::Requested < Event; end
38
+ class Event::CacheHit < Event; end
33
39
 
34
40
  Attributes = RestGraphStruct.members.map(&:to_sym)
35
41
 
@@ -45,7 +51,6 @@ class RestGraph < RestGraphStruct
45
51
  # setup defaults
46
52
  module DefaultAttributes
47
53
  extend self
48
- def default_data ; {} ; end
49
54
  def default_auto_decode ; true ; end
50
55
  def default_graph_server; 'https://graph.facebook.com/'; end
51
56
  def default_old_server ; 'https://api.facebook.com/' ; end
@@ -53,11 +58,13 @@ class RestGraph < RestGraphStruct
53
58
  def default_lang ; 'en-us' ; end
54
59
  def default_app_id ; nil ; end
55
60
  def default_secret ; nil ; end
61
+ def default_data ; {} ; end
62
+ def default_cache ; nil ; end
56
63
  def default_error_handler
57
64
  lambda{ |error| raise ::RestGraph::Error.new(error) }
58
65
  end
59
66
  def default_log_handler
60
- lambda{ |duration, url| }
67
+ lambda{ |event| }
61
68
  end
62
69
  end
63
70
  extend DefaultAttributes
@@ -80,20 +87,24 @@ class RestGraph < RestGraphStruct
80
87
  !!access_token
81
88
  end
82
89
 
90
+ def url path, query={}, server=graph_server
91
+ "#{server}#{path}#{build_query_string(query)}"
92
+ end
93
+
83
94
  def get path, query={}, opts={}
84
- request(graph_server, path, query, :get, nil, opts[:suppress_decode])
95
+ request(:get , url(path, query, graph_server), opts)
85
96
  end
86
97
 
87
98
  def delete path, query={}, opts={}
88
- request(graph_server, path, query, :delete, nil, opts[:suppress_decode])
99
+ request(:delete, url(path, query, graph_server), opts)
89
100
  end
90
101
 
91
102
  def post path, payload, query={}, opts={}
92
- request(graph_server, path, query, :post, payload, opts[:suppress_decode])
103
+ request(:post , url(path, query, graph_server), opts, payload)
93
104
  end
94
105
 
95
106
  def put path, payload, query={}, opts={}
96
- request(graph_server, path, query, :put, payload, opts[:suppress_decode])
107
+ request(:put , url(path, query, graph_server), opts, payload)
97
108
  end
98
109
 
99
110
  # cookies, app_id, secrect related below
@@ -119,6 +130,18 @@ class RestGraph < RestGraphStruct
119
130
  rescue JSON::ParserError
120
131
  end
121
132
 
133
+ # facebook's new signed_request...
134
+
135
+ def parse_signed_request! request
136
+ sig_encoded, json_encoded = request.split('.')
137
+ sig, json = [sig_encoded, json_encoded].map{ |str|
138
+ "#{str.tr('-_', '+/')}==".unpack('m').first
139
+ }
140
+ self.data = JSON.parse(json) if
141
+ secret && OpenSSL::HMAC.digest('sha256', secret, json_encoded) == sig
142
+ rescue JSON::ParserError
143
+ end
144
+
122
145
  # oauth related
123
146
 
124
147
  def authorize_url opts={}
@@ -129,20 +152,23 @@ class RestGraph < RestGraphStruct
129
152
  def authorize! opts={}
130
153
  query = {:client_id => app_id, :client_secret => secret}.merge(opts)
131
154
  self.data = Rack::Utils.parse_query(
132
- request(graph_server, 'oauth/access_token', query, :get, nil, true))
155
+ request(:get, url('oauth/access_token', query),
156
+ :suppress_decode => true))
133
157
  end
134
158
 
135
159
  # old rest facebook api, i will definitely love to remove them someday
136
160
 
137
161
  def old_rest path, query={}, opts={}
138
- request(old_server, "method/#{path}",
139
- {:format => 'json'}.merge(query), :get, nil, opts[:suppress_decode])
162
+ request(
163
+ :get,
164
+ url("method/#{path}", {:format => 'json'}.merge(query), old_server),
165
+ opts)
140
166
  end
141
167
 
142
168
  def exchange_sessions opts={}
143
169
  query = {:client_id => app_id, :client_secret => secret,
144
170
  :type => 'client_cred'}.merge(opts)
145
- request(graph_server, 'oauth/exchange_sessions', query, :post)
171
+ request(:post, url('oauth/exchange_sessions', query))
146
172
  end
147
173
 
148
174
  def fql code, query={}, opts={}
@@ -162,15 +188,14 @@ class RestGraph < RestGraphStruct
162
188
  end
163
189
 
164
190
  private
165
- def request server, path, opts, method, payload=nil, suppress_decode=nil
191
+ def request meth, uri, opts={}, payload=nil
166
192
  start_time = Time.now
167
- res = RestClient::Resource.new(server)[path + build_query_string(opts)]
168
- post_request(
169
- res.send(method, *[payload, build_headers].compact), suppress_decode)
193
+ post_request(cache_get(uri) || fetch(meth, uri, payload),
194
+ opts[:suppress_decode])
170
195
  rescue RestClient::Exception => e
171
- post_request(e.http_body, suppress_decode)
196
+ post_request(e.http_body, opts[:suppress_decode])
172
197
  ensure
173
- log_handler.call(Time.now - start_time, res.url)
198
+ log_handler.call(Event::Requested.new(Time.now - start_time, uri))
174
199
  end
175
200
 
176
201
  def build_query_string query={}
@@ -200,7 +225,9 @@ class RestGraph < RestGraphStruct
200
225
  end
201
226
 
202
227
  def check_error hash
203
- if error_handler && hash.kind_of?(Hash) && hash['error']
228
+ if error_handler && hash.kind_of?(Hash) &&
229
+ (hash['error'] || # from graph api
230
+ hash['error_code']) # from fql
204
231
  error_handler.call(hash)
205
232
  else
206
233
  hash
@@ -213,4 +240,26 @@ class RestGraph < RestGraphStruct
213
240
 
214
241
  Digest::MD5.hexdigest(args + secret)
215
242
  end
243
+
244
+ def cache_key uri
245
+ Digest::MD5.hexdigest(uri)
246
+ end
247
+
248
+ def cache_get uri
249
+ return unless cache
250
+ start_time = Time.now
251
+ cache[cache_key(uri)].tap{ |result|
252
+ log_handler.call(Event::CacheHit.new(Time.now - start_time, uri)) if
253
+ result
254
+ }
255
+ end
256
+
257
+ def fetch meth, uri, payload
258
+ RestClient::Request.execute(:method => meth, :url => uri,
259
+ :headers => build_headers,
260
+ :payload => payload).
261
+ tap{ |result|
262
+ cache[cache_key(uri)] = result if cache
263
+ }
264
+ end
216
265
  end
@@ -1,12 +1,23 @@
1
1
 
2
2
  require 'rest-graph'
3
3
 
4
- module RestGraph::DefaultAttributes
5
- def default_canvas
6
- ''
4
+ class RestGraph
5
+ module DefaultAttributes
6
+ def default_canvas ; '' ; end
7
+ def default_auto_authorize ; false; end
8
+ def default_auto_authorize_options; {} ; end
9
+ def default_auto_authorize_scope ; '' ; end
10
+ def default_write_session ; false; end
11
+ end
12
+
13
+ module RailsCache
14
+ def [] key ; read(key) ; end
15
+ def []= key, value; write(key, value); end
7
16
  end
8
17
  end
9
18
 
19
+ ::ActiveSupport::Cache::Store.send(:include, ::RestGraph::RailsCache)
20
+
10
21
  module RestGraph::RailsUtil
11
22
  module Helper
12
23
  def rest_graph
@@ -21,30 +32,16 @@ module RestGraph::RailsUtil
21
32
  controller.helper(::RestGraph::RailsUtil::Helper)
22
33
  end
23
34
 
24
- def rest_graph_options
25
- @rest_graph_options ||=
26
- {:canvas => '',
27
- :auto_authorize => false,
28
- :auto_authorize_options => {},
29
- :auto_authorize_scope => '',
30
- :write_session => false}
31
- end
32
-
33
- def rest_graph_options_new
34
- @rest_graph_options_new ||=
35
- {:error_handler => method(:rest_graph_authorize),
36
- :log_handler => method(:rest_graph_log)}
37
- end
38
-
39
35
  def rest_graph_setup options={}
40
- rest_graph_options .merge!(rest_graph_extract_options(options, :reject))
36
+ rest_graph_options_ctl.merge!(rest_graph_extract_options(options, :reject))
41
37
  rest_graph_options_new.merge!(rest_graph_extract_options(options, :select))
42
38
 
43
39
  rest_graph_check_cookie
40
+ rest_graph_check_params_signed_request
44
41
  rest_graph_check_params_session
45
42
  rest_graph_check_code
46
43
 
47
- # there are above 3 ways to check the user identity!
44
+ # there are above 4 ways to check the user identity!
48
45
  # if nor of them passed, then we can suppose the user
49
46
  # didn't authorize for us, but we can check if user has authorized
50
47
  # before, in that case, the fbs would be inside session,
@@ -64,8 +61,8 @@ module RestGraph::RailsUtil
64
61
  if redirect || rest_graph_auto_authorize?
65
62
  @rest_graph_authorize_url = rest_graph.authorize_url(
66
63
  {:redirect_uri => rest_graph_normalized_request_uri,
67
- :scope => rest_graph_options[:auto_authorize_scope]}.
68
- merge( rest_graph_options[:auto_authorize_options]))
64
+ :scope => rest_graph_oget(:auto_authorize_scope)}.
65
+ merge(rest_graph_oget(:auto_authorize_options)))
69
66
 
70
67
  logger.debug("DEBUG: RestGraph: redirect to #{@rest_graph_authorize_url}")
71
68
 
@@ -79,7 +76,6 @@ module RestGraph::RailsUtil
79
76
  def rest_graph_authorize_redirect
80
77
  if !rest_graph_in_canvas?
81
78
  redirect_to @rest_graph_authorize_url
82
-
83
79
  else
84
80
  render :inline => <<-HTML
85
81
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
@@ -104,7 +100,27 @@ module RestGraph::RailsUtil
104
100
 
105
101
  module_function
106
102
 
107
- # ==================== checking utility ====================
103
+ # ==================== options utility =======================
104
+
105
+ def rest_graph_oget key
106
+ if rest_graph_options_ctl.has_key?(key)
107
+ rest_graph_options_ctl[key]
108
+ else
109
+ RestGraph.send("default_#{key}")
110
+ end
111
+ end
112
+
113
+ def rest_graph_options_ctl
114
+ @rest_graph_options_ctl ||= {}
115
+ end
116
+
117
+ def rest_graph_options_new
118
+ @rest_graph_options_new ||=
119
+ {:error_handler => method(:rest_graph_authorize),
120
+ :log_handler => method(:rest_graph_log)}
121
+ end
122
+
123
+ # ==================== checking utility ======================
108
124
 
109
125
  # if we're not in canvas nor code passed,
110
126
  # we could check out cookies as well.
@@ -117,24 +133,37 @@ module RestGraph::RailsUtil
117
133
  " #{rest_graph.data.inspect}")
118
134
  end
119
135
 
136
+ def rest_graph_check_params_signed_request
137
+ return if rest_graph.authorized? || !params[:signed_request]
138
+
139
+ rest_graph.parse_signed_request!(params[:signed_request])
140
+ logger.debug("DEBUG: RestGraph: detected signed_request, parsed:" \
141
+ " #{rest_graph.data.inspect}")
142
+
143
+ if rest_graph.authorized?
144
+ rest_graph_write_session
145
+ else
146
+ logger.warn(
147
+ "WARN: RestGraph: bad signed_request: #{params[:signed_request]}")
148
+ end
149
+ end
150
+
120
151
  # if the code is bad or not existed,
121
152
  # check if there's one in session,
122
153
  # meanwhile, there the sig and access_token is correct,
123
154
  # that means we're in the context of canvas
124
155
  def rest_graph_check_params_session
125
- return if rest_graph.authorized? || !params[:session]
156
+ return if rest_graph.authorized? || !params[:session]
126
157
 
127
158
  rest_graph.parse_json!(params[:session])
128
159
  logger.debug("DEBUG: RestGraph: detected session, parsed:" \
129
160
  " #{rest_graph.data.inspect}")
130
161
 
131
162
  if rest_graph.authorized?
132
- @fb_sig_in_canvas = true
163
+ rest_graph_write_session
133
164
  else
134
165
  logger.warn("WARN: RestGraph: bad session: #{params[:session]}")
135
166
  end
136
-
137
- rest_graph_write_session
138
167
  end
139
168
 
140
169
  # exchange the code with access_token
@@ -148,7 +177,7 @@ module RestGraph::RailsUtil
148
177
  "#{rest_graph_normalized_request_uri}, " \
149
178
  "parsed: #{rest_graph.data.inspect}")
150
179
 
151
- rest_graph_write_session
180
+ rest_graph_write_session if rest_graph.authorized?
152
181
  end
153
182
 
154
183
  def rest_graph_check_rails_session
@@ -159,24 +188,31 @@ module RestGraph::RailsUtil
159
188
  " #{rest_graph.data.inspect}")
160
189
  end
161
190
 
162
- # ==================== others ====================
191
+ # ==================== others ================================
163
192
 
164
193
  def rest_graph_write_session
165
- return if !rest_graph.authorized? || !rest_graph_options[:write_session]
194
+ return if !rest_graph_oget(:write_session)
166
195
 
167
196
  fbs = rest_graph.data.to_a.map{ |k_v| k_v.join('=') }.join('&')
168
197
  session['fbs'] = fbs
169
198
  logger.debug("DEBUG: RestGraph: wrote session: fbs => #{fbs}")
170
199
  end
171
200
 
172
- def rest_graph_log duration, url
173
- logger.debug("DEBUG: RestGraph: spent #{duration} requesting #{url}")
201
+ def rest_graph_log event
202
+ message = "DEBUG: RestGraph: spent #{sprintf('%f', event.duration)} "
203
+ case event
204
+ when RestGraph::Event::Requested
205
+ logger.debug(message + "requesting #{event.url}")
206
+
207
+ when RestGraph::Event::CacheHit
208
+ logger.debug(message + "cache hit' #{event.url}")
209
+ end
174
210
  end
175
211
 
176
212
  def rest_graph_normalized_request_uri
177
213
  if rest_graph_in_canvas?
178
214
  "http://apps.facebook.com/" \
179
- "#{rest_graph_canvas}#{request.request_uri}"
215
+ "#{rest_graph_oget(:canvas)}#{request.request_uri}"
180
216
  else
181
217
  request.url
182
218
  end.sub(/[\&\?]session=[^\&]+/, '').
@@ -184,21 +220,13 @@ module RestGraph::RailsUtil
184
220
  end
185
221
 
186
222
  def rest_graph_in_canvas?
187
- !rest_graph_options[:canvas].empty?
188
- end
189
-
190
- def rest_graph_canvas
191
- if rest_graph_options[:canvas].empty?
192
- RestGraph.default_canvas
193
- else
194
- rest_graph_options[:canvas]
195
- end
223
+ !rest_graph_oget(:canvas).blank?
196
224
  end
197
225
 
198
226
  def rest_graph_auto_authorize?
199
- !rest_graph_options[:auto_authorize_scope ].empty? ||
200
- !rest_graph_options[:auto_authorize_options].empty? ||
201
- rest_graph_options[:auto_authorize]
227
+ !rest_graph_oget(:auto_authorize_scope) .blank? ||
228
+ !rest_graph_oget(:auto_authorize_options).blank? ||
229
+ rest_graph_oget(:auto_authorize)
202
230
  end
203
231
 
204
232
  def rest_graph_extract_options options, method
@@ -1,4 +1,4 @@
1
1
 
2
2
  require 'rest-graph'
3
3
 
4
- RestGraph::VERSION = '1.4.0'
4
+ RestGraph::VERSION = '1.4.1'
data/rest-graph.gemspec CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{rest-graph}
5
- s.version = "1.4.0"
5
+ s.version = "1.4.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Cardinal Blue", "Lin Jen-Shin (aka godfat 真常)"]
9
- s.date = %q{2010-07-15}
9
+ s.date = %q{2010-07-26}
10
10
  s.description = %q{ A super simple Facebook Open Graph API client}
11
11
  s.email = %q{dev (XD) cardinalblue.com}
12
- s.extra_rdoc_files = ["CHANGES", "LICENSE", "README", "TODO", "example/rails/README", "example/rails/config/rest-graph.yaml", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "rest-graph.gemspec"]
13
- s.files = ["CHANGES", "LICENSE", "README", "README.rdoc", "Rakefile", "TODO", "example/rails/README", "example/rails/Rakefile", "example/rails/app/controllers/application_controller.rb", "example/rails/config/boot.rb", "example/rails/config/environment.rb", "example/rails/config/environments/development.rb", "example/rails/config/environments/production.rb", "example/rails/config/environments/test.rb", "example/rails/config/initializers/cookie_verification_secret.rb", "example/rails/config/initializers/new_rails_defaults.rb", "example/rails/config/initializers/session_store.rb", "example/rails/config/rest-graph.yaml", "example/rails/config/routes.rb", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "example/rails/test/functional/application_controller_test.rb", "example/rails/test/test_helper.rb", "init.rb", "lib/rest-graph.rb", "lib/rest-graph/auto_load.rb", "lib/rest-graph/facebook_util.rb", "lib/rest-graph/load_config.rb", "lib/rest-graph/rails_util.rb", "lib/rest-graph/version.rb", "rest-graph.gemspec", "test/common.rb", "test/config/rest-graph.yaml", "test/test_default.rb", "test/test_handler.rb", "test/test_load_config.rb", "test/test_oauth.rb", "test/test_old.rb", "test/test_parse.rb", "test/test_rest-graph.rb"]
12
+ s.extra_rdoc_files = ["CHANGES", "Gemfile", "Gemfile.lock", "LICENSE", "README", "TODO", "example/rails/README", "example/rails/config/rest-graph.yaml", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "rest-graph.gemspec"]
13
+ s.files = ["CHANGES", "Gemfile", "Gemfile.lock", "LICENSE", "README", "README.rdoc", "Rakefile", "TODO", "example/rails/README", "example/rails/Rakefile", "example/rails/app/controllers/application_controller.rb", "example/rails/config/boot.rb", "example/rails/config/environment.rb", "example/rails/config/environments/development.rb", "example/rails/config/environments/production.rb", "example/rails/config/environments/test.rb", "example/rails/config/initializers/cookie_verification_secret.rb", "example/rails/config/initializers/new_rails_defaults.rb", "example/rails/config/initializers/session_store.rb", "example/rails/config/rest-graph.yaml", "example/rails/config/routes.rb", "example/rails/log", "example/rails/script/console", "example/rails/script/server", "example/rails/test/functional/application_controller_test.rb", "example/rails/test/test_helper.rb", "init.rb", "lib/rest-graph.rb", "lib/rest-graph/auto_load.rb", "lib/rest-graph/facebook_util.rb", "lib/rest-graph/load_config.rb", "lib/rest-graph/rails_util.rb", "lib/rest-graph/version.rb", "rest-graph.gemspec", "test/common.rb", "test/config/rest-graph.yaml", "test/test_default.rb", "test/test_handler.rb", "test/test_load_config.rb", "test/test_oauth.rb", "test/test_old.rb", "test/test_parse.rb", "test/test_rest-graph.rb"]
14
14
  s.homepage = %q{http://github.com/cardinalblue/rest-graph}
15
15
  s.rdoc_options = ["--main", "README.rdoc"]
16
16
  s.require_paths = ["lib"]
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency(%q<json>, [">= 1.4.3"])
29
29
  s.add_development_dependency(%q<rack>, [">= 1.2.1"])
30
30
  s.add_development_dependency(%q<rr>, [">= 0.10.11"])
31
- s.add_development_dependency(%q<webmock>, [">= 1.3.1"])
31
+ s.add_development_dependency(%q<webmock>, [">= 1.3.2"])
32
32
  s.add_development_dependency(%q<bacon>, [">= 1.1.0"])
33
33
  s.add_development_dependency(%q<bones>, [">= 3.4.7"])
34
34
  else
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
36
36
  s.add_dependency(%q<json>, [">= 1.4.3"])
37
37
  s.add_dependency(%q<rack>, [">= 1.2.1"])
38
38
  s.add_dependency(%q<rr>, [">= 0.10.11"])
39
- s.add_dependency(%q<webmock>, [">= 1.3.1"])
39
+ s.add_dependency(%q<webmock>, [">= 1.3.2"])
40
40
  s.add_dependency(%q<bacon>, [">= 1.1.0"])
41
41
  s.add_dependency(%q<bones>, [">= 3.4.7"])
42
42
  end
@@ -45,7 +45,7 @@ Gem::Specification.new do |s|
45
45
  s.add_dependency(%q<json>, [">= 1.4.3"])
46
46
  s.add_dependency(%q<rack>, [">= 1.2.1"])
47
47
  s.add_dependency(%q<rr>, [">= 0.10.11"])
48
- s.add_dependency(%q<webmock>, [">= 1.3.1"])
48
+ s.add_dependency(%q<webmock>, [">= 1.3.2"])
49
49
  s.add_dependency(%q<bacon>, [">= 1.1.0"])
50
50
  s.add_dependency(%q<bones>, [">= 3.4.7"])
51
51
  end
@@ -4,3 +4,4 @@ test:
4
4
  secret: <%= 'r41829'.reverse %>
5
5
  auto_decode: false
6
6
  lang: zh-tw
7
+ auto_authorize_scope: 'publish_stream'
data/test/test_handler.rb CHANGED
@@ -8,49 +8,81 @@ end
8
8
  require 'json'
9
9
 
10
10
  describe RestGraph do
11
- before do
12
- @id = lambda{ |obj| obj }
13
- @error = '{"error":{"type":"Exception","message":"(#2500)"}}'
14
- @error_hash = JSON.parse(@error)
15
-
11
+ after do
16
12
  reset_webmock
17
- stub_request(:get, 'https://graph.facebook.com/me').
18
- to_return(:body => @error)
13
+ RR.verify
19
14
  end
20
15
 
21
- it 'would call error_handler if error occurred' do
22
- RestGraph.new(:error_handler => @id).get('me').should == @error_hash
23
- end
16
+ describe 'log handler' do
17
+ it 'would log whenever doing network request' do
18
+ stub_request(:get, 'https://graph.facebook.com/me').
19
+ to_return(:body => '{}')
20
+
21
+ mock(Time).now{ 666 }
22
+ mock(Time).now{ 999 }
23
+
24
+ logger = []
25
+ rg = RestGraph.new(:log_handler => lambda{ |e|
26
+ logger << [e.duration, e.url] })
27
+ rg.get('me')
24
28
 
25
- it 'would raise ::RestGraph::Error in default error_handler' do
26
- begin
27
- RestGraph.new.get('me')
28
- rescue ::RestGraph::Error => e
29
- e.message.should == @error_hash
29
+ logger.last.should == [333, 'https://graph.facebook.com/me']
30
30
  end
31
31
  end
32
- end
33
32
 
34
- describe RestGraph do
35
- before do
36
- reset_webmock
37
- end
33
+ describe 'with Graph API' do
34
+ before do
35
+ @id = lambda{ |obj| obj }
36
+ @error = '{"error":{"type":"Exception","message":"(#2500)"}}'
37
+ @error_hash = JSON.parse(@error)
38
38
 
39
- after do
40
- RR.verify
39
+ stub_request(:get, 'https://graph.facebook.com/me').
40
+ to_return(:body => @error)
41
+ end
42
+
43
+ it 'would call error_handler if error occurred' do
44
+ RestGraph.new(:error_handler => @id).get('me').should == @error_hash
45
+ end
46
+
47
+ it 'would raise ::RestGraph::Error in default error_handler' do
48
+ begin
49
+ RestGraph.new.get('me')
50
+ rescue ::RestGraph::Error => e
51
+ e.message.should == @error_hash
52
+ end
53
+ end
41
54
  end
42
55
 
43
- it 'would log whenever doing network request' do
44
- stub_request(:get, 'https://graph.facebook.com/me').
45
- to_return(:body => '{}')
56
+ describe 'with FQL API' do
57
+ # Example of an actual response (without newline)
58
+ # {"error_code":603,"error_msg":"Unknown table: bad_table",
59
+ # "request_args":[{"key":"method","value":"fql.query"},
60
+ # {"key":"format","value":"json"},
61
+ # {"key":"query","value":
62
+ # "SELECT name FROM bad_table WHERE uid=12345"}]}
63
+ before do
64
+ @id = lambda{ |obj| obj }
65
+ @fql_error = '{"error_code":603,"error_msg":"Unknown table: bad"}'
66
+ @fql_error_hash = JSON.parse(@fql_error)
46
67
 
47
- mock(Time).now{ 666 }
48
- mock(Time).now{ 999 }
68
+ @bad_fql_query = 'SELECT name FROM bad_table WHERE uid="12345"'
69
+ bad_fql_request = "https://api.facebook.com/method/fql.query?" \
70
+ "format=json&query=#{CGI.escape(@bad_fql_query)}"
49
71
 
50
- logger = []
51
- rg = RestGraph.new(:log_handler => lambda{ |d, u| logger << [d, u] })
52
- rg.get('me')
72
+ stub_request(:get, bad_fql_request).to_return(:body => @fql_error)
73
+ end
53
74
 
54
- logger.last.should == [333, 'https://graph.facebook.com/me']
75
+ it 'would call error_handler if error occurred' do
76
+ RestGraph.new(:error_handler => @id).fql(@bad_fql_query).
77
+ should == @fql_error_hash
78
+ end
79
+
80
+ it 'would raise ::RestGraph::Error in default error_handler' do
81
+ begin
82
+ RestGraph.new.fql(@bad_fql_query)
83
+ rescue ::RestGraph::Error => e
84
+ e.message.should == @fql_error_hash
85
+ end
86
+ end
55
87
  end
56
88
  end
data/test/test_oauth.rb CHANGED
@@ -7,11 +7,14 @@ end
7
7
 
8
8
  describe RestGraph do
9
9
  before do
10
- reset_webmock
11
10
  @rg = RestGraph.new(:app_id => '29', :secret => '18')
12
11
  @uri = 'http://zzz.tw'
13
12
  end
14
13
 
14
+ after do
15
+ reset_webmock
16
+ end
17
+
15
18
  it 'would return correct oauth url' do
16
19
  TestHelper.normalize_url(@rg.authorize_url(:redirect_uri => @uri)).
17
20
  should == 'https://graph.facebook.com/oauth/authorize?' \
data/test/test_old.rb CHANGED
@@ -6,11 +6,8 @@ else
6
6
  end
7
7
 
8
8
  describe RestGraph do
9
- before do
10
- reset_webmock
11
- end
12
-
13
9
  after do
10
+ reset_webmock
14
11
  RR.verify
15
12
  end
16
13
 
data/test/test_parse.rb CHANGED
@@ -68,4 +68,23 @@ describe RestGraph do
68
68
  should == {'feed' => 'me', 'sig' => "20393e7823730308938a86ecf1c88b14"}
69
69
  end
70
70
 
71
+ it 'would parse signed_request' do
72
+ secret = 'aloha'
73
+ json = {'ooh' => 'dir', 'moo' => 'bar'}.to_json
74
+ encode = lambda{ |str|
75
+ [str].pack('m').tr("\n=", '').tr('+/', '-_')
76
+ }
77
+ json_encoded = encode[json]
78
+ sig = OpenSSL::HMAC.digest('sha256', secret, json_encoded)
79
+ signed_request = "#{encode[sig]}.#{json_encoded}"
80
+
81
+ rg = RestGraph.new(:secret => secret)
82
+ rg.parse_signed_request!(signed_request)
83
+ rg.data['ooh'].should == 'dir'
84
+ rg.data['moo'].should == 'bar'
85
+
86
+ signed_request = "#{encode[sig[0..-4]+'bad']}.#{json_encoded}"
87
+ rg.parse_signed_request!(signed_request).should == nil
88
+ end
89
+
71
90
  end
@@ -6,11 +6,8 @@ else
6
6
  end
7
7
 
8
8
  describe RestGraph do
9
- before do
10
- reset_webmock
11
- end
12
-
13
9
  after do
10
+ reset_webmock
14
11
  RR.verify
15
12
  end
16
13
 
@@ -42,6 +39,13 @@ describe RestGraph do
42
39
  should == '?message=hi%21%21&subject=%28%26oh%26%29'
43
40
  end
44
41
 
42
+ it 'would generate correct url' do
43
+ TestHelper.normalize_url(
44
+ RestGraph.new(:access_token => 'awesome').url('path', :query => 'string')).
45
+ should ==
46
+ 'https://graph.facebook.com/path?access_token=awesome&query=string'
47
+ end
48
+
45
49
  it 'would request to correct server' do
46
50
  stub_request(:get, 'http://nothing.godfat.org/me').with(
47
51
  :headers => {'Accept' => 'text/plain',
@@ -113,4 +117,17 @@ describe RestGraph do
113
117
  to_return(:body => 'ok')
114
118
  RestGraph.new(:auto_decode => false).get('search', :q => o).should == 'ok'
115
119
  end
120
+
121
+ it 'would enable cache if passing cache' do
122
+ url, body = "https://graph.facebook.com/cache", '{"message":"ok"}'
123
+ stub_request(:get, url).to_return(:body => body)
124
+
125
+ cache = {}
126
+ rg = RestGraph.new(:cache => cache, :auto_decode => false)
127
+ 3.times{
128
+ rg.get('cache').should == body
129
+ reset_webmock
130
+ }
131
+ cache.should == {rg.send(:cache_key, url) => body}
132
+ end
116
133
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rest-graph
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 5
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 4
9
- - 0
10
- version: 1.4.0
9
+ - 1
10
+ version: 1.4.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Cardinal Blue
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-07-15 00:00:00 +08:00
19
+ date: 2010-08-04 00:00:00 +08:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -91,12 +91,12 @@ dependencies:
91
91
  requirements:
92
92
  - - ">="
93
93
  - !ruby/object:Gem::Version
94
- hash: 25
94
+ hash: 31
95
95
  segments:
96
96
  - 1
97
97
  - 3
98
- - 1
99
- version: 1.3.1
98
+ - 2
99
+ version: 1.3.2
100
100
  type: :development
101
101
  version_requirements: *id005
102
102
  - !ruby/object:Gem::Dependency
@@ -139,6 +139,8 @@ extensions: []
139
139
 
140
140
  extra_rdoc_files:
141
141
  - CHANGES
142
+ - Gemfile
143
+ - Gemfile.lock
142
144
  - LICENSE
143
145
  - README
144
146
  - TODO
@@ -150,6 +152,8 @@ extra_rdoc_files:
150
152
  - rest-graph.gemspec
151
153
  files:
152
154
  - CHANGES
155
+ - Gemfile
156
+ - Gemfile.lock
153
157
  - LICENSE
154
158
  - README
155
159
  - README.rdoc
@@ -176,7 +180,6 @@ files:
176
180
  - init.rb
177
181
  - lib/rest-graph.rb
178
182
  - lib/rest-graph/auto_load.rb
179
- - lib/rest-graph/facebook_util.rb
180
183
  - lib/rest-graph/load_config.rb
181
184
  - lib/rest-graph/rails_util.rb
182
185
  - lib/rest-graph/version.rb
@@ -1,27 +0,0 @@
1
-
2
- require 'rest-graph'
3
-
4
- module RestGraph::FacebookUtil
5
- module_function
6
- def ext_perm
7
- %w[publish_stream create_event rsvp_event sms offline_access manage_pages
8
- email read_insights read_stream read_mailbox ads_management xmpp_login
9
- user_about_me user_activities user_birthday user_education_history
10
- user_events user_groups user_hometown user_interests user_likes
11
- user_location user_notes user_online_presence user_photo_video_tags
12
- user_photos user_relationships user_religion_politics user_status
13
- user_videos user_videos user_work_history read_friendlists read_requests
14
- friends_about_me friends_activities friends_birthday
15
- friends_education_history friends_events friends_groups friends_hometown
16
- friends_interests friends_likes friends_location friends_notes
17
- friends_online_presence friends_photo_video_tags friends_photos
18
- friends_relationships friends_religion_politics friends_status
19
- friends_videos friends_website friends_work_history]
20
- end
21
-
22
- def method_name
23
-
24
- end
25
- end
26
-
27
- RestGraph.send(:include, RestGraph::FacebookUtil)