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
@@ -1,43 +1,64 @@
1
1
  module ActionView
2
- module Helpers
3
- # Provides methods for converting a number into a formatted string that currently represents
4
- # one of the following forms: phone number, percentage, money, or precision level.
2
+ module Helpers #:nodoc:
3
+ # Provides methods for converting a numbers into formatted strings.
4
+ # Methods are provided for phone numbers, currency, percentage,
5
+ # precision, positional notation, and file size.
5
6
  module NumberHelper
6
-
7
- # Formats a +number+ into a US phone number string. The +options+ can be a hash used to customize the format of the output.
8
- # The area code can be surrounded by parentheses by setting +:area_code+ to true; default is false
9
- # The delimiter can be set using +:delimiter+; default is "-"
10
- # Examples:
11
- # number_to_phone(1235551234) => 123-555-1234
12
- # number_to_phone(1235551234, {:area_code => true}) => (123) 555-1234
13
- # number_to_phone(1235551234, {:delimiter => " "}) => 123 555 1234
14
- # number_to_phone(1235551234, {:area_code => true, :extension => 555}) => (123) 555-1234 x 555
7
+ # Formats a +number+ into a US phone number. You can customize the format
8
+ # in the +options+ hash.
9
+ # * <tt>:area_code</tt> - Adds parentheses around the area code.
10
+ # * <tt>:delimiter</tt> - Specifies the delimiter to use, defaults to "-".
11
+ # * <tt>:extension</tt> - Specifies an extension to add to the end of the
12
+ # generated number
13
+ # * <tt>:country_code</tt> - Sets the country code for the phone number.
14
+ #
15
+ # number_to_phone(1235551234) => 123-555-1234
16
+ # number_to_phone(1235551234, :area_code => true) => (123) 555-1234
17
+ # number_to_phone(1235551234, :delimiter => " ") => 123 555 1234
18
+ # number_to_phone(1235551234, :area_code => true, :extension => 555) => (123) 555-1234 x 555
19
+ # number_to_phone(1235551234, :country_code => 1)
15
20
  def number_to_phone(number, options = {})
16
- options = options.stringify_keys
17
- area_code = options.delete("area_code") { false }
18
- delimiter = options.delete("delimiter") { "-" }
19
- extension = options.delete("extension") { "" }
21
+ number = number.to_s.strip unless number.nil?
22
+ options = options.stringify_keys
23
+ area_code = options["area_code"] || nil
24
+ delimiter = options["delimiter"] || "-"
25
+ extension = options["extension"].to_s.strip || nil
26
+ country_code = options["country_code"] || nil
27
+
20
28
  begin
21
- str = area_code == true ? number.to_s.gsub(/([0-9]{3})([0-9]{3})([0-9]{4})/,"(\\1) \\2#{delimiter}\\3") : number.to_s.gsub(/([0-9]{3})([0-9]{3})([0-9]{4})/,"\\1#{delimiter}\\2#{delimiter}\\3")
22
- extension.to_s.strip.empty? ? str : "#{str} x #{extension.to_s.strip}"
29
+ str = ""
30
+ str << "+#{country_code}#{delimiter}" unless country_code.blank?
31
+ str << if area_code
32
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
33
+ else
34
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
35
+ end
36
+ str << " x #{extension}" unless extension.blank?
37
+ str
23
38
  rescue
24
39
  number
25
40
  end
26
41
  end
27
42
 
28
- # Formats a +number+ into a currency string. The +options+ hash can be used to customize the format of the output.
29
- # The +number+ can contain a level of precision using the +precision+ key; default is 2
30
- # The currency type can be set using the +unit+ key; default is "$"
31
- # The unit separator can be set using the +separator+ key; default is "."
32
- # The delimiter can be set using the +delimiter+ key; default is ","
33
- # Examples:
34
- # number_to_currency(1234567890.50) => $1,234,567,890.50
35
- # number_to_currency(1234567890.506) => $1,234,567,890.51
36
- # number_to_currency(1234567890.50, {:unit => "&pound;", :separator => ",", :delimiter => ""}) => &pound;1234567890,50
43
+ # Formats a +number+ into a currency string. You can customize the format
44
+ # in the +options+ hash.
45
+ # * <tt>:precision</tt> - Sets the level of precision, defaults to 2
46
+ # * <tt>:unit</tt> - Sets the denomination of the currency, defaults to "$"
47
+ # * <tt>:separator</tt> - Sets the separator between the units, defaults to "."
48
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter, defaults to ","
49
+ #
50
+ # number_to_currency(1234567890.50) => $1,234,567,890.50
51
+ # number_to_currency(1234567890.506) => $1,234,567,890.51
52
+ # number_to_currency(1234567890.506, :precision => 3) => $1,234,567,890.506
53
+ # number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "")
54
+ # => &pound;1234567890,50
37
55
  def number_to_currency(number, options = {})
38
- options = options.stringify_keys
39
- precision, unit, separator, delimiter = options.delete("precision") { 2 }, options.delete("unit") { "$" }, options.delete("separator") { "." }, options.delete("delimiter") { "," }
40
- separator = "" unless precision > 0
56
+ options = options.stringify_keys
57
+ precision = options["precision"] || 2
58
+ unit = options["unit"] || "$"
59
+ separator = precision > 0 ? options["separator"] || "." : ""
60
+ delimiter = options["delimiter"] || ","
61
+
41
62
  begin
42
63
  parts = number_with_precision(number, precision).split('.')
43
64
  unit + number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s
@@ -46,16 +67,19 @@ module ActionView
46
67
  end
47
68
  end
48
69
 
49
- # Formats a +number+ as into a percentage string. The +options+ hash can be used to customize the format of the output.
50
- # The +number+ can contain a level of precision using the +precision+ key; default is 3
51
- # The unit separator can be set using the +separator+ key; default is "."
52
- # Examples:
53
- # number_to_percentage(100) => 100.000%
54
- # number_to_percentage(100, {:precision => 0}) => 100%
55
- # number_to_percentage(302.0574, {:precision => 2}) => 302.06%
70
+ # Formats a +number+ as a percentage string. You can customize the
71
+ # format in the +options+ hash.
72
+ # * <tt>:precision</tt> - Sets the level of precision, defaults to 3
73
+ # * <tt>:separator</tt> - Sets the separator between the units, defaults to "."
74
+ #
75
+ # number_to_percentage(100) => 100.000%
76
+ # number_to_percentage(100, {:precision => 0}) => 100%
77
+ # number_to_percentage(302.0574, {:precision => 2}) => 302.06%
56
78
  def number_to_percentage(number, options = {})
57
- options = options.stringify_keys
58
- precision, separator = options.delete("precision") { 3 }, options.delete("separator") { "." }
79
+ options = options.stringify_keys
80
+ precision = options["precision"] || 3
81
+ separator = options["separator"] || "."
82
+
59
83
  begin
60
84
  number = number_with_precision(number, precision)
61
85
  parts = number.split('.')
@@ -69,41 +93,63 @@ module ActionView
69
93
  end
70
94
  end
71
95
 
72
- # Formats a +number+ with a +delimiter+.
73
- # Example:
74
- # number_with_delimiter(12345678) => 12,345,678
75
- def number_with_delimiter(number, delimiter=",")
76
- number.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
96
+ # Formats a +number+ with grouped thousands using +delimiter+. You
97
+ # can customize the format in the +options+ hash.
98
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter, defaults to ","
99
+ # * <tt>:separator</tt> - Sets the separator between the units, defaults to "."
100
+ #
101
+ # number_with_delimiter(12345678) => 12,345,678
102
+ # number_with_delimiter(12345678.05) => 12,345,678.05
103
+ # number_with_delimiter(12345678, :delimiter => ".") => 12.345.678
104
+ def number_with_delimiter(number, delimiter=",", separator=".")
105
+ begin
106
+ parts = number.to_s.split(separator)
107
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
108
+ parts.join separator
109
+ rescue
110
+ number
111
+ end
77
112
  end
78
-
79
- # Returns a formatted-for-humans file size.
113
+
114
+ # Formats a +number+ with the specified level of +precision+. The default
115
+ # level of precision is 3.
116
+ #
117
+ # number_with_precision(111.2345) => 111.235
118
+ # number_with_precision(111.2345, 2) => 111.24
119
+ def number_with_precision(number, precision=3)
120
+ "%01.#{precision}f" % number
121
+ rescue
122
+ number
123
+ end
124
+
125
+ # Formats the bytes in +size+ into a more understandable representation.
126
+ # Useful for reporting file sizes to users. This method returns nil if
127
+ # +size+ cannot be converted into a number. You can change the default
128
+ # precision of 1 in +precision+.
80
129
  #
81
- # Examples:
82
- # human_size(123) => 123 Bytes
83
- # human_size(1234) => 1.2 KB
84
- # human_size(12345) => 12.1 KB
85
- # human_size(1234567) => 1.2 MB
86
- # human_size(1234567890) => 1.1 GB
87
- def number_to_human_size(size)
130
+ # number_to_human_size(123) => 123 Bytes
131
+ # number_to_human_size(1234) => 1.2 KB
132
+ # number_to_human_size(12345) => 12.1 KB
133
+ # number_to_human_size(1234567) => 1.2 MB
134
+ # number_to_human_size(1234567890) => 1.1 GB
135
+ # number_to_human_size(1234567890123) => 1.1 TB
136
+ # number_to_human_size(1234567, 2) => 1.18 MB
137
+ def number_to_human_size(size, precision=1)
138
+ size = Kernel.Float(size)
88
139
  case
89
- when size < 1.kilobyte: '%d Bytes' % size
90
- when size < 1.megabyte: '%.1f KB' % (size / 1.0.kilobyte)
91
- when size < 1.gigabyte: '%.1f MB' % (size / 1.0.megabyte)
92
- when size < 1.terabyte: '%.1f GB' % (size / 1.0.gigabyte)
93
- else '%.1f TB' % (size / 1.0.terabyte)
140
+ when size == 1 : "1 Byte"
141
+ when size < 1.kilobyte: "%d Bytes" % size
142
+ when size < 1.megabyte: "%.#{precision}f KB" % (size / 1.0.kilobyte)
143
+ when size < 1.gigabyte: "%.#{precision}f MB" % (size / 1.0.megabyte)
144
+ when size < 1.terabyte: "%.#{precision}f GB" % (size / 1.0.gigabyte)
145
+ else "%.#{precision}f TB" % (size / 1.0.terabyte)
94
146
  end.sub('.0', '')
95
147
  rescue
96
148
  nil
97
149
  end
98
150
 
99
151
  alias_method :human_size, :number_to_human_size # deprecated alias
100
-
101
- # Formats a +number+ with a level of +precision+.
102
- # Example:
103
- # number_with_precision(111.2345) => 111.235
104
- def number_with_precision(number, precision=3)
105
- sprintf("%01.#{precision}f", number)
106
- end
152
+ deprecate :human_size => :number_to_human_size
107
153
  end
108
154
  end
109
155
  end
@@ -1,4 +1,3 @@
1
- require File.dirname(__FILE__) + '/javascript_helper'
2
1
  require 'set'
3
2
 
4
3
  module ActionView
@@ -39,7 +38,7 @@ module ActionView
39
38
  # XMLHttpRequest. The result of that request can then be inserted into a
40
39
  # DOM object whose id can be specified with <tt>options[:update]</tt>.
41
40
  # Usually, the result would be a partial prepared by the controller with
42
- # either render_partial or render_partial_collection.
41
+ # render :partial.
43
42
  #
44
43
  # Examples:
45
44
  # link_to_remote "Delete this post", :update => "posts",
@@ -60,6 +59,12 @@ module ActionView
60
59
  # influence how the target DOM element is updated. It must be one of
61
60
  # <tt>:before</tt>, <tt>:top</tt>, <tt>:bottom</tt>, or <tt>:after</tt>.
62
61
  #
62
+ # The method used is by default POST. You can also specify GET or you
63
+ # can simulate PUT or DELETE over POST. All specified with <tt>options[:method]</tt>
64
+ #
65
+ # Example:
66
+ # link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete
67
+ #
63
68
  # By default, these remote requests are processed asynchronous during
64
69
  # which various JavaScript callbacks can be triggered (for progress
65
70
  # indicators and the likes). All callbacks get access to the
@@ -159,15 +164,20 @@ module ActionView
159
164
  #
160
165
  # By default the fall-through action is the same as the one specified in
161
166
  # the :url (and the default method is :post).
162
- def form_remote_tag(options = {})
167
+ #
168
+ # form_remote_tag also takes a block, like form_tag:
169
+ # <% form_remote_tag :url => '/posts' do -%>
170
+ # <div><%= submit_tag 'Save' %></div>
171
+ # <% end -%>
172
+ def form_remote_tag(options = {}, &block)
163
173
  options[:form] = true
164
174
 
165
175
  options[:html] ||= {}
166
- options[:html][:onsubmit] = "#{remote_function(options)}; return false;"
167
- options[:html][:action] = options[:html][:action] || url_for(options[:url])
168
- options[:html][:method] = options[:html][:method] || "post"
176
+ options[:html][:onsubmit] =
177
+ (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") +
178
+ "#{remote_function(options)}; return false;"
169
179
 
170
- tag("form", options[:html], true)
180
+ form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block)
171
181
  end
172
182
 
173
183
  # Works like form_remote_tag, but uses form_for semantics.
@@ -194,81 +204,6 @@ module ActionView
194
204
  tag("input", options[:html], false)
195
205
  end
196
206
 
197
- # Returns a JavaScript function (or expression) that'll update a DOM
198
- # element according to the options passed.
199
- #
200
- # * <tt>:content</tt>: The content to use for updating. Can be left out
201
- # if using block, see example.
202
- # * <tt>:action</tt>: Valid options are :update (assumed by default),
203
- # :empty, :remove
204
- # * <tt>:position</tt> If the :action is :update, you can optionally
205
- # specify one of the following positions: :before, :top, :bottom,
206
- # :after.
207
- #
208
- # Examples:
209
- # <%= javascript_tag(update_element_function("products",
210
- # :position => :bottom, :content => "<p>New product!</p>")) %>
211
- #
212
- # <% replacement_function = update_element_function("products") do %>
213
- # <p>Product 1</p>
214
- # <p>Product 2</p>
215
- # <% end %>
216
- # <%= javascript_tag(replacement_function) %>
217
- #
218
- # This method can also be used in combination with remote method call
219
- # where the result is evaluated afterwards to cause multiple updates on
220
- # a page. Example:
221
- #
222
- # # Calling view
223
- # <%= form_remote_tag :url => { :action => "buy" },
224
- # :complete => evaluate_remote_response %>
225
- # all the inputs here...
226
- #
227
- # # Controller action
228
- # def buy
229
- # @product = Product.find(1)
230
- # end
231
- #
232
- # # Returning view
233
- # <%= update_element_function(
234
- # "cart", :action => :update, :position => :bottom,
235
- # :content => "<p>New Product: #{@product.name}</p>")) %>
236
- # <% update_element_function("status", :binding => binding) do %>
237
- # You've bought a new product!
238
- # <% end %>
239
- #
240
- # Notice how the second call doesn't need to be in an ERb output block
241
- # since it uses a block and passes in the binding to render directly.
242
- # This trick will however only work in ERb (not Builder or other
243
- # template forms).
244
- #
245
- # See also JavaScriptGenerator and update_page.
246
- def update_element_function(element_id, options = {}, &block)
247
- content = escape_javascript(options[:content] || '')
248
- content = escape_javascript(capture(&block)) if block
249
-
250
- javascript_function = case (options[:action] || :update)
251
- when :update
252
- if options[:position]
253
- "new Insertion.#{options[:position].to_s.camelize}('#{element_id}','#{content}')"
254
- else
255
- "$('#{element_id}').innerHTML = '#{content}'"
256
- end
257
-
258
- when :empty
259
- "$('#{element_id}').innerHTML = ''"
260
-
261
- when :remove
262
- "Element.remove('#{element_id}')"
263
-
264
- else
265
- raise ArgumentError, "Invalid action, choose one of :update, :remove, :empty"
266
- end
267
-
268
- javascript_function << ";\n"
269
- options[:binding] ? concat(javascript_function, options[:binding]) : javascript_function
270
- end
271
-
272
207
  # Returns 'eval(request.responseText)' which is the JavaScript function
273
208
  # that form_remote_tag can call in :complete to evaluate a multiple
274
209
  # update return document using update_element_function calls.
@@ -289,7 +224,7 @@ module ActionView
289
224
  javascript_options = options_for_ajax(options)
290
225
 
291
226
  update = ''
292
- if options[:update] and options[:update].is_a?Hash
227
+ if options[:update] && options[:update].is_a?(Hash)
293
228
  update = []
294
229
  update << "success:'#{options[:update][:success]}'" if options[:update][:success]
295
230
  update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
@@ -303,7 +238,7 @@ module ActionView
303
238
  "new Ajax.Updater(#{update}, "
304
239
 
305
240
  url_options = options[:url]
306
- url_options = url_options.merge(:escape => false) if url_options.is_a? Hash
241
+ url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
307
242
  function << "'#{url_for(url_options)}'"
308
243
  function << ", #{javascript_options})"
309
244
 
@@ -438,7 +373,7 @@ module ActionView
438
373
  if ActionView::Base.debug_rjs
439
374
  source = javascript.dup
440
375
  javascript.replace "try {\n#{source}\n} catch (e) "
441
- javascript << "{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }}'); throw e }"
376
+ javascript << "{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }}'); throw e }"
442
377
  end
443
378
  end
444
379
  end
@@ -453,6 +388,12 @@ module ActionView
453
388
  JavaScriptElementProxy.new(self, id)
454
389
  end
455
390
 
391
+ # Returns an object whose <tt>#to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
392
+ # expression as an argument to another JavaScriptGenerator method.
393
+ def literal(code)
394
+ ActiveSupport::JSON::Variable.new(code.to_s)
395
+ end
396
+
456
397
  # Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
457
398
  # used for further method calls. Examples:
458
399
  #
@@ -526,7 +467,7 @@ module ActionView
526
467
  #
527
468
  # # Replace the DOM element having ID 'person-45' with the
528
469
  # # 'person' partial for the appropriate object.
529
- # replace_html 'person-45', :partial => 'person', :object => @person
470
+ # replace 'person-45', :partial => 'person', :object => @person
530
471
  #
531
472
  # This allows the same partial that is used for the +insert_html+ to
532
473
  # be also used for the input to +replace+ without resorting to
@@ -550,22 +491,22 @@ module ActionView
550
491
 
551
492
  # Removes the DOM elements with the given +ids+ from the page.
552
493
  def remove(*ids)
553
- record "#{javascript_object_for(ids)}.each(Element.remove)"
494
+ loop_on_multiple_args 'Element.remove', ids
554
495
  end
555
496
 
556
497
  # Shows hidden DOM elements with the given +ids+.
557
498
  def show(*ids)
558
- call 'Element.show', *ids
499
+ loop_on_multiple_args 'Element.show', ids
559
500
  end
560
501
 
561
502
  # Hides the visible DOM elements with the given +ids+.
562
503
  def hide(*ids)
563
- call 'Element.hide', *ids
504
+ loop_on_multiple_args 'Element.hide', ids
564
505
  end
565
506
 
566
507
  # Toggles the visibility of the DOM elements with the given +ids+.
567
508
  def toggle(*ids)
568
- call 'Element.toggle', *ids
509
+ loop_on_multiple_args 'Element.toggle', ids
569
510
  end
570
511
 
571
512
  # Displays an alert dialog with the given +message+.
@@ -573,16 +514,18 @@ module ActionView
573
514
  call 'alert', message
574
515
  end
575
516
 
576
- # Redirects the browser to the given +location+, in the same form as
577
- # +url_for+.
517
+ # Redirects the browser to the given +location+, in the same form as +url_for+.
578
518
  def redirect_to(location)
579
519
  assign 'window.location.href', @context.url_for(location)
580
520
  end
581
521
 
582
- # Calls the JavaScript +function+, optionally with the given
583
- # +arguments+.
584
- def call(function, *arguments)
585
- record "#{function}(#{arguments_for_call(arguments)})"
522
+ # Calls the JavaScript +function+, optionally with the given +arguments+.
523
+ #
524
+ # If a block is given, the block will be passed to a new JavaScriptGenerator;
525
+ # the resulting JavaScript code will then be wrapped inside <tt>function() { ... }</tt>
526
+ # and passed as the called function's final argument.
527
+ def call(function, *arguments, &block)
528
+ record "#{function}(#{arguments_for_call(arguments, block)})"
586
529
  end
587
530
 
588
531
  # Assigns the JavaScript +variable+ the given +value+.
@@ -633,12 +576,18 @@ module ActionView
633
576
  end
634
577
 
635
578
  private
579
+ def loop_on_multiple_args(method, ids)
580
+ record(ids.size>1 ?
581
+ "#{javascript_object_for(ids)}.each(#{method})" :
582
+ "#{method}(#{ids.first.to_json})")
583
+ end
584
+
636
585
  def page
637
586
  self
638
587
  end
639
588
 
640
589
  def record(line)
641
- returning line = "#{line.to_s.chomp.gsub /\;$/, ''};" do
590
+ returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do
642
591
  self << line
643
592
  end
644
593
  end
@@ -653,10 +602,16 @@ module ActionView
653
602
  object.respond_to?(:to_json) ? object.to_json : object.inspect
654
603
  end
655
604
 
656
- def arguments_for_call(arguments)
605
+ def arguments_for_call(arguments, block = nil)
606
+ arguments << block_to_function(block) if block
657
607
  arguments.map { |argument| javascript_object_for(argument) }.join ', '
658
608
  end
659
609
 
610
+ def block_to_function(block)
611
+ generator = self.class.new(@context, &block)
612
+ literal("function() { #{generator.to_s} }")
613
+ end
614
+
660
615
  def method_missing(method, *arguments)
661
616
  JavaScriptProxy.new(self, method.to_s.camelize)
662
617
  end
@@ -673,8 +628,11 @@ module ActionView
673
628
  # Works like update_page but wraps the generated JavaScript in a <script>
674
629
  # tag. Use this to include generated JavaScript in an ERb template.
675
630
  # See JavaScriptGenerator for more information.
676
- def update_page_tag(&block)
677
- javascript_tag update_page(&block)
631
+ #
632
+ # +html_options+ may be a hash of <script> attributes to be passed
633
+ # to ActionView::Helpers::JavaScriptHelper#javascript_tag.
634
+ def update_page_tag(html_options = {}, &block)
635
+ javascript_tag update_page(&block), html_options
678
636
  end
679
637
 
680
638
  protected
@@ -738,16 +696,16 @@ module ActionView
738
696
  end
739
697
 
740
698
  private
741
- def method_missing(method, *arguments)
699
+ def method_missing(method, *arguments, &block)
742
700
  if method.to_s =~ /(.*)=$/
743
701
  assign($1, arguments.first)
744
702
  else
745
- call("#{method.to_s.camelize(:lower)}", *arguments)
703
+ call("#{method.to_s.camelize(:lower)}", *arguments, &block)
746
704
  end
747
705
  end
748
706
 
749
- def call(function, *arguments)
750
- append_to_function_chain!("#{function}(#{@generator.send(:arguments_for_call, arguments)})")
707
+ def call(function, *arguments, &block)
708
+ append_to_function_chain!("#{function}(#{@generator.send(:arguments_for_call, arguments, block)})")
751
709
  self
752
710
  end
753
711
 
@@ -756,7 +714,7 @@ module ActionView
756
714
  end
757
715
 
758
716
  def function_chain
759
- @function_chain ||= @generator.instance_variable_get("@lines")
717
+ @function_chain ||= @generator.instance_variable_get(:@lines)
760
718
  end
761
719
 
762
720
  def append_to_function_chain!(call)
@@ -771,6 +729,21 @@ module ActionView
771
729
  super(generator, "$(#{id.to_json})")
772
730
  end
773
731
 
732
+ # Allows access of element attributes through +attribute+. Examples:
733
+ #
734
+ # page['foo']['style'] # => $('foo').style;
735
+ # page['foo']['style']['color'] # => $('blank_slate').style.color;
736
+ # page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
737
+ # page['foo']['style'].color = 'red' # => $('blank_slate').style.color = 'red';
738
+ def [](attribute)
739
+ append_to_function_chain!(attribute)
740
+ self
741
+ end
742
+
743
+ def []=(variable, value)
744
+ assign(variable, value)
745
+ end
746
+
774
747
  def replace_html(*options_for_render)
775
748
  call 'update', @generator.send(:render, *options_for_render)
776
749
  end
@@ -779,8 +752,8 @@ module ActionView
779
752
  call 'replace', @generator.send(:render, *options_for_render)
780
753
  end
781
754
 
782
- def reload
783
- replace :partial => @id.to_s
755
+ def reload(options_for_replace = {})
756
+ replace(options_for_replace.merge({ :partial => @id.to_s }))
784
757
  end
785
758
 
786
759
  end
@@ -811,8 +784,8 @@ module ActionView
811
784
  end
812
785
 
813
786
  class JavaScriptCollectionProxy < JavaScriptProxy #:nodoc:
814
- ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :find_all, :select, :max, :min, :partition, :reject, :sort_by]
815
- ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each]
787
+ ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :find_all, :select, :max, :min, :partition, :reject, :sort_by] unless defined? ENUMERABLE_METHODS_WITH_RETURN
788
+ ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each] unless defined? ENUMERABLE_METHODS
816
789
  attr_reader :generator
817
790
  delegate :arguments_for_call, :to => :generator
818
791
 
@@ -899,3 +872,5 @@ module ActionView
899
872
  end
900
873
  end
901
874
  end
875
+
876
+ require File.dirname(__FILE__) + '/javascript_helper'