rfacebook 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -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