facebooker-lite 1.0.67

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 (118) hide show
  1. data/.autotest +15 -0
  2. data/CHANGELOG.rdoc +24 -0
  3. data/COPYING.rdoc +19 -0
  4. data/Manifest.txt +114 -0
  5. data/README.rdoc +120 -0
  6. data/Rakefile +94 -0
  7. data/TODO.rdoc +4 -0
  8. data/facebooker.gemspec +42 -0
  9. data/generators/xd_receiver/templates/xd_receiver.html +10 -0
  10. data/generators/xd_receiver/templates/xd_receiver_ssl.html +10 -0
  11. data/generators/xd_receiver/xd_receiver_generator.rb +10 -0
  12. data/init.rb +25 -0
  13. data/install.rb +12 -0
  14. data/lib/facebooker.rb +261 -0
  15. data/lib/facebooker/adapters/adapter_base.rb +91 -0
  16. data/lib/facebooker/adapters/bebo_adapter.rb +77 -0
  17. data/lib/facebooker/adapters/facebook_adapter.rb +60 -0
  18. data/lib/facebooker/admin.rb +42 -0
  19. data/lib/facebooker/application.rb +37 -0
  20. data/lib/facebooker/attachment.rb +51 -0
  21. data/lib/facebooker/batch_request.rb +45 -0
  22. data/lib/facebooker/data.rb +57 -0
  23. data/lib/facebooker/feed.rb +78 -0
  24. data/lib/facebooker/logging.rb +44 -0
  25. data/lib/facebooker/mobile.rb +20 -0
  26. data/lib/facebooker/mock/service.rb +50 -0
  27. data/lib/facebooker/mock/session.rb +18 -0
  28. data/lib/facebooker/model.rb +139 -0
  29. data/lib/facebooker/models/affiliation.rb +10 -0
  30. data/lib/facebooker/models/album.rb +11 -0
  31. data/lib/facebooker/models/applicationproperties.rb +39 -0
  32. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  33. data/lib/facebooker/models/comment.rb +9 -0
  34. data/lib/facebooker/models/cookie.rb +10 -0
  35. data/lib/facebooker/models/education_info.rb +11 -0
  36. data/lib/facebooker/models/event.rb +28 -0
  37. data/lib/facebooker/models/family_relative_info.rb +7 -0
  38. data/lib/facebooker/models/friend_list.rb +16 -0
  39. data/lib/facebooker/models/group.rb +36 -0
  40. data/lib/facebooker/models/info_item.rb +10 -0
  41. data/lib/facebooker/models/info_section.rb +10 -0
  42. data/lib/facebooker/models/location.rb +8 -0
  43. data/lib/facebooker/models/message_thread.rb +89 -0
  44. data/lib/facebooker/models/notifications.rb +17 -0
  45. data/lib/facebooker/models/page.rb +46 -0
  46. data/lib/facebooker/models/photo.rb +19 -0
  47. data/lib/facebooker/models/tag.rb +12 -0
  48. data/lib/facebooker/models/user.rb +751 -0
  49. data/lib/facebooker/models/video.rb +9 -0
  50. data/lib/facebooker/models/work_info.rb +10 -0
  51. data/lib/facebooker/parser.rb +970 -0
  52. data/lib/facebooker/rails/backwards_compatible_param_checks.rb +31 -0
  53. data/lib/facebooker/rails/controller.rb +353 -0
  54. data/lib/facebooker/rails/extensions/action_controller.rb +47 -0
  55. data/lib/facebooker/rails/extensions/rack_setup.rb +16 -0
  56. data/lib/facebooker/rails/extensions/routing.rb +15 -0
  57. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  58. data/lib/facebooker/rails/facebook_request_fix.rb +28 -0
  59. data/lib/facebooker/rails/facebook_request_fix_2-3.rb +31 -0
  60. data/lib/facebooker/rails/facebook_session_handling.rb +68 -0
  61. data/lib/facebooker/rails/facebook_url_rewriting.rb +60 -0
  62. data/lib/facebooker/rails/helpers/fb_connect.rb +75 -0
  63. data/lib/facebooker/rails/integration_session.rb +38 -0
  64. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  65. data/lib/facebooker/rails/publisher.rb +608 -0
  66. data/lib/facebooker/rails/routing.rb +49 -0
  67. data/lib/facebooker/rails/test_helpers.rb +68 -0
  68. data/lib/facebooker/rails/utilities.rb +22 -0
  69. data/lib/facebooker/server_cache.rb +24 -0
  70. data/lib/facebooker/service.rb +103 -0
  71. data/lib/facebooker/service/base_service.rb +19 -0
  72. data/lib/facebooker/service/curl_service.rb +44 -0
  73. data/lib/facebooker/service/net_http_service.rb +12 -0
  74. data/lib/facebooker/service/typhoeus_multi_service.rb +27 -0
  75. data/lib/facebooker/service/typhoeus_service.rb +17 -0
  76. data/lib/facebooker/session.rb +786 -0
  77. data/lib/facebooker/stream_post.rb +19 -0
  78. data/lib/facebooker/version.rb +9 -0
  79. data/lib/net/http_multipart_post.rb +123 -0
  80. data/lib/rack/facebook.rb +89 -0
  81. data/lib/rack/facebook_session.rb +21 -0
  82. data/lib/tasks/facebooker.rake +19 -0
  83. data/lib/tasks/facebooker.rb +2 -0
  84. data/lib/tasks/tunnel.rake +46 -0
  85. data/rails/init.rb +1 -0
  86. data/setup.rb +1585 -0
  87. data/templates/layout.erb +24 -0
  88. data/test/facebooker/adapters_test.rb +191 -0
  89. data/test/facebooker/admin_test.rb +102 -0
  90. data/test/facebooker/application_test.rb +110 -0
  91. data/test/facebooker/attachment_test.rb +72 -0
  92. data/test/facebooker/batch_request_test.rb +83 -0
  93. data/test/facebooker/data_test.rb +86 -0
  94. data/test/facebooker/logging_test.rb +43 -0
  95. data/test/facebooker/mobile_test.rb +45 -0
  96. data/test/facebooker/model_test.rb +133 -0
  97. data/test/facebooker/models/event_test.rb +15 -0
  98. data/test/facebooker/models/page_test.rb +56 -0
  99. data/test/facebooker/models/photo_test.rb +16 -0
  100. data/test/facebooker/models/user_test.rb +1074 -0
  101. data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +25 -0
  102. data/test/facebooker/rails/facebook_url_rewriting_test.rb +76 -0
  103. data/test/facebooker/rails/integration_session_test.rb +13 -0
  104. data/test/facebooker/rails/publisher_test.rb +538 -0
  105. data/test/facebooker/rails_integration_test.rb +1543 -0
  106. data/test/facebooker/server_cache_test.rb +44 -0
  107. data/test/facebooker/service_test.rb +58 -0
  108. data/test/facebooker/session_test.rb +883 -0
  109. data/test/facebooker_test.rb +1263 -0
  110. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  111. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  112. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  113. data/test/net/http_multipart_post_test.rb +52 -0
  114. data/test/rack/facebook_session_test.rb +34 -0
  115. data/test/rack/facebook_test.rb +73 -0
  116. data/test/rails_test_helper.rb +36 -0
  117. data/test/test_helper.rb +74 -0
  118. metadata +278 -0
@@ -0,0 +1,9 @@
1
+ require 'facebooker/model'
2
+ module Facebooker
3
+ class Video
4
+ include Model
5
+ attr_accessor :vid, :owner, :title,
6
+ :link, :description, :created,
7
+ :story_fbid
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Facebooker
2
+ class WorkInfo
3
+ include Model
4
+ attr_accessor :end_date, :start_date, :company_name, :description, :position
5
+ attr_reader :location
6
+ def location=(location)
7
+ @location = location.kind_of?(Hash) ? Location.from_hash(location) : location
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,970 @@
1
+ require 'rexml/document'
2
+ require 'facebooker/session'
3
+
4
+ begin
5
+ require 'nokogiri'
6
+ rescue Exception
7
+ end
8
+
9
+ module Facebooker
10
+ class Parser
11
+
12
+ module REXMLElementExtensions # :nodoc:
13
+ def content
14
+ self.text || ''
15
+ end
16
+
17
+ def [] key
18
+ attributes[key]
19
+ end
20
+
21
+ def text?
22
+ false
23
+ end
24
+ end
25
+
26
+ module REXMLTextExtensions # :nodoc:
27
+ def text?
28
+ true
29
+ end
30
+ end
31
+
32
+ ::REXML::Element.__send__(:include, REXMLElementExtensions)
33
+ ::REXML::Text.__send__(:include, REXMLTextExtensions)
34
+
35
+ def self.parse(method, data)
36
+ Errors.process(data)
37
+ parser = Parser::PARSERS[method]
38
+ raise "Can't find a parser for '#{method}'" unless parser
39
+ parser.process(data)
40
+ end
41
+
42
+ def self.array_of(response_element, element_name)
43
+ values_to_return = []
44
+ response_element.children.each do |element|
45
+ next if element.text?
46
+ next unless element.name == element_name
47
+ values_to_return << yield(element)
48
+ end
49
+ values_to_return
50
+ end
51
+
52
+ def self.array_of_text_values(response_element, element_name)
53
+ array_of(response_element, element_name) do |element|
54
+ element.content.strip
55
+ end
56
+ end
57
+
58
+ def self.array_of_hashes(response_element, element_name)
59
+ array_of(response_element, element_name) do |element|
60
+ hashinate(element)
61
+ end
62
+ end
63
+
64
+ def self.element(name, data)
65
+ data = data.body rescue data # either data or an HTTP response
66
+ if Object.const_defined?(:Nokogiri)
67
+ xml = Nokogiri::XML(data.strip)
68
+ if node = xml.at(name)
69
+ return node
70
+ end
71
+ if xml.root.name == name
72
+ return xml.root
73
+ end
74
+ else
75
+ doc = REXML::Document.new(data)
76
+ doc.elements.each(name) do |element|
77
+ return element
78
+ end
79
+ end
80
+ raise "Element #{name} not found in #{data}"
81
+ end
82
+
83
+ def self.hash_or_value_for(element)
84
+ if element.children.size == 1 && element.children.first.text?
85
+ element.content.strip
86
+ else
87
+ # We can have lists in not list item
88
+ if element['list'] == 'true'
89
+ element.children.reject{|c| c.text? }.map { |subchild| hash_or_value_for(subchild)}
90
+ else
91
+ hashinate(element)
92
+ end
93
+ end
94
+ end
95
+
96
+ def self.hashinate(response_element)
97
+ response_element.children.reject{|c| c.text? }.inject({}) do |hash, child|
98
+ # If the node hasn't any child, and is not a list, we want empty strings, not empty hashes,
99
+ # except if attributes['nil'] == true
100
+ hash[child.name] =
101
+ if (child['nil'] == 'true')
102
+ nil
103
+ elsif (child.children.size == 1 && child.children.first.text?) || (child.children.size == 0 && child['list'] != 'true')
104
+ anonymous_field_from(child, hash) || child.content.strip
105
+ elsif child['list'] == 'true'
106
+ child.children.reject{|c| c.text? }.map { |subchild| hash_or_value_for(subchild)}
107
+ else
108
+ child.children.reject{|c| c.text? }.inject({}) do |subhash, subchild|
109
+ subhash[subchild.name] = hash_or_value_for(subchild)
110
+ subhash
111
+ end
112
+ end #if (child.attributes)
113
+ hash
114
+ end #do |hash, child|
115
+ end
116
+
117
+ def self.hash_by_key_or_value_for(element, convert_1_to_true=false)
118
+ if element.children.size == 0
119
+ { element['key'] => nil }
120
+ elsif element.children.size == 1 && element.children.first.text?
121
+ { element['key'] => (convert_1_to_true ? element.content.strip == '1' : element.content.strip) }
122
+ else
123
+ hashinate_by_key(element, convert_1_to_true)
124
+ end
125
+ end
126
+
127
+ # A modification to hashinate. The new dashboard API returns XML in a different format than
128
+ # the other calls. What used to be the element name has become an attribute called "key".
129
+ def self.hashinate_by_key(response_element, convert_1_to_true=false)
130
+ response_element.children.reject{|c| c.text? }.inject({}) do |hash, child|
131
+
132
+ # If the node hasn't any child, and is not a list, we want empty strings, not empty hashes,
133
+ # except if attributes['nil'] == true
134
+ hash[child['key']] =
135
+ if (child['nil'] == 'true')
136
+ nil
137
+ elsif (child.children.size == 1 && child.children.first.text?) || (child.children.size == 0 && child['list'] != 'true')
138
+ anonymous_field_from(child, hash) || (convert_1_to_true ? child.content.strip == '1' : child.content.strip)
139
+ elsif child['list'] == 'true' && child.children.reject {|subchild| subchild.text?}.all? { |subchild| !subchild.text? && subchild['key'].nil? }
140
+ child.children.reject{|c| c.text? }.map { |subchild| hash_by_key_or_value_for(subchild, convert_1_to_true)}
141
+ elsif child['list'] == 'true'
142
+ hash_by_key_or_value_for(child, convert_1_to_true)
143
+ else
144
+ child.children.reject{|c| c.text? }.inject({}) do |subhash, subchild|
145
+ subhash[subchild['key']] = hash_by_key_or_value_for(subchild, convert_1_to_true)
146
+ subhash
147
+ end
148
+ end
149
+ hash
150
+ end
151
+ end
152
+
153
+
154
+
155
+ def self.booleanize(response)
156
+ response == "1" ? true : false
157
+ end
158
+
159
+ def self.anonymous_field_from(child, hash)
160
+ if child.name == 'anon'
161
+ (hash[child.name] || []) << child.content.strip
162
+ end
163
+ end
164
+
165
+ end
166
+
167
+ class RevokeAuthorization < Parser#:nodoc:
168
+ def self.process(data)
169
+ booleanize(data)
170
+ end
171
+ end
172
+
173
+ class RevokeExtendedPermission < Parser#:nodoc:
174
+ def self.process(data)
175
+ booleanize(element('auth_revokeExtendedPermission_response', data).content.strip)
176
+ end
177
+ end
178
+
179
+ class CreateToken < Parser#:nodoc:
180
+ def self.process(data)
181
+ element('auth_createToken_response', data).content.strip
182
+ end
183
+ end
184
+
185
+ class RegisterUsers < Parser
186
+ def self.process(data)
187
+ array_of_text_values(element("connect_registerUsers_response", data), "connect_registerUsers_response_elt")
188
+ end
189
+ end
190
+
191
+ class UnregisterUsers < Parser
192
+ def self.process(data)
193
+ array_of_text_values(element("connect_unregisterUsers_response", data), "connect_unregisterUsers_response_elt")
194
+ end
195
+ end
196
+
197
+ class GetUnconnectedFriendsCount < Parser
198
+ def self.process(data)
199
+ hash_or_value_for(element("connect_getUnconnectedFriendsCount_response",data)).to_i
200
+ end
201
+ end
202
+
203
+ class GetSession < Parser#:nodoc:
204
+ def self.process(data)
205
+ hashinate(element('auth_getSession_response', data))
206
+ end
207
+ end
208
+
209
+ class IsAppUser < Parser#:nodoc:
210
+ def self.process(data)
211
+ element('users_isAppUser_response', data).content == '1'
212
+ end
213
+ end
214
+
215
+ class GetFriends < Parser#:nodoc:
216
+ def self.process(data)
217
+ array_of_text_values(element('friends_get_response', data), 'uid')
218
+ end
219
+ end
220
+
221
+ class FriendListsGet < Parser#:nodoc:
222
+ def self.process(data)
223
+ array_of_hashes(element('friends_getLists_response', data), 'friendlist')
224
+ end
225
+ end
226
+
227
+ class UserInfo < Parser#:nodoc:
228
+ def self.process(data)
229
+ array_of_hashes(element('users_getInfo_response', data), 'user')
230
+ end
231
+ end
232
+
233
+ class UserStandardInfo < Parser#:nodoc:
234
+ def self.process(data)
235
+ array_of_hashes(element('users_getStandardInfo_response', data), 'standard_user_info')
236
+ end
237
+ end
238
+
239
+ class GetLoggedInUser < Parser#:nodoc:
240
+ def self.process(data)
241
+ Integer(element('users_getLoggedInUser_response', data).content.strip)
242
+ end
243
+ end
244
+
245
+ class PagesIsAdmin < Parser#:nodoc:
246
+ def self.process(data)
247
+ element('pages_isAdmin_response', data).content.strip == '1'
248
+ end
249
+ end
250
+
251
+ class PagesGetInfo < Parser#:nodoc:
252
+ def self.process(data)
253
+ array_of_hashes(element('pages_getInfo_response', data), 'page')
254
+ end
255
+ end
256
+
257
+ class PagesIsFan < Parser#:nodoc:
258
+ def self.process(data)
259
+ element('pages_isFan_response', data).content.strip == '1'
260
+ end
261
+ end
262
+
263
+ class PublishStoryToUser < Parser#:nodoc:
264
+ def self.process(data)
265
+ element('feed_publishStoryToUser_response', data).content.strip
266
+ end
267
+ end
268
+
269
+ class StreamPublish < Parser#:nodoc:
270
+ def self.process(data)
271
+ element('stream_publish_response', data).content.strip
272
+ end
273
+ end
274
+
275
+ class StreamAddComment < Parser#:nodoc:
276
+ def self.process(data)
277
+ element('stream_addComment_response', data).content.strip
278
+ end
279
+ end
280
+
281
+ class StreamAddLike < Parser#:nodoc:
282
+ def self.process(data)
283
+ element('stream_addLike_response', data).content.strip
284
+ end
285
+ end
286
+
287
+ class RegisterTemplateBundle < Parser#:nodoc:
288
+ def self.process(data)
289
+ element('feed_registerTemplateBundle_response', data).content.to_i
290
+ end
291
+ end
292
+
293
+ class GetRegisteredTemplateBundles < Parser
294
+ def self.process(data)
295
+ array_of_hashes(element('feed_getRegisteredTemplateBundles_response',data), 'template_bundle')
296
+ end
297
+ end
298
+
299
+ class DeactivateTemplateBundleByID < Parser#:nodoc:
300
+ def self.process(data)
301
+ element('feed_deactivateTemplateBundleByID_response', data).content.strip == '1'
302
+ end
303
+ end
304
+
305
+ class PublishUserAction < Parser#:nodoc:
306
+ def self.process(data)
307
+ element('feed_publishUserAction_response', data).children[1].content.strip == "1"
308
+ end
309
+ end
310
+
311
+ class UploadNativeStrings < Parser#:nodoc:
312
+ def self.process(data)
313
+ element('intl_uploadNativeStrings_response', data).content.strip
314
+ end
315
+ end
316
+
317
+ class PublishActionOfUser < Parser#:nodoc:
318
+ def self.process(data)
319
+ element('feed_publishActionOfUser_response', data).content.strip
320
+ end
321
+ end
322
+
323
+ class PublishTemplatizedAction < Parser#:nodoc:
324
+ def self.process(data)
325
+ element('feed_publishTemplatizedAction_response', data).children[1].content.strip
326
+ end
327
+ end
328
+
329
+ class SetAppProperties < Parser#:nodoc:
330
+ def self.process(data)
331
+ element('admin_setAppProperties_response', data).content.strip
332
+ end
333
+ end
334
+
335
+ class GetAppProperties < Parser#:nodoc:
336
+ def self.process(data)
337
+ element('admin_getAppProperties_response', data).content.strip
338
+ end
339
+ end
340
+
341
+ class SetRestrictionInfo < Parser#:nodoc:
342
+ def self.process(data)
343
+ element('admin_setRestrictionInfo_response', data).content.strip
344
+ end
345
+ end
346
+
347
+ class GetRestrictionInfo < Parser#:nodoc:
348
+ def self.process(data)
349
+ element('admin_getRestrictionInfo_response', data).content.strip
350
+ end
351
+ end
352
+
353
+ class GetAllocation < Parser#:nodoc:
354
+ def self.process(data)
355
+ element('admin_getAllocation_response', data).content.strip
356
+ end
357
+ end
358
+
359
+ class GetPublicInfo < Parser#:nodoc:
360
+ def self.process(data)
361
+ hashinate(element('application_getPublicInfo_response', data))
362
+ end
363
+ end
364
+
365
+ class CommentsAdd < Parser#:nodoc:
366
+ def self.process(data)
367
+ element('comments_add_response', data).content.strip
368
+ end
369
+ end
370
+
371
+ class CommentsRemove < Parser#:nodoc:
372
+ def self.process(data)
373
+ booleanize(data)
374
+ end
375
+ end
376
+
377
+ class CommentsGet < Parser#:nodoc:
378
+ def self.process(data)
379
+ array_of_hashes(element('comments_get_response', data), 'comment')
380
+ end
381
+ end
382
+
383
+ class BatchRun < Parser #:nodoc:
384
+ class << self
385
+ def current_batch=(current_batch)
386
+ Thread.current[:facebooker_current_batch]=current_batch
387
+ end
388
+ def current_batch
389
+ Thread.current[:facebooker_current_batch]
390
+ end
391
+ end
392
+ def self.process(data)
393
+ array_of_text_values(element('batch_run_response',data),"batch_run_response_elt").each_with_index do |response,i|
394
+ batch_request=current_batch[i]
395
+ body=Struct.new(:body).new
396
+ body.body=response
397
+ begin
398
+ batch_request.result=Parser.parse(batch_request.method,body)
399
+ rescue Exception=>ex
400
+ batch_request.exception_raised=ex
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ class GetAppUsers < Parser#:nodoc:
407
+ def self.process(data)
408
+ array_of_text_values(element('friends_getAppUsers_response', data), 'uid')
409
+ end
410
+ end
411
+
412
+ class MessageGetThreadsInFolder < Parser#:nodoc:
413
+ def self.process(data)
414
+ array_of_hashes(element('message_getThreadsInFolder_response', data), 'thread')
415
+ end
416
+ end
417
+
418
+ class NotificationsGet < Parser#:nodoc:
419
+ def self.process(data)
420
+ hashinate(element('notifications_get_response', data))
421
+ end
422
+ end
423
+
424
+ class NotificationsSend < Parser#:nodoc:
425
+ def self.process(data)
426
+ element('notifications_send_response', data).content.strip
427
+ end
428
+ end
429
+
430
+ class NotificationsSendEmail < Parser#:nodoc:
431
+ def self.process(data)
432
+ element('notifications_sendEmail_response', data).content.strip
433
+ end
434
+ end
435
+
436
+ class GetTags < Parser#nodoc:
437
+ def self.process(data)
438
+ array_of_hashes(element('photos_getTags_response', data), 'photo_tag')
439
+ end
440
+ end
441
+
442
+ class AddTags < Parser#nodoc:
443
+ def self.process(data)
444
+ element('photos_addTag_response', data)
445
+ end
446
+ end
447
+
448
+ class GetPhotos < Parser#nodoc:
449
+ def self.process(data)
450
+ array_of_hashes(element('photos_get_response', data), 'photo')
451
+ end
452
+ end
453
+
454
+ class GetAlbums < Parser#nodoc:
455
+ def self.process(data)
456
+ array_of_hashes(element('photos_getAlbums_response', data), 'album')
457
+ end
458
+ end
459
+
460
+ class GetStream < Parser #:nodoc:
461
+ def self.process(data)
462
+ response = {}
463
+ response[:albums] = array_of_hashes(element('stream_get_response/albums', data), 'album')
464
+ response[:posts] = array_of_hashes(element('stream_get_response/posts', data), 'stream_post')
465
+ response[:profile] = array_of_hashes(element('stream_get_response/profiles', data), 'profile')
466
+ response
467
+ end
468
+ end
469
+
470
+ class CreateAlbum < Parser#:nodoc:
471
+ def self.process(data)
472
+ hashinate(element('photos_createAlbum_response', data))
473
+ end
474
+ end
475
+
476
+ class UploadPhoto < Parser#:nodoc:
477
+ def self.process(data)
478
+ hashinate(element('photos_upload_response', data))
479
+ end
480
+ end
481
+
482
+ class UploadVideo < Parser#:nodoc:
483
+ def self.process(data)
484
+ hashinate(element('video_upload_response', data))
485
+ end
486
+ end
487
+
488
+ class SendRequest < Parser#:nodoc:
489
+ def self.process(data)
490
+ element('notifications_sendRequest_response', data).content.strip
491
+ end
492
+ end
493
+
494
+ class ProfileFBML < Parser#:nodoc:
495
+ def self.process(data)
496
+ element('profile_getFBML_response', data).content.strip
497
+ end
498
+ end
499
+
500
+ class ProfileFBMLSet < Parser#:nodoc:
501
+ def self.process(data)
502
+ element('profile_setFBML_response', data).content.strip
503
+ end
504
+ end
505
+
506
+ class ProfileInfo < Parser#:nodoc:
507
+ def self.process(data)
508
+ hashinate(element('profile_getInfo_response info_fields', data))
509
+ end
510
+ end
511
+
512
+ class ProfileInfoSet < Parser#:nodoc:
513
+ def self.process(data)
514
+ element('profile_setInfo_response', data).content.strip
515
+ end
516
+ end
517
+
518
+ class FqlQuery < Parser#nodoc
519
+ def self.process(data)
520
+ root = element('fql_query_response', data)
521
+ first_child = root.children.reject{|c| c.text? }.first
522
+ first_child.nil? ? [] : [first_child.name, array_of_hashes(root, first_child.name)]
523
+ end
524
+ end
525
+
526
+ class FqlMultiquery < Parser#nodoc
527
+ def self.process(data)
528
+ root = element('fql_multiquery_response', data)
529
+ root.children.reject { |child| child.text? }.map do |elm|
530
+ elm.children.reject { |child| child.text? }.map do |query|
531
+ if 'name' == query.name
532
+ query.text
533
+ else
534
+ list = query.children.reject { |child| child.text? }
535
+ if list.length == 0
536
+ []
537
+ else
538
+ [list.first.name, array_of_hashes(query, list.first.name)]
539
+ end
540
+ end
541
+ end
542
+ end
543
+ end
544
+ end
545
+
546
+ class SetRefHandle < Parser#:nodoc:
547
+ def self.process(data)
548
+ element('fbml_setRefHandle_response', data).content.strip
549
+ end
550
+ end
551
+
552
+ class RefreshRefURL < Parser#:nodoc:
553
+ def self.process(data)
554
+ element('fbml_refreshRefUrl_response', data).content.strip
555
+ end
556
+ end
557
+
558
+ class RefreshImgSrc < Parser#:nodoc:
559
+ def self.process(data)
560
+ element('fbml_refreshImgSrc_response', data).content.strip
561
+ end
562
+ end
563
+
564
+ class SetCookie < Parser#:nodoc:
565
+ def self.process(data)
566
+ element('data_setCookie_response', data).content.strip
567
+ end
568
+ end
569
+
570
+ class GetCookies < Parser#:nodoc:
571
+ def self.process(data)
572
+ array_of_hashes(element('data_getCookie_response', data), 'cookies')
573
+ end
574
+ end
575
+
576
+ class EventsRsvp < Parser#:nodoc:
577
+ def self.process(data)
578
+ element('events_rsvp_response', data).content.strip
579
+ end
580
+ end
581
+
582
+ class EventsCreate < Parser#:nodoc:
583
+ def self.process(data)
584
+ element('events_create_response', data).content.strip
585
+ end
586
+ end
587
+
588
+ class EventsCancel < Parser#:nodoc:
589
+ def self.process(data)
590
+ element('events_cancel_response', data).content.strip
591
+ end
592
+ end
593
+
594
+ class EventsGet < Parser#:nodoc:
595
+ def self.process(data)
596
+ array_of_hashes(element('events_get_response', data), 'event')
597
+ end
598
+ end
599
+
600
+ class GroupGetMembers < Parser#:nodoc:
601
+ def self.process(data)
602
+ root = element('groups_getMembers_response', data)
603
+ result = ['members', 'admins', 'officers', 'not_replied'].map do |position|
604
+ array_of(root, position) {|element| element}.map do |element|
605
+ array_of_text_values(element, 'uid').map do |uid|
606
+ {:position => position}.merge(:uid => uid)
607
+ end
608
+ end
609
+ end.flatten
610
+ end
611
+ end
612
+
613
+ class EventMembersGet < Parser#:nodoc:
614
+ def self.process(data)
615
+ root = element('events_getMembers_response', data)
616
+ result = ['attending', 'declined', 'unsure', 'not_replied'].map do |rsvp_status|
617
+ array_of(root, rsvp_status) {|element| element}.map do |element|
618
+ array_of_text_values(element, 'uid').map do |uid|
619
+ {:rsvp_status => rsvp_status}.merge(:uid => uid)
620
+ end
621
+ end
622
+ end.flatten
623
+ end
624
+ end
625
+
626
+ class GroupsGet < Parser#:nodoc:
627
+ def self.process(data)
628
+ array_of_hashes(element('groups_get_response', data), 'group')
629
+ end
630
+ end
631
+
632
+ class AreFriends < Parser#:nodoc:
633
+ def self.process(data)
634
+ array_of_hashes(element('friends_areFriends_response', data), 'friend_info').inject({}) do |memo, hash|
635
+ memo[[Integer(hash['uid1']), Integer(hash['uid2'])].sort] = are_friends?(hash['are_friends'])
636
+ memo
637
+ end
638
+ end
639
+
640
+ private
641
+ def self.are_friends?(raw_value)
642
+ if raw_value == '1'
643
+ true
644
+ elsif raw_value == '0'
645
+ false
646
+ else
647
+ nil
648
+ end
649
+ end
650
+ end
651
+
652
+ class SetStatus < Parser
653
+ def self.process(data)
654
+ element('users_setStatus_response',data).content.strip == '1'
655
+ end
656
+ end
657
+
658
+ class GetStatus < Parser # :nodoc:
659
+ def self.process(data)
660
+ array_of_hashes(element('status_get_response',data),'user_status')
661
+ end
662
+ end
663
+
664
+ class GetPreference < Parser#:nodoc:
665
+ def self.process(data)
666
+ element('data_getUserPreference_response', data).content.strip
667
+ end
668
+ end
669
+
670
+ class SetPreference < Parser#:nodoc:
671
+ def self.process(data)
672
+ element('data_setUserPreference_response', data).content.strip
673
+ end
674
+ end
675
+
676
+ class UserHasPermission < Parser
677
+ def self.process(data)
678
+ element('users_hasAppPermission_response', data).content.strip
679
+ end
680
+ end
681
+
682
+ class SmsSend < Parser#:nodoc:
683
+ def self.process(data)
684
+ element('sms_send_response', data).content.strip == '0'
685
+ end
686
+ end
687
+
688
+ class SmsCanSend < Parser#:nodoc:
689
+ def self.process(data)
690
+ element('sms_canSend_response', data).content.strip
691
+ end
692
+ end
693
+
694
+ class DashboardGetCount < Parser
695
+ def self.process(data)
696
+ element('dashboard_getCount_response', data).content.strip
697
+ end
698
+ end
699
+
700
+ class DashboardSetCount < Parser
701
+ def self.process(data)
702
+ element('dashboard_setCount_response', data).content.strip == '1'
703
+ end
704
+ end
705
+
706
+ class DashboardIncrementCount < Parser
707
+ def self.process(data)
708
+ element('dashboard_incrementCount_response', data).content.strip == '1'
709
+ end
710
+ end
711
+
712
+ class DashboardDecrementCount < Parser
713
+ def self.process(data)
714
+ element('dashboard_decrementCount_response', data).content.strip == '1'
715
+ end
716
+ end
717
+
718
+ class DashboardMultiGetCount < Parser
719
+ def self.process(data)
720
+ hashinate_by_key(element('dashboard_multiGetCount_response', data))
721
+ end
722
+ end
723
+
724
+ class DashboardMultiSetCount < Parser
725
+ def self.process(data)
726
+ hashinate_by_key(element('dashboard_multiSetCount_response', data), true)
727
+ end
728
+ end
729
+
730
+ class DashboardMultiIncrementCount < Parser
731
+ def self.process(data)
732
+ hashinate_by_key(element('dashboard_multiIncrementCount_response', data), true)
733
+ end
734
+ end
735
+
736
+ class DashboardMultiDecrementCount < Parser
737
+ def self.process(data)
738
+ hashinate_by_key(element('dashboard_multiDecrementCount_response', data), true)
739
+ end
740
+ end
741
+
742
+ class DashboardAddGlobalNews < Parser
743
+ def self.process(data)
744
+ element('dashboard_addGlobalNews_response', data).content.strip
745
+ end
746
+ end
747
+
748
+ # Currently, always returns all
749
+ class DashboardGetGlobalNews < Parser
750
+ def self.process(data)
751
+ hashinate_by_key(element('dashboard_getGlobalNews_response', data))
752
+ end
753
+ end
754
+
755
+ class DashboardClearGlobalNews < Parser
756
+ def self.process(data)
757
+ hashinate_by_key(element('dashboard_clearGlobalNews_response', data), true)
758
+ end
759
+ end
760
+
761
+ class DashboardAddNews < Parser
762
+ def self.process(data)
763
+ element('dashboard_addNews_response', data).content.strip
764
+ end
765
+ end
766
+
767
+ class DashboardGetNews < Parser
768
+ def self.process(data)
769
+ hashinate_by_key(element('dashboard_getNews_response', data))
770
+ end
771
+ end
772
+
773
+ class DashboardClearNews < Parser
774
+ def self.process(data)
775
+ hashinate_by_key(element('dashboard_clearNews_response', data), true)
776
+ end
777
+ end
778
+
779
+ class DashboardMultiAddNews < Parser
780
+ def self.process(data)
781
+ hashinate_by_key(element('dashboard_multiAddNews_response', data))
782
+ end
783
+ end
784
+
785
+ class DashboardMultiClearNews < Parser
786
+ def self.process(data)
787
+ hashinate_by_key(element('dashboard_multiClearNews_response', data), true)
788
+ end
789
+ end
790
+
791
+ class DashboardMultiGetNews < Parser
792
+ def self.process(data)
793
+ hashinate_by_key(element('dashboard_multiGetNews_response', data))
794
+ end
795
+ end
796
+
797
+ class DashboardPublishActivity < Parser
798
+ def self.process(data)
799
+ element('dashboard_publishActivity_response', data).content.strip
800
+ end
801
+ end
802
+
803
+ class DashboardRemoveActivity < Parser
804
+ def self.process(data)
805
+ hashinate_by_key(element('dashboard_removeActivity_response', data), true)
806
+ end
807
+ end
808
+
809
+ class DashboardGetActivity < Parser
810
+ def self.process(data)
811
+ hashinate_by_key(element('dashboard_getActivity_response', data))
812
+ end
813
+ end
814
+
815
+
816
+ class Errors < Parser#:nodoc:
817
+ EXCEPTIONS = {
818
+ 1 => Facebooker::Session::UnknownError,
819
+ 2 => Facebooker::Session::ServiceUnavailable,
820
+ 4 => Facebooker::Session::MaxRequestsDepleted,
821
+ 5 => Facebooker::Session::HostNotAllowed,
822
+ 100 => Facebooker::Session::MissingOrInvalidParameter,
823
+ 101 => Facebooker::Session::InvalidAPIKey,
824
+ 102 => Facebooker::Session::SessionExpired,
825
+ 103 => Facebooker::Session::CallOutOfOrder,
826
+ 104 => Facebooker::Session::IncorrectSignature,
827
+ 120 => Facebooker::Session::InvalidAlbumId,
828
+ 200 => Facebooker::Session::PermissionError,
829
+ 250 => Facebooker::Session::ExtendedPermissionRequired,
830
+ 321 => Facebooker::Session::AlbumIsFull,
831
+ 324 => Facebooker::Session::MissingOrInvalidImageFile,
832
+ 325 => Facebooker::Session::TooManyUnapprovedPhotosPending,
833
+ 330 => Facebooker::Session::TemplateDataMissingRequiredTokens,
834
+ 340 => Facebooker::Session::TooManyUserCalls,
835
+ 341 => Facebooker::Session::TooManyUserActionCalls,
836
+ 342 => Facebooker::Session::InvalidFeedTitleLink,
837
+ 343 => Facebooker::Session::InvalidFeedTitleLength,
838
+ 344 => Facebooker::Session::InvalidFeedTitleName,
839
+ 345 => Facebooker::Session::BlankFeedTitle,
840
+ 346 => Facebooker::Session::FeedBodyLengthTooLong,
841
+ 347 => Facebooker::Session::InvalidFeedPhotoSource,
842
+ 348 => Facebooker::Session::InvalidFeedPhotoLink,
843
+ 330 => Facebooker::Session::FeedMarkupInvalid,
844
+ 360 => Facebooker::Session::FeedTitleDataInvalid,
845
+ 361 => Facebooker::Session::FeedTitleTemplateInvalid,
846
+ 362 => Facebooker::Session::FeedBodyDataInvalid,
847
+ 363 => Facebooker::Session::FeedBodyTemplateInvalid,
848
+ 364 => Facebooker::Session::FeedPhotosNotRetrieved,
849
+ 366 => Facebooker::Session::FeedTargetIdsInvalid,
850
+ 601 => Facebooker::Session::FQLParseError,
851
+ 602 => Facebooker::Session::FQLFieldDoesNotExist,
852
+ 603 => Facebooker::Session::FQLTableDoesNotExist,
853
+ 604 => Facebooker::Session::FQLStatementNotIndexable,
854
+ 605 => Facebooker::Session::FQLFunctionDoesNotExist,
855
+ 606 => Facebooker::Session::FQLWrongNumberArgumentsPassedToFunction,
856
+ 612 => Facebooker::Session::ReadMailboxExtendedPermissionRequired,
857
+ 807 => Facebooker::Session::TemplateBundleInvalid
858
+ }
859
+ def self.process(data)
860
+ response_element = element('error_response', data) rescue nil
861
+ if response_element
862
+ hash = hashinate(response_element)
863
+ exception = EXCEPTIONS[Integer(hash['error_code'])] || StandardError
864
+ raise exception, hash['error_msg']
865
+ end
866
+ end
867
+ end
868
+
869
+ class Parser
870
+ PARSERS = {
871
+ 'facebook.auth.revokeAuthorization' => RevokeAuthorization,
872
+ 'facebook.auth.revokeExtendedPermission' => RevokeExtendedPermission,
873
+ 'facebook.auth.createToken' => CreateToken,
874
+ 'facebook.auth.getSession' => GetSession,
875
+ 'facebook.connect.registerUsers' => RegisterUsers,
876
+ 'facebook.connect.unregisterUsers' => UnregisterUsers,
877
+ 'facebook.connect.getUnconnectedFriendsCount' => GetUnconnectedFriendsCount,
878
+ 'facebook.users.getInfo' => UserInfo,
879
+ 'facebook.users.getStandardInfo' => UserStandardInfo,
880
+ 'facebook.users.setStatus' => SetStatus,
881
+ 'facebook.status.get' => GetStatus,
882
+ 'facebook.users.getLoggedInUser' => GetLoggedInUser,
883
+ 'facebook.users.hasAppPermission' => UserHasPermission,
884
+ 'facebook.users.isAppUser' => IsAppUser,
885
+ 'facebook.pages.isAdmin' => PagesIsAdmin,
886
+ 'facebook.pages.getInfo' => PagesGetInfo,
887
+ 'facebook.pages.isFan' => PagesIsFan,
888
+ 'facebook.friends.get' => GetFriends,
889
+ 'facebook.friends.getLists' => FriendListsGet,
890
+ 'facebook.friends.areFriends' => AreFriends,
891
+ 'facebook.friends.getAppUsers' => GetAppUsers,
892
+ 'facebook.feed.publishStoryToUser' => PublishStoryToUser,
893
+ 'facebook.feed.publishActionOfUser' => PublishActionOfUser,
894
+ 'facebook.feed.publishTemplatizedAction' => PublishTemplatizedAction,
895
+ 'facebook.feed.registerTemplateBundle' => RegisterTemplateBundle,
896
+ 'facebook.feed.deactivateTemplateBundleByID' => DeactivateTemplateBundleByID,
897
+ 'facebook.feed.getRegisteredTemplateBundles' => GetRegisteredTemplateBundles,
898
+ 'facebook.feed.publishUserAction' => PublishUserAction,
899
+ 'facebook.message.getThreadsInFolder' => MessageGetThreadsInFolder,
900
+ 'facebook.notifications.get' => NotificationsGet,
901
+ 'facebook.notifications.send' => NotificationsSend,
902
+ 'facebook.notifications.sendRequest' => SendRequest,
903
+ 'facebook.profile.getFBML' => ProfileFBML,
904
+ 'facebook.profile.setFBML' => ProfileFBMLSet,
905
+ 'facebook.profile.getInfo' => ProfileInfo,
906
+ 'facebook.profile.setInfo' => ProfileInfoSet,
907
+ 'facebook.fbml.setRefHandle' => SetRefHandle,
908
+ 'facebook.fbml.refreshRefUrl' => RefreshRefURL,
909
+ 'facebook.fbml.refreshImgSrc' => RefreshImgSrc,
910
+ 'facebook.data.setCookie' => SetCookie,
911
+ 'facebook.data.getCookies' => GetCookies,
912
+ 'facebook.admin.setAppProperties' => SetAppProperties,
913
+ 'facebook.admin.getAppProperties' => GetAppProperties,
914
+ 'facebook.admin.setRestrictionInfo' => SetRestrictionInfo,
915
+ 'facebook.admin.getRestrictionInfo' => GetRestrictionInfo,
916
+ 'facebook.admin.getAllocation' => GetAllocation,
917
+ 'facebook.application.getPublicInfo' => GetPublicInfo,
918
+ 'facebook.batch.run' => BatchRun,
919
+ 'facebook.fql.query' => FqlQuery,
920
+ 'facebook.fql.multiquery' => FqlMultiquery,
921
+ 'facebook.photos.get' => GetPhotos,
922
+ 'facebook.photos.getAlbums' => GetAlbums,
923
+ 'facebook.photos.createAlbum' => CreateAlbum,
924
+ 'facebook.photos.getTags' => GetTags,
925
+ 'facebook.photos.addTag' => AddTags,
926
+ 'facebook.photos.upload' => UploadPhoto,
927
+ 'facebook.stream.get' => GetStream,
928
+ 'facebook.stream.publish' => StreamPublish,
929
+ 'facebook.stream.addComment' => StreamAddComment,
930
+ 'facebook.stream.addLike' => StreamAddLike,
931
+ 'facebook.events.create' => EventsCreate,
932
+ 'facebook.events.cancel' => EventsCancel,
933
+ 'facebook.events.get' => EventsGet,
934
+ 'facebook.events.rsvp' => EventsRsvp,
935
+ 'facebook.groups.get' => GroupsGet,
936
+ 'facebook.events.getMembers' => EventMembersGet,
937
+ 'facebook.groups.getMembers' => GroupGetMembers,
938
+ 'facebook.notifications.sendEmail' => NotificationsSendEmail,
939
+ 'facebook.data.getUserPreference' => GetPreference,
940
+ 'facebook.data.setUserPreference' => SetPreference,
941
+ 'facebook.video.upload' => UploadVideo,
942
+ 'facebook.sms.send' => SmsSend,
943
+ 'facebook.sms.canSend' => SmsCanSend,
944
+ 'facebook.comments.add' => CommentsAdd,
945
+ 'facebook.comments.remove' => CommentsRemove,
946
+ 'facebook.comments.get' => CommentsGet,
947
+ 'facebook.dashboard.setCount' => DashboardSetCount,
948
+ 'facebook.dashboard.getCount' => DashboardGetCount,
949
+ 'facebook.dashboard.incrementCount' => DashboardIncrementCount,
950
+ 'facebook.dashboard.decrementCount' => DashboardDecrementCount,
951
+ 'facebook.dashboard.multiGetCount' => DashboardMultiGetCount,
952
+ 'facebook.dashboard.multiSetCount' => DashboardMultiSetCount,
953
+ 'facebook.dashboard.multiIncrementCount' => DashboardMultiIncrementCount,
954
+ 'facebook.dashboard.multiDecrementCount' => DashboardMultiDecrementCount,
955
+ 'facebook.dashboard.addGlobalNews' => DashboardAddGlobalNews,
956
+ 'facebook.dashboard.getGlobalNews' => DashboardGetGlobalNews,
957
+ 'facebook.dashboard.clearGlobalNews' => DashboardClearGlobalNews,
958
+ 'facebook.dashboard.addNews' => DashboardAddNews,
959
+ 'facebook.dashboard.getNews' => DashboardGetNews,
960
+ 'facebook.dashboard.clearNews' => DashboardClearNews,
961
+ 'facebook.dashboard.multiAddNews' => DashboardMultiAddNews,
962
+ 'facebook.dashboard.multiGetNews' => DashboardMultiGetNews,
963
+ 'facebook.dashboard.multiClearNews' => DashboardMultiClearNews,
964
+ 'facebook.dashboard.publishActivity' => DashboardPublishActivity,
965
+ 'facebook.dashboard.removeActivity' => DashboardRemoveActivity,
966
+ 'facebook.dashboard.getActivity' => DashboardGetActivity,
967
+ 'facebook.intl.uploadNativeStrings' => UploadNativeStrings
968
+ }
969
+ end
970
+ end