facebooker 1.0.29 → 1.0.30

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 (39) hide show
  1. data/.autotest +15 -0
  2. data/Manifest.txt +129 -0
  3. data/Rakefile +2 -3
  4. data/examples/desktop_login.rb +14 -0
  5. data/facebooker.gemspec +43 -0
  6. data/lib/facebooker.rb +41 -39
  7. data/lib/facebooker/adapters/adapter_base.rb +3 -2
  8. data/lib/facebooker/adapters/bebo_adapter.rb +6 -4
  9. data/lib/facebooker/batch_request.rb +11 -10
  10. data/lib/facebooker/logging.rb +6 -13
  11. data/lib/facebooker/mobile.rb +2 -2
  12. data/lib/facebooker/model.rb +15 -13
  13. data/lib/facebooker/models/applicationproperties.rb +1 -1
  14. data/lib/facebooker/models/applicationrestrictions.rb +3 -3
  15. data/lib/facebooker/models/event.rb +2 -2
  16. data/lib/facebooker/models/friend_list.rb +2 -2
  17. data/lib/facebooker/models/group.rb +4 -4
  18. data/lib/facebooker/models/page.rb +3 -2
  19. data/lib/facebooker/models/photo.rb +2 -2
  20. data/lib/facebooker/models/user.rb +13 -5
  21. data/lib/facebooker/models/work_info.rb +3 -2
  22. data/lib/facebooker/rails/controller.rb +6 -1
  23. data/lib/facebooker/rails/facebook_request_fix.rb +12 -8
  24. data/lib/facebooker/rails/facebook_session_handling.rb +1 -2
  25. data/lib/facebooker/rails/facebook_url_rewriting.rb +14 -11
  26. data/lib/facebooker/rails/helpers.rb +4 -3
  27. data/lib/facebooker/rails/helpers/fb_connect.rb +8 -2
  28. data/lib/facebooker/rails/publisher.rb +36 -30
  29. data/lib/facebooker/session.rb +81 -73
  30. data/lib/facebooker/version.rb +1 -1
  31. data/test/facebooker/adapters_test.rb +23 -23
  32. data/test/facebooker/model_test.rb +10 -0
  33. data/test/facebooker/rails/publisher_test.rb +97 -88
  34. data/test/facebooker/rails_integration_test.rb +44 -43
  35. data/test/facebooker/session_test.rb +5 -5
  36. data/test/rack/facebook_test.rb +2 -3
  37. data/test/rails_test_helper.rb +14 -0
  38. data/test/test_helper.rb +5 -4
  39. metadata +31 -17
@@ -88,31 +88,32 @@ module Facebooker
88
88
  #
89
89
  # Publisher makes many helpers available, including the linking and asset helpers
90
90
  class Publisher
91
-
91
+
92
92
  #story sizes from the Facebooker API
93
93
  ONE_LINE=1
94
94
  SHORT=2
95
95
  FULL=4
96
-
96
+
97
97
  def initialize
98
- @controller = PublisherController.new
98
+ @from = nil
99
+ @full_story_template = nil
100
+ @recipients = nil
101
+ @controller = PublisherController.new
99
102
  end
100
-
103
+
101
104
  # use facebook options everywhere
102
105
  def request_comes_from_facebook?
103
106
  true
104
107
  end
105
-
108
+
106
109
  class FacebookTemplate < ::ActiveRecord::Base
107
-
108
-
109
110
  cattr_accessor :template_cache
110
111
  self.template_cache = {}
111
-
112
+
112
113
  def self.inspect(*args)
113
114
  "FacebookTemplate"
114
115
  end
115
-
116
+
116
117
  def template_changed?(hash)
117
118
  if respond_to?(:content_hash)
118
119
  content_hash != hash
@@ -180,20 +181,21 @@ module Facebooker
180
181
  (publisher.full_story_template and publisher.full_story_template.to_a.sort_by{|e| e[0].to_s})
181
182
  ].to_json
182
183
  end
183
-
184
+
184
185
  def template_name(klass,method)
185
186
  "#{klass.name}::#{method}"
186
187
  end
187
188
  end
188
189
  end
189
-
190
+
190
191
  class_inheritable_accessor :master_helper_module
191
- attr_accessor :one_line_story_templates, :short_story_templates, :action_links
192
-
192
+ attr_accessor :one_line_story_templates, :short_story_templates
193
+ attr_writer :action_links
194
+
193
195
  cattr_accessor :skip_registry
194
196
  self.skip_registry = false
195
-
196
-
197
+
198
+
197
199
  class InvalidSender < StandardError; end
198
200
  class UnknownBodyType < StandardError; end
199
201
  class UnspecifiedBodyType < StandardError; end
@@ -219,20 +221,21 @@ module Facebooker
219
221
  end
220
222
  class UserAction
221
223
  attr_accessor :data
222
- attr_accessor :target_ids
224
+ attr_reader :target_ids
223
225
  attr_accessor :body_general
224
226
  attr_accessor :template_id
225
227
  attr_accessor :template_name
226
228
  attr_accessor :story_size
227
-
229
+
228
230
  def target_ids=(val)
229
231
  @target_ids = val.is_a?(Array) ? val.join(",") : val
230
232
  end
233
+
231
234
  def data_hash
232
235
  data||{}
233
236
  end
234
237
  end
235
-
238
+
236
239
  cattr_accessor :ignore_errors
237
240
  attr_accessor :_body
238
241
 
@@ -288,12 +291,12 @@ module Facebooker
288
291
  @one_line_story_templates ||= []
289
292
  @one_line_story_templates << str
290
293
  end
291
-
294
+
292
295
  def short_story_template(title,body,params={})
293
296
  @short_story_templates ||= []
294
297
  @short_story_templates << params.merge(:template_title=>title, :template_body=>body)
295
298
  end
296
-
299
+
297
300
  def action_links(*links)
298
301
  if links.blank?
299
302
  @action_links
@@ -493,38 +496,41 @@ module Facebooker
493
496
  args.each do |arg|
494
497
  case arg
495
498
  when Symbol,String
496
- add_template_helper("#{arg.to_s.classify}Helper".constantize)
499
+ add_template_helper("#{arg.to_s.classify}Helper".constantize)
497
500
  when Module
498
501
  add_template_helper(arg)
499
502
  end
500
503
  end
501
504
  end
502
-
505
+
503
506
  def add_template_helper(helper_module) #:nodoc:
504
507
  master_helper_module.send :include,helper_module
505
508
  include master_helper_module
506
509
  end
507
510
 
508
-
511
+
509
512
  def inherited(child)
510
- super
513
+ super
511
514
  child.master_helper_module=Module.new
512
515
  child.master_helper_module.__send__(:include,self.master_helper_module)
513
516
  child.send(:include, child.master_helper_module)
514
517
  FacebookTemplate.clear_cache!
515
518
  end
516
-
519
+
517
520
  end
518
521
  class PublisherController
519
522
  include Facebooker::Rails::Publisher.master_helper_module
520
523
  include ActionController::UrlWriter
521
-
522
- def self.default_url_options(*args)
523
- Facebooker::Rails::Publisher.default_url_options(*args)
524
+
525
+ class << self
526
+ alias :old_default_url_options :default_url_options
527
+ def default_url_options(*args)
528
+ Facebooker::Rails::Publisher.default_url_options(*args)
529
+ end
524
530
  end
525
-
531
+
526
532
  end
527
-
533
+
528
534
  end
529
535
  end
530
536
  end
@@ -6,7 +6,7 @@ module Facebooker
6
6
  # other than the logged in user (if that's unallowed)
7
7
  class NonSessionUser < StandardError; end
8
8
  class Session
9
-
9
+
10
10
  #
11
11
  # Raised when a facebook session has expired. This
12
12
  # happens when the timeout is reached, or when the
@@ -62,49 +62,49 @@ module Facebooker
62
62
  class UserRegistrationFailed < StandardError
63
63
  attr_accessor :failed_users
64
64
  end
65
-
65
+
66
66
  API_SERVER_BASE_URL = ENV["FACEBOOKER_API"] == "new" ? "api.new.facebook.com" : "api.facebook.com"
67
67
  API_PATH_REST = "/restserver.php"
68
68
  WWW_SERVER_BASE_URL = ENV["FACEBOOKER_API"] == "new" ? "www.new.facebook.com" : "www.facebook.com"
69
69
  WWW_PATH_LOGIN = "/login.php"
70
70
  WWW_PATH_ADD = "/add.php"
71
71
  WWW_PATH_INSTALL = "/install.php"
72
-
72
+
73
73
  attr_writer :auth_token
74
74
  attr_reader :session_key
75
-
75
+
76
76
  def self.create(api_key=nil, secret_key=nil)
77
77
  api_key ||= self.api_key
78
78
  secret_key ||= self.secret_key
79
79
  raise ArgumentError unless !api_key.nil? && !secret_key.nil?
80
80
  new(api_key, secret_key)
81
81
  end
82
-
82
+
83
83
  def self.api_key
84
84
  extract_key_from_environment(:api) || extract_key_from_configuration_file(:api) rescue report_inability_to_find_key(:api)
85
85
  end
86
-
86
+
87
87
  def self.secret_key
88
88
  extract_key_from_environment(:secret) || extract_key_from_configuration_file(:secret) rescue report_inability_to_find_key(:secret)
89
89
  end
90
-
90
+
91
91
  def self.current
92
92
  Thread.current['facebook_session']
93
93
  end
94
-
94
+
95
95
  def self.current=(session)
96
96
  Thread.current['facebook_session'] = session
97
97
  end
98
-
98
+
99
99
  def login_url(options={})
100
100
  options = default_login_url_options.merge(options)
101
101
  "#{Facebooker.login_url_base(@api_key)}#{login_url_optional_parameters(options)}"
102
102
  end
103
-
103
+
104
104
  def install_url(options={})
105
105
  "#{Facebooker.install_url_base(@api_key)}#{install_url_optional_parameters(options)}"
106
106
  end
107
-
107
+
108
108
  # The url to get user to approve extended permissions
109
109
  # http://wiki.developers.facebook.com/index.php/Extended_permission
110
110
  #
@@ -122,20 +122,20 @@ module Facebooker
122
122
  options = default_login_url_options.merge(options)
123
123
  "http://#{Facebooker.www_server_base_url}/authorize.php?api_key=#{@api_key}&v=1.0&ext_perm=#{permission}#{install_url_optional_parameters(options)}"
124
124
  end
125
-
125
+
126
126
  def install_url_optional_parameters(options)
127
127
  optional_parameters = []
128
128
  optional_parameters += add_next_parameters(options)
129
129
  optional_parameters.join
130
130
  end
131
-
131
+
132
132
  def add_next_parameters(options)
133
133
  opts = []
134
134
  opts << "&next=#{CGI.escape(options[:next])}" if options[:next]
135
135
  opts << "&next_cancel=#{CGI.escape(options[:next_cancel])}" if options[:next_cancel]
136
136
  opts
137
137
  end
138
-
138
+
139
139
  def login_url_optional_parameters(options)
140
140
  # It is important that unused options are omitted as stuff like &canvas=false will still display the canvas.
141
141
  optional_parameters = []
@@ -145,48 +145,54 @@ module Facebooker
145
145
  optional_parameters << "&canvas=true" if options[:canvas]
146
146
  optional_parameters.join
147
147
  end
148
-
148
+
149
149
  def default_login_url_options
150
150
  {}
151
151
  end
152
-
152
+
153
153
  def initialize(api_key, secret_key)
154
- @api_key = api_key
155
- @secret_key = secret_key
154
+ @api_key = api_key
155
+ @secret_key = secret_key
156
+ @batch_request = nil
157
+ @session_key = nil
158
+ @uid = nil
159
+ @auth_token = nil
160
+ @secret_from_session = nil
161
+ @expires = nil
156
162
  end
157
-
163
+
158
164
  def secret_for_method(method_name)
159
165
  @secret_key
160
166
  end
161
-
167
+
162
168
  def auth_token
163
169
  @auth_token ||= post 'facebook.auth.createToken'
164
170
  end
165
-
171
+
166
172
  def infinite?
167
173
  @expires == 0
168
174
  end
169
-
175
+
170
176
  def expired?
171
177
  @expires.nil? || (!infinite? && Time.at(@expires) <= Time.now)
172
178
  end
173
-
179
+
174
180
  def secured?
175
181
  !@session_key.nil? && !expired?
176
182
  end
177
-
183
+
178
184
  def secure!
179
185
  response = post 'facebook.auth.getSession', :auth_token => auth_token
180
186
  secure_with!(response['session_key'], response['uid'], response['expires'], response['secret'])
181
187
  end
182
-
188
+
183
189
  def secure_with!(session_key, uid = nil, expires = nil, secret_from_session = nil)
184
190
  @session_key = session_key
185
191
  @uid = uid ? Integer(uid) : post('facebook.users.getLoggedInUser', :session_key => session_key)
186
192
  @expires = Integer(expires)
187
193
  @secret_from_session = secret_from_session
188
194
  end
189
-
195
+
190
196
  def fql_query(query, format = 'XML')
191
197
  post('facebook.fql.query', :query => query, :format => format) do |response|
192
198
  type = response.shift
@@ -212,11 +218,11 @@ module Facebooker
212
218
  end
213
219
  end
214
220
  end
215
-
221
+
216
222
  def user
217
223
  @user ||= User.new(uid, self)
218
224
  end
219
-
225
+
220
226
  #
221
227
  # This one has so many parameters, a Hash seemed cleaner than a long param list. Options can be:
222
228
  # :uid => Filter by events associated with a user with this uid
@@ -231,7 +237,7 @@ module Facebooker
231
237
  end
232
238
  end
233
239
  end
234
-
240
+
235
241
  def event_members(eid)
236
242
  @members ||= post('facebook.events.getMembers', :eid => eid) do |response|
237
243
  response.map do |attendee_hash|
@@ -273,21 +279,21 @@ module Facebooker
273
279
  def server_cache
274
280
  Facebooker::ServerCache.new(self)
275
281
  end
276
-
282
+
277
283
  #
278
284
  # Returns a proxy object for handling calls to the Facebook Data API
279
285
  def data
280
286
  Facebooker::Data.new(self)
281
287
  end
282
-
288
+
283
289
  def admin
284
290
  Facebooker::Admin.new(self)
285
291
  end
286
-
292
+
287
293
  def mobile
288
294
  Facebooker::Mobile.new(self)
289
295
  end
290
-
296
+
291
297
  #
292
298
  # Given an array like:
293
299
  # [[userid, otheruserid], [yetanotherid, andanotherid]]
@@ -303,7 +309,7 @@ module Facebooker
303
309
  end
304
310
  post('facebook.friends.areFriends', :uids1 => uids1.join(','), :uids2 => uids2.join(','))
305
311
  end
306
-
312
+
307
313
  def get_photos(pids = nil, subj_id = nil, aid = nil)
308
314
  if [subj_id, pids, aid].all? {|arg| arg.nil?}
309
315
  raise ArgumentError, "Can't get a photo without a picture, album or subject ID"
@@ -314,7 +320,7 @@ module Facebooker
314
320
  end
315
321
  end
316
322
  end
317
-
323
+
318
324
  def get_albums(aids)
319
325
  @albums = post('facebook.photos.getAlbums', :aids => aids) do |response|
320
326
  response.map do |hash|
@@ -322,7 +328,7 @@ module Facebooker
322
328
  end
323
329
  end
324
330
  end
325
-
331
+
326
332
  def get_tags(pids)
327
333
  @tags = post('facebook.photos.getTags', :pids => pids) do |response|
328
334
  response.map do |hash|
@@ -330,14 +336,14 @@ module Facebooker
330
336
  end
331
337
  end
332
338
  end
333
-
339
+
334
340
  def add_tags(pid, x, y, tag_uid = nil, tag_text = nil )
335
341
  if [tag_uid, tag_text].all? {|arg| arg.nil?}
336
342
  raise ArgumentError, "Must enter a name or string for this tag"
337
343
  end
338
344
  @tags = post('facebook.photos.addTag', :pid => pid, :tag_uid => tag_uid, :tag_text => tag_text, :x => x, :y => y )
339
345
  end
340
-
346
+
341
347
  def send_notification(user_ids, fbml, email_fbml = nil)
342
348
  params = {:notification => fbml, :to_ids => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(',')}
343
349
  if email_fbml
@@ -348,25 +354,25 @@ module Facebooker
348
354
  unless uid?
349
355
  params[:type]="app_to_user"
350
356
  end
351
-
357
+
352
358
  post 'facebook.notifications.send', params,uid?
353
359
  end
354
-
360
+
355
361
  ##
356
362
  # Register a template bundle with Facebook.
357
363
  # returns the template id to use to send using this template
358
364
  def register_template_bundle(one_line_story_templates,short_story_templates=nil,full_story_template=nil, action_links=nil)
359
365
  parameters = {:one_line_story_templates => Array(one_line_story_templates).to_json}
360
-
366
+
361
367
  parameters[:action_links] = action_links.to_json unless action_links.blank?
362
-
368
+
363
369
  parameters[:short_story_templates] = Array(short_story_templates).to_json unless short_story_templates.blank?
364
370
 
365
371
  parameters[:full_story_template] = full_story_template.to_json unless full_story_template.blank?
366
372
 
367
373
  post("facebook.feed.registerTemplateBundle", parameters, false)
368
374
  end
369
-
375
+
370
376
  ##
371
377
  # publish a previously rendered template bundle
372
378
  # see http://wiki.developers.facebook.com/index.php/Feed.publishUserAction
@@ -378,8 +384,8 @@ module Facebooker
378
384
  parameters[:story_size] = story_size unless story_size.nil?
379
385
  post("facebook.feed.publishUserAction", parameters)
380
386
  end
381
-
382
-
387
+
388
+
383
389
  ##
384
390
  # Send email to as many as 100 users at a time
385
391
  def send_email(user_ids, subject, text, fbml = nil)
@@ -387,17 +393,17 @@ module Facebooker
387
393
  params = {:fbml => fbml, :recipients => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(','), :text => text, :subject => subject}
388
394
  post 'facebook.notifications.sendEmail', params
389
395
  end
390
-
396
+
391
397
  # Only serialize the bare minimum to recreate the session.
392
398
  def marshal_load(variables)#:nodoc:
393
399
  fields_to_serialize.each_with_index{|field, index| instance_variable_set_value(field, variables[index])}
394
400
  end
395
-
401
+
396
402
  # Only serialize the bare minimum to recreate the session.
397
403
  def marshal_dump#:nodoc:
398
404
  fields_to_serialize.map{|field| instance_variable_value(field)}
399
405
  end
400
-
406
+
401
407
  # Only serialize the bare minimum to recreate the session.
402
408
  def to_yaml( opts = {} )
403
409
  YAML::quick_emit(self.object_id, opts) do |out|
@@ -408,29 +414,29 @@ module Facebooker
408
414
  end
409
415
  end
410
416
  end
411
-
417
+
412
418
  def instance_variable_set_value(field, value)
413
419
  self.instance_variable_set("@#{field}", value)
414
420
  end
415
-
421
+
416
422
  def instance_variable_value(field)
417
423
  self.instance_variable_get("@#{field}")
418
424
  end
419
-
425
+
420
426
  def fields_to_serialize
421
427
  %w(session_key uid expires secret_from_session auth_token api_key secret_key)
422
428
  end
423
-
429
+
424
430
  class Desktop < Session
425
431
  def login_url
426
432
  super + "&auth_token=#{auth_token}"
427
433
  end
428
-
434
+
429
435
  def secret_for_method(method_name)
430
436
  secret = auth_request_methods.include?(method_name) ? super : @secret_from_session
431
437
  secret
432
438
  end
433
-
439
+
434
440
  def post(method, params = {},use_session=false)
435
441
  if method == 'facebook.profile.getFBML' || method == 'facebook.profile.setFBML'
436
442
  raise NonSessionUser.new("User #{@uid} is not the logged in user.") unless @uid == params[:uid]
@@ -442,17 +448,17 @@ module Facebooker
442
448
  ['facebook.auth.getSession', 'facebook.auth.createToken']
443
449
  end
444
450
  end
445
-
451
+
446
452
  def batch_request?
447
453
  @batch_request
448
454
  end
449
-
455
+
450
456
  def add_to_batch(req,&proc)
451
457
  batch_request = BatchRequest.new(req,proc)
452
458
  Thread.current[:facebooker_current_batch_queue]<<batch_request
453
459
  batch_request
454
460
  end
455
-
461
+
456
462
  # Submit the enclosed requests for this session inside a batch
457
463
  #
458
464
  # All requests will be sent to Facebook at the end of the block
@@ -500,7 +506,7 @@ module Facebooker
500
506
  @batch_request=false
501
507
  BatchRun.current_batch=nil
502
508
  end
503
-
509
+
504
510
  def post_without_logging(method, params = {}, use_session_key = true, &proc)
505
511
  add_facebook_params(params, method)
506
512
  use_session_key && @session_key && params[:session_key] ||= @session_key
@@ -513,7 +519,7 @@ module Facebooker
513
519
  result
514
520
  end
515
521
  end
516
-
522
+
517
523
  def post(method, params = {}, use_session_key = true, &proc)
518
524
  if batch_request?
519
525
  post_without_logging(method, params, use_session_key, &proc)
@@ -523,7 +529,7 @@ module Facebooker
523
529
  end
524
530
  end
525
531
  end
526
-
532
+
527
533
  def post_file(method, params = {})
528
534
  base = params.delete(:base)
529
535
  Logging.log_fb_api(method, params) do
@@ -532,16 +538,18 @@ module Facebooker
532
538
  service.post_file(params.merge(:base => base, :sig => signature_for(params.reject{|key, value| key.nil?})))
533
539
  end
534
540
  end
535
-
536
-
541
+
542
+
543
+ @configuration_file_path = nil
544
+
537
545
  def self.configuration_file_path
538
546
  @configuration_file_path || File.expand_path("~/.facebookerrc")
539
547
  end
540
-
548
+
541
549
  def self.configuration_file_path=(path)
542
550
  @configuration_file_path = path
543
551
  end
544
-
552
+
545
553
  private
546
554
  def add_facebook_params(hash, method)
547
555
  hash[:method] = method
@@ -549,36 +557,36 @@ module Facebooker
549
557
  hash[:call_id] = Time.now.to_f.to_s unless method == 'facebook.auth.getSession'
550
558
  hash[:v] = "1.0"
551
559
  end
552
-
560
+
553
561
  # This ultimately delgates to the adapter
554
562
  def self.extract_key_from_environment(key_name)
555
563
  Facebooker.send(key_name.to_s + "_key") rescue nil
556
564
  end
557
-
565
+
558
566
  def self.extract_key_from_configuration_file(key_name)
559
567
  read_configuration_file[key_name]
560
568
  end
561
-
569
+
562
570
  def self.report_inability_to_find_key(key_name)
563
571
  raise ConfigurationMissing, "Could not find configuration information for #{key_name}"
564
572
  end
565
-
573
+
566
574
  def self.read_configuration_file
567
575
  eval(File.read(configuration_file_path))
568
576
  end
569
-
577
+
570
578
  def service
571
579
  @service ||= Service.new(Facebooker.api_server_base, Facebooker.api_rest_path, @api_key)
572
580
  end
573
-
581
+
574
582
  def uid
575
583
  @uid || (secure!; @uid)
576
584
  end
577
-
585
+
578
586
  def uid?
579
587
  !! @uid
580
588
  end
581
-
589
+
582
590
  def signature_for(params)
583
591
  raw_string = params.inject([]) do |collection, pair|
584
592
  collection << pair.join("=")
@@ -587,7 +595,7 @@ module Facebooker
587
595
  Digest::MD5.hexdigest([raw_string, secret_for_method(params[:method])].join)
588
596
  end
589
597
  end
590
-
598
+
591
599
  class CanvasSession < Session
592
600
  def default_login_url_options
593
601
  {:canvas => true}