fbooker 1.0.53

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/.autotest +15 -0
  2. data/CHANGELOG.rdoc +24 -0
  3. data/COPYING.rdoc +19 -0
  4. data/Manifest.txt +144 -0
  5. data/README.rdoc +113 -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 +252 -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 +13 -0
  52. data/lib/facebooker/batch_request.rb +45 -0
  53. data/lib/facebooker/data.rb +57 -0
  54. data/lib/facebooker/feed.rb +78 -0
  55. data/lib/facebooker/logging.rb +44 -0
  56. data/lib/facebooker/mobile.rb +20 -0
  57. data/lib/facebooker/mock/service.rb +50 -0
  58. data/lib/facebooker/mock/session.rb +18 -0
  59. data/lib/facebooker/model.rb +139 -0
  60. data/lib/facebooker/models/affiliation.rb +10 -0
  61. data/lib/facebooker/models/album.rb +11 -0
  62. data/lib/facebooker/models/applicationproperties.rb +39 -0
  63. data/lib/facebooker/models/applicationrestrictions.rb +10 -0
  64. data/lib/facebooker/models/cookie.rb +10 -0
  65. data/lib/facebooker/models/education_info.rb +11 -0
  66. data/lib/facebooker/models/event.rb +28 -0
  67. data/lib/facebooker/models/family_relative_info.rb +7 -0
  68. data/lib/facebooker/models/friend_list.rb +16 -0
  69. data/lib/facebooker/models/group.rb +36 -0
  70. data/lib/facebooker/models/info_item.rb +10 -0
  71. data/lib/facebooker/models/info_section.rb +10 -0
  72. data/lib/facebooker/models/location.rb +8 -0
  73. data/lib/facebooker/models/notifications.rb +17 -0
  74. data/lib/facebooker/models/page.rb +44 -0
  75. data/lib/facebooker/models/photo.rb +19 -0
  76. data/lib/facebooker/models/tag.rb +12 -0
  77. data/lib/facebooker/models/user.rb +552 -0
  78. data/lib/facebooker/models/video.rb +9 -0
  79. data/lib/facebooker/models/work_info.rb +10 -0
  80. data/lib/facebooker/parser.rb +717 -0
  81. data/lib/facebooker/rails/backwards_compatible_param_checks.rb +31 -0
  82. data/lib/facebooker/rails/controller.rb +353 -0
  83. data/lib/facebooker/rails/cucumber.rb +28 -0
  84. data/lib/facebooker/rails/cucumber/world.rb +40 -0
  85. data/lib/facebooker/rails/extensions/action_controller.rb +48 -0
  86. data/lib/facebooker/rails/extensions/rack_setup.rb +6 -0
  87. data/lib/facebooker/rails/extensions/routing.rb +15 -0
  88. data/lib/facebooker/rails/facebook_form_builder.rb +112 -0
  89. data/lib/facebooker/rails/facebook_pretty_errors.rb +22 -0
  90. data/lib/facebooker/rails/facebook_request_fix.rb +28 -0
  91. data/lib/facebooker/rails/facebook_request_fix_2-3.rb +31 -0
  92. data/lib/facebooker/rails/facebook_session_handling.rb +68 -0
  93. data/lib/facebooker/rails/facebook_url_helper.rb +192 -0
  94. data/lib/facebooker/rails/facebook_url_rewriting.rb +60 -0
  95. data/lib/facebooker/rails/helpers.rb +870 -0
  96. data/lib/facebooker/rails/helpers/fb_connect.rb +122 -0
  97. data/lib/facebooker/rails/integration_session.rb +38 -0
  98. data/lib/facebooker/rails/profile_publisher_extensions.rb +42 -0
  99. data/lib/facebooker/rails/publisher.rb +595 -0
  100. data/lib/facebooker/rails/routing.rb +49 -0
  101. data/lib/facebooker/rails/test_helpers.rb +68 -0
  102. data/lib/facebooker/rails/utilities.rb +22 -0
  103. data/lib/facebooker/server_cache.rb +24 -0
  104. data/lib/facebooker/service.rb +102 -0
  105. data/lib/facebooker/service/base_service.rb +19 -0
  106. data/lib/facebooker/service/curl_service.rb +44 -0
  107. data/lib/facebooker/service/net_http_service.rb +12 -0
  108. data/lib/facebooker/service/typhoeus_multi_service.rb +27 -0
  109. data/lib/facebooker/service/typhoeus_service.rb +17 -0
  110. data/lib/facebooker/session.rb +726 -0
  111. data/lib/facebooker/version.rb +9 -0
  112. data/lib/net/http_multipart_post.rb +123 -0
  113. data/lib/rack/facebook.rb +89 -0
  114. data/lib/tasks/facebooker.rake +18 -0
  115. data/lib/tasks/tunnel.rake +46 -0
  116. data/rails/init.rb +1 -0
  117. data/setup.rb +1585 -0
  118. data/templates/layout.erb +24 -0
  119. data/test/facebooker/adapters_test.rb +191 -0
  120. data/test/facebooker/admin_test.rb +102 -0
  121. data/test/facebooker/application_test.rb +41 -0
  122. data/test/facebooker/batch_request_test.rb +83 -0
  123. data/test/facebooker/data_test.rb +86 -0
  124. data/test/facebooker/logging_test.rb +43 -0
  125. data/test/facebooker/mobile_test.rb +45 -0
  126. data/test/facebooker/model_test.rb +133 -0
  127. data/test/facebooker/models/event_test.rb +15 -0
  128. data/test/facebooker/models/page_test.rb +46 -0
  129. data/test/facebooker/models/photo_test.rb +16 -0
  130. data/test/facebooker/models/user_test.rb +397 -0
  131. data/test/facebooker/rails/facebook_request_fix_2-3_test.rb +25 -0
  132. data/test/facebooker/rails/facebook_url_rewriting_test.rb +76 -0
  133. data/test/facebooker/rails/integration_session_test.rb +13 -0
  134. data/test/facebooker/rails/publisher_test.rb +519 -0
  135. data/test/facebooker/rails_integration_test.rb +1511 -0
  136. data/test/facebooker/server_cache_test.rb +44 -0
  137. data/test/facebooker/service_test.rb +58 -0
  138. data/test/facebooker/session_test.rb +883 -0
  139. data/test/facebooker_test.rb +951 -0
  140. data/test/net/http_multipart_post_test.rb +52 -0
  141. data/test/rack/facebook_test.rb +73 -0
  142. metadata +235 -0
@@ -0,0 +1,60 @@
1
+ module ::ActionController
2
+ if Rails.version < '2.3'
3
+ class AbstractRequest
4
+ def relative_url_root
5
+ Facebooker.path_prefix
6
+ end
7
+ end
8
+ else
9
+ class Request
10
+ def relative_url_root
11
+ Facebooker.path_prefix
12
+ end
13
+ end
14
+ end
15
+
16
+ class Base
17
+ class << self
18
+ alias :old_relative_url_root :relative_url_root
19
+ def relative_url_root
20
+ Facebooker.path_prefix
21
+ end
22
+ end
23
+ end
24
+
25
+ class UrlRewriter
26
+ include Facebooker::Rails::BackwardsCompatibleParamChecks
27
+
28
+ RESERVED_OPTIONS << :canvas
29
+
30
+ def link_to_new_canvas?
31
+ one_or_true @request.parameters["fb_sig_in_new_facebook"]
32
+ end
33
+
34
+ def link_to_canvas?(params, options)
35
+ option_override = options[:canvas]
36
+ return false if option_override == false # important to check for false. nil should use default behavior
37
+ option_override || (can_safely_access_request_parameters? && (one_or_true(@request.parameters["fb_sig_in_canvas"]) || one_or_true(@request.parameters[:fb_sig_in_canvas]) || one_or_true(@request.parameters["fb_sig_is_ajax"]) ))
38
+ end
39
+
40
+ #rails blindly tries to merge things that may be nil into the parameters. Make sure this won't break
41
+ def can_safely_access_request_parameters?
42
+ @request.request_parameters
43
+ end
44
+
45
+ def rewrite_url_with_facebooker(*args)
46
+ options = args.first.is_a?(Hash) ? args.first : args.last
47
+ is_link_to_canvas = link_to_canvas?(@request.request_parameters, options)
48
+ if is_link_to_canvas && !options.has_key?(:host)
49
+ options[:host] = Facebooker.canvas_server_base
50
+ end
51
+ options.delete(:canvas)
52
+ Facebooker.request_for_canvas(is_link_to_canvas) do
53
+ rewrite_url_without_facebooker(*args)
54
+ end
55
+ end
56
+
57
+ alias_method_chain :rewrite_url, :facebooker
58
+
59
+ end
60
+ end
@@ -0,0 +1,870 @@
1
+ require 'action_pack'
2
+
3
+ module Facebooker
4
+ module Rails
5
+
6
+ # Facebook specific helpers for creating FBML
7
+ #
8
+ # All helpers that take a user as a parameter will get the Facebook UID from the facebook_id attribute if it exists.
9
+ # It will use to_s if the facebook_id attribute is not present.
10
+ #
11
+ module Helpers
12
+
13
+ include Facebooker::Rails::Helpers::FbConnect
14
+
15
+ # Create an fb:dialog
16
+ # id must be a unique name e.g. "my_dialog"
17
+ # cancel_button is true or false
18
+ def fb_dialog( id, cancel_button, &block )
19
+ content = capture(&block)
20
+ if ignore_binding?
21
+ concat( content_tag("fb:dialog", content, {:id => id, :cancel_button => cancel_button}) )
22
+ else
23
+ concat( content_tag("fb:dialog", content, {:id => id, :cancel_button => cancel_button}), block.binding )
24
+ end
25
+ end
26
+
27
+ def fbjs_library
28
+ "<script>var _token = '#{form_authenticity_token}';var _hostname = '#{ActionController::Base.asset_host}'</script>"+
29
+ "#{javascript_include_tag 'facebooker'}"
30
+ end
31
+
32
+ def fb_iframe(src, options = {})
33
+ content_tag "fb:iframe", '', options.merge({ :src => src })
34
+ end
35
+
36
+ def fb_swf(src, options = {})
37
+ tag "fb:swf", options.merge(:swfsrc => src)
38
+ end
39
+
40
+ def fb_dialog_title( title )
41
+ content_tag "fb:dialog-title", title
42
+ end
43
+
44
+ def fb_dialog_content( &block )
45
+ content = capture(&block)
46
+ if ignore_binding?
47
+ concat( content_tag("fb:dialog-content", content) )
48
+ else
49
+ concat( content_tag("fb:dialog-content", content), block.binding )
50
+ end
51
+ end
52
+
53
+ def fb_dialog_button( type, value, options={} )
54
+ options.assert_valid_keys FB_DIALOG_BUTTON_VALID_OPTION_KEYS
55
+ options.merge! :type => type, :value => value
56
+ tag "fb:dialog-button", options
57
+ end
58
+
59
+ FB_DIALOG_BUTTON_VALID_OPTION_KEYS = [:close_dialog, :href, :form_id, :clickrewriteurl, :clickrewriteid, :clickrewriteform]
60
+
61
+ def fb_show_feed_dialog(action, user_message = "", prompt = "", callback = nil)
62
+ update_page do |page|
63
+ page.call "Facebook.showFeedDialog",action.template_id,action.data,action.body_general,action.target_ids,callback,prompt,user_message
64
+ end
65
+ end
66
+
67
+
68
+ # Create an fb:request-form without a selector
69
+ #
70
+ # The block passed to this tag is used as the content of the form
71
+ #
72
+ # The message param is the name sent to content_for that specifies the body of the message
73
+ #
74
+ # For example,
75
+ #
76
+ # <% content_for("invite_message") do %>
77
+ # This gets sent in the invite. <%= fb_req_choice("with a button!",new_poke_path) %>
78
+ # <% end %>
79
+ # <% fb_request_form("Poke","invite_message",create_poke_path) do %>
80
+ # Send a poke to: <%= fb_friend_selector %> <br />
81
+ # <%= fb_request_form_submit %>
82
+ # <% end %>
83
+ def fb_request_form(type,message_param,url,options={},&block)
84
+ content = capture(&block)
85
+ message = @template.instance_variable_get("@content_for_#{message_param}")
86
+ if ignore_binding?
87
+ concat(content_tag("fb:request-form", content + token_tag,
88
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>message}.merge(options)))
89
+ else
90
+ concat(content_tag("fb:request-form", content + token_tag,
91
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>message}.merge(options)),
92
+ block.binding)
93
+ end
94
+ end
95
+
96
+ # Create a submit button for an <fb:request-form>
97
+ # If the request is for an individual user you can optionally
98
+ # Provide the user and a label for the request button.
99
+ # For example
100
+ # <% content_for("invite_user") do %>
101
+ # This gets sent in the invite. <%= fb_req_choice("Come join us!",new_invite_path) %>
102
+ # <% end %>
103
+ # <% fb_request_form("Invite","invite_user",create_invite_path) do %>
104
+ # Invite <%= fb_name(@facebook_user.friends.first.id)%> to the party <br />
105
+ # <%= fb_request_form_submit(:uid => @facebook_user.friends.first.id, :label => "Invite %n") %>
106
+ # <% end %>
107
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:request-form-submit for options
108
+ def fb_request_form_submit(options={})
109
+ tag("fb:request-form-submit",stringify_vals(options))
110
+ end
111
+
112
+
113
+ # Create an fb:request-form with an fb_multi_friend_selector inside
114
+ #
115
+ # The content of the block are used as the message on the form,
116
+ #
117
+ # For example:
118
+ # <% fb_multi_friend_request("Poke","Choose some friends to Poke",create_poke_path) do %>
119
+ # If you select some friends, they will see this message.
120
+ # <%= fb_req_choice("They will get this button, too",new_poke_path) %>
121
+ # <% end %>
122
+ def fb_multi_friend_request(type,friend_selector_message,url,&block)
123
+ content = capture(&block)
124
+ if ignore_binding?
125
+ concat(content_tag("fb:request-form",
126
+ fb_multi_friend_selector(friend_selector_message) + token_tag,
127
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>content}
128
+ ))
129
+ else
130
+ concat(content_tag("fb:request-form",
131
+ fb_multi_friend_selector(friend_selector_message) + token_tag,
132
+ {:action=>url,:method=>"post",:invite=>true,:type=>type,:content=>content}
133
+ ),
134
+ block.binding)
135
+ end
136
+ end
137
+
138
+ # Render an <fb:friend-selector> element
139
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:friend-selector for options
140
+ #
141
+ def fb_friend_selector(options={})
142
+ tag("fb:friend-selector",stringify_vals(options))
143
+ end
144
+
145
+ # Render an <fb:multi-friend-input> element
146
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-input for options
147
+ def fb_multi_friend_input(options={})
148
+ tag "fb:multi-friend-input",stringify_vals(options)
149
+ end
150
+
151
+ # Render an <fb:multi-friend-selector> with the passed in welcome message
152
+ # Full version shows all profile pics for friends.
153
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-selector for options
154
+ # <em> Note: </em> I don't think the block is used here.
155
+ def fb_multi_friend_selector(message,options={},&block)
156
+ options = options.dup
157
+ tag("fb:multi-friend-selector",stringify_vals({:showborder=>false,:actiontext=>message,:max=>20}.merge(options)))
158
+ end
159
+
160
+ # Render a condensed <fb:multi-friend-selector> with the passed in welcome message
161
+ # Condensed version show checkboxes for each friend.
162
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:multi-friend-selector_%28condensed%29 for options
163
+ # <em> Note: </em> I don't think the block is used here.
164
+ def fb_multi_friend_selector_condensed(options={},&block)
165
+ options = options.dup
166
+ tag("fb:multi-friend-selector",stringify_vals(options.merge(:condensed=>true)))
167
+ end
168
+
169
+ # Render a button in a request using the <fb:req-choice> tag
170
+ # url must be an absolute url
171
+ # This should be used inside the block of an fb_multi_friend_request
172
+ def fb_req_choice(label,url)
173
+ tag "fb:req-choice",:label=>label,:url=>url
174
+ end
175
+
176
+ # Create a facebook form using <fb:editor>
177
+ #
178
+ # It yields a form builder that will convert the standard rails form helpers
179
+ # into the facebook specific version.
180
+ #
181
+ # Example:
182
+ # <% facebook_form_for(:poke,@poke,:url => create_poke_path) do |f| %>
183
+ # <%= f.text_field :message, :label=>"message" %>
184
+ # <%= f.buttons "Save Poke" %>
185
+ # <% end %>
186
+ #
187
+ # will generate
188
+ #
189
+ # <fb:editor action="/pokes/create">
190
+ # <fb:editor-text name="poke[message]" id="poke_message" value="" label="message" />
191
+ # <fb:editor-buttonset>
192
+ # <fb:editor-button label="Save Poke"
193
+ # </fb:editor-buttonset>
194
+ # </fb:editor>
195
+ def facebook_form_for( record_or_name_or_array,*args, &proc)
196
+
197
+ raise ArgumentError, "Missing block" unless block_given?
198
+ options = args.last.is_a?(Hash) ? args.pop : {}
199
+
200
+ case record_or_name_or_array
201
+ when String, Symbol
202
+ object_name = record_or_name_or_array
203
+ when Array
204
+ object = record_or_name_or_array.last
205
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
206
+ apply_form_for_options!(record_or_name_or_array, options)
207
+ args.unshift object
208
+ else
209
+ object = record_or_name_or_array
210
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
211
+ apply_form_for_options!([object], options)
212
+ args.unshift object
213
+ end
214
+ method = (options[:html]||{})[:method]
215
+ options[:builder] ||= Facebooker::Rails::FacebookFormBuilder
216
+ editor_options={}
217
+
218
+ action=options.delete(:url)
219
+ editor_options[:action]= action unless action.blank?
220
+ width=options.delete(:width)
221
+ editor_options[:width]=width unless width.blank?
222
+ width=options.delete(:labelwidth)
223
+ editor_options[:labelwidth]=width unless width.blank?
224
+
225
+ if ignore_binding?
226
+ concat(tag("fb:editor",editor_options,true))
227
+ concat(tag(:input,{:type=>"hidden",:name=>:_method, :value=>method},false)) unless method.blank?
228
+ concat(token_tag)
229
+ fields_for( object_name,*(args << options), &proc)
230
+ concat("</fb:editor>")
231
+ else
232
+ concat(tag("fb:editor",editor_options,true) , proc.binding)
233
+ concat(tag(:input,{:type=>"hidden",:name=>:_method, :value=>method},false), proc.binding) unless method.blank?
234
+ concat(token_tag, proc.binding)
235
+ fields_for( object_name,*(args << options), &proc)
236
+ concat("</fb:editor>",proc.binding)
237
+ end
238
+ end
239
+
240
+ # Render an fb:application-name tag
241
+ #
242
+ # This renders the current application name via fbml. See
243
+ # http://wiki.developers.facebook.com/index.php/Fb:application-name
244
+ # for a full description.
245
+ #
246
+ def fb_application_name(options={})
247
+ tag "fb:application-name", stringify_vals(options)
248
+ end
249
+
250
+ # Render an fb:name tag for the given user
251
+ # This renders the name of the user specified. You can use this tag as both subject and object of
252
+ # a sentence. <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:name for full description.
253
+ # Use this tag on FBML pages instead of retrieving the user's info and rendering the name explicitly.
254
+ #
255
+ def fb_name(user, options={})
256
+ options = options.dup
257
+ options.transform_keys!(FB_NAME_OPTION_KEYS_TO_TRANSFORM)
258
+ options.assert_valid_keys(FB_NAME_VALID_OPTION_KEYS)
259
+ options.merge!(:uid => cast_to_facebook_id(user))
260
+ content_tag("fb:name",nil, stringify_vals(options))
261
+ end
262
+
263
+ FB_NAME_OPTION_KEYS_TO_TRANSFORM = {:first_name_only => :firstnameonly,
264
+ :last_name_only => :lastnameonly,
265
+ :show_network => :shownetwork,
266
+ :use_you => :useyou,
267
+ :if_cant_see => :ifcantsee,
268
+ :subject_id => :subjectid}
269
+ FB_NAME_VALID_OPTION_KEYS = [:firstnameonly, :linked, :lastnameonly, :possessive, :reflexive,
270
+ :shownetwork, :useyou, :ifcantsee, :capitalize, :subjectid]
271
+
272
+ # Render an <fb:pronoun> tag for the specified user
273
+ # Options give flexibility for placing in any part of a sentence.
274
+ # <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:pronoun for complete list of options.
275
+ #
276
+ def fb_pronoun(user, options={})
277
+ options = options.dup
278
+ options.transform_keys!(FB_PRONOUN_OPTION_KEYS_TO_TRANSFORM)
279
+ options.assert_valid_keys(FB_PRONOUN_VALID_OPTION_KEYS)
280
+ options.merge!(:uid => cast_to_facebook_id(user))
281
+ content_tag("fb:pronoun",nil, stringify_vals(options))
282
+ end
283
+
284
+ FB_PRONOUN_OPTION_KEYS_TO_TRANSFORM = {:use_you => :useyou, :use_they => :usethey}
285
+ FB_PRONOUN_VALID_OPTION_KEYS = [:useyou, :possessive, :reflexive, :objective,
286
+ :usethey, :capitalize]
287
+
288
+ # Render an fb:ref tag.
289
+ # Options must contain either url or handle.
290
+ # * <em> url </em> The URL from which to fetch the FBML. You may need to call fbml.refreshRefUrl to refresh cache
291
+ # * <em> handle </em> The string previously set by fbml.setRefHandle that identifies the FBML
292
+ # <em> See </em> http://wiki.developers.facebook.com/index.php/Fb:ref for complete description
293
+ def fb_ref(options)
294
+ options.assert_valid_keys(FB_REF_VALID_OPTION_KEYS)
295
+ validate_fb_ref_has_either_url_or_handle(options)
296
+ validate_fb_ref_does_not_have_both_url_and_handle(options)
297
+ tag("fb:ref", stringify_vals(options))
298
+ end
299
+
300
+ def validate_fb_ref_has_either_url_or_handle(options)
301
+ unless options.has_key?(:url) || options.has_key?(:handle)
302
+ raise ArgumentError, "fb_ref requires :url or :handle"
303
+ end
304
+ end
305
+
306
+ def validate_fb_ref_does_not_have_both_url_and_handle(options)
307
+ if options.has_key?(:url) && options.has_key?(:handle)
308
+ raise ArgumentError, "fb_ref may not have both :url and :handle"
309
+ end
310
+ end
311
+
312
+ FB_REF_VALID_OPTION_KEYS = [:url, :handle]
313
+
314
+ # Render an <fb:profile-pic> for the specified user.
315
+ #
316
+ # You can optionally specify the size using the :size=> option. Valid
317
+ # sizes are :thumb, :small, :normal and :square. Or, you can specify
318
+ # width and height settings instead, just like an img tag.
319
+ #
320
+ # You can optionally specify whether or not to include the facebook icon
321
+ # overlay using the :facebook_logo => true option. Default is false.
322
+ #
323
+ def fb_profile_pic(user, options={})
324
+ options = options.dup
325
+ validate_fb_profile_pic_size(options)
326
+ options.transform_keys!(FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM)
327
+ options.assert_valid_keys(FB_PROFILE_PIC_VALID_OPTION_KEYS)
328
+ options.merge!(:uid => cast_to_facebook_id(user))
329
+ content_tag("fb:profile-pic", nil,stringify_vals(options))
330
+ end
331
+
332
+ FB_PROFILE_PIC_OPTION_KEYS_TO_TRANSFORM = {:facebook_logo => 'facebook-logo'}
333
+ FB_PROFILE_PIC_VALID_OPTION_KEYS = [:size, :linked, 'facebook-logo', :width, :height]
334
+
335
+
336
+ # Render an fb:photo tag.
337
+ # photo is either a Facebooker::Photo or an id of a Facebook photo or an object that responds to photo_id.
338
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:photo for complete list of options.
339
+ def fb_photo(photo, options={})
340
+ options = options.dup
341
+ options.assert_valid_keys(FB_PHOTO_VALID_OPTION_KEYS)
342
+ options.merge!(:pid => cast_to_photo_id(photo))
343
+ validate_fb_photo_size(options)
344
+ validate_fb_photo_align_value(options)
345
+ content_tag("fb:photo",nil, stringify_vals(options))
346
+ end
347
+
348
+ FB_PHOTO_VALID_OPTION_KEYS = [:uid, :size, :align]
349
+
350
+ def cast_to_photo_id(object)
351
+ object.respond_to?(:photo_id) ? object.photo_id : object
352
+ end
353
+
354
+ VALID_FB_SHARED_PHOTO_SIZES = [:thumb, :small, :normal, :square]
355
+ VALID_FB_PHOTO_SIZES = VALID_FB_SHARED_PHOTO_SIZES
356
+ VALID_FB_PROFILE_PIC_SIZES = VALID_FB_SHARED_PHOTO_SIZES
357
+ VALID_PERMISSIONS=[:email, :offline_access, :status_update, :photo_upload, :create_listing, :create_event, :rsvp_event, :sms, :video_upload,
358
+ :publish_stream, :read_stream]
359
+
360
+ # Render an fb:tabs tag containing some number of fb:tab_item tags.
361
+ # Example:
362
+ # <% fb_tabs do %>
363
+ # <%= fb_tab_item("Home", "home") %>
364
+ # <%= fb_tab_item("Office", "office") %>
365
+ # <% end %>
366
+ def fb_tabs(&block)
367
+ content = capture(&block)
368
+ if ignore_binding?
369
+ concat(content_tag("fb:tabs", content))
370
+ else
371
+ concat(content_tag("fb:tabs", content), block.binding)
372
+ end
373
+ end
374
+
375
+ # Render an fb:tab_item tag.
376
+ # Use this in conjunction with fb_tabs
377
+ # Options can contains :selected => true to indicate that a tab is the current tab.
378
+ # <em> See: </em> http://wiki.developers.facebook.com/index.php/Fb:tab-item for complete list of options
379
+ def fb_tab_item(title, url, options={})
380
+ options= options.dup
381
+ options.assert_valid_keys(FB_TAB_ITEM_VALID_OPTION_KEYS)
382
+ options.merge!(:title => title, :href => url)
383
+ validate_fb_tab_item_align_value(options)
384
+ tag("fb:tab-item", stringify_vals(options))
385
+ end
386
+
387
+ FB_TAB_ITEM_VALID_OPTION_KEYS = [:align, :selected]
388
+
389
+ def validate_fb_tab_item_align_value(options)
390
+ if options.has_key?(:align) && !VALID_FB_TAB_ITEM_ALIGN_VALUES.include?(options[:align].to_sym)
391
+ raise(ArgumentError, "Unknown value for align: #{options[:align]}")
392
+ end
393
+ end
394
+
395
+ def validate_fb_photo_align_value(options)
396
+ if options.has_key?(:align) && !VALID_FB_PHOTO_ALIGN_VALUES.include?(options[:align].to_sym)
397
+ raise(ArgumentError, "Unknown value for align: #{options[:align]}")
398
+ end
399
+ end
400
+
401
+ VALID_FB_SHARED_ALIGN_VALUES = [:left, :right]
402
+ VALID_FB_PHOTO_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
403
+ VALID_FB_TAB_ITEM_ALIGN_VALUES = VALID_FB_SHARED_ALIGN_VALUES
404
+
405
+
406
+ # Create a Facebook wall. It can contain fb_wall_posts
407
+ #
408
+ # For Example:
409
+ # <% fb_wall do %>
410
+ # <%= fb_wall_post(@user,"This is my message") %>
411
+ # <%= fb_wall_post(@otheruser,"This is another message") %>
412
+ # <% end %>
413
+ def fb_wall(&proc)
414
+ content = capture(&proc)
415
+ if ignore_binding?
416
+ concat(content_tag("fb:wall",content,{}))
417
+ else
418
+ concat(content_tag("fb:wall",content,{}),proc.binding)
419
+ end
420
+ end
421
+
422
+ # Render an <fb:wallpost> tag
423
+ # TODO: Optionally takes a time parameter t = int The current time, which is displayed in epoch seconds.
424
+ def fb_wallpost(user,message)
425
+ content_tag("fb:wallpost",message,:uid=>cast_to_facebook_id(user))
426
+ end
427
+ alias_method :fb_wall_post, :fb_wallpost
428
+
429
+ # Render an <fb:error> tag
430
+ # If message and text are present then this will render fb:error and fb:message tag
431
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
432
+ def fb_error(message, text=nil)
433
+ fb_status_msg("error", message, text)
434
+ end
435
+
436
+ # Render an <fb:explanation> tag
437
+ # If message and text are present then this will render fb:error and fb:message tag
438
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
439
+ def fb_explanation(message, text=nil)
440
+ fb_status_msg("explanation", message, text)
441
+ end
442
+
443
+ # Render an <fb:success> tag
444
+ # If message and text are present then this will render fb:error and fb:message tag
445
+ # TODO: Optionally takes a decoration tag with value of 'no_padding' or 'shorten'
446
+ def fb_success(message, text=nil)
447
+ fb_status_msg("success", message, text)
448
+ end
449
+
450
+ # Render flash values as <fb:message> and <fb:error> tags
451
+ #
452
+ # values in flash[:notice] will be rendered as an <fb:message>
453
+ #
454
+ # values in flash[:error] will be rendered as an <fb:error>
455
+ # TODO: Allow flash[:info] to render fb_explanation
456
+ def facebook_messages
457
+ message=""
458
+ unless flash[:notice].blank?
459
+ message += fb_success(*flash[:notice])
460
+ end
461
+ unless flash[:error].blank?
462
+ message += fb_error(*flash[:error])
463
+ end
464
+ message
465
+ end
466
+
467
+ # Create a dashboard. It can contain fb_action, fb_help, and fb_create_button
468
+ #
469
+ # For Example:
470
+ # <% fb_dashboard do %>
471
+ # <%= APP_NAME %>
472
+ # <%= fb_action 'My Matches', search_path %>
473
+ # <%= fb_help 'Feedback', "http://www.facebook.com/apps/application.php?id=6236036681" %>
474
+ # <%= fb_create_button 'Invite Friends', main_path %>
475
+ # <% end %>
476
+ def fb_dashboard(&proc)
477
+ if block_given?
478
+ content = capture(&proc)
479
+ if ignore_binding?
480
+ concat(content_tag("fb:dashboard",content,{}))
481
+ else
482
+ concat(content_tag("fb:dashboard",content,{}),proc.binding)
483
+ end
484
+ else
485
+ content_tag("fb:dashboard",content,{})
486
+ end
487
+ end
488
+
489
+ # Content for the wide profile box goes in this tag
490
+ def fb_wide(&proc)
491
+ content = capture(&proc)
492
+ if ignore_binding?
493
+ concat(content_tag("fb:wide", content, {}))
494
+ else
495
+ concat(content_tag("fb:wide", content, {}), proc.binding)
496
+ end
497
+ end
498
+
499
+ # Content for the narrow profile box goes in this tag
500
+ def fb_narrow(&proc)
501
+ content = capture(&proc)
502
+ if ignore_binding?
503
+ concat(content_tag("fb:narrow", content, {}))
504
+ else
505
+ concat(content_tag("fb:narrow", content, {}), proc.binding)
506
+ end
507
+ end
508
+
509
+ # Renders an action using the <fb:action> tag
510
+ def fb_action(name, url)
511
+ "<fb:action href=\"#{url_for(url)}\">#{name}</fb:action>"
512
+ end
513
+
514
+ # Render a <fb:help> tag
515
+ # For use inside <fb:dashboard>
516
+ def fb_help(name, url)
517
+ "<fb:help href=\"#{url_for(url)}\">#{name}</fb:help>"
518
+ end
519
+
520
+ # Render a <fb:create-button> tag
521
+ # For use inside <fb:dashboard>
522
+ def fb_create_button(name, url)
523
+ "<fb:create-button href=\"#{url_for(url)}\">#{name}</fb:create-button>"
524
+ end
525
+
526
+ # Create a comment area
527
+ # All the data for this content area is stored on the facebook servers.
528
+ # <em>See:</em> http://wiki.developers.facebook.com/index.php/Fb:comments for full details
529
+ def fb_comments(xid,canpost=true,candelete=false,numposts=5,options={})
530
+ options = options.dup
531
+ title = (title = options.delete(:title)) ? fb_title(title) : nil
532
+ content_tag "fb:comments",title,stringify_vals(options.merge(:xid=>xid,:canpost=>canpost.to_s,:candelete=>candelete.to_s,:numposts=>numposts))
533
+ end
534
+
535
+ # Adds a title to the title bar
536
+ #
537
+ # Facebook | App Name | This is the canvas page window title
538
+ #
539
+ # +title+: This is the canvas page window
540
+ def fb_title(title)
541
+ "<fb:title>#{title}</fb:title>"
542
+ end
543
+
544
+ # Create a Google Analytics tag
545
+ #
546
+ # +uacct+: Your Urchin/Google Analytics account ID.
547
+ def fb_google_analytics(uacct, options={})
548
+ options = options.dup
549
+ tag "fb:google-analytics", stringify_vals(options.merge(:uacct => uacct))
550
+ end
551
+
552
+ # Render if-is-app-user tag
553
+ # This tag renders the enclosing content only if the user specified has accepted the terms of service for the application.
554
+ # Use fb_if_user_has_added_app to determine wether the user has added the app.
555
+ # Example:
556
+ # <% fb_if_is_app_user(@facebook_user) do %>
557
+ # Thanks for accepting our terms of service!
558
+ # <% fb_else do %>
559
+ # Hey you haven't agreed to our terms. <%= link_to("Please accept our terms of service.", :action => "terms_of_service") %>
560
+ # <% end %>
561
+ #<% end %>
562
+ def fb_if_is_app_user(user=nil,options={},&proc)
563
+ content = capture(&proc)
564
+ options = options.dup
565
+ options.merge!(:uid=>cast_to_facebook_id(user)) if user
566
+ if ignore_binding?
567
+ concat(content_tag("fb:if-is-app-user",content,stringify_vals(options)))
568
+ else
569
+ concat(content_tag("fb:if-is-app-user",content,stringify_vals(options)),proc.binding)
570
+ end
571
+ end
572
+
573
+ # Render if-user-has-added-app tag
574
+ # This tag renders the enclosing content only if the user specified has installed the application
575
+ #
576
+ # Example:
577
+ # <% fb_if_user_has_added_app(@facebook_user) do %>
578
+ # Hey you are an app user!
579
+ # <% fb_else do %>
580
+ # Hey you aren't an app user. <%= link_to("Add App and see the other side.", :action => "added_app") %>
581
+ # <% end %>
582
+ #<% end %>
583
+ def fb_if_user_has_added_app(user,options={},&proc)
584
+ content = capture(&proc)
585
+ options = options.dup
586
+ if ignore_binding?
587
+ concat(content_tag("fb:if-user-has-added-app", content, stringify_vals(options.merge(:uid=>cast_to_facebook_id(user)))))
588
+ else
589
+ concat(content_tag("fb:if-user-has-added-app", content, stringify_vals(options.merge(:uid=>cast_to_facebook_id(user)))),proc.binding)
590
+ end
591
+ end
592
+
593
+ # Render fb:if-is-user tag
594
+ # This tag only renders enclosing content if the user is one of those specified
595
+ # user can be a single user or an Array of users
596
+ # Example:
597
+ # <% fb_if_is_user(@check_user) do %>
598
+ # <%= fb_name(@facebook_user) %> are one of the users. <%= link_to("Check the other side", :action => "friend") %>
599
+ # <% fb_else do %>
600
+ # <%= fb_name(@facebook_user) %> are not one of the users <%= fb_name(@check_user) %>
601
+ # <%= link_to("Check the other side", :action => "you") %>
602
+ # <% end %>
603
+ # <% end %>
604
+ def fb_if_is_user(user,&proc)
605
+ content = capture(&proc)
606
+ user = [user] unless user.is_a? Array
607
+ user_list=user.map{|u| cast_to_facebook_id(u)}.join(",")
608
+ if ignore_binding?
609
+ concat(content_tag("fb:if-is-user",content,{:uid=>user_list}))
610
+ else
611
+ concat(content_tag("fb:if-is-user",content,{:uid=>user_list}),proc.binding)
612
+ end
613
+ end
614
+
615
+ # Render fb:else tag
616
+ # Must be used within if block such as fb_if_is_user or fb_if_is_app_user . See example in fb_if_is_app_user
617
+ def fb_else(&proc)
618
+ content = capture(&proc)
619
+ if ignore_binding?
620
+ concat(content_tag("fb:else",content))
621
+ else
622
+ concat(content_tag("fb:else",content),proc.binding)
623
+ end
624
+ end
625
+
626
+ #
627
+ # Return the URL for the about page of the application
628
+ def fb_about_url
629
+ "http://#{Facebooker.www_server_base_url}/apps/application.php?api_key=#{Facebooker.api_key}"
630
+ end
631
+
632
+ #
633
+ # Embed a discussion board named xid on the current page
634
+ # <em>See</em http://wiki.developers.facebook.com/index.php/Fb:board for more details
635
+ # Options are:
636
+ # * canpost
637
+ # * candelete
638
+ # * canmark
639
+ # * cancreatet
640
+ # * numtopics
641
+ # * callbackurl
642
+ # * returnurl
643
+ #
644
+ def fb_board(xid,options={})
645
+ options = options.dup
646
+ title = (title = options.delete(:title)) ? fb_title(title) : nil
647
+ content_tag("fb:board", title, stringify_vals(options.merge(:xid=>xid)))
648
+ end
649
+
650
+ # Renders an 'Add to Profile' button
651
+ # The button allows a user to add condensed profile box to the main profile
652
+ def fb_add_profile_section
653
+ tag "fb:add-section-button",:section=>"profile"
654
+ end
655
+
656
+ # Renders an 'Add to Info' button
657
+ # The button allows a user to add an application info section to her Info tab
658
+ def fb_add_info_section
659
+ tag "fb:add-section-button",:section=>"info"
660
+ end
661
+
662
+ # Renders a link that, when clicked, initiates a dialog requesting the specified extended permission from the user.
663
+ #
664
+ # You can prompt a user with the following permissions:
665
+ # * email
666
+ # * read_stream
667
+ # * publish_stream
668
+ # * offline_access
669
+ # * status_update
670
+ # * photo_upload
671
+ # * create_event
672
+ # * rsvp_event
673
+ # * sms
674
+ # * video_upload
675
+ # * create_note
676
+ # * share_item
677
+ # Example:
678
+ # <%= fb_prompt_permission('email', "Would you like to receive email from our application?" ) %>
679
+ #
680
+ # See http://wiki.developers.facebook.com/index.php/Fb:prompt-permission for
681
+ # more details. Correct as of 7th June 2009.
682
+ #
683
+ def fb_prompt_permission(permission,message,callback=nil)
684
+ raise(ArgumentError, "Unknown value for permission: #{permission}") unless VALID_PERMISSIONS.include?(permission.to_sym)
685
+ args={:perms=>permission}
686
+ args[:next_fbjs]=callback unless callback.nil?
687
+ content_tag("fb:prompt-permission",message,args)
688
+ end
689
+
690
+ # Renders a link to prompt for multiple permissions at once.
691
+ #
692
+ # Example:
693
+ # <%= fb_prompt_permissions(['email','offline_access','status_update'], 'Would you like to grant some permissions?')
694
+ def fb_prompt_permissions(permissions,message,callback=nil)
695
+ permissions.each do |p|
696
+ raise(ArgumentError, "Unknown value for permission: #{p}") unless VALID_PERMISSIONS.include?(p.to_sym)
697
+ end
698
+ args={:perms=>permissions*','}
699
+ args[:next_fbjs]=callback unless callback.nil?
700
+ content_tag("fb:prompt-permission",message,args)
701
+ end
702
+
703
+ # Renders an <fb:eventlink /> tag that displays the event name and links to the event's page.
704
+ def fb_eventlink(eid)
705
+ content_tag "fb:eventlink",nil,:eid=>eid
706
+ end
707
+
708
+ # Renders an <fb:grouplink /> tag that displays the group name and links to the group's page.
709
+ def fb_grouplink(gid)
710
+ content_tag "fb:grouplink",nil,:gid=>gid
711
+ end
712
+
713
+ # Returns the status of the user
714
+ def fb_user_status(user,linked=true)
715
+ content_tag "fb:user-status",nil,stringify_vals(:uid=>cast_to_facebook_id(user), :linked=>linked)
716
+ end
717
+
718
+ # Renders a standard 'Share' button for the specified URL.
719
+ def fb_share_button(url)
720
+ content_tag "fb:share-button",nil,:class=>"url",:href=>url
721
+ end
722
+
723
+ # Renders the FBML on a Facebook server inside an iframe.
724
+ #
725
+ # Meant to be used for a Facebook Connect site or an iframe application
726
+ def fb_serverfbml(options={},&proc)
727
+ inner = capture(&proc)
728
+ concat(content_tag("fb:serverfbml",inner,options),&proc.binding)
729
+ end
730
+
731
+ def fb_container(options={},&proc)
732
+ inner = capture(&proc)
733
+ concat(content_tag("fb:container",inner,options),&proc.binding)
734
+ end
735
+
736
+ # Renders an fb:time element
737
+ #
738
+ # Example:
739
+ # <%= fb_time(Time.now, :tz => 'America/New York', :preposition => true) %>
740
+ #
741
+ # See http://wiki.developers.facebook.com/index.php/Fb:time for
742
+ # more details
743
+ def fb_time(time, options={})
744
+ tag "fb:time",stringify_vals({:t => time.to_i}.merge(options))
745
+ end
746
+
747
+ # Renders a fb:intl element
748
+ #
749
+ # Example:
750
+ # <%= fb_intl('Age', :desc => 'Label for the age form field', :delimiters => '[]') %>
751
+ #
752
+ # See http://wiki.developers.facebook.com/index.php/Fb:intl for
753
+ # more details
754
+ def fb_intl(text=nil, options={}, &proc)
755
+ raise ArgumentError, "Missing block or text" unless block_given? or text
756
+ content = block_given? ? capture(&proc) : text
757
+ content_tag("fb:intl", content, stringify_vals(options))
758
+ end
759
+
760
+ # Renders a fb:intl-token element
761
+ #
762
+ # Example:
763
+ # <%= fb_intl-token('number', 5) %>
764
+ #
765
+ # See http://wiki.developers.facebook.com/index.php/Fb:intl-token for
766
+ # more details
767
+ def fb_intl_token(name, text=nil, &proc)
768
+ raise ArgumentError, "Missing block or text" unless block_given? or text
769
+ content = block_given? ? capture(&proc) : text
770
+ content_tag("fb:intl-token", content, stringify_vals({:name => name}))
771
+ end
772
+
773
+ # Renders a fb:date element
774
+ #
775
+ # Example:
776
+ # <%= fb_date(Time.now, :format => 'verbose', :tz => 'America/New York') %>
777
+ #
778
+ # See http://wiki.developers.facebook.com/index.php/Fb:date for
779
+ # more details
780
+ def fb_date(time, options={})
781
+ tag "fb:date", stringify_vals({:t => time.to_i}.merge(options))
782
+ end
783
+
784
+ # Renders a fb:fbml-attribute element
785
+ #
786
+ # Example:
787
+ # <%= fb_fbml_attribute('title', Education) %>
788
+ #
789
+ # The options hash is passed to the fb:intl element that is generated inside this element
790
+ # and can have the keys available for the fb:intl element.
791
+ #
792
+ # See http://wiki.developers.facebook.com/index.php/Fb:fbml-attribute for
793
+ # more details
794
+ def fb_fbml_attribute(name, text, options={})
795
+ content_tag("fb:fbml-attribute", fb_intl(text, options), stringify_vals({:name => name}))
796
+ end
797
+
798
+ protected
799
+
800
+ def cast_to_facebook_id(object)
801
+ Facebooker::User.cast_to_facebook_id(object)
802
+ end
803
+
804
+ def validate_fb_profile_pic_size(options)
805
+ if options.has_key?(:size) && !VALID_FB_PROFILE_PIC_SIZES.include?(options[:size].to_sym)
806
+ raise(ArgumentError, "Unknown value for size: #{options[:size]}")
807
+ end
808
+ end
809
+
810
+ def validate_fb_photo_size(options)
811
+ if options.has_key?(:size) && !VALID_FB_PHOTO_SIZES.include?(options[:size].to_sym)
812
+ raise(ArgumentError, "Unknown value for size: #{options[:size]}")
813
+ end
814
+ end
815
+
816
+ private
817
+ def stringify_vals(hash)
818
+ result={}
819
+ hash.each do |key,value|
820
+ result[key]=value.to_s
821
+ end
822
+ result
823
+ end
824
+
825
+ def fb_status_msg(type, message, text)
826
+ if text.blank?
827
+ tag("fb:#{type}", :message => message)
828
+ else
829
+ content_tag("fb:#{type}", content_tag("fb:message", message) + text)
830
+ end
831
+ end
832
+
833
+ def token_tag
834
+ unless protect_against_forgery?
835
+ ''
836
+ else
837
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
838
+ end
839
+ end
840
+
841
+ def ignore_binding?
842
+ ActionPack::VERSION::MAJOR >= 2 && ActionPack::VERSION::MINOR >= 2
843
+ end
844
+ end
845
+ end
846
+ end
847
+
848
+ class Hash
849
+ def transform_keys!(transformation_hash)
850
+ transformation_hash.each_pair{|key, value| transform_key!(key, value)}
851
+ end
852
+
853
+
854
+ def transform_key!(old_key, new_key)
855
+ swapkey!(new_key, old_key)
856
+ end
857
+
858
+ ### This method is lifted from Ruby Facets core
859
+ def swapkey!( newkey, oldkey )
860
+ self[newkey] = self.delete(oldkey) if self.has_key?(oldkey)
861
+ self
862
+ end
863
+
864
+ # We can allow css attributes.
865
+ FB_ALWAYS_VALID_OPTION_KEYS = [:class, :style]
866
+ def assert_valid_keys(*valid_keys)
867
+ unknown_keys = keys - [valid_keys + FB_ALWAYS_VALID_OPTION_KEYS].flatten
868
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
869
+ end
870
+ end