rfacebook 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -38,5 +38,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
38
 
39
39
 
40
40
  Some code was inspired by techniques used in Alpha Chen's old client.
41
- Some code was ported from the official PHP5 client.
42
41
 
@@ -32,10 +32,35 @@ require "facebook_session"
32
32
  module RFacebook
33
33
 
34
34
  class FacebookDesktopSession < FacebookSession
35
+
36
+ ################################################################################################
37
+ ################################################################################################
38
+ # :section: Properties
39
+ ################################################################################################
40
+
41
+ attr_reader :session_secret # you should need this for infinite desktop sessions
42
+
43
+ ################################################################################################
44
+ ################################################################################################
45
+ # :section: Initialization
46
+ ################################################################################################
47
+
48
+ # Function: initialize
49
+ # Constructs a FacebookDesktopSession. Also calls the API to grab an auth_token.
50
+ #
51
+ # Parameters:
52
+ # api_key - your API key
53
+ # api_secret - your API secret
54
+ # options.suppress_errors - boolean, set to true if you don't want errors to be thrown (defaults to false)
55
+ def initialize(api_key, api_secret, suppress_errors = false)
56
+ super(api_key, api_secret, suppress_errors)
57
+ @desktop_auth_token = get_auth_token
58
+ end
35
59
 
36
- # you'll need to access session_secret (in addition to the session_key) to be able to
37
- # call "activate_with_previous_session" when using infinite sessions
38
- attr_reader :session_secret
60
+ ################################################################################################
61
+ ################################################################################################
62
+ # :section: URL Accessors
63
+ ################################################################################################
39
64
 
40
65
  # Function: get_login_url
41
66
  # Gets the authentication URL
@@ -62,22 +87,20 @@ class FacebookDesktopSession < FacebookSession
62
87
  return "http://#{WWW_SERVER_BASE_URL}#{WWW_PATH_LOGIN}?v=1.0&api_key=#{@api_key}&auth_token=#{@desktop_auth_token}#{optionalPopup}#{optionalNext}#{optionalSkipCookie}#{optionalHideCheckbox}"
63
88
  end
64
89
 
90
+ ################################################################################################
91
+ ################################################################################################
92
+ # :section: Session Activation
93
+ ################################################################################################
65
94
 
66
- # Function: initialize
67
- # Constructs a FacebookSession
68
- #
69
- # Parameters:
70
- # api_key - your API key
71
- # api_secret - your API secret
72
- # desktop - boolean, whether this is a desktop client or not (defaults to false)
73
- # options.suppress_exceptions - boolean, set to true if you don't want exceptions to be thrown (defaults to false)
74
- def initialize(api_key, api_secret, suppress_exceptions = false)
75
- super(api_key, api_secret, suppress_exceptions)
76
- @desktop_auth_token = get_auth_token
77
- end
78
-
95
+ # Function: activate
96
+ # Use the internal auth_token to activate
79
97
  def activate
80
- activate_with_token(@desktop_auth_token)
98
+ result = call_method("auth.getSession", {:auth_token => @desktop_auth_token}, true)
99
+ if result != nil
100
+ @session_user_id = result.at("uid").inner_html
101
+ @session_key = result.at("session_key").inner_html
102
+ @session_secret = result.at("secret").inner_html
103
+ end
81
104
  end
82
105
 
83
106
  # Function: activate_with_previous_session
@@ -85,6 +108,7 @@ class FacebookDesktopSession < FacebookSession
85
108
  #
86
109
  # Parameters:
87
110
  # key - the session key to use
111
+ # secret - the session secret to use
88
112
  def activate_with_previous_session(key, secret)
89
113
  # set the session key and secret
90
114
  @session_key = key
@@ -95,50 +119,33 @@ class FacebookDesktopSession < FacebookSession
95
119
  @session_user_id = result.at("users_getLoggedInUser_response").inner_html
96
120
  end
97
121
 
98
- def is_valid?
99
- return (is_activated? and !session_expired?)
100
- end
101
-
102
- def session_user_id
103
- return @session_user_id
104
- end
105
-
106
- def session_key
107
- return @session_key
108
- end
109
-
110
122
  protected
111
123
 
112
- def is_activated?
113
- return (@session_key != nil and @session_secret != nil)
114
- end
115
-
116
- # Function: activate_with_token
117
- # Gets the session information available after current user logs in.
118
- #
119
- # Parameters:
120
- # auth_token - string token returned by auth.createToken (see: <get_auth_token>)
121
- def activate_with_token(auth_token)
122
- result = call_method("auth.getSession", {:auth_token => auth_token}, true)
123
- if result != nil
124
- @session_user_id = result.at("uid").inner_html
125
- @session_key = result.at("session_key").inner_html
126
- @session_secret = result.at("secret").inner_html
127
- end
128
- return result
129
- end
130
-
131
124
  # Function: auth_createToken
132
125
  # Returns a string auth_token
133
- def get_auth_token
126
+ def get_auth_token # :nodoc:
134
127
  result = call_method("auth.createToken", {})
135
128
  result = result.at("auth_createToken_response").inner_html.to_s ||= result.at("auth_createToken_response").inner_html.to_s
136
129
  return result
137
130
  end
138
131
 
132
+
133
+ ################################################################################################
134
+ ################################################################################################
135
+ # :section: Template Methods
136
+ ################################################################################################
137
+
138
+ # Function: is_activated?
139
+ # Returns true when we have activated ourselves somehow
140
+ def is_activated?
141
+ return (@session_key != nil and @session_secret != nil)
142
+ end
143
+
139
144
  # Function: get_secret
140
- # Template method, used by super::signature to generate a signature
141
- def get_secret(params)
145
+ # Used by super::signature to generate a signature.
146
+ # Desktop sessions should use their session_secret rather than the api_secret
147
+ # for any calls other than the call to createToken and getSession
148
+ def get_secret(params) # :nodoc:
142
149
 
143
150
  if ( params[:method] != "facebook.auth.getSession" and params[:method] != "facebook.auth.createToken")
144
151
  return @session_secret
@@ -29,7 +29,6 @@
29
29
 
30
30
  #
31
31
  # Some code was inspired by techniques used in Alpha Chen's old client.
32
- # Some code was ported from the official PHP5 client.
33
32
  #
34
33
 
35
34
  require "digest/md5"
@@ -49,111 +48,123 @@ WWW_PATH_INSTALL = "/install.php"
49
48
 
50
49
  class FacebookSession
51
50
 
52
- # error handling accessors
53
- attr_reader :last_call_was_successful, :last_error
54
- attr_writer :suppress_exceptions
55
-
56
- # SECTION: StandardErrors
51
+ ################################################################################################
52
+ ################################################################################################
53
+ # :section: Errors
54
+ ################################################################################################
57
55
 
58
56
  class RemoteStandardError < StandardError; end
59
57
  class ExpiredSessionStandardError < StandardError; end
60
58
  class NotActivatedStandardError < StandardError; end
59
+
60
+ ################################################################################################
61
+ ################################################################################################
62
+ # :section: Properties
63
+ ################################################################################################
64
+
65
+ # Property: session_user_id
66
+ # The user id of the user associated with this sesssion.
67
+ attr_reader :session_user_id
68
+
69
+ # Property: session_key
70
+ # The key for this session. You will need to save this for infinite sessions.
71
+ attr_reader :session_key
72
+
73
+ # Property: session_expires
74
+ # The expiration time of this session, as given from Facebook API login.
75
+ attr_reader :session_expires
76
+
77
+ # Property: last_error_message
78
+ # Contains a string message of the last error received from Facebook.
79
+ attr_reader :last_error_message
80
+
81
+ # Property: last_error_code
82
+ # Contains an integer code of the last error received from Facebook.
83
+ attr_reader :last_error_code
84
+
85
+ # Property: suppress_errors
86
+ # By default, RFacebook will throw exceptions when errors occur.
87
+ # You can set suppress_errors=true to stop these exceptions
88
+ # from being thrown.
89
+ attr_accessor :suppress_errors
90
+
91
+ # Property: logger
92
+ # Can be set to any valid logger (for example, RAIL_DEFAULT_LOGGER)
93
+ attr_accessor :logger
94
+
95
+ # Function: is_activated?
96
+ # Returns true when the session has been activated in some way
97
+ # THIS IS AN ABSTRACT METHOD (the value is determined differently for Web and Desktop sessions)
98
+ def is_activated?
99
+ raise StandardError
100
+ end
101
+
102
+ # Function: is_expired?
103
+ # Returns true when the session has expired
104
+ def is_expired?
105
+ # TODO: this should look at the @session_expires as well
106
+ return (@session_expired == true)
107
+ end
108
+
109
+ # Function: is_valid?
110
+ # Returns true when the session is definitely prepared to make API calls
111
+ def is_valid?
112
+ return (is_activated? and !is_expired?)
113
+ end
61
114
 
62
- # SECTION: Public Methods
115
+ # Function: is_ready?
116
+ # alias for is_valid?
117
+ def is_ready?
118
+ return is_valid?
119
+ end
120
+
121
+ ################################################################################################
122
+ ################################################################################################
123
+ # :section: Initialization
124
+ ################################################################################################
63
125
 
64
126
  # Function: initialize
65
127
  # Constructs a FacebookSession
66
128
  #
67
129
  # Parameters:
68
- # api_key - your API key
69
- # api_secret - your API secret
70
- # suppress_exceptions - boolean, set to true if you don't want exceptions to be thrown (defaults to false)
71
- def initialize(api_key, api_secret, suppress_exceptions = false)
130
+ # api_key - your API key
131
+ # api_secret - your API secret
132
+ # suppress_errors - boolean, set to true if you don't want exceptions to be thrown (defaults to false)
133
+ #
134
+ def initialize(api_key, api_secret, suppress_errors = false)
72
135
 
73
136
  # required parameters
74
137
  @api_key = api_key
75
138
  @api_secret = api_secret
76
-
77
- # calculated parameters
78
- @api_server_base_url = API_SERVER_BASE_URL
79
- @api_server_path = API_PATH_REST
80
139
 
81
140
  # optional parameters
82
- @suppress_exceptions = suppress_exceptions
141
+ @suppress_errors = suppress_errors
83
142
 
84
143
  # initialize internal state
85
- @last_call_was_successful = true
86
- @last_error = nil
144
+ @last_error_message = nil
145
+ @last_error_code = nil
87
146
  @session_expired = false
88
147
 
89
148
  # cache object for API calls
90
149
  @callcache = {}
91
-
92
- # default to XML formatted responses
93
- # @response_format = "XML"
94
-
95
- end
96
-
97
- def session_expired?
98
- return (@session_expired == true)
99
- end
100
-
101
- def logger
102
- return @logger
103
- end
104
-
105
- def logger=(externalLogger)
106
- @logger = externalLogger
107
- end
108
-
109
- # SECTION: Public Abstract Interface
110
-
111
- def is_valid?
112
- raise StandardError
113
- end
114
-
115
- def session_key
116
- raise StandardError
117
- end
118
-
119
- def session_user_id
120
- raise StandardError
121
- end
122
-
123
- def session_expires
124
- raise StandardError
125
- end
126
-
127
- def session_uid # deprecated
128
- return session_user_id
150
+
129
151
  end
130
-
131
- # def use_json
132
- # @response_format = "JSON"
133
- # end
134
- #
135
- # def use_xml
136
- # @response_format = "XML"
137
- # end
138
-
139
- # SECTION: Protected Abstract Interface
152
+
153
+ ################################################################################################
154
+ ################################################################################################
155
+ # :section: API Calls
156
+ ################################################################################################
140
157
  protected
141
-
142
- def get_secret(params)
143
- raise StandardError
144
- end
145
-
146
- def is_activated?
147
- raise StandardError
148
- end
149
-
150
- # SECTION: Protected Concrete Interface
151
-
158
+
152
159
  # Function: method_missing
153
160
  # This allows *any* Facebook method to be called, using the Ruby
154
161
  # mechanism for responding to unimplemented methods. Basically,
155
162
  # this converts a call to "auth_getSession" to "auth.getSession"
156
- # and does the proper API call using the parameter hash given.
163
+ # and does the proper API call using the parameter hash given.
164
+ #
165
+ # This allows you to call an API method such as facebook.users.getInfo
166
+ # by calling "fbsession.users_getInfo"
167
+ #
157
168
  def method_missing(methodSymbol, *params)
158
169
  tokens = methodSymbol.to_s.split("_")
159
170
  if tokens[0] == "cached"
@@ -163,23 +174,6 @@ class FacebookSession
163
174
  return call_method(tokens.join("."), params.first)
164
175
  end
165
176
  end
166
-
167
- def cached_call_method(method,params)
168
- key = cache_key_for(method,params)
169
- @logger.debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#cached_call_method - #{method}(#{params.inspect}) - attempting to hit cache" if @logger
170
- return @callcache[key] ||= call_method(method,params)
171
- end
172
-
173
- def cache_key_for(method,params)
174
- pairs = []
175
- params.each do |k,v|
176
- if v.is_a?(Array)
177
- v = v.join(",")
178
- end
179
- pairs << "#{k}=#{v}"
180
- end
181
- return "#{method}(#{pairs.sort.join("...")})".to_sym
182
- end
183
177
 
184
178
 
185
179
  # Function: call_method
@@ -189,7 +183,7 @@ class FacebookSession
189
183
  # method - i.e. "users.getInfo"
190
184
  # params - hash of key,value pairs for the parameters to this method
191
185
  # use_ssl - set to true if the call will be made over SSL
192
- def call_method(method, params={}, use_ssl=false)
186
+ def call_method(method, params={}, use_ssl=false) # :nodoc:
193
187
 
194
188
  @logger.debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#call_method - #{method}(#{params.inspect}) - making remote call" if @logger
195
189
 
@@ -202,7 +196,7 @@ class FacebookSession
202
196
  if (!params)
203
197
  params = {}
204
198
  end
205
- params = params.dup # patch courtesy of Project Agape
199
+ params = params.dup
206
200
 
207
201
  # params[:format] ||= @response_format
208
202
  params[:method] = "facebook.#{method}"
@@ -220,33 +214,31 @@ class FacebookSession
220
214
  # set up the param_signature value in the params
221
215
  params[:sig] = param_signature(params)
222
216
 
223
- # prepare internal state
224
- @last_call_was_successful = true
225
-
226
- # do the request
227
- xmlstring = post_request(@api_server_base_url, @api_server_path, method, params, use_ssl)
228
- # @logger.debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#call_method - #{method}(#{params.inspect}) - raw XML response: #{xmlstring}" if @logger
229
- xml = Facepricot.new(xmlstring)
217
+ # make the remote call and contain the results in a Facepricot XML object
218
+ rawxml = post_request(params, use_ssl)
219
+ xml = Facepricot.new(rawxml)
230
220
 
231
221
  # error checking
232
222
  if xml.at("error_response")
233
223
 
234
224
  @logger.debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#call_method - #{method}(#{params.inspect}) - remote call failed" if @logger
235
225
 
236
- @last_call_was_successful = false
237
- code = xml.at("error_code").inner_html
226
+ code = xml.at("error_code").inner_html.to_i
238
227
  msg = xml.at("error_msg").inner_html
239
- @last_error = "ERROR #{code}: #{msg} (#{method.inspect}, #{params.inspect})"
228
+ @last_error_message = "ERROR #{code}: #{msg} (#{method.inspect}, #{params.inspect})"
240
229
  @last_error_code = code
241
230
 
242
231
  # check to see if this error was an expired session error
243
- if code.to_i == 102
232
+ if code == 102
244
233
  @session_expired = true
245
- raise ExpiredSessionStandardError, @last_error unless @suppress_exceptions == true
234
+ raise ExpiredSessionStandardError, @last_error_message unless @suppress_errors == true
246
235
  end
247
236
 
237
+ # TODO: check for method not existing error (what code is it?)
238
+ # and convert it to a Ruby "NoMethodError"
239
+
248
240
  # otherwise, just throw a generic expired session
249
- raise RemoteStandardError, @last_error unless @suppress_exceptions == true
241
+ raise RemoteStandardError, @last_error_message unless @suppress_errors == true
250
242
 
251
243
  return nil
252
244
  end
@@ -254,43 +246,99 @@ class FacebookSession
254
246
  return xml
255
247
  end
256
248
 
257
-
258
- private
259
-
260
- # SECTION: Private Concrete Interface
261
-
262
249
  # Function: post_request
263
- # Does a post to the given server/path, using the params as formdata
250
+ # Posts a request to the remote Facebook API servers, and returns the
251
+ # raw body of the result
264
252
  #
265
253
  # Parameters:
266
- # api_server_base_url - i.e. "api.facebook.com"
267
- # api_server_path - i.e. "/restserver.php"
268
- # method - i.e. "facebook.users.getInfo"
269
- # params - hash of key/value pairs that get sent as form data to the server
270
- # use_ssl - set to true if you want to use SSL for this request
271
- def post_request(api_server_base_url, api_server_path, method, params, use_ssl)
254
+ # params - a Hash of the post parameters to send to the REST API
255
+ # use_ssl - defaults to false, set to true if you want to use SSL for the POST
256
+ def post_request(params, use_ssl=false)
257
+
272
258
  # get a server handle
273
259
  port = (use_ssl == true) ? 443 : 80
274
- http_server = Net::HTTP.new(@api_server_base_url, port)
260
+ http_server = Net::HTTP.new(API_SERVER_BASE_URL, port)
275
261
  http_server.use_ssl = use_ssl
276
262
 
277
263
  # build a request
278
- http_request = Net::HTTP::Post.new(@api_server_path)
264
+ http_request = Net::HTTP::Post.new(API_PATH_REST)
279
265
  http_request.form_data = params
280
- response = http_server.start{|http| http.request(http_request)}.body
281
266
 
282
- # return the text of the body
283
- return response
267
+ # get the response XML
268
+ return http_server.start{|http| http.request(http_request)}.body
284
269
  end
285
270
 
271
+ # Function: cached_call_method
272
+ # Does the same thing as call_method, except that the response is cached for
273
+ # the lifetime of the FacebookSession.
274
+ #
275
+ # Parameters:
276
+ # method - i.e. "users.getInfo"
277
+ # params - hash of key,value pairs for the parameters to this method
278
+ # use_ssl - set to true if the call will be made over SSL
279
+ def cached_call_method(method,params={},use_ssl=false) # :nodoc:
280
+ key = cache_key_for(method,params)
281
+ @logger.debug "** RFACEBOOK(GEM) - RFacebook::FacebookSession\#cached_call_method - #{method}(#{params.inspect}) - attempting to hit cache" if @logger
282
+ return @callcache[key] ||= call_method(method,params,use_ssl)
283
+ end
284
+
285
+ # Function: cache_key_for
286
+ # Key to use for cached method calls.
287
+ #
288
+ # Parameters:
289
+ # method - the API method being cached
290
+ # params - a Hash of the parameters being sent to the API
291
+ #
292
+ def cache_key_for(method,params) # :nodoc:
293
+ pairs = []
294
+ params.each do |k,v|
295
+ if v.is_a?(Array)
296
+ v = v.join(",")
297
+ end
298
+ pairs << "#{k}=#{v}"
299
+ end
300
+ return "#{method}(#{pairs.sort.join("...")})".to_sym
301
+ end
302
+
303
+ ################################################################################################
304
+ ################################################################################################
305
+ # :section: Signature Generation
306
+ ################################################################################################
307
+
308
+ # Function: get_secret
309
+ # Returns the proper secret to sign a set of parameters with.
310
+ # A WebSession will always use the api_secret, but a DesktopSession
311
+ # sometimes needs to use a session_secret.
312
+ #
313
+ # THIS IS AN ABSTRACT METHOD
314
+ #
315
+ # Parameters:
316
+ # params - a Hash containing the parameters to sign
317
+ #
318
+ def get_secret(params) # :nodoc:
319
+ raise StandardError
320
+ end
321
+
286
322
  # Function: param_signature
287
323
  # Generates a param_signature for a call to the API, per the spec on Facebook
288
324
  # see: <http://developers.facebook.com/documentation.php?v=1.0&doc=auth>
289
- def param_signature(params)
325
+ #
326
+ # Parameters:
327
+ # params - a Hash containing the parameters to sign
328
+ #
329
+ def param_signature(params) # :nodoc:
290
330
  return generate_signature(params, get_secret(params));
291
331
  end
292
332
 
293
- def generate_signature(hash, secret)
333
+ # Function: generate_signature
334
+ # Generates a Facebook signature. Used for generating API calls, as well
335
+ # as for checking the signature from a Canvas page
336
+ #
337
+ # Parameters:
338
+ # hash - a Hash containing the parameters to sign
339
+ # secret - the API or session secret to use to sign the parameters
340
+ #
341
+ def generate_signature(hash, secret) # :nodoc:
294
342
  args = []
295
343
  hash.each do |k,v|
296
344
  args << "#{k}=#{v}"
@@ -300,6 +348,43 @@ class FacebookSession
300
348
  return Digest::MD5.hexdigest("#{requestStr}#{secret}")
301
349
  end
302
350
 
351
+ ################################################################################################
352
+ ################################################################################################
353
+ # :section: Deprecated Methods
354
+ ################################################################################################
355
+ public
356
+
357
+ # DEPRECATED in favor of session_user_id
358
+ def session_uid # :nodoc:
359
+ @logger.debug "** RFACEBOOK(GEM) - DEPRECATION NOTICE - fbsession.session_uid is deprecated in favor of fbsession.session_user_id" if @logger
360
+ return self.session_user_id
361
+ end
362
+
363
+ # DEPRECATED in favor of last_error_message
364
+ def last_error # :nodoc:
365
+ @logger.debug "** RFACEBOOK(GEM) - DEPRECATION NOTICE - fbsession.last_error is deprecated in favor of fbsession.last_error_message" if @logger
366
+ return self.last_error_message
367
+ end
368
+
369
+ # DEPRECATED in favor of suppress_errors
370
+ def suppress_exceptions # :nodoc:
371
+ @logger.debug "** RFACEBOOK(GEM) - DEPRECATION NOTICE - fbsession.suppress_exceptions is deprecated in favor of fbsession.suppress_errors" if @logger
372
+ return self.suppress_errors
373
+ end
374
+
375
+ # DEPRECATED in favor of suppress_errors
376
+ def suppress_exceptions=(value) # :nodoc:
377
+ @logger.debug "** RFACEBOOK(GEM) - DEPRECATION NOTICE - fbsession.suppress_exceptions is deprecated in favor of fbsession.suppress_errors" if @logger
378
+ self.suppress_errors = value
379
+ end
380
+
381
+ # DEPRECATED in favor of is_expired?
382
+ def session_expired?
383
+ @logger.debug "** RFACEBOOK(GEM) - DEPRECATION NOTICE - fbsession.session_expired? is deprecated in favor of fbsession.is_expired?" if @logger
384
+ return self.is_expired?
385
+ end
386
+
387
+
303
388
  end
304
389
 
305
390
  end