jet-facebooker 1.0.64

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