facebooker 1.0.31 → 1.0.41

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 (43) hide show
  1. data/CHANGELOG.rdoc +7 -0
  2. data/Manifest.txt +5 -0
  3. data/README.rdoc +9 -0
  4. data/facebooker.gemspec +8 -9
  5. data/generators/facebook/templates/public/javascripts/facebooker.js +10 -16
  6. data/init.rb +4 -1
  7. data/install.rb +1 -1
  8. data/lib/facebooker.rb +75 -20
  9. data/lib/facebooker/adapters/bebo_adapter.rb +9 -9
  10. data/lib/facebooker/data.rb +14 -14
  11. data/lib/facebooker/models/page.rb +16 -0
  12. data/lib/facebooker/models/photo.rb +7 -0
  13. data/lib/facebooker/models/user.rb +75 -28
  14. data/lib/facebooker/parser.rb +187 -127
  15. data/lib/facebooker/rails/backwards_compatible_param_checks.rb +31 -0
  16. data/lib/facebooker/rails/controller.rb +30 -7
  17. data/lib/facebooker/rails/extensions/rack_setup.rb +5 -1
  18. data/lib/facebooker/rails/facebook_request_fix.rb +5 -5
  19. data/lib/facebooker/rails/facebook_request_fix_2-3.rb +31 -0
  20. data/lib/facebooker/rails/facebook_url_rewriting.rb +7 -2
  21. data/lib/facebooker/rails/helpers.rb +29 -5
  22. data/lib/facebooker/rails/helpers/fb_connect.rb +14 -5
  23. data/lib/facebooker/rails/publisher.rb +26 -12
  24. data/lib/facebooker/service.rb +64 -56
  25. data/lib/facebooker/session.rb +56 -22
  26. data/lib/facebooker/version.rb +1 -1
  27. data/lib/rack/facebook.rb +26 -14
  28. data/lib/tasks/tunnel.rake +1 -1
  29. data/test/facebooker/adapters_test.rb +78 -0
  30. data/test/facebooker/data_test.rb +14 -14
  31. data/test/facebooker/models/page_test.rb +46 -0
  32. data/test/facebooker/models/photo_test.rb +16 -0
  33. data/test/facebooker/models/user_test.rb +83 -48
  34. data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +25 -0
  35. data/test/facebooker/rails/facebook_url_rewriting_test.rb +39 -0
  36. data/test/facebooker/rails/publisher_test.rb +5 -1
  37. data/test/facebooker/rails_integration_test.rb +52 -8
  38. data/test/facebooker/service_test.rb +58 -0
  39. data/test/facebooker/session_test.rb +106 -92
  40. data/test/facebooker_test.rb +2 -2
  41. data/test/rack/facebook_test.rb +4 -4
  42. data/test/test_helper.rb +10 -3
  43. metadata +14 -4
@@ -72,6 +72,7 @@ module Facebooker
72
72
 
73
73
  attr_writer :auth_token
74
74
  attr_reader :session_key
75
+ attr_reader :secret_from_session
75
76
 
76
77
  def self.create(api_key=nil, secret_key=nil)
77
78
  api_key ||= self.api_key
@@ -181,10 +182,14 @@ module Facebooker
181
182
  !@session_key.nil? && !expired?
182
183
  end
183
184
 
184
- def secure!
185
- response = post 'facebook.auth.getSession', :auth_token => auth_token
185
+ def secure!(args = {})
186
+ response = post 'facebook.auth.getSession', :auth_token => auth_token, :generate_session_secret => args[:generate_session_secret] ? "1" : "0"
186
187
  secure_with!(response['session_key'], response['uid'], response['expires'], response['secret'])
187
188
  end
189
+
190
+ def secure_with_session_secret!
191
+ self.secure!(:generate_session_secret => true)
192
+ end
188
193
 
189
194
  def secure_with!(session_key, uid = nil, expires = nil, secret_from_session = nil)
190
195
  @session_key = session_key
@@ -193,30 +198,55 @@ module Facebooker
193
198
  @secret_from_session = secret_from_session
194
199
  end
195
200
 
201
+ def fql_build_object(type, hash)
202
+ case type
203
+ when 'user'
204
+ user = User.new
205
+ user.session = self
206
+ user.populate_from_hash!(hash)
207
+ user
208
+ when 'photo'
209
+ Photo.from_hash(hash)
210
+ when 'page'
211
+ Page.from_hash(hash)
212
+ when 'page_admin'
213
+ Page.from_hash(hash)
214
+ when 'group'
215
+ Group.from_hash(hash)
216
+ when 'event_member'
217
+ Event::Attendance.from_hash(hash)
218
+ else
219
+ hash
220
+ end
221
+ end
222
+
196
223
  def fql_query(query, format = 'XML')
197
224
  post('facebook.fql.query', :query => query, :format => format) do |response|
198
225
  type = response.shift
199
226
  return [] if type.nil?
200
227
  response.shift.map do |hash|
201
- case type
202
- when 'user'
203
- user = User.new
204
- user.session = self
205
- user.populate_from_hash!(hash)
206
- user
207
- when 'photo'
208
- Photo.from_hash(hash)
209
- when 'page'
210
- Page.from_hash(hash)
211
- when 'page_admin'
212
- Page.from_hash(hash)
213
- when 'event_member'
214
- Event::Attendance.from_hash(hash)
215
- else
216
- hash
228
+ fql_build_object(type, hash)
229
+ end
230
+ end
231
+ end
232
+
233
+ def fql_multiquery(queries, format = 'XML')
234
+ results = {}
235
+ post('facebook.fql.multiquery', :queries => queries.to_json, :format => format) do |responses|
236
+ responses.each do |response|
237
+ name = response.shift
238
+ response = response.shift
239
+ type = response.shift
240
+ value = []
241
+ unless type.nil?
242
+ value = response.shift.map do |hash|
243
+ fql_build_object(type, hash)
244
+ end
217
245
  end
246
+ results[name] = value
218
247
  end
219
248
  end
249
+ results
220
250
  end
221
251
 
222
252
  def user
@@ -270,9 +300,11 @@ module Facebooker
270
300
 
271
301
  # Takes page_id and uid, returns true if uid is a fan of the page_id
272
302
  def is_fan(page_id, uid)
273
- post('facebook.pages.isFan', :page_id=>page_id, :uid=>uid)
303
+ puts "Deprecated. Use Page#user_is_fan? instead"
304
+ Page.new(page_id).user_is_fan?(uid)
274
305
  end
275
306
 
307
+
276
308
  #
277
309
  # Returns a proxy object for handling calls to Facebook cached items
278
310
  # such as images and FBML ref handles
@@ -388,10 +420,10 @@ module Facebooker
388
420
 
389
421
  ##
390
422
  # Send email to as many as 100 users at a time
391
- def send_email(user_ids, subject, text, fbml = nil)
423
+ def send_email(user_ids, subject, text, fbml = nil)
392
424
  user_ids = Array(user_ids)
393
425
  params = {:fbml => fbml, :recipients => user_ids.map{ |id| User.cast_to_facebook_id(id)}.join(','), :text => text, :subject => subject}
394
- post 'facebook.notifications.sendEmail', params
426
+ post 'facebook.notifications.sendEmail', params, false
395
427
  end
396
428
 
397
429
  # Only serialize the bare minimum to recreate the session.
@@ -589,7 +621,9 @@ module Facebooker
589
621
 
590
622
  def signature_for(params)
591
623
  raw_string = params.inject([]) do |collection, pair|
592
- collection << pair.join("=")
624
+ collection << pair.map { |x|
625
+ Array === x ? Facebooker.json_encode(x) : x
626
+ }.join("=")
593
627
  collection
594
628
  end.sort.join
595
629
  Digest::MD5.hexdigest([raw_string, secret_for_method(params[:method])].join)
@@ -2,7 +2,7 @@ module Facebooker #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
4
  MINOR = 0
5
- TINY = 31
5
+ TINY = 41
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -24,29 +24,41 @@ module Rack
24
24
  # end
25
25
  #
26
26
  class Facebook
27
- def initialize(app, secret_key, &condition)
27
+ def initialize(app, &condition)
28
28
  @app = app
29
- @secret_key = secret_key
30
29
  @condition = condition
31
30
  end
32
31
 
33
32
  def call(env)
34
- if @condition.nil? || @condition.call(env)
35
- request = Rack::Request.new(env)
36
- fb_params = extract_fb_sig_params(request.POST)
37
- unless fb_params.empty?
38
- unless signature_is_valid?(fb_params, request.POST['fb_sig'])
39
- return Rack::Response.new(["Invalid Facebook signature"], 400).finish
40
- end
41
- env['REQUEST_METHOD'] = fb_params["request_method"] if fb_params["request_method"]
42
- convert_parameters!(request.POST)
33
+ return @app.call(env) unless @condition.nil? || @condition.call(env)
34
+
35
+ request = Rack::Request.new(env)
36
+ fb_sig, fb_params = nil, nil
37
+
38
+ [ request.POST, request.GET ].each do |params|
39
+ fb_sig, fb_params = fb_sig_and_params( params )
40
+ break if fb_sig
41
+ end
42
+
43
+ return @app.call(env) if fb_params.empty?
44
+
45
+ Facebooker.with_application(fb_params['api_key']) do
46
+ unless signature_is_valid?(fb_params, fb_sig)
47
+ return Rack::Response.new(["Invalid Facebook signature"], 400).finish
43
48
  end
49
+ env['REQUEST_METHOD'] = fb_params["request_method"] if fb_params["request_method"]
50
+ convert_parameters!(request.params)
51
+ @app.call(env)
44
52
  end
45
- @app.call(env)
46
53
  end
47
54
 
48
55
  private
49
56
 
57
+ def fb_sig_and_params( params )
58
+ return nil, [] unless params['fb_sig']
59
+ return params['fb_sig'], extract_fb_sig_params(params)
60
+ end
61
+
50
62
  def extract_fb_sig_params(params)
51
63
  params.inject({}) do |collection, (param, value)|
52
64
  collection[param.sub(/^fb_sig_/, '')] = value if param[0,7] == 'fb_sig_'
@@ -56,7 +68,7 @@ module Rack
56
68
 
57
69
  def signature_is_valid?(fb_params, actual_sig)
58
70
  raw_string = fb_params.map{ |*args| args.join('=') }.sort.join
59
- expected_signature = Digest::MD5.hexdigest([raw_string, @secret_key].join)
71
+ expected_signature = Digest::MD5.hexdigest([raw_string, Facebooker.secret_key].join)
60
72
  actual_sig == expected_signature
61
73
  end
62
74
 
@@ -74,4 +86,4 @@ module Rack
74
86
  end
75
87
  end
76
88
  end
77
- end
89
+ end
@@ -28,7 +28,7 @@ namespace :facebooker do
28
28
 
29
29
  task :config => :environment do
30
30
  facebook_config = File.dirname(__FILE__) + '/../../../../../config/facebooker.yml'
31
- FACEBOOKER = YAML.load_file(facebook_config)[RAILS_ENV]
31
+ FACEBOOKER = YAML.load(ERB.new(File.read(facebook_config)).result)[RAILS_ENV]
32
32
  @public_host_username = FACEBOOKER['tunnel']['public_host_username']
33
33
  @public_host = FACEBOOKER['tunnel']['public_host']
34
34
  @public_port = FACEBOOKER['tunnel']['public_port']
@@ -93,4 +93,82 @@ class Facebooker::AdaptersTest < Test::Unit::TestCase
93
93
  def test_bebo_process_data
94
94
 
95
95
  end
96
+
97
+ def test_fetch_config_for_can_find_top_level_api_key
98
+ old = Facebooker.instance_variable_get('@raw_facebooker_configuration')
99
+ # Now that we've backed up the old value...
100
+ raw_fb_config = { 'api_key' => 'a key' }
101
+ Facebooker.instance_variable_set('@raw_facebooker_configuration', raw_fb_config)
102
+ assert_equal Facebooker.fetch_config_for( 'a key' ), raw_fb_config
103
+ ensure
104
+ # Put the old value back
105
+ Facebooker.instance_variable_set('@raw_facebooker_configuration', old)
106
+ end
107
+
108
+ def test_fetch_config_for_can_find_deep_api_key
109
+ old = Facebooker.instance_variable_get('@raw_facebooker_configuration')
110
+ # Now that we've backed up the old value...
111
+ raw_fb_config = { 'api_key' => 'a key',
112
+ 'alternative_keys' => {
113
+ 'another key' => { 'secret_key' => 'sdfsd' },
114
+ 'yet another key' => { 'secret_key' => '9ho2h' } } }
115
+ Facebooker.instance_variable_set('@raw_facebooker_configuration', raw_fb_config)
116
+ assert_equal raw_fb_config['alternative_keys']['another key'].merge( 'api_key' => 'another key' ),
117
+ Facebooker.fetch_config_for( 'another key' )
118
+ ensure
119
+ # Put the old value back
120
+ Facebooker.instance_variable_set('@raw_facebooker_configuration', old)
121
+ end
122
+
123
+ def test_fetch_config_for_returns_false_if_no_apikey_found
124
+ old = Facebooker.instance_variable_get('@raw_facebooker_configuration')
125
+ # Now that we've backed up the old value...
126
+ raw_fb_config = { 'api_key' => 'a key' }
127
+ Facebooker.instance_variable_set('@raw_facebooker_configuration', raw_fb_config)
128
+ assert ! Facebooker.fetch_config_for( 'another key' )
129
+ ensure
130
+ # Put the old value back
131
+ Facebooker.instance_variable_set('@raw_facebooker_configuration', old)
132
+ end
133
+
134
+ def test_with_application_yields_if_no_config_is_found
135
+ flexmock( Facebooker ).
136
+ should_receive( :fetch_config_for ).
137
+ and_return( false )
138
+ # Is there a better way to assert the block is yielded?
139
+ @changes_to_true = false
140
+ Facebooker.with_application('somekey not found') do
141
+ @changes_to_true = true
142
+ end
143
+ assert @changes_to_true
144
+ end
145
+
146
+ def test_with_application_changes_config_inside_block
147
+ flexmock( Facebooker ).
148
+ should_receive( :fetch_config_for ).
149
+ and_return({ 'api_key' => 'a key',
150
+ 'secret_key' => 'my secret key' })
151
+ Facebooker.with_application('a key') do
152
+ @secret_in_block = Facebooker.secret_key
153
+ end
154
+ # Check outside the block, assures the assertion gets run
155
+ assert_equal 'my secret key', @secret_in_block
156
+ end
157
+
158
+ def test_with_application_restores_last_config_outside_block
159
+ flexmock( Facebooker ).
160
+ should_receive( :fetch_config_for ).
161
+ and_return( { 'api_key' => 'a key',
162
+ 'secret_key' => 'my secret key' },
163
+ { 'api_key' => 'another key',
164
+ 'secret_key' => 'my other secret key' } )
165
+ Facebooker.with_application('a key') do
166
+ Facebooker.with_application('another key') do
167
+ end
168
+ @secret_in_outer_block = Facebooker.secret_key
169
+ end
170
+ # Check outside the block, assures the assertion gets run
171
+ assert_equal 'my secret key', @secret_in_outer_block
172
+ end
173
+
96
174
  end
@@ -6,46 +6,46 @@ class Facebooker::DataTest < Test::Unit::TestCase
6
6
  #make sure we use net::http since that's what the tests expect
7
7
  Facebooker.use_curl=false
8
8
  end
9
-
9
+
10
10
  def test_can_ask_facebook_to_set_a_cookies
11
11
  expect_http_posts_with_responses(example_set_cookie_xml)
12
12
  assert(@session.data.set_cookie(12345, 'name', 'value'))
13
13
  end
14
-
14
+
15
15
  def test_can_ask_facebook_to_get_cookies
16
16
  expect_http_posts_with_responses(example_get_cookies_xml)
17
17
  assert(@session.data.get_cookies(12345))
18
18
  end
19
-
19
+
20
20
  def test_can_get_cookies_for_user
21
21
  mock_http = establish_session
22
22
  mock_http.should_receive(:post_form).and_return(example_get_cookies_xml).once.ordered(:posts)
23
- cookies = @session.data.get_cookies(508508326)
23
+ cookies = @session.data.get_cookies(508508326)
24
24
  assert_equal 'Foo', cookies.first.name
25
25
  assert_equal 'Bar', cookies.first.value
26
26
  end
27
-
27
+
28
28
  def test_can_ask_facebook_to_set_a_preference
29
29
  expect_http_posts_with_responses(example_set_preference_xml)
30
30
  assert(@session.data.set_preference(0, 'hello'))
31
31
  end
32
-
32
+
33
33
  def test_can_ask_facebook_to_get_preference
34
34
  expect_http_posts_with_responses(example_get_preference_xml)
35
35
  assert(@session.data.get_preference(0))
36
36
  end
37
-
37
+
38
38
  def test_can_get_preference
39
39
  mock_http = establish_session
40
40
  mock_http.should_receive(:post_form).and_return(example_get_preference_xml).once.ordered(:posts)
41
- assert_equal 'hello', @session.data.get_preference(0)
41
+ assert_equal 'hello', @session.data.get_preference(0)
42
42
  end
43
43
 
44
44
  private
45
45
  def example_set_cookie_xml
46
46
  <<-XML
47
47
  <?xml version="1.0" encoding="UTF-8"?>
48
- <data_setCookie_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
48
+ <data_setCookie_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
49
49
  xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd">1</data_setCookie_response>
50
50
  XML
51
51
  end
@@ -53,7 +53,7 @@ class Facebooker::DataTest < Test::Unit::TestCase
53
53
  def example_get_cookies_xml
54
54
  <<-XML
55
55
  <?xml version="1.0" encoding="UTF-8"?>
56
- <data_getCookie_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
56
+ <data_getCookie_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
57
57
  xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd">
58
58
  <cookies>
59
59
  <uid>508508326</uid>
@@ -65,11 +65,11 @@ class Facebooker::DataTest < Test::Unit::TestCase
65
65
  </data_getCookie_response>
66
66
  XML
67
67
  end
68
-
68
+
69
69
  def example_set_preference_xml
70
70
  <<-XML
71
71
  <?xml version="1.0" encoding="UTF-8"?>
72
- <data_setUserPreference_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
72
+ <data_setUserPreference_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
73
73
  xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd"/>
74
74
  XML
75
75
  end
@@ -77,10 +77,10 @@ class Facebooker::DataTest < Test::Unit::TestCase
77
77
  def example_get_preference_xml
78
78
  <<-XML
79
79
  <?xml version="1.0" encoding="UTF-8"?>
80
- <data_getUserPreference_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
80
+ <data_getUserPreference_response xmlns="http://api.facebook.com/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
81
81
  xsi:schemaLocation="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd">
82
82
  hello
83
83
  </data_getUserPreference_response>
84
84
  XML
85
85
  end
86
- end
86
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+ require 'active_support'
3
+
4
+ class Facebooker::PageTest < Test::Unit::TestCase
5
+
6
+ def test_should_be_able_to_populate_with_photo_id_as_integer
7
+ p = Facebooker::Page.new(12345)
8
+ assert_equal(12345,p.page_id)
9
+ end
10
+
11
+ def test_should_be_ble_to_initialize_with_photo_id_as_string
12
+ p = Facebooker::Page.new("12345")
13
+ assert_equal("12345",p.page_id)
14
+ end
15
+
16
+ def test_should_be_able_to_initialize_with_hash
17
+ p = Facebooker::Page.new(:page_id=>12345,:name=>"test page")
18
+ assert_equal("test page",p.name)
19
+ assert_equal(12345,p.page_id)
20
+ end
21
+
22
+ def test_should_be_able_to_see_if_user_is_fan_with_id
23
+ Facebooker::Session.current.expects(:post).with("facebook.pages.isFan",:page_id=>12345,:uid=>12451752).returns(true)
24
+ p = Facebooker::Page.new(12345)
25
+ assert p.user_is_fan?(12451752)
26
+ end
27
+
28
+ def test_should_be_able_to_see_if_user_is_fan_with_user
29
+ Facebooker::Session.current.expects(:post).with("facebook.pages.isFan",:page_id=>12345,:uid=>12451752).returns(false)
30
+ p = Facebooker::Page.new(12345)
31
+ assert !p.user_is_fan?(Facebooker::User.new(12451752))
32
+ end
33
+
34
+ def test_should_be_able_to_see_if_user_is_admin_with_id
35
+ Facebooker::Session.current.expects(:post).with("facebook.pages.isAdmin",:page_id=>12345,:uid=>12451752).returns(false)
36
+ p = Facebooker::Page.new(12345)
37
+ assert !p.user_is_admin?(12451752)
38
+
39
+ end
40
+
41
+ def test_should_be_able_to_see_if_user_is_admin_with_user
42
+ Facebooker::Session.current.expects(:post).with("facebook.pages.isAdmin",:page_id=>12345,:uid=>12451752).returns(true)
43
+ p = Facebooker::Page.new(12345)
44
+ assert p.user_is_admin?(Facebooker::User.new(12451752))
45
+ end
46
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+ require 'active_support'
3
+
4
+ class Facebooker::UserTest < Test::Unit::TestCase
5
+
6
+ def test_pid_should_be_treated_as_a_string
7
+ @photo = Facebooker::Photo.new(:pid=>"100000025509592_6801")
8
+ assert_equal("100000025509592_6801",@photo.pid)
9
+ end
10
+
11
+ def test_setting_id_should_also_use_new_method
12
+ @photo = Facebooker::Photo.new(:id=>"100000025509592_6801")
13
+ assert_equal("100000025509592_6801",@photo.id)
14
+ end
15
+
16
+ end