yury-facebooker 0.9.5

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 (48) hide show
  1. data/CHANGELOG.txt +0 -0
  2. data/COPYING +19 -0
  3. data/History.txt +16 -0
  4. data/Manifest.txt +77 -0
  5. data/README +46 -0
  6. data/README.txt +81 -0
  7. data/Rakefile +62 -0
  8. data/TODO.txt +10 -0
  9. data/facebooker.yml.tpl +41 -0
  10. data/init.rb +53 -0
  11. data/install.rb +7 -0
  12. data/lib/facebooker.rb +143 -0
  13. data/lib/facebooker/data.rb +40 -0
  14. data/lib/facebooker/feed.rb +78 -0
  15. data/lib/facebooker/model.rb +123 -0
  16. data/lib/facebooker/parser.rb +518 -0
  17. data/lib/facebooker/rails/controller.rb +241 -0
  18. data/lib/facebooker/rails/facebook_asset_path.rb +18 -0
  19. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  20. data/lib/facebooker/rails/facebook_request_fix.rb +24 -0
  21. data/lib/facebooker/rails/facebook_session_handling.rb +58 -0
  22. data/lib/facebooker/rails/facebook_url_rewriting.rb +39 -0
  23. data/lib/facebooker/rails/helpers.rb +615 -0
  24. data/lib/facebooker/rails/routing.rb +49 -0
  25. data/lib/facebooker/rails/test_helpers.rb +88 -0
  26. data/lib/facebooker/rails/utilities.rb +22 -0
  27. data/lib/facebooker/server_cache.rb +24 -0
  28. data/lib/facebooker/service.rb +31 -0
  29. data/lib/facebooker/session.rb +550 -0
  30. data/lib/facebooker/version.rb +9 -0
  31. data/lib/net/http_multipart_post.rb +123 -0
  32. data/lib/tasks/facebooker.rake +17 -0
  33. data/lib/tasks/tunnel.rake +43 -0
  34. data/setup.rb +1585 -0
  35. data/test/event_test.rb +15 -0
  36. data/test/facebook_cache_test.rb +43 -0
  37. data/test/facebook_data_test.rb +50 -0
  38. data/test/facebooker_test.rb +855 -0
  39. data/test/fixtures/multipart_post_body_with_only_parameters.txt +33 -0
  40. data/test/fixtures/multipart_post_body_with_single_file.txt +38 -0
  41. data/test/fixtures/multipart_post_body_with_single_file_that_has_nil_key.txt +38 -0
  42. data/test/http_multipart_post_test.rb +54 -0
  43. data/test/model_test.rb +91 -0
  44. data/test/rails_integration_test.rb +993 -0
  45. data/test/session_test.rb +559 -0
  46. data/test/test_helper.rb +60 -0
  47. data/test/user_test.rb +219 -0
  48. metadata +130 -0
@@ -0,0 +1,40 @@
1
+ module Facebooker
2
+ class Data
3
+ def initialize(session)
4
+ @session = session
5
+ end
6
+
7
+ ##
8
+ # ** BETA ***
9
+ # Sets a cookie on Facebook
10
+ # +user+ The user for whom this cookie needs to be set.
11
+ # +name+ Name of the cookie
12
+ # +value+ Value of the cookie
13
+ # Optional:
14
+ # +expires+ Time when the cookie should expire. If not specified, the cookie never expires.
15
+ # +path+ Path relative to the application's callback URL, with which the cookie should be associated. (default value is /?
16
+ def set_cookie(user, name, value, expires=nil, path=nil)
17
+ @session.post('facebook.data.setCookie',
18
+ :uid => User.cast_to_facebook_id(user),
19
+ :name => name,
20
+ :value => value,
21
+ :expires => expires,
22
+ :path => path) {|response| response == '1'}
23
+ end
24
+
25
+ ##
26
+ # ** BETA ***
27
+ # Gets a cookie stored on Facebook
28
+ # +user+ The user from whom to get the cookies.
29
+ # Optional:
30
+ # +name+ The name of the cookie. If not specified, all the cookies for the given user get returned.
31
+ def get_cookies(user, name=nil)
32
+ @cookies = @session.post(
33
+ 'facebook.data.getCookies', :uid => User.cast_to_facebook_id(user), :name => name) do |response|
34
+ response.map do |hash|
35
+ Cookie.from_hash(hash)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,78 @@
1
+ module Facebooker
2
+ module Feed
3
+ METHODS = {'Action' => 'facebook.feed.publishActionOfUser', 'Story' => 'facebook.feed.publishStoryToUser',
4
+ 'TemplatizedAction' => 'facebook.feed.publishTemplatizedAction' }
5
+
6
+ class ActionBase
7
+ 1.upto(4) do |num|
8
+ attr_accessor "image_#{num}"
9
+ attr_accessor "image_#{num}_link"
10
+ end
11
+
12
+ def add_image(image,link=nil)
13
+ 1.upto(4) do |num|
14
+ if send("image_#{num}").blank?
15
+ send("image_#{num}=",image)
16
+ send("image_#{num}_link=",link) unless link.nil?
17
+ return num
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+ protected
24
+ def image_params
25
+ image_hash = {}
26
+ 1.upto(4) do |num|
27
+ image_attribute = "image_#{num}"
28
+ image_link_attribute = image_attribute + "_link"
29
+ self.__send__(image_attribute) ? image_hash[image_attribute] = self.__send__(image_attribute) : nil
30
+ self.__send__(image_link_attribute) ? image_hash[image_link_attribute] = self.__send__(image_link_attribute) : nil
31
+ end
32
+ image_hash
33
+ end
34
+ end
35
+
36
+ ##
37
+ # Representation of a templatized action to be published into a user's news feed
38
+ class TemplatizedAction < ActionBase
39
+ attr_accessor :page_actor_id, :title_template, :title_data, :body_template, :body_data, :body_general, :target_ids
40
+
41
+ def to_params
42
+ raise "Must set title_template" if self.title_template.nil?
43
+ { :page_actor_id => page_actor_id,
44
+ :title_template => title_template,
45
+ :title_data => convert_json(title_data),
46
+ :body_template => body_template,
47
+ :body_data => convert_json(body_data),
48
+ :body_general => body_general,
49
+ :target_ids => target_ids }.merge image_params
50
+ end
51
+
52
+ def convert_json(hash_or_string)
53
+ (hash_or_string.is_a?(Hash) and hash_or_string.respond_to?(:to_json)) ? hash_or_string.to_json : hash_or_string
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Representation of a story to be published into a user's news feed.
59
+ class Story < ActionBase
60
+ attr_accessor :title, :body
61
+
62
+ ##
63
+ # Converts Story to a Hash of its attributes for use as parameters to Facebook REST API calls
64
+ def to_params
65
+ raise "Must set title before converting" if self.title.nil?
66
+ { :title => title, :body => body }.merge image_params
67
+ end
68
+
69
+ end
70
+ Action = Story.dup
71
+ def Action.name
72
+ "Action"
73
+ end
74
+ ##
75
+ # Representation of an action to be published into a user's news feed. Alias for Story.
76
+ class Action; end
77
+ end
78
+ end
@@ -0,0 +1,123 @@
1
+ module Facebooker
2
+ ##
3
+ # helper methods primarily supporting the management of Ruby objects which are populatable via Hashes.
4
+ # Since most Facebook API calls accept and return hashes of data (as XML), the Model module allows us to
5
+ # directly populate a model's attributes given a Hash with matching key names.
6
+ module Model
7
+ class UnboundSessionException < Exception; end
8
+ def self.included(includer)
9
+ includer.extend ClassMethods
10
+ includer.__send__(:attr_writer, :session)
11
+ includer.__send__(:attr_reader, :anonymous_fields)
12
+ end
13
+ module ClassMethods
14
+ ##
15
+ # Instantiate a new instance of the class into which we are included and populate that instance's
16
+ # attributes given the provided Hash. Key names in the Hash should map to attribute names on the model.
17
+ def from_hash(hash)
18
+ instance = new(hash)
19
+ yield instance if block_given?
20
+ instance
21
+ end
22
+
23
+ ##
24
+ # Create a standard attr_writer and a populating_attr_reader
25
+ def populating_attr_accessor(*symbols)
26
+ attr_writer *symbols
27
+ populating_attr_reader *symbols
28
+ end
29
+
30
+ ##
31
+ # Create a reader that will attempt to populate the model if it has not already been populated
32
+ def populating_attr_reader(*symbols)
33
+ symbols.each do |symbol|
34
+ define_method(symbol) do
35
+ populate unless populated?
36
+ instance_variable_get("@#{symbol}")
37
+ end
38
+ end
39
+ end
40
+
41
+ def populating_hash_settable_accessor(symbol, klass)
42
+ populating_attr_reader symbol
43
+ hash_settable_writer(symbol, klass)
44
+ end
45
+
46
+ def populating_hash_settable_list_accessor(symbol, klass)
47
+ populating_attr_reader symbol
48
+ hash_settable_list_writer(symbol, klass)
49
+ end
50
+
51
+ #
52
+ # Declares an attribute named ::symbol:: which can be set with either an instance of ::klass::
53
+ # or a Hash which will be used to populate a new instance of ::klass::.
54
+ def hash_settable_accessor(symbol, klass)
55
+ attr_reader symbol
56
+ hash_settable_writer(symbol, klass)
57
+ end
58
+
59
+ def hash_settable_writer(symbol, klass)
60
+ define_method("#{symbol}=") do |value|
61
+ instance_variable_set("@#{symbol}", value.kind_of?(Hash) ? klass.from_hash(value) : value)
62
+ end
63
+ end
64
+
65
+ #
66
+ # Declares an attribute named ::symbol:: which can be set with either a list of instances of ::klass::
67
+ # or a list of Hashes which will be used to populate a new instance of ::klass::.
68
+ def hash_settable_list_accessor(symbol, klass)
69
+ attr_reader symbol
70
+ hash_settable_list_writer(symbol, klass)
71
+ end
72
+
73
+ def hash_settable_list_writer(symbol, klass)
74
+ define_method("#{symbol}=") do |list|
75
+ instance_variable_set("@#{symbol}", list.map do |item|
76
+ item.kind_of?(Hash) ? klass.from_hash(item) : item
77
+ end)
78
+ end
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Centralized, error-checked place for a model to get the session to which it is bound.
84
+ # Any Facebook API queries require a Session instance.
85
+ def session
86
+ @session || (raise UnboundSessionException, "Must bind this object to a Facebook session before querying")
87
+ end
88
+
89
+ #
90
+ # This gets populated from FQL queries.
91
+ def anon=(value)
92
+ @anonymous_fields = value
93
+ end
94
+
95
+ def initialize(hash = {})
96
+ populate_from_hash!(hash)
97
+ end
98
+
99
+ def populate
100
+ raise NotImplementedError, "#{self.class} included me and should have overriden me"
101
+ end
102
+
103
+ def populated?
104
+ !@populated.nil?
105
+ end
106
+
107
+ ##
108
+ # Set model's attributes via Hash. Keys should map directly to the model's attribute names.
109
+ def populate_from_hash!(hash)
110
+ unless hash.empty?
111
+ hash.each do |key, value|
112
+ set_attr_method = "#{key}="
113
+ if respond_to?(set_attr_method)
114
+ self.__send__(set_attr_method, value)
115
+ else
116
+ Facebooker::Logging.log_info("**Warning**, Attempt to set non-attribute: #{key}",hash)
117
+ end
118
+ end
119
+ @populated = true
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,518 @@
1
+ require 'rexml/document'
2
+ require 'facebooker/session'
3
+ module Facebooker
4
+ class Parser
5
+
6
+ module REXMLElementExtensions
7
+ def text_value
8
+ self.children.first.to_s.strip
9
+ end
10
+ end
11
+
12
+ ::REXML::Element.__send__(:include, REXMLElementExtensions)
13
+
14
+ def self.parse(method, data)
15
+ Errors.process(data)
16
+ parser = Parser::PARSERS[method]
17
+ parser.process(
18
+ data
19
+ )
20
+ end
21
+
22
+ def self.array_of(response_element, element_name)
23
+ values_to_return = []
24
+ response_element.elements.each(element_name) do |element|
25
+ values_to_return << yield(element)
26
+ end
27
+ values_to_return
28
+ end
29
+
30
+ def self.array_of_text_values(response_element, element_name)
31
+ array_of(response_element, element_name) do |element|
32
+ element.text_value
33
+ end
34
+ end
35
+
36
+ def self.array_of_hashes(response_element, element_name)
37
+ array_of(response_element, element_name) do |element|
38
+ hashinate(element)
39
+ end
40
+ end
41
+
42
+ def self.element(name, data)
43
+ data = data.body rescue data # either data or an HTTP response
44
+ doc = REXML::Document.new(data)
45
+ doc.elements.each(name) do |element|
46
+ return element
47
+ end
48
+ raise "Element #{name} not found in #{data}"
49
+ end
50
+
51
+ def self.hash_or_value_for(element)
52
+ if element.children.size == 1 && element.children.first.kind_of?(REXML::Text)
53
+ element.text_value
54
+ else
55
+ hashinate(element)
56
+ end
57
+ end
58
+
59
+ def self.hashinate(response_element)
60
+ response_element.children.reject{|c| c.kind_of? REXML::Text}.inject({}) do |hash, child|
61
+ hash[child.name] = if child.children.size == 1 && child.children.first.kind_of?(REXML::Text)
62
+ anonymous_field_from(child, hash) || child.text_value
63
+ else
64
+ if child.attributes['list'] == 'true'
65
+ child.children.reject{|c| c.kind_of? REXML::Text}.map do |subchild|
66
+ hash_or_value_for(subchild)
67
+ end
68
+ else
69
+ child.children.reject{|c| c.kind_of? REXML::Text}.inject({}) do |subhash, subchild|
70
+ subhash[subchild.name] = hash_or_value_for(subchild)
71
+ subhash
72
+ end
73
+ end
74
+ end
75
+ hash
76
+ end
77
+ end
78
+
79
+ def self.anonymous_field_from(child, hash)
80
+ if child.name == 'anon'
81
+ (hash[child.name] || []) << child.text_value
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ class CreateToken < Parser#:nodoc:
88
+ def self.process(data)
89
+ element('auth_createToken_response', data).text_value
90
+ end
91
+ end
92
+
93
+ class RegisterUsers
94
+ def self.process(data)
95
+ Facebooker.json_decode(data)
96
+ end
97
+ end
98
+
99
+ class GetSession < Parser#:nodoc:
100
+ def self.process(data)
101
+ hashinate(element('auth_getSession_response', data))
102
+ end
103
+ end
104
+
105
+ class GetFriends < Parser#:nodoc:
106
+ def self.process(data)
107
+ array_of_text_values(element('friends_get_response', data), 'uid')
108
+ end
109
+ end
110
+
111
+ class FriendListsGet < Parser#:nodoc:
112
+ def self.process(data)
113
+ array_of_hashes(element('friends_getLists_response', data), 'friendlist')
114
+ end
115
+ end
116
+
117
+ class UserInfo < Parser#:nodoc:
118
+ def self.process(data)
119
+ array_of_hashes(element('users_getInfo_response', data), 'user')
120
+ end
121
+ end
122
+
123
+ class GetLoggedInUser < Parser#:nodoc:
124
+ def self.process(data)
125
+ Integer(element('users_getLoggedInUser_response', data).text_value)
126
+ end
127
+ end
128
+
129
+ class PagesIsAdmin < Parser#:nodoc:
130
+ def self.process(data)
131
+ element('pages_isAdmin_response', data).text_value == '1'
132
+ end
133
+ end
134
+
135
+ class PagesGetInfo < Parser#:nodoc:
136
+ def self.process(data)
137
+ array_of_hashes(element('pages_getInfo_response', data), 'page')
138
+ end
139
+ end
140
+
141
+ class PublishStoryToUser < Parser#:nodoc:
142
+ def self.process(data)
143
+ element('feed_publishStoryToUser_response', data).text_value
144
+ end
145
+ end
146
+
147
+ class RegisterTemplateBundle < Parser#:nodoc:
148
+ def self.process(data)
149
+ element('feed_registerTemplateBundle_response', data).text_value.to_i
150
+ end
151
+ end
152
+
153
+ class GetRegisteredTemplateBundles < Parser
154
+ def self.process(data)
155
+ array_of_hashes(element('feed_getRegisteredTemplateBundles_response',data), 'template_bundle')
156
+ end
157
+ end
158
+
159
+ class DeactivateTemplateBundleByID < Parser#:nodoc:
160
+ def self.process(data)
161
+ element('feed_deactivateTemplateBundleByID_response', data).text_value == '1'
162
+ end
163
+ end
164
+
165
+ class PublishUserAction < Parser#:nodoc:
166
+ def self.process(data)
167
+ element('feed_publishUserAction_response', data).children[1].text_value == "1"
168
+ end
169
+ end
170
+
171
+ class PublishActionOfUser < Parser#:nodoc:
172
+ def self.process(data)
173
+ element('feed_publishActionOfUser_response', data).text_value
174
+ end
175
+ end
176
+
177
+ class PublishTemplatizedAction < Parser#:nodoc:
178
+ def self.process(data)
179
+ element('feed_publishTemplatizedAction_response', data).children[1].text_value
180
+ end
181
+ end
182
+
183
+ class SetAppProperties < Parser#:nodoc:
184
+ def self.process(data)
185
+ element('admin_setAppProperties_response', data).text_value
186
+ end
187
+ end
188
+
189
+ class GetAppProperties < Parser#:nodoc:
190
+ def self.process(data)
191
+ element('admin_getAppProperties_response', data).text_value
192
+ end
193
+ end
194
+
195
+ class GetAllocation < Parser#:nodoc:
196
+ def self.process(data)
197
+ element('admin_getAllocation_response', data).text_value
198
+ end
199
+ end
200
+
201
+ class BatchRun < Parser #:nodoc:
202
+ class << self
203
+ def current_batch=(current_batch)
204
+ Thread.current[:facebooker_current_batch]=current_batch
205
+ end
206
+ def current_batch
207
+ Thread.current[:facebooker_current_batch]
208
+ end
209
+ end
210
+ def self.process(data)
211
+ array_of_text_values(element('batch_run_response',data),"batch_run_response_elt").each_with_index do |response,i|
212
+ batch_request=current_batch[i]
213
+ body=Struct.new(:body).new
214
+ body.body=CGI.unescapeHTML(response)
215
+ begin
216
+ batch_request.result=Parser.parse(batch_request.method,body)
217
+ rescue Exception=>ex
218
+ batch_request.exception_raised=ex
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ class GetAppUsers < Parser#:nodoc:
225
+ def self.process(data)
226
+ array_of_text_values(element('friends_getAppUsers_response', data), 'uid')
227
+ end
228
+ end
229
+
230
+ class NotificationsGet < Parser#:nodoc:
231
+ def self.process(data)
232
+ hashinate(element('notifications_get_response', data))
233
+ end
234
+ end
235
+
236
+ class NotificationsSend < Parser#:nodoc:
237
+ def self.process(data)
238
+ element('notifications_send_response', data).text_value
239
+ end
240
+ end
241
+
242
+ class NotificationsSendEmail < Parser#:nodoc:
243
+ def self.process(data)
244
+ element('notifications_sendEmail_response', data).text_value
245
+ end
246
+ end
247
+
248
+ class GetTags < Parser#nodoc:
249
+ def self.process(data)
250
+ array_of_hashes(element('photos_getTags_response', data), 'photo_tag')
251
+ end
252
+ end
253
+
254
+ class AddTags < Parser#nodoc:
255
+ def self.process(data)
256
+ element('photos_addTag_response', data)
257
+ end
258
+ end
259
+
260
+ class GetPhotos < Parser#nodoc:
261
+ def self.process(data)
262
+ array_of_hashes(element('photos_get_response', data), 'photo')
263
+ end
264
+ end
265
+
266
+ class GetAlbums < Parser#nodoc:
267
+ def self.process(data)
268
+ array_of_hashes(element('photos_getAlbums_response', data), 'album')
269
+ end
270
+ end
271
+
272
+ class CreateAlbum < Parser#:nodoc:
273
+ def self.process(data)
274
+ hashinate(element('photos_createAlbum_response', data))
275
+ end
276
+ end
277
+
278
+ class UploadPhoto < Parser#:nodoc:
279
+ def self.process(data)
280
+ hashinate(element('photos_upload_response', data))
281
+ end
282
+ end
283
+
284
+ class SendRequest < Parser#:nodoc:
285
+ def self.process(data)
286
+ element('notifications_sendRequest_response', data).text_value
287
+ end
288
+ end
289
+
290
+ class ProfileFBML < Parser#:nodoc:
291
+ def self.process(data)
292
+ element('profile_getFBML_response', data).text_value
293
+ end
294
+ end
295
+
296
+ class ProfileFBMLSet < Parser#:nodoc:
297
+ def self.process(data)
298
+ element('profile_setFBML_response', data).text_value
299
+ end
300
+ end
301
+
302
+ class ProfileInfo < Parser#:nodoc:
303
+ def self.process(data)
304
+ hashinate(element('profile_getInfo_response info_fields', data))
305
+ end
306
+ end
307
+
308
+ class ProfileInfoSet < Parser#:nodoc:
309
+ def self.process(data)
310
+ element('profile_setInfo_response', data).text_value
311
+ end
312
+ end
313
+
314
+ class FqlQuery < Parser#nodoc
315
+ def self.process(data)
316
+ root = element('fql_query_response', data)
317
+ first_child = root.children.reject{|c| c.kind_of?(REXML::Text)}.first
318
+ first_child.nil? ? [] : [first_child.name, array_of_hashes(root, first_child.name)]
319
+ end
320
+ end
321
+
322
+ class SetRefHandle < Parser#:nodoc:
323
+ def self.process(data)
324
+ element('fbml_setRefHandle_response', data).text_value
325
+ end
326
+ end
327
+
328
+ class RefreshRefURL < Parser#:nodoc:
329
+ def self.process(data)
330
+ element('fbml_refreshRefUrl_response', data).text_value
331
+ end
332
+ end
333
+
334
+ class RefreshImgSrc < Parser#:nodoc:
335
+ def self.process(data)
336
+ element('fbml_refreshImgSrc_response', data).text_value
337
+ end
338
+ end
339
+
340
+ class SetCookie < Parser#:nodoc:
341
+ def self.process(data)
342
+ element('data_setCookie_response', data).text_value
343
+ end
344
+ end
345
+
346
+ class GetCookies < Parser#:nodoc:
347
+ def self.process(data)
348
+ array_of_hashes(element('data_getCookie_response', data), 'cookies')
349
+ end
350
+ end
351
+
352
+ class EventsGet < Parser#:nodoc:
353
+ def self.process(data)
354
+ array_of_hashes(element('events_get_response', data), 'event')
355
+ end
356
+ end
357
+
358
+ class GroupGetMembers < Parser#:nodoc:
359
+ def self.process(data)
360
+ root = element('groups_getMembers_response', data)
361
+ result = ['members', 'admins', 'officers', 'not_replied'].map do |position|
362
+ array_of(root, position) {|element| element}.map do |element|
363
+ array_of_text_values(element, 'uid').map do |uid|
364
+ {:position => position}.merge(:uid => uid)
365
+ end
366
+ end
367
+ end.flatten
368
+ end
369
+ end
370
+
371
+ class EventMembersGet < Parser#:nodoc:
372
+ def self.process(data)
373
+ root = element('events_getMembers_response', data)
374
+ result = ['attending', 'declined', 'unsure', 'not_replied'].map do |rsvp_status|
375
+ array_of(root, rsvp_status) {|element| element}.map do |element|
376
+ array_of_text_values(element, 'uid').map do |uid|
377
+ {:rsvp_status => rsvp_status}.merge(:uid => uid)
378
+ end
379
+ end
380
+ end.flatten
381
+ end
382
+ end
383
+
384
+ class GroupsGet < Parser#:nodoc:
385
+ def self.process(data)
386
+ array_of_hashes(element('groups_get_response', data), 'group')
387
+ end
388
+ end
389
+
390
+ class AreFriends < Parser#:nodoc:
391
+ def self.process(data)
392
+ array_of_hashes(element('friends_areFriends_response', data), 'friend_info').inject({}) do |memo, hash|
393
+ memo[[Integer(hash['uid1']), Integer(hash['uid2'])].sort] = are_friends?(hash['are_friends'])
394
+ memo
395
+ end
396
+ end
397
+
398
+ private
399
+ def self.are_friends?(raw_value)
400
+ if raw_value == '1'
401
+ true
402
+ elsif raw_value == '0'
403
+ false
404
+ else
405
+ nil
406
+ end
407
+ end
408
+ end
409
+
410
+ class SetStatus < Parser
411
+ def self.process(data)
412
+ element('users_setStatus_response',data).text_value == '1'
413
+ end
414
+ end
415
+
416
+ class Errors < Parser#:nodoc:
417
+ EXCEPTIONS = {
418
+ 1 => Facebooker::Session::UnknownError,
419
+ 2 => Facebooker::Session::ServiceUnavailable,
420
+ 4 => Facebooker::Session::MaxRequestsDepleted,
421
+ 5 => Facebooker::Session::HostNotAllowed,
422
+ 100 => Facebooker::Session::MissingOrInvalidParameter,
423
+ 101 => Facebooker::Session::InvalidAPIKey,
424
+ 102 => Facebooker::Session::SessionExpired,
425
+ 103 => Facebooker::Session::CallOutOfOrder,
426
+ 104 => Facebooker::Session::IncorrectSignature,
427
+ 120 => Facebooker::Session::InvalidAlbumId,
428
+ 250 => Facebooker::Session::ExtendedPermissionRequired,
429
+ 321 => Facebooker::Session::AlbumIsFull,
430
+ 324 => Facebooker::Session::MissingOrInvalidImageFile,
431
+ 325 => Facebooker::Session::TooManyUnapprovedPhotosPending,
432
+ 330 => Facebooker::Session::TemplateDataMissingRequiredTokens,
433
+ 340 => Facebooker::Session::TooManyUserCalls,
434
+ 341 => Facebooker::Session::TooManyUserActionCalls,
435
+ 342 => Facebooker::Session::InvalidFeedTitleLink,
436
+ 343 => Facebooker::Session::InvalidFeedTitleLength,
437
+ 344 => Facebooker::Session::InvalidFeedTitleName,
438
+ 345 => Facebooker::Session::BlankFeedTitle,
439
+ 346 => Facebooker::Session::FeedBodyLengthTooLong,
440
+ 347 => Facebooker::Session::InvalidFeedPhotoSource,
441
+ 348 => Facebooker::Session::InvalidFeedPhotoLink,
442
+ 330 => Facebooker::Session::FeedMarkupInvalid,
443
+ 360 => Facebooker::Session::FeedTitleDataInvalid,
444
+ 361 => Facebooker::Session::FeedTitleTemplateInvalid,
445
+ 362 => Facebooker::Session::FeedBodyDataInvalid,
446
+ 363 => Facebooker::Session::FeedBodyTemplateInvalid,
447
+ 364 => Facebooker::Session::FeedPhotosNotRetrieved,
448
+ 366 => Facebooker::Session::FeedTargetIdsInvalid,
449
+ 601 => Facebooker::Session::FQLParseError,
450
+ 602 => Facebooker::Session::FQLFieldDoesNotExist,
451
+ 603 => Facebooker::Session::FQLTableDoesNotExist,
452
+ 604 => Facebooker::Session::FQLStatementNotIndexable,
453
+ 605 => Facebooker::Session::FQLFunctionDoesNotExist,
454
+ 606 => Facebooker::Session::FQLWrongNumberArgumentsPassedToFunction,
455
+ 807 => Facebooker::Session::TemplateBundleInvalid
456
+ }
457
+ def self.process(data)
458
+ response_element = element('error_response', data) rescue nil
459
+ if response_element
460
+ hash = hashinate(response_element)
461
+ exception = EXCEPTIONS[Integer(hash['error_code'])] || StandardError
462
+ raise exception.new(hash['error_msg'])
463
+ end
464
+ end
465
+ end
466
+
467
+ class Parser
468
+ PARSERS = {
469
+ 'facebook.auth.createToken' => CreateToken,
470
+ 'facebook.auth.getSession' => GetSession,
471
+ 'facebook.connect.registerUsers' => RegisterUsers,
472
+ 'facebook.users.getInfo' => UserInfo,
473
+ 'facebook.users.setStatus' => SetStatus,
474
+ 'facebook.users.getLoggedInUser' => GetLoggedInUser,
475
+ 'facebook.pages.isAdmin' => PagesIsAdmin,
476
+ 'facebook.pages.getInfo' => PagesGetInfo,
477
+ 'facebook.friends.get' => GetFriends,
478
+ 'facebook.friends.getLists' => FriendListsGet,
479
+ 'facebook.friends.areFriends' => AreFriends,
480
+ 'facebook.friends.getAppUsers' => GetAppUsers,
481
+ 'facebook.feed.publishStoryToUser' => PublishStoryToUser,
482
+ 'facebook.feed.publishActionOfUser' => PublishActionOfUser,
483
+ 'facebook.feed.publishTemplatizedAction' => PublishTemplatizedAction,
484
+ 'facebook.feed.registerTemplateBundle' => RegisterTemplateBundle,
485
+ 'facebook.feed.deactivateTemplateBundleByID' => DeactivateTemplateBundleByID,
486
+ 'facebook.feed.getRegisteredTemplateBundles' => GetRegisteredTemplateBundles,
487
+ 'facebook.feed.publishUserAction' => PublishUserAction,
488
+ 'facebook.notifications.get' => NotificationsGet,
489
+ 'facebook.notifications.send' => NotificationsSend,
490
+ 'facebook.notifications.sendRequest' => SendRequest,
491
+ 'facebook.profile.getFBML' => ProfileFBML,
492
+ 'facebook.profile.setFBML' => ProfileFBMLSet,
493
+ 'facebook.profile.getInfo' => ProfileInfo,
494
+ 'facebook.profile.setInfo' => ProfileInfoSet,
495
+ 'facebook.fbml.setRefHandle' => SetRefHandle,
496
+ 'facebook.fbml.refreshRefUrl' => RefreshRefURL,
497
+ 'facebook.fbml.refreshImgSrc' => RefreshImgSrc,
498
+ 'facebook.data.setCookie' => SetCookie,
499
+ 'facebook.data.getCookies' => GetCookies,
500
+ 'facebook.admin.setAppProperties' => SetAppProperties,
501
+ 'facebook.admin.getAppProperties' => GetAppProperties,
502
+ 'facebook.admin.getAllocation' => GetAllocation,
503
+ 'facebook.batch.run' => BatchRun,
504
+ 'facebook.fql.query' => FqlQuery,
505
+ 'facebook.photos.get' => GetPhotos,
506
+ 'facebook.photos.getAlbums' => GetAlbums,
507
+ 'facebook.photos.createAlbum' => CreateAlbum,
508
+ 'facebook.photos.getTags' => GetTags,
509
+ 'facebook.photos.addTag' => AddTags,
510
+ 'facebook.photos.upload' => UploadPhoto,
511
+ 'facebook.events.get' => EventsGet,
512
+ 'facebook.groups.get' => GroupsGet,
513
+ 'facebook.events.getMembers' => EventMembersGet,
514
+ 'facebook.groups.getMembers' => GroupGetMembers,
515
+ 'facebook.notifications.sendEmail' => NotificationsSendEmail
516
+ }
517
+ end
518
+ end