tenderlove-facebooker 1.0.16.20090319151701

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/COPYING.rdoc +19 -0
  3. data/Manifest.txt +123 -0
  4. data/README.rdoc +103 -0
  5. data/Rakefile +86 -0
  6. data/TODO.rdoc +4 -0
  7. data/examples/desktop_login.rb +14 -0
  8. data/facebooker.gemspec +38 -0
  9. data/generators/facebook/facebook_generator.rb +14 -0
  10. data/generators/facebook/templates/config/facebooker.yml +46 -0
  11. data/generators/facebook/templates/public/javascripts/facebooker.js +99 -0
  12. data/generators/facebook_controller/USAGE +33 -0
  13. data/generators/facebook_controller/facebook_controller_generator.rb +40 -0
  14. data/generators/facebook_controller/templates/controller.rb +7 -0
  15. data/generators/facebook_controller/templates/functional_test.rb +11 -0
  16. data/generators/facebook_controller/templates/helper.rb +2 -0
  17. data/generators/facebook_controller/templates/view.fbml.erb +2 -0
  18. data/generators/facebook_controller/templates/view.html.erb +2 -0
  19. data/generators/facebook_publisher/facebook_publisher_generator.rb +14 -0
  20. data/generators/facebook_publisher/templates/create_facebook_templates.rb +15 -0
  21. data/generators/facebook_publisher/templates/publisher.rb +3 -0
  22. data/generators/facebook_scaffold/USAGE +27 -0
  23. data/generators/facebook_scaffold/facebook_scaffold_generator.rb +118 -0
  24. data/generators/facebook_scaffold/templates/controller.rb +93 -0
  25. data/generators/facebook_scaffold/templates/facebook_style.css +2579 -0
  26. data/generators/facebook_scaffold/templates/functional_test.rb +89 -0
  27. data/generators/facebook_scaffold/templates/helper.rb +2 -0
  28. data/generators/facebook_scaffold/templates/layout.fbml.erb +6 -0
  29. data/generators/facebook_scaffold/templates/layout.html.erb +17 -0
  30. data/generators/facebook_scaffold/templates/style.css +74 -0
  31. data/generators/facebook_scaffold/templates/view_edit.fbml.erb +13 -0
  32. data/generators/facebook_scaffold/templates/view_edit.html.erb +18 -0
  33. data/generators/facebook_scaffold/templates/view_index.fbml.erb +24 -0
  34. data/generators/facebook_scaffold/templates/view_index.html.erb +24 -0
  35. data/generators/facebook_scaffold/templates/view_new.fbml.erb +12 -0
  36. data/generators/facebook_scaffold/templates/view_new.html.erb +17 -0
  37. data/generators/facebook_scaffold/templates/view_show.fbml.erb +10 -0
  38. data/generators/facebook_scaffold/templates/view_show.html.erb +10 -0
  39. data/generators/publisher/publisher_generator.rb +14 -0
  40. data/generators/xd_receiver/templates/xd_receiver.html +10 -0
  41. data/generators/xd_receiver/xd_receiver_generator.rb +9 -0
  42. data/init.rb +72 -0
  43. data/install.rb +12 -0
  44. data/lib/facebooker/adapters/adapter_base.rb +87 -0
  45. data/lib/facebooker/adapters/bebo_adapter.rb +75 -0
  46. data/lib/facebooker/adapters/facebook_adapter.rb +52 -0
  47. data/lib/facebooker/admin.rb +42 -0
  48. data/lib/facebooker/batch_request.rb +44 -0
  49. data/lib/facebooker/data.rb +57 -0
  50. data/lib/facebooker/feed.rb +78 -0
  51. data/lib/facebooker/logging.rb +51 -0
  52. data/lib/facebooker/mobile.rb +20 -0
  53. data/lib/facebooker/mock/service.rb +50 -0
  54. data/lib/facebooker/mock/session.rb +18 -0
  55. data/lib/facebooker/model.rb +135 -0
  56. data/lib/facebooker/models/affiliation.rb +10 -0
  57. data/lib/facebooker/models/album.rb +11 -0
  58. data/lib/facebooker/models/applicationproperties.rb +39 -0
  59. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  60. data/lib/facebooker/models/cookie.rb +10 -0
  61. data/lib/facebooker/models/education_info.rb +11 -0
  62. data/lib/facebooker/models/event.rb +28 -0
  63. data/lib/facebooker/models/friend_list.rb +16 -0
  64. data/lib/facebooker/models/group.rb +36 -0
  65. data/lib/facebooker/models/info_item.rb +10 -0
  66. data/lib/facebooker/models/info_section.rb +10 -0
  67. data/lib/facebooker/models/location.rb +8 -0
  68. data/lib/facebooker/models/notifications.rb +17 -0
  69. data/lib/facebooker/models/page.rb +27 -0
  70. data/lib/facebooker/models/photo.rb +12 -0
  71. data/lib/facebooker/models/tag.rb +12 -0
  72. data/lib/facebooker/models/user.rb +426 -0
  73. data/lib/facebooker/models/video.rb +9 -0
  74. data/lib/facebooker/models/work_info.rb +9 -0
  75. data/lib/facebooker/parser.rb +589 -0
  76. data/lib/facebooker/rails/controller.rb +304 -0
  77. data/lib/facebooker/rails/cucumber/world.rb +46 -0
  78. data/lib/facebooker/rails/cucumber.rb +28 -0
  79. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  80. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  81. data/lib/facebooker/rails/facebook_request_fix.rb +24 -0
  82. data/lib/facebooker/rails/facebook_session_handling.rb +69 -0
  83. data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
  84. data/lib/facebooker/rails/facebook_url_rewriting.rb +39 -0
  85. data/lib/facebooker/rails/helpers/fb_connect.rb +89 -0
  86. data/lib/facebooker/rails/helpers.rb +762 -0
  87. data/lib/facebooker/rails/integration_session.rb +38 -0
  88. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  89. data/lib/facebooker/rails/publisher.rb +526 -0
  90. data/lib/facebooker/rails/routing.rb +49 -0
  91. data/lib/facebooker/rails/test_helpers.rb +68 -0
  92. data/lib/facebooker/rails/utilities.rb +22 -0
  93. data/lib/facebooker/server_cache.rb +24 -0
  94. data/lib/facebooker/service.rb +94 -0
  95. data/lib/facebooker/session.rb +584 -0
  96. data/lib/facebooker/version.rb +9 -0
  97. data/lib/facebooker.rb +174 -0
  98. data/lib/net/http_multipart_post.rb +123 -0
  99. data/lib/tasks/facebooker.rake +18 -0
  100. data/lib/tasks/tunnel.rake +46 -0
  101. data/rails/init.rb +1 -0
  102. data/setup.rb +1585 -0
  103. data/templates/layout.erb +24 -0
  104. data/test/facebooker/adapters_test.rb +96 -0
  105. data/test/facebooker/admin_test.rb +102 -0
  106. data/test/facebooker/batch_request_test.rb +83 -0
  107. data/test/facebooker/data_test.rb +86 -0
  108. data/test/facebooker/logging_test.rb +43 -0
  109. data/test/facebooker/mobile_test.rb +45 -0
  110. data/test/facebooker/model_test.rb +123 -0
  111. data/test/facebooker/models/event_test.rb +15 -0
  112. data/test/facebooker/models/user_test.rb +295 -0
  113. data/test/facebooker/rails/publisher_test.rb +452 -0
  114. data/test/facebooker/rails_integration_test.rb +1312 -0
  115. data/test/facebooker/server_cache_test.rb +44 -0
  116. data/test/facebooker/session_test.rb +614 -0
  117. data/test/facebooker_test.rb +925 -0
  118. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  119. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  120. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  121. data/test/net/http_multipart_post_test.rb +52 -0
  122. data/test/rails_test_helper.rb +11 -0
  123. data/test/test_helper.rb +66 -0
  124. metadata +217 -0
@@ -0,0 +1,584 @@
1
+ require 'cgi'
2
+
3
+ module Facebooker
4
+ #
5
+ # Raised when trying to perform an operation on a user
6
+ # other than the logged in user (if that's unallowed)
7
+ class NonSessionUser < StandardError; end
8
+ class Session
9
+ class SessionExpired < StandardError; end
10
+ class UnknownError < StandardError; end
11
+ class ServiceUnavailable < StandardError; end
12
+ class MaxRequestsDepleted < StandardError; end
13
+ class HostNotAllowed < StandardError; end
14
+ class MissingOrInvalidParameter < StandardError; end
15
+ class InvalidAPIKey < StandardError; end
16
+ class SessionExpired < StandardError; end
17
+ class CallOutOfOrder < StandardError; end
18
+ class IncorrectSignature < StandardError; end
19
+ class SignatureTooOld < StandardError; end
20
+ class TooManyUserCalls < StandardError; end
21
+ class TooManyUserActionCalls < StandardError; end
22
+ class InvalidFeedTitleLink < StandardError; end
23
+ class InvalidFeedTitleLength < StandardError; end
24
+ class InvalidFeedTitleName < StandardError; end
25
+ class BlankFeedTitle < StandardError; end
26
+ class FeedBodyLengthTooLong < StandardError; end
27
+ class InvalidFeedPhotoSource < StandardError; end
28
+ class InvalidFeedPhotoLink < StandardError; end
29
+ class TemplateDataMissingRequiredTokens < StandardError; end
30
+ class FeedMarkupInvalid < StandardError; end
31
+ class FeedTitleDataInvalid < StandardError; end
32
+ class FeedTitleTemplateInvalid < StandardError; end
33
+ class FeedBodyDataInvalid < StandardError; end
34
+ class FeedBodyTemplateInvalid < StandardError; end
35
+ class FeedPhotosNotRetrieved < StandardError; end
36
+ class FeedTargetIdsInvalid < StandardError; end
37
+ class TemplateBundleInvalid < StandardError; end
38
+ class ConfigurationMissing < StandardError; end
39
+ class FQLParseError < StandardError; end
40
+ class FQLFieldDoesNotExist < StandardError; end
41
+ class FQLTableDoesNotExist < StandardError; end
42
+ class FQLStatementNotIndexable < StandardError; end
43
+ class FQLFunctionDoesNotExist < StandardError; end
44
+ class FQLWrongNumberArgumentsPassedToFunction < StandardError; end
45
+ class InvalidAlbumId < StandardError; end
46
+ class AlbumIsFull < StandardError; end
47
+ class MissingOrInvalidImageFile < StandardError; end
48
+ class TooManyUnapprovedPhotosPending < StandardError; end
49
+ class ExtendedPermissionRequired < StandardError; end
50
+ class InvalidFriendList < StandardError; end
51
+ class UserRegistrationFailed < StandardError
52
+ attr_accessor :failed_users
53
+ end
54
+
55
+ API_SERVER_BASE_URL = ENV["FACEBOOKER_API"] == "new" ? "api.new.facebook.com" : "api.facebook.com"
56
+ API_PATH_REST = "/restserver.php"
57
+ WWW_SERVER_BASE_URL = ENV["FACEBOOKER_API"] == "new" ? "www.new.facebook.com" : "www.facebook.com"
58
+ WWW_PATH_LOGIN = "/login.php"
59
+ WWW_PATH_ADD = "/add.php"
60
+ WWW_PATH_INSTALL = "/install.php"
61
+
62
+ attr_writer :auth_token
63
+ attr_reader :session_key
64
+
65
+ def self.create(api_key=nil, secret_key=nil)
66
+ api_key ||= self.api_key
67
+ secret_key ||= self.secret_key
68
+ raise ArgumentError unless !api_key.nil? && !secret_key.nil?
69
+ new(api_key, secret_key)
70
+ end
71
+
72
+ def self.api_key
73
+ extract_key_from_environment(:api) || extract_key_from_configuration_file(:api) rescue report_inability_to_find_key(:api)
74
+ end
75
+
76
+ def self.secret_key
77
+ extract_key_from_environment(:secret) || extract_key_from_configuration_file(:secret) rescue report_inability_to_find_key(:secret)
78
+ end
79
+
80
+ def self.current
81
+ Thread.current['facebook_session']
82
+ end
83
+
84
+ def self.current=(session)
85
+ Thread.current['facebook_session'] = session
86
+ end
87
+
88
+ def login_url(options={})
89
+ options = default_login_url_options.merge(options)
90
+ "#{Facebooker.login_url_base(@api_key)}#{login_url_optional_parameters(options)}"
91
+ end
92
+
93
+ def install_url(options={})
94
+ "#{Facebooker.install_url_base(@api_key)}#{install_url_optional_parameters(options)}"
95
+ end
96
+
97
+ # The url to get user to approve extended permissions
98
+ # http://wiki.developers.facebook.com/index.php/Extended_permission
99
+ #
100
+ # permissions:
101
+ # * email
102
+ # * offline_access
103
+ # * status_update
104
+ # * photo_upload
105
+ # * video_upload
106
+ # * create_listing
107
+ # * create_event
108
+ # * rsvp_event
109
+ # * sms
110
+ def permission_url(permission,options={})
111
+ options = default_login_url_options.merge(options)
112
+ "http://#{Facebooker.www_server_base_url}/authorize.php?api_key=#{@api_key}&v=1.0&ext_perm=#{permission}#{install_url_optional_parameters(options)}"
113
+ end
114
+
115
+ def install_url_optional_parameters(options)
116
+ optional_parameters = []
117
+ optional_parameters += add_next_parameters(options)
118
+ optional_parameters.join
119
+ end
120
+
121
+ def add_next_parameters(options)
122
+ opts = []
123
+ opts << "&next=#{CGI.escape(options[:next])}" if options[:next]
124
+ opts << "&next_cancel=#{CGI.escape(options[:next_cancel])}" if options[:next_cancel]
125
+ opts
126
+ end
127
+
128
+ def login_url_optional_parameters(options)
129
+ # It is important that unused options are omitted as stuff like &canvas=false will still display the canvas.
130
+ optional_parameters = []
131
+ optional_parameters += add_next_parameters(options)
132
+ optional_parameters << "&skipcookie=true" if options[:skip_cookie]
133
+ optional_parameters << "&hide_checkbox=true" if options[:hide_checkbox]
134
+ optional_parameters << "&canvas=true" if options[:canvas]
135
+ optional_parameters.join
136
+ end
137
+
138
+ def default_login_url_options
139
+ {}
140
+ end
141
+
142
+ def initialize(api_key, secret_key)
143
+ @api_key = api_key
144
+ @secret_key = secret_key
145
+ end
146
+
147
+ def secret_for_method(method_name)
148
+ @secret_key
149
+ end
150
+
151
+ def auth_token
152
+ @auth_token ||= post 'facebook.auth.createToken'
153
+ end
154
+
155
+ def infinite?
156
+ @expires == 0
157
+ end
158
+
159
+ def expired?
160
+ @expires.nil? || (!infinite? && Time.at(@expires) <= Time.now)
161
+ end
162
+
163
+ def secured?
164
+ !@session_key.nil? && !expired?
165
+ end
166
+
167
+ def secure!
168
+ response = post 'facebook.auth.getSession', :auth_token => auth_token
169
+ secure_with!(response['session_key'], response['uid'], response['expires'], response['secret'])
170
+ end
171
+
172
+ def secure_with!(session_key, uid = nil, expires = nil, secret_from_session = nil)
173
+ @session_key = session_key
174
+ @uid = uid ? Integer(uid) : post('facebook.users.getLoggedInUser', :session_key => session_key)
175
+ @expires = Integer(expires)
176
+ @secret_from_session = secret_from_session
177
+ end
178
+
179
+ def fql_query(query, format = 'XML')
180
+ post('facebook.fql.query', :query => query, :format => format) do |response|
181
+ type = response.shift
182
+ return [] if type.nil?
183
+ response.shift.map do |hash|
184
+ case type
185
+ when 'user'
186
+ user = User.new
187
+ user.session = self
188
+ user.populate_from_hash!(hash)
189
+ user
190
+ when 'photo'
191
+ Photo.from_hash(hash)
192
+ when 'page'
193
+ Page.from_hash(hash)
194
+ when 'page_admin'
195
+ Page.from_hash(hash)
196
+ when 'event_member'
197
+ Event::Attendance.from_hash(hash)
198
+ else
199
+ hash
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ def user
206
+ @user ||= User.new(uid, self)
207
+ end
208
+
209
+ #
210
+ # This one has so many parameters, a Hash seemed cleaner than a long param list. Options can be:
211
+ # :uid => Filter by events associated with a user with this uid
212
+ # :eids => Filter by this list of event ids. This is a comma-separated list of eids.
213
+ # :start_time => Filter with this UTC as lower bound. A missing or zero parameter indicates no lower bound. (Time or Integer)
214
+ # :end_time => Filter with this UTC as upper bound. A missing or zero parameter indicates no upper bound. (Time or Integer)
215
+ # :rsvp_status => Filter by this RSVP status.
216
+ def events(options = {})
217
+ @events ||= post('facebook.events.get', options) do |response|
218
+ response.map do |hash|
219
+ Event.from_hash(hash)
220
+ end
221
+ end
222
+ end
223
+
224
+ def event_members(eid)
225
+ @members ||= post('facebook.events.getMembers', :eid => eid) do |response|
226
+ response.map do |attendee_hash|
227
+ Event::Attendance.from_hash(attendee_hash)
228
+ end
229
+ end
230
+ end
231
+
232
+ def users_standard(user_ids, fields=[])
233
+ post("facebook.users.getStandardInfo",:uids=>user_ids.join(","),:fields=>User.standard_fields(fields)) do |users|
234
+ users.map { |u| User.new(u)}
235
+ end
236
+ end
237
+
238
+ def users(user_ids, fields=[])
239
+ post("facebook.users.getInfo",:uids=>user_ids.join(","),:fields=>User.user_fields(fields)) do |users|
240
+ users.map { |u| User.new(u)}
241
+ end
242
+ end
243
+
244
+ def pages(options = {})
245
+ raise ArgumentError, 'fields option is mandatory' unless options.has_key?(:fields)
246
+ @pages ||= {}
247
+ @pages[options] ||= post('facebook.pages.getInfo', options) do |response|
248
+ response.map do |hash|
249
+ Page.from_hash(hash)
250
+ end
251
+ end
252
+ end
253
+
254
+ # Takes page_id and uid, returns true if uid is a fan of the page_id
255
+ def is_fan(page_id, uid)
256
+ post('facebook.pages.isFan', :page_id=>page_id, :uid=>uid)
257
+ end
258
+
259
+ #
260
+ # Returns a proxy object for handling calls to Facebook cached items
261
+ # such as images and FBML ref handles
262
+ def server_cache
263
+ Facebooker::ServerCache.new(self)
264
+ end
265
+
266
+ #
267
+ # Returns a proxy object for handling calls to the Facebook Data API
268
+ def data
269
+ Facebooker::Data.new(self)
270
+ end
271
+
272
+ def admin
273
+ Facebooker::Admin.new(self)
274
+ end
275
+
276
+ def mobile
277
+ Facebooker::Mobile.new(self)
278
+ end
279
+
280
+ #
281
+ # Given an array like:
282
+ # [[userid, otheruserid], [yetanotherid, andanotherid]]
283
+ # returns a Hash indicating friendship of those pairs:
284
+ # {[userid, otheruserid] => true, [yetanotherid, andanotherid] => false}
285
+ # if one of the Hash values is nil, it means the facebook platform's answer is "I don't know"
286
+ def check_friendship(array_of_pairs_of_users)
287
+ uids1 = []
288
+ uids2 = []
289
+ array_of_pairs_of_users.each do |pair|
290
+ uids1 << pair.first
291
+ uids2 << pair.last
292
+ end
293
+ post('facebook.friends.areFriends', :uids1 => uids1.join(','), :uids2 => uids2.join(','))
294
+ end
295
+
296
+ def get_photos(pids = nil, subj_id = nil, aid = nil)
297
+ if [subj_id, pids, aid].all? {|arg| arg.nil?}
298
+ raise ArgumentError, "Can't get a photo without a picture, album or subject ID"
299
+ end
300
+ @photos = post('facebook.photos.get', :subj_id => subj_id, :pids => pids, :aid => aid ) do |response|
301
+ response.map do |hash|
302
+ Photo.from_hash(hash)
303
+ end
304
+ end
305
+ end
306
+
307
+ def get_albums(aids)
308
+ @albums = post('facebook.photos.getAlbums', :aids => aids) do |response|
309
+ response.map do |hash|
310
+ Album.from_hash(hash)
311
+ end
312
+ end
313
+ end
314
+
315
+ def get_tags(pids)
316
+ @tags = post('facebook.photos.getTags', :pids => pids) do |response|
317
+ response.map do |hash|
318
+ Tag.from_hash(hash)
319
+ end
320
+ end
321
+ end
322
+
323
+ def add_tags(pid, x, y, tag_uid = nil, tag_text = nil )
324
+ if [tag_uid, tag_text].all? {|arg| arg.nil?}
325
+ raise ArgumentError, "Must enter a name or string for this tag"
326
+ end
327
+ @tags = post('facebook.photos.addTag', :pid => pid, :tag_uid => tag_uid, :tag_text => tag_text, :x => x, :y => y )
328
+ end
329
+
330
+ def send_notification(user_ids, fbml, email_fbml = nil)
331
+ params = {:notification => fbml, :to_ids => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(',')}
332
+ if email_fbml
333
+ params[:email] = email_fbml
334
+ end
335
+ params[:type]="user_to_user"
336
+ # if there is no uid, this is an announcement
337
+ unless uid?
338
+ params[:type]="app_to_user"
339
+ end
340
+
341
+ post 'facebook.notifications.send', params,uid?
342
+ end
343
+
344
+ ##
345
+ # Register a template bundle with Facebook.
346
+ # returns the template id to use to send using this template
347
+ def register_template_bundle(one_line_story_templates,short_story_templates=nil,full_story_template=nil, action_links=nil)
348
+ parameters = {:one_line_story_templates => Array(one_line_story_templates).to_json}
349
+
350
+ parameters[:action_links] = action_links.to_json unless action_links.blank?
351
+
352
+ parameters[:short_story_templates] = Array(short_story_templates).to_json unless short_story_templates.blank?
353
+
354
+ parameters[:full_story_template] = full_story_template.to_json unless full_story_template.blank?
355
+
356
+ post("facebook.feed.registerTemplateBundle", parameters, false)
357
+ end
358
+
359
+ ##
360
+ # publish a previously rendered template bundle
361
+ # see http://wiki.developers.facebook.com/index.php/Feed.publishUserAction
362
+ #
363
+ def publish_user_action(bundle_id,data={},target_ids=nil,body_general=nil)
364
+ parameters={:template_bundle_id=>bundle_id,:template_data=>data.to_json}
365
+ parameters[:target_ids] = target_ids unless target_ids.blank?
366
+ parameters[:body_general] = body_general unless body_general.blank?
367
+ post("facebook.feed.publishUserAction", parameters)
368
+ end
369
+
370
+
371
+ ##
372
+ # Send email to as many as 100 users at a time
373
+ def send_email(user_ids, subject, text, fbml = nil)
374
+ user_ids = Array(user_ids)
375
+ params = {:fbml => fbml, :recipients => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(','), :text => text, :subject => subject}
376
+ post 'facebook.notifications.sendEmail', params
377
+ end
378
+
379
+ # Only serialize the bare minimum to recreate the session.
380
+ def marshal_load(variables)#:nodoc:
381
+ fields_to_serialize.each_with_index{|field, index| instance_variable_set_value(field, variables[index])}
382
+ end
383
+
384
+ # Only serialize the bare minimum to recreate the session.
385
+ def marshal_dump#:nodoc:
386
+ fields_to_serialize.map{|field| instance_variable_value(field)}
387
+ end
388
+
389
+ # Only serialize the bare minimum to recreate the session.
390
+ def to_yaml( opts = {} )
391
+ YAML::quick_emit(self.object_id, opts) do |out|
392
+ out.map(taguri) do |map|
393
+ fields_to_serialize.each do |field|
394
+ map.add(field, instance_variable_value(field))
395
+ end
396
+ end
397
+ end
398
+ end
399
+
400
+ def instance_variable_set_value(field, value)
401
+ self.instance_variable_set("@#{field}", value)
402
+ end
403
+
404
+ def instance_variable_value(field)
405
+ self.instance_variable_get("@#{field}")
406
+ end
407
+
408
+ def fields_to_serialize
409
+ %w(session_key uid expires secret_from_session auth_token api_key secret_key)
410
+ end
411
+
412
+ class Desktop < Session
413
+ def login_url
414
+ super + "&auth_token=#{auth_token}"
415
+ end
416
+
417
+ def secret_for_method(method_name)
418
+ secret = auth_request_methods.include?(method_name) ? super : @secret_from_session
419
+ secret
420
+ end
421
+
422
+ def post(method, params = {},use_session=false)
423
+ if method == 'facebook.profile.getFBML' || method == 'facebook.profile.setFBML'
424
+ raise NonSessionUser.new("User #{@uid} is not the logged in user.") unless @uid == params[:uid]
425
+ end
426
+ super
427
+ end
428
+ private
429
+ def auth_request_methods
430
+ ['facebook.auth.getSession', 'facebook.auth.createToken']
431
+ end
432
+ end
433
+
434
+ def batch_request?
435
+ @batch_request
436
+ end
437
+
438
+ def add_to_batch(req,&proc)
439
+ batch_request = BatchRequest.new(req,proc)
440
+ Thread.current[:facebooker_current_batch_queue]<<batch_request
441
+ batch_request
442
+ end
443
+
444
+ # Submit the enclosed requests for this session inside a batch
445
+ #
446
+ # All requests will be sent to Facebook at the end of the block
447
+ # each method inside the block will return a proxy object
448
+ # attempting to access the proxy before the end of the block will yield an exception
449
+ #
450
+ # For Example:
451
+ #
452
+ # facebook_session.batch do
453
+ # @send_result = facebook_session.send_notification([12451752],"Woohoo")
454
+ # @albums = facebook_session.user.albums
455
+ # end
456
+ # puts @albums.first.inspect
457
+ #
458
+ # is valid, however
459
+ #
460
+ # facebook_session.batch do
461
+ # @send_result = facebook_session.send_notification([12451752],"Woohoo")
462
+ # @albums = facebook_session.user.albums
463
+ # puts @albums.first.inspect
464
+ # end
465
+ #
466
+ # will raise Facebooker::BatchRequest::UnexecutedRequest
467
+ #
468
+ # If an exception is raised while processing the result, that exception will be
469
+ # re-raised on the next access to that object or when exception_raised? is called
470
+ #
471
+ # for example, if the send_notification resulted in TooManyUserCalls being raised,
472
+ # calling
473
+ # @send_result.exception_raised?
474
+ # would re-raise that exception
475
+ # if there was an error retrieving the albums, it would be re-raised when
476
+ # @albums.first
477
+ # is called
478
+ #
479
+ def batch(serial_only=false)
480
+ @batch_request=true
481
+ Thread.current[:facebooker_current_batch_queue]=[]
482
+ yield
483
+ # Set the batch request to false so that post will execute the batch job
484
+ @batch_request=false
485
+ BatchRun.current_batch=Thread.current[:facebooker_current_batch_queue]
486
+ post("facebook.batch.run",:method_feed=>BatchRun.current_batch.map{|q| q.uri}.to_json,:serial_only=>serial_only.to_s)
487
+ ensure
488
+ @batch_request=false
489
+ BatchRun.current_batch=nil
490
+ end
491
+
492
+ def post_without_logging(method, params = {}, use_session_key = true, &proc)
493
+ add_facebook_params(params, method)
494
+ use_session_key && @session_key && params[:session_key] ||= @session_key
495
+ final_params=params.merge(:sig => signature_for(params))
496
+ if batch_request?
497
+ add_to_batch(final_params,&proc)
498
+ else
499
+ result = service.post(final_params)
500
+ result = yield result if block_given?
501
+ result
502
+ end
503
+ end
504
+
505
+ def post(method, params = {}, use_session_key = true, &proc)
506
+ if batch_request?
507
+ post_without_logging(method, params, use_session_key, &proc)
508
+ else
509
+ Logging.log_fb_api(method, params) do
510
+ post_without_logging(method, params, use_session_key, &proc)
511
+ end
512
+ end
513
+ end
514
+
515
+ def post_file(method, params = {})
516
+ base = params.delete(:base)
517
+ Logging.log_fb_api(method, params) do
518
+ add_facebook_params(params, method)
519
+ @session_key && params[:session_key] ||= @session_key
520
+ service.post_file(params.merge(:base => base, :sig => signature_for(params.reject{|key, value| key.nil?})))
521
+ end
522
+ end
523
+
524
+
525
+ def self.configuration_file_path
526
+ @configuration_file_path || File.expand_path("~/.facebookerrc")
527
+ end
528
+
529
+ def self.configuration_file_path=(path)
530
+ @configuration_file_path = path
531
+ end
532
+
533
+ private
534
+ def add_facebook_params(hash, method)
535
+ hash[:method] = method
536
+ hash[:api_key] = @api_key
537
+ hash[:call_id] = Time.now.to_f.to_s unless method == 'facebook.auth.getSession'
538
+ hash[:v] = "1.0"
539
+ end
540
+
541
+ # This ultimately delgates to the adapter
542
+ def self.extract_key_from_environment(key_name)
543
+ Facebooker.send(key_name.to_s + "_key") rescue nil
544
+ end
545
+
546
+ def self.extract_key_from_configuration_file(key_name)
547
+ read_configuration_file[key_name]
548
+ end
549
+
550
+ def self.report_inability_to_find_key(key_name)
551
+ raise ConfigurationMissing, "Could not find configuration information for #{key_name}"
552
+ end
553
+
554
+ def self.read_configuration_file
555
+ eval(File.read(configuration_file_path))
556
+ end
557
+
558
+ def service
559
+ @service ||= Service.new(Facebooker.api_server_base, Facebooker.api_rest_path, @api_key)
560
+ end
561
+
562
+ def uid
563
+ @uid || (secure!; @uid)
564
+ end
565
+
566
+ def uid?
567
+ !! @uid
568
+ end
569
+
570
+ def signature_for(params)
571
+ raw_string = params.inject([]) do |collection, pair|
572
+ collection << pair.join("=")
573
+ collection
574
+ end.sort.join
575
+ Digest::MD5.hexdigest([raw_string, secret_for_method(params[:method])].join)
576
+ end
577
+ end
578
+
579
+ class CanvasSession < Session
580
+ def default_login_url_options
581
+ {:canvas => true}
582
+ end
583
+ end
584
+ end
@@ -0,0 +1,9 @@
1
+ module Facebooker #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 16
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end