actionpack 1.12.5 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (179) hide show
  1. data/CHANGELOG +517 -15
  2. data/MIT-LICENSE +1 -1
  3. data/README +18 -20
  4. data/Rakefile +7 -4
  5. data/examples/address_book_controller.rb +3 -3
  6. data/examples/blog_controller.cgi +3 -3
  7. data/examples/debate_controller.cgi +5 -5
  8. data/lib/action_controller.rb +2 -2
  9. data/lib/action_controller/assertions.rb +73 -311
  10. data/lib/action_controller/{deprecated_assertions.rb → assertions/deprecated_assertions.rb} +32 -8
  11. data/lib/action_controller/assertions/dom_assertions.rb +25 -0
  12. data/lib/action_controller/assertions/model_assertions.rb +12 -0
  13. data/lib/action_controller/assertions/response_assertions.rb +140 -0
  14. data/lib/action_controller/assertions/routing_assertions.rb +82 -0
  15. data/lib/action_controller/assertions/selector_assertions.rb +571 -0
  16. data/lib/action_controller/assertions/tag_assertions.rb +117 -0
  17. data/lib/action_controller/base.rb +334 -163
  18. data/lib/action_controller/benchmarking.rb +3 -6
  19. data/lib/action_controller/caching.rb +83 -22
  20. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -7
  21. data/lib/action_controller/cgi_ext/cgi_methods.rb +167 -173
  22. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +43 -22
  23. data/lib/action_controller/cgi_process.rb +50 -27
  24. data/lib/action_controller/components.rb +21 -25
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/{dependencies.rb → deprecated_dependencies.rb} +9 -27
  27. data/lib/action_controller/filters.rb +448 -225
  28. data/lib/action_controller/flash.rb +24 -20
  29. data/lib/action_controller/helpers.rb +2 -5
  30. data/lib/action_controller/integration.rb +40 -16
  31. data/lib/action_controller/layout.rb +11 -8
  32. data/lib/action_controller/macros/auto_complete.rb +3 -2
  33. data/lib/action_controller/macros/in_place_editing.rb +3 -2
  34. data/lib/action_controller/mime_responds.rb +41 -29
  35. data/lib/action_controller/mime_type.rb +68 -10
  36. data/lib/action_controller/pagination.rb +4 -3
  37. data/lib/action_controller/request.rb +22 -14
  38. data/lib/action_controller/rescue.rb +25 -22
  39. data/lib/action_controller/resources.rb +302 -0
  40. data/lib/action_controller/response.rb +20 -2
  41. data/lib/action_controller/response.rb.rej +17 -0
  42. data/lib/action_controller/routing.rb +1165 -567
  43. data/lib/action_controller/scaffolding.rb +30 -31
  44. data/lib/action_controller/session/active_record_store.rb +2 -0
  45. data/lib/action_controller/session/drb_store.rb +4 -0
  46. data/lib/action_controller/session/mem_cache_store.rb +4 -0
  47. data/lib/action_controller/session_management.rb +6 -9
  48. data/lib/action_controller/status_codes.rb +89 -0
  49. data/lib/action_controller/streaming.rb +6 -15
  50. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +5 -5
  51. data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -2
  52. data/lib/action_controller/templates/rescues/routing_error.rhtml +4 -4
  53. data/lib/action_controller/templates/rescues/template_error.rhtml +1 -1
  54. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  55. data/lib/action_controller/test_process.rb +52 -30
  56. data/lib/action_controller/url_rewriter.rb +63 -29
  57. data/lib/action_controller/vendor/html-scanner/html/document.rb +1 -0
  58. data/lib/action_controller/vendor/html-scanner/html/node.rb +3 -4
  59. data/lib/action_controller/vendor/html-scanner/html/selector.rb +822 -0
  60. data/lib/action_controller/verification.rb +22 -11
  61. data/lib/action_pack.rb +1 -1
  62. data/lib/action_pack/version.rb +2 -2
  63. data/lib/action_view.rb +1 -1
  64. data/lib/action_view/base.rb +46 -43
  65. data/lib/action_view/compiled_templates.rb +1 -1
  66. data/lib/action_view/helpers/active_record_helper.rb +54 -17
  67. data/lib/action_view/helpers/asset_tag_helper.rb +97 -46
  68. data/lib/action_view/helpers/capture_helper.rb +1 -1
  69. data/lib/action_view/helpers/date_helper.rb +258 -136
  70. data/lib/action_view/helpers/debug_helper.rb +1 -1
  71. data/lib/action_view/helpers/deprecated_helper.rb +34 -0
  72. data/lib/action_view/helpers/form_helper.rb +75 -35
  73. data/lib/action_view/helpers/form_options_helper.rb +7 -5
  74. data/lib/action_view/helpers/form_tag_helper.rb +44 -6
  75. data/lib/action_view/helpers/java_script_macros_helper.rb +59 -46
  76. data/lib/action_view/helpers/javascript_helper.rb +71 -10
  77. data/lib/action_view/helpers/javascripts/controls.js +41 -23
  78. data/lib/action_view/helpers/javascripts/dragdrop.js +105 -76
  79. data/lib/action_view/helpers/javascripts/effects.js +293 -163
  80. data/lib/action_view/helpers/javascripts/prototype.js +897 -389
  81. data/lib/action_view/helpers/javascripts/prototype.js.rej +561 -0
  82. data/lib/action_view/helpers/number_helper.rb +111 -65
  83. data/lib/action_view/helpers/prototype_helper.rb +84 -109
  84. data/lib/action_view/helpers/scriptaculous_helper.rb +5 -0
  85. data/lib/action_view/helpers/tag_helper.rb +69 -16
  86. data/lib/action_view/helpers/text_helper.rb +149 -112
  87. data/lib/action_view/helpers/url_helper.rb +200 -107
  88. data/lib/action_view/template_error.rb +66 -42
  89. data/test/abstract_unit.rb +4 -2
  90. data/test/active_record_unit.rb +84 -56
  91. data/test/activerecord/active_record_assertions_test.rb +26 -18
  92. data/test/activerecord/active_record_store_test.rb +4 -36
  93. data/test/activerecord/pagination_test.rb +1 -6
  94. data/test/controller/action_pack_assertions_test.rb +230 -113
  95. data/test/controller/addresses_render_test.rb +2 -6
  96. data/test/controller/assert_select_test.rb +576 -0
  97. data/test/controller/base_test.rb +73 -3
  98. data/test/controller/caching_test.rb +228 -0
  99. data/test/controller/capture_test.rb +12 -10
  100. data/test/controller/cgi_test.rb +89 -12
  101. data/test/controller/components_test.rb +24 -2
  102. data/test/controller/content_type_test.rb +139 -0
  103. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  104. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  105. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  106. data/test/controller/cookie_test.rb +33 -25
  107. data/test/controller/deprecated_instance_variables_test.rb +48 -0
  108. data/test/controller/deprecation/deprecated_base_methods_test.rb +60 -0
  109. data/test/controller/fake_controllers.rb +0 -1
  110. data/test/controller/filters_test.rb +301 -16
  111. data/test/controller/flash_test.rb +19 -2
  112. data/test/controller/helper_test.rb +2 -2
  113. data/test/controller/integration_test.rb +154 -0
  114. data/test/controller/layout_test.rb +115 -1
  115. data/test/controller/mime_responds_test.rb +94 -0
  116. data/test/controller/mime_type_test.rb +9 -0
  117. data/test/controller/new_render_test.rb +161 -11
  118. data/test/controller/raw_post_test.rb +52 -15
  119. data/test/controller/redirect_test.rb +27 -14
  120. data/test/controller/render_test.rb +76 -29
  121. data/test/controller/request_test.rb +55 -4
  122. data/test/controller/resources_test.rb +274 -0
  123. data/test/controller/routing_test.rb +1533 -824
  124. data/test/controller/selector_test.rb +628 -0
  125. data/test/controller/send_file_test.rb +9 -1
  126. data/test/controller/session_management_test.rb +51 -0
  127. data/test/controller/test_test.rb +113 -29
  128. data/test/controller/url_rewriter_test.rb +86 -17
  129. data/test/controller/verification_test.rb +19 -17
  130. data/test/controller/webservice_test.rb +0 -7
  131. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  132. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  133. data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
  134. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  135. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +1 -0
  136. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +1 -0
  137. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +1 -0
  138. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +1 -0
  139. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +1 -0
  140. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +1 -0
  141. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +1 -0
  142. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +1 -0
  143. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +1 -0
  144. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +1 -0
  145. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +1 -0
  146. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +1 -0
  147. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +1 -0
  148. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +1 -0
  149. data/test/fixtures/multipart/binary_file +0 -0
  150. data/test/fixtures/public/javascripts/application.js +1 -0
  151. data/test/fixtures/test/_hello.rxml +1 -0
  152. data/test/fixtures/test/hello_world_container.rxml +3 -0
  153. data/test/fixtures/topic.rb +2 -2
  154. data/test/template/active_record_helper_test.rb +83 -12
  155. data/test/template/asset_tag_helper_test.rb +75 -95
  156. data/test/template/compiled_templates_test.rb +1 -0
  157. data/test/template/date_helper_test.rb +873 -181
  158. data/test/template/deprecated_helper_test.rb +36 -0
  159. data/test/template/deprecated_instance_variables_test.rb +43 -0
  160. data/test/template/form_helper_test.rb +77 -1
  161. data/test/template/form_options_helper_test.rb +4 -0
  162. data/test/template/form_tag_helper_test.rb +66 -2
  163. data/test/template/java_script_macros_helper_test.rb +4 -1
  164. data/test/template/javascript_helper_test.rb +29 -0
  165. data/test/template/number_helper_test.rb +63 -27
  166. data/test/template/prototype_helper_test.rb +77 -34
  167. data/test/template/tag_helper_test.rb +34 -6
  168. data/test/template/text_helper_test.rb +69 -34
  169. data/test/template/url_helper_test.rb +168 -16
  170. data/test/testing_sandbox.rb +7 -22
  171. metadata +66 -20
  172. data/filler.txt +0 -50
  173. data/lib/action_controller/code_generation.rb +0 -235
  174. data/lib/action_controller/vendor/xml_simple.rb +0 -1019
  175. data/test/controller/caching_filestore.rb +0 -74
  176. data/test/fixtures/application_root/app/controllers/a_class_that_contains_a_controller/poorly_placed_controller.rb +0 -7
  177. data/test/fixtures/application_root/app/controllers/module_that_holds_controllers/nested_controller.rb +0 -3
  178. data/test/fixtures/application_root/app/models/a_class_that_contains_a_controller.rb +0 -7
  179. data/test/fixtures/dont_load.rb +0 -3
@@ -7,7 +7,7 @@ module ActionView
7
7
  begin
8
8
  Marshal::dump(object)
9
9
  "<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", "&nbsp; ")}</pre>"
10
- rescue Object => e
10
+ rescue Exception => e # errors from Marshal or YAML
11
11
  # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
12
12
  "<code class='debug_dump'>#{h(object.inspect)}</code>"
13
13
  end
@@ -0,0 +1,34 @@
1
+ module ActionView
2
+ module Helpers
3
+ module PrototypeHelper
4
+
5
+ def update_element_function(element_id, options = {}, &block)
6
+ content = escape_javascript(options[:content] || '')
7
+ content = escape_javascript(capture(&block)) if block
8
+
9
+ javascript_function = case (options[:action] || :update)
10
+ when :update
11
+ if options[:position]
12
+ "new Insertion.#{options[:position].to_s.camelize}('#{element_id}','#{content}')"
13
+ else
14
+ "$('#{element_id}').innerHTML = '#{content}'"
15
+ end
16
+
17
+ when :empty
18
+ "$('#{element_id}').innerHTML = ''"
19
+
20
+ when :remove
21
+ "Element.remove('#{element_id}')"
22
+
23
+ else
24
+ raise ArgumentError, "Invalid action, choose one of :update, :remove, :empty"
25
+ end
26
+
27
+ javascript_function << ";\n"
28
+ options[:binding] ? concat(javascript_function, options[:binding]) : javascript_function
29
+ end
30
+ deprecate :update_element_function => "use RJS instead"
31
+
32
+ end
33
+ end
34
+ end
@@ -142,11 +142,13 @@ module ActionView
142
142
  #
143
143
  # Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base.
144
144
  # Like collection_select and datetime_select.
145
- def fields_for(object_name, *args, &proc)
145
+ def fields_for(object_name, *args, &block)
146
146
  raise ArgumentError, "Missing block" unless block_given?
147
147
  options = args.last.is_a?(Hash) ? args.pop : {}
148
148
  object = args.first
149
- yield((options[:builder] || FormBuilder).new(object_name, object, self, options, proc))
149
+
150
+ builder = options[:builder] || ActionView::Base.default_form_builder
151
+ yield builder.new(object_name, object, self, options, block)
150
152
  end
151
153
 
152
154
  # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -238,7 +240,11 @@ module ActionView
238
240
  @template_object, @local_binding = template_object, local_binding
239
241
  @object = object
240
242
  if @object_name.sub!(/\[\]$/,"")
241
- @auto_index = @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}").id_before_type_cast
243
+ if object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:id_before_type_cast)
244
+ @auto_index = object.id_before_type_cast
245
+ else
246
+ raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to id_before_type_cast: #{object.inspect}"
247
+ end
242
248
  end
243
249
  end
244
250
 
@@ -250,7 +256,7 @@ module ActionView
250
256
  options.delete("size")
251
257
  end
252
258
  options["type"] = field_type
253
- options["value"] ||= value_before_type_cast unless field_type == "file"
259
+ options["value"] ||= value_before_type_cast(object) unless field_type == "file"
254
260
  add_default_name_and_id(options)
255
261
  tag("input", options)
256
262
  end
@@ -259,9 +265,15 @@ module ActionView
259
265
  options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
260
266
  options["type"] = "radio"
261
267
  options["value"] = tag_value
262
- options["checked"] = "checked" if value.to_s == tag_value.to_s
268
+ if options.has_key?("checked")
269
+ cv = options.delete "checked"
270
+ checked = cv == true || cv == "checked"
271
+ else
272
+ checked = self.class.radio_button_checked?(value(object), tag_value)
273
+ end
274
+ options["checked"] = "checked" if checked
263
275
  pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
264
- options["id"] = @auto_index ?
276
+ options["id"] ||= defined?(@auto_index) ?
265
277
  "#{@object_name}_#{@auto_index}_#{@method_name}_#{pretty_tag_value}" :
266
278
  "#{@object_name}_#{@method_name}_#{pretty_tag_value}"
267
279
  add_default_name_and_id(options)
@@ -271,37 +283,32 @@ module ActionView
271
283
  def to_text_area_tag(options = {})
272
284
  options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
273
285
  add_default_name_and_id(options)
274
- content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast), options)
286
+
287
+ if size = options.delete("size")
288
+ options["cols"], options["rows"] = size.split("x")
289
+ end
290
+
291
+ content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast(object)), options)
275
292
  end
276
293
 
277
294
  def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
278
295
  options = options.stringify_keys
279
296
  options["type"] = "checkbox"
280
297
  options["value"] = checked_value
281
- checked = case value
282
- when TrueClass, FalseClass
283
- value
284
- when NilClass
285
- false
286
- when Integer
287
- value != 0
288
- when String
289
- value == checked_value
290
- else
291
- value.to_i != 0
292
- end
293
- if checked || options["checked"] == "checked"
294
- options["checked"] = "checked"
298
+ if options.has_key?("checked")
299
+ cv = options.delete "checked"
300
+ checked = cv == true || cv == "checked"
295
301
  else
296
- options.delete("checked")
302
+ checked = self.class.check_box_checked?(value(object), checked_value)
297
303
  end
304
+ options["checked"] = "checked" if checked
298
305
  add_default_name_and_id(options)
299
306
  tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value)
300
307
  end
301
308
 
302
309
  def to_date_tag()
303
310
  defaults = DEFAULT_DATE_OPTIONS.dup
304
- date = value || Date.today
311
+ date = value(object) || Date.today
305
312
  options = Proc.new { |position| defaults.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") }
306
313
  html_day_select(date, options.call(3)) +
307
314
  html_month_select(date, options.call(2)) +
@@ -311,6 +318,7 @@ module ActionView
311
318
  def to_boolean_select_tag(options = {})
312
319
  options = options.stringify_keys
313
320
  add_default_name_and_id(options)
321
+ value = value(object)
314
322
  tag_text = "<select"
315
323
  tag_text << tag_options(options)
316
324
  tag_text << "><option value=\"false\""
@@ -321,24 +329,51 @@ module ActionView
321
329
  end
322
330
 
323
331
  def to_content_tag(tag_name, options = {})
324
- content_tag(tag_name, value, options)
332
+ content_tag(tag_name, value(object), options)
325
333
  end
326
334
 
327
335
  def object
328
336
  @object || @template_object.instance_variable_get("@#{@object_name}")
329
337
  end
330
338
 
331
- def value
332
- unless object.nil?
333
- object.send(@method_name)
334
- end
339
+ def value(object)
340
+ self.class.value(object, @method_name)
335
341
  end
336
342
 
337
- def value_before_type_cast
338
- unless object.nil?
339
- object.respond_to?(@method_name + "_before_type_cast") ?
340
- object.send(@method_name + "_before_type_cast") :
341
- object.send(@method_name)
343
+ def value_before_type_cast(object)
344
+ self.class.value_before_type_cast(object, @method_name)
345
+ end
346
+
347
+ class << self
348
+ def value(object, method_name)
349
+ object.send method_name unless object.nil?
350
+ end
351
+
352
+ def value_before_type_cast(object, method_name)
353
+ unless object.nil?
354
+ object.respond_to?(method_name + "_before_type_cast") ?
355
+ object.send(method_name + "_before_type_cast") :
356
+ object.send(method_name)
357
+ end
358
+ end
359
+
360
+ def check_box_checked?(value, checked_value)
361
+ case value
362
+ when TrueClass, FalseClass
363
+ value
364
+ when NilClass
365
+ false
366
+ when Integer
367
+ value != 0
368
+ when String
369
+ value == checked_value
370
+ else
371
+ value.to_i != 0
372
+ end
373
+ end
374
+
375
+ def radio_button_checked?(value, checked_value)
376
+ value.to_s == checked_value.to_s
342
377
  end
343
378
  end
344
379
 
@@ -348,7 +383,7 @@ module ActionView
348
383
  options["name"] ||= tag_name_with_index(options["index"])
349
384
  options["id"] ||= tag_id_with_index(options["index"])
350
385
  options.delete("index")
351
- elsif @auto_index
386
+ elsif defined?(@auto_index)
352
387
  options["name"] ||= tag_name_with_index(@auto_index)
353
388
  options["id"] ||= tag_id_with_index(@auto_index)
354
389
  else
@@ -379,7 +414,7 @@ module ActionView
379
414
  class_inheritable_accessor :field_helpers
380
415
  self.field_helpers = (FormHelper.instance_methods - ['form_for'])
381
416
 
382
- attr_accessor :object_name, :object
417
+ attr_accessor :object_name, :object, :options
383
418
 
384
419
  def initialize(object_name, object, template, options, proc)
385
420
  @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
@@ -403,4 +438,9 @@ module ActionView
403
438
  end
404
439
  end
405
440
  end
441
+
442
+ class Base
443
+ cattr_accessor :default_form_builder
444
+ self.default_form_builder = ::ActionView::Helpers::FormBuilder
445
+ end
406
446
  end
@@ -26,7 +26,7 @@ module ActionView
26
26
  #
27
27
  # Another common case is a select tag for an <tt>belongs_to</tt>-associated object. For example,
28
28
  #
29
- # select("post", "person_id", Person.find_all.collect {|p| [ p.name, p.id ] })
29
+ # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] })
30
30
  #
31
31
  # could become:
32
32
  #
@@ -43,7 +43,7 @@ module ActionView
43
43
  # See options_for_select for the required format of the choices parameter.
44
44
  #
45
45
  # Example with @post.person_id => 1:
46
- # select("post", "person_id", Person.find_all.collect {|p| [ p.name, p.id ] }, { :include_blank => true })
46
+ # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank => true })
47
47
  #
48
48
  # could become:
49
49
  #
@@ -113,7 +113,6 @@ module ActionView
113
113
 
114
114
  options_for_select = container.inject([]) do |options, element|
115
115
  if !element.is_a?(String) and element.respond_to?(:first) and element.respond_to?(:last)
116
- is_selected = ( (selected.respond_to?(:include?) ? selected.include?(element.last) : element.last == selected) )
117
116
  is_selected = ( (selected.respond_to?(:include?) && !selected.is_a?(String) ? selected.include?(element.last) : element.last == selected) )
118
117
  if is_selected
119
118
  options << "<option value=\"#{html_escape(element.last.to_s)}\" selected=\"selected\">#{html_escape(element.first.to_s)}</option>"
@@ -121,7 +120,6 @@ module ActionView
121
120
  options << "<option value=\"#{html_escape(element.last.to_s)}\">#{html_escape(element.first.to_s)}</option>"
122
121
  end
123
122
  else
124
- is_selected = ( (selected.respond_to?(:include?) ? selected.include?(element) : element == selected) )
125
123
  is_selected = ( (selected.respond_to?(:include?) && !selected.is_a?(String) ? selected.include?(element) : element == selected) )
126
124
  options << ((is_selected) ? "<option value=\"#{html_escape(element.to_s)}\" selected=\"selected\">#{html_escape(element.to_s)}</option>" : "<option value=\"#{html_escape(element.to_s)}\">#{html_escape(element.to_s)}</option>")
127
125
  end
@@ -299,13 +297,15 @@ module ActionView
299
297
  def to_select_tag(choices, options, html_options)
300
298
  html_options = html_options.stringify_keys
301
299
  add_default_name_and_id(html_options)
300
+ value = value(object)
302
301
  selected_value = options.has_key?(:selected) ? options[:selected] : value
303
- content_tag("select", add_options(options_for_select(choices, selected_value), options, value), html_options)
302
+ content_tag("select", add_options(options_for_select(choices, selected_value), options, selected_value), html_options)
304
303
  end
305
304
 
306
305
  def to_collection_select_tag(collection, value_method, text_method, options, html_options)
307
306
  html_options = html_options.stringify_keys
308
307
  add_default_name_and_id(html_options)
308
+ value = value(object)
309
309
  content_tag(
310
310
  "select", add_options(options_from_collection_for_select(collection, value_method, text_method, value), options, value), html_options
311
311
  )
@@ -314,12 +314,14 @@ module ActionView
314
314
  def to_country_select_tag(priority_countries, options, html_options)
315
315
  html_options = html_options.stringify_keys
316
316
  add_default_name_and_id(html_options)
317
+ value = value(object)
317
318
  content_tag("select", add_options(country_options_for_select(value, priority_countries), options, value), html_options)
318
319
  end
319
320
 
320
321
  def to_time_zone_select_tag(priority_zones, options, html_options)
321
322
  html_options = html_options.stringify_keys
322
323
  add_default_name_and_id(html_options)
324
+ value = value(object)
323
325
  content_tag("select",
324
326
  add_options(
325
327
  time_zone_options_for_select(value, priority_zones, options[:model] || TimeZone),
@@ -12,14 +12,49 @@ module ActionView
12
12
  # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
13
13
  # ActionController::Base#url_for. The method for the form defaults to POST.
14
14
  #
15
+ # Examples:
16
+ # * <tt>form_tag('/posts') => <form action="/posts" method="post"></tt>
17
+ # * <tt>form_tag('/posts/1', :method => :put) => <form action="/posts/1" method="put"></tt>
18
+ # * <tt>form_tag('/upload', :multipart => true) => <form action="/upload" method="post" enctype="multipart/form-data"></tt>
19
+ #
20
+ # ERb example:
21
+ # <% form_tag '/posts' do -%>
22
+ # <div><%= submit_tag 'Save' %></div>
23
+ # <% end -%>
24
+ #
25
+ # Will output:
26
+ # <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
27
+ #
15
28
  # Options:
16
29
  # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
17
- # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
18
- def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &proc)
19
- html_options = { "method" => "post" }.merge(options.stringify_keys)
30
+ # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
31
+ # If "put", "delete", or another verb is used, a hidden input with name _method
32
+ # is added to simulate the verb over post.
33
+ def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
34
+ html_options = options.stringify_keys
20
35
  html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
21
- html_options["action"] = url_for(url_for_options, *parameters_for_url)
22
- tag :form, html_options, true
36
+ html_options["action"] = url_for(url_for_options, *parameters_for_url)
37
+
38
+ method_tag = ""
39
+
40
+ case method = html_options.delete("method").to_s
41
+ when /^get$/i # must be case-insentive, but can't use downcase as might be nil
42
+ html_options["method"] = "get"
43
+ when /^post$/i, "", nil
44
+ html_options["method"] = "post"
45
+ else
46
+ html_options["method"] = "post"
47
+ method_tag = content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method), :style => 'margin:0;padding:0')
48
+ end
49
+
50
+ if block_given?
51
+ content = capture(&block)
52
+ concat(tag(:form, html_options, true) + method_tag, block.binding)
53
+ concat(content, block.binding)
54
+ concat("</form>", block.binding)
55
+ else
56
+ tag(:form, html_options, true) + method_tag
57
+ end
23
58
  end
24
59
 
25
60
  alias_method :start_form_tag, :form_tag
@@ -28,6 +63,8 @@ module ActionView
28
63
  def end_form_tag
29
64
  "</form>"
30
65
  end
66
+
67
+ deprecate :end_form_tag, :start_form_tag => :form_tag
31
68
 
32
69
  # Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
33
70
  # choice selection box.
@@ -110,7 +147,8 @@ module ActionView
110
147
 
111
148
  # Creates a radio button.
112
149
  def radio_button_tag(name, value, checked = false, options = {})
113
- html_options = { "type" => "radio", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
150
+ pretty_tag_value = value.to_s.gsub(/\s/, "_").gsub(/(?!-)\W/, "").downcase
151
+ html_options = { "type" => "radio", "name" => name, "id" => "#{name}_#{pretty_tag_value}", "value" => value }.update(options.stringify_keys)
114
152
  html_options["checked"] = "checked" if checked
115
153
  tag :input, html_options
116
154
  end
@@ -7,6 +7,8 @@ module ActionView
7
7
  # editing relies on ActionController::Base.in_place_edit_for and the autocompletion relies on
8
8
  # ActionController::Base.auto_complete_for.
9
9
  module JavaScriptMacrosHelper
10
+ # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
11
+ #
10
12
  # Makes an HTML element specified by the DOM ID +field_id+ become an in-place
11
13
  # editor of a property.
12
14
  #
@@ -27,20 +29,21 @@ module ActionView
27
29
  # <tt>:url</tt>:: Specifies the url where the updated value should
28
30
  # be sent after the user presses "ok".
29
31
  #
30
- #
31
32
  # Addtional +options+ are:
32
33
  # <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
33
34
  # <tt>:cols</tt>:: Number of characters the text input should span (works for both INPUT and TEXTAREA)
34
35
  # <tt>:size</tt>:: Synonym for :cols when using a single line text input.
35
36
  # <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
36
37
  # <tt>:save_text</tt>:: The text on the save link. (default: "ok")
37
- # <tt>:loading_text</tt>:: The text to display when submitting to the server (default: "Saving...")
38
+ # <tt>:loading_text</tt>:: The text to display while the data is being loaded from the server (default: "Loading...")
39
+ # <tt>:saving_text</tt>:: The text to display when submitting to the server (default: "Saving...")
38
40
  # <tt>:external_control</tt>:: The id of an external control used to enter edit mode.
39
41
  # <tt>:load_text_url</tt>:: URL where initial value of editor (content) is retrieved.
40
42
  # <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
41
43
  # <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
42
44
  # in the AJAX call, +form+ is an implicit parameter
43
45
  # <tt>:script</tt>:: Instructs the in-place editor to evaluate the remote JavaScript response (default: false)
46
+ # <tt>:click_to_edit_text</tt>::The text shown during mouseover the editable text (default: "Click to edit")
44
47
  def in_place_editor(field_id, options = {})
45
48
  function = "new Ajax.InPlaceEditor("
46
49
  function << "'#{field_id}', "
@@ -50,6 +53,7 @@ module ActionView
50
53
  js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
51
54
  js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
52
55
  js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
56
+ js_options['savingText'] = %('#{options[:saving_text]}') if options[:saving_text]
53
57
  js_options['rows'] = options[:rows] if options[:rows]
54
58
  js_options['cols'] = options[:cols] if options[:cols]
55
59
  js_options['size'] = options[:size] if options[:size]
@@ -58,6 +62,7 @@ module ActionView
58
62
  js_options['ajaxOptions'] = options[:options] if options[:options]
59
63
  js_options['evalScripts'] = options[:script] if options[:script]
60
64
  js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
65
+ js_options['clickToEditText'] = %('#{options[:click_to_edit_text]}') if options[:click_to_edit_text]
61
66
  function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
62
67
 
63
68
  function << ')'
@@ -65,6 +70,8 @@ module ActionView
65
70
  javascript_tag(function)
66
71
  end
67
72
 
73
+ # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
74
+ #
68
75
  # Renders the value of the specified object and method with in-place editing capabilities.
69
76
  #
70
77
  # See the RDoc on ActionController::InPlaceEditing to learn more about this.
@@ -76,14 +83,16 @@ module ActionView
76
83
  in_place_editor(tag_options[:id], in_place_editor_options)
77
84
  end
78
85
 
86
+ # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
87
+ #
79
88
  # Adds AJAX autocomplete functionality to the text input field with the
80
89
  # DOM ID specified by +field_id+.
81
90
  #
82
- # This function expects that the called action returns a HTML <ul> list,
91
+ # This function expects that the called action returns an HTML <ul> list,
83
92
  # or nothing if no entries should be displayed for autocompletion.
84
93
  #
85
94
  # You'll probably want to turn the browser's built-in autocompletion off,
86
- # so be sure to include a autocomplete="off" attribute with your text
95
+ # so be sure to include an <tt>autocomplete="off"</tt> attribute with your text
87
96
  # input field.
88
97
  #
89
98
  # The autocompleter object is assigned to a Javascript variable named <tt>field_id</tt>_auto_completer.
@@ -91,45 +100,45 @@ module ActionView
91
100
  # other means than user input (for that specific case, call the <tt>activate</tt> method on that object).
92
101
  #
93
102
  # Required +options+ are:
94
- # <tt>:url</tt>:: URL to call for autocompletion results
95
- # in url_for format.
103
+ # <tt>:url</tt>:: URL to call for autocompletion results
104
+ # in url_for format.
96
105
  #
97
106
  # Addtional +options+ are:
98
- # <tt>:update</tt>:: Specifies the DOM ID of the element whose
99
- # innerHTML should be updated with the autocomplete
100
- # entries returned by the AJAX request.
101
- # Defaults to field_id + '_auto_complete'
102
- # <tt>:with</tt>:: A JavaScript expression specifying the
103
- # parameters for the XMLHttpRequest. This defaults
104
- # to 'fieldname=value'.
105
- # <tt>:frequency</tt>:: Determines the time to wait after the last keystroke
106
- # for the AJAX request to be initiated.
107
- # <tt>:indicator</tt>:: Specifies the DOM ID of an element which will be
108
- # displayed while autocomplete is running.
109
- # <tt>:tokens</tt>:: A string or an array of strings containing
110
- # separator tokens for tokenized incremental
111
- # autocompletion. Example: <tt>:tokens => ','</tt> would
112
- # allow multiple autocompletion entries, separated
113
- # by commas.
114
- # <tt>:min_chars</tt>:: The minimum number of characters that should be
115
- # in the input field before an Ajax call is made
116
- # to the server.
117
- # <tt>:on_hide</tt>:: A Javascript expression that is called when the
118
- # autocompletion div is hidden. The expression
119
- # should take two variables: element and update.
120
- # Element is a DOM element for the field, update
121
- # is a DOM element for the div from which the
122
- # innerHTML is replaced.
123
- # <tt>:on_show</tt>:: Like on_hide, only now the expression is called
124
- # then the div is shown.
125
- # <tt>:after_update_element</tt>:: A Javascript expression that is called when the
126
- # user has selected one of the proposed values.
127
- # The expression should take two variables: element and value.
128
- # Element is a DOM element for the field, value
129
- # is the value selected by the user.
130
- # <tt>:select</tt>:: Pick the class of the element from which the value for
131
- # insertion should be extracted. If this is not specified,
132
- # the entire element is used.
107
+ # <tt>:update</tt>:: Specifies the DOM ID of the element whose
108
+ # innerHTML should be updated with the autocomplete
109
+ # entries returned by the AJAX request.
110
+ # Defaults to <tt>field_id</tt> + '_auto_complete'
111
+ # <tt>:with</tt>:: A JavaScript expression specifying the
112
+ # parameters for the XMLHttpRequest. This defaults
113
+ # to 'fieldname=value'.
114
+ # <tt>:frequency</tt>:: Determines the time to wait after the last keystroke
115
+ # for the AJAX request to be initiated.
116
+ # <tt>:indicator</tt>:: Specifies the DOM ID of an element which will be
117
+ # displayed while autocomplete is running.
118
+ # <tt>:tokens</tt>:: A string or an array of strings containing
119
+ # separator tokens for tokenized incremental
120
+ # autocompletion. Example: <tt>:tokens => ','</tt> would
121
+ # allow multiple autocompletion entries, separated
122
+ # by commas.
123
+ # <tt>:min_chars</tt>:: The minimum number of characters that should be
124
+ # in the input field before an Ajax call is made
125
+ # to the server.
126
+ # <tt>:on_hide</tt>:: A Javascript expression that is called when the
127
+ # autocompletion div is hidden. The expression
128
+ # should take two variables: element and update.
129
+ # Element is a DOM element for the field, update
130
+ # is a DOM element for the div from which the
131
+ # innerHTML is replaced.
132
+ # <tt>:on_show</tt>:: Like on_hide, only now the expression is called
133
+ # then the div is shown.
134
+ # <tt>:after_update_element</tt>:: A Javascript expression that is called when the
135
+ # user has selected one of the proposed values.
136
+ # The expression should take two variables: element and value.
137
+ # Element is a DOM element for the field, value
138
+ # is the value selected by the user.
139
+ # <tt>:select</tt>:: Pick the class of the element from which the value for
140
+ # insertion should be extracted. If this is not specified,
141
+ # the entire element is used.
133
142
  def auto_complete_field(field_id, options = {})
134
143
  function = "var #{field_id}_auto_completer = new Ajax.Autocompleter("
135
144
  function << "'#{field_id}', "
@@ -141,6 +150,7 @@ module ActionView
141
150
  js_options[:callback] = "function(element, value) { return #{options[:with]} }" if options[:with]
142
151
  js_options[:indicator] = "'#{options[:indicator]}'" if options[:indicator]
143
152
  js_options[:select] = "'#{options[:select]}'" if options[:select]
153
+ js_options[:paramName] = "'#{options[:param_name]}'" if options[:param_name]
144
154
  js_options[:frequency] = "#{options[:frequency]}" if options[:frequency]
145
155
 
146
156
  { :after_update_element => :afterUpdateElement,
@@ -153,6 +163,8 @@ module ActionView
153
163
  javascript_tag(function)
154
164
  end
155
165
 
166
+ # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
167
+ #
156
168
  # Use this method in your view to generate a return for the AJAX autocomplete requests.
157
169
  #
158
170
  # Example action:
@@ -161,7 +173,7 @@ module ActionView
161
173
  # @items = Item.find(:all,
162
174
  # :conditions => [ 'LOWER(description) LIKE ?',
163
175
  # '%' + request.raw_post.downcase + '%' ])
164
- # render :inline => '<%= auto_complete_result(@items, 'description') %>'
176
+ # render :inline => "<%= auto_complete_result(@items, 'description') %>"
165
177
  # end
166
178
  #
167
179
  # The auto_complete_result can of course also be called from a view belonging to the
@@ -172,12 +184,14 @@ module ActionView
172
184
  content_tag("ul", items.uniq)
173
185
  end
174
186
 
187
+ # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
188
+ #
175
189
  # Wrapper for text_field with added AJAX autocompletion functionality.
176
190
  #
177
191
  # In your controller, you'll need to define an action called
178
- # auto_complete_for_object_method to respond the AJAX calls,
192
+ # auto_complete_for to respond the AJAX calls,
179
193
  #
180
- # See the RDoc on ActionController::AutoComplete to learn more about this.
194
+ # See the RDoc on ActionController::Macros::AutoComplete to learn more about this.
181
195
  def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})
182
196
  (completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
183
197
  text_field(object, method, tag_options) +
@@ -187,7 +201,7 @@ module ActionView
187
201
 
188
202
  private
189
203
  def auto_complete_stylesheet
190
- content_tag("style", <<-EOT
204
+ content_tag('style', <<-EOT, :type => 'text/css')
191
205
  div.auto_complete {
192
206
  width: 350px;
193
207
  background: #fff;
@@ -212,7 +226,6 @@ module ActionView
212
226
  padding:0;
213
227
  }
214
228
  EOT
215
- )
216
229
  end
217
230
 
218
231
  end