actionpack 2.3.4 → 2.3.5

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 (74) hide show
  1. data/CHANGELOG +12 -0
  2. data/Rakefile +1 -1
  3. data/lib/action_controller.rb +3 -1
  4. data/lib/action_controller/assertions/dom_assertions.rb +19 -3
  5. data/lib/action_controller/assertions/selector_assertions.rb +16 -10
  6. data/lib/action_controller/base.rb +1 -1
  7. data/lib/action_controller/caching.rb +1 -0
  8. data/lib/action_controller/cookies.rb +2 -1
  9. data/lib/action_controller/http_authentication.rb +3 -2
  10. data/lib/action_controller/integration.rb +15 -3
  11. data/lib/action_controller/layout.rb +6 -1
  12. data/lib/action_controller/middlewares.rb +2 -0
  13. data/lib/action_controller/polymorphic_routes.rb +6 -21
  14. data/lib/action_controller/rack_lint_patch.rb +36 -0
  15. data/lib/action_controller/request_forgery_protection.rb +6 -2
  16. data/lib/action_controller/response.rb +2 -1
  17. data/lib/action_controller/string_coercion.rb +29 -0
  18. data/lib/action_controller/test_case.rb +6 -1
  19. data/lib/action_controller/test_process.rb +1 -1
  20. data/lib/action_controller/translation.rb +2 -2
  21. data/lib/action_controller/uploaded_file.rb +2 -2
  22. data/lib/action_controller/vendor/html-scanner/html/node.rb +1 -1
  23. data/lib/action_pack/version.rb +1 -1
  24. data/lib/action_view.rb +3 -3
  25. data/lib/action_view/base.rb +6 -1
  26. data/lib/action_view/erb/util.rb +6 -0
  27. data/lib/action_view/helpers.rb +2 -0
  28. data/lib/action_view/helpers/active_record_helper.rb +3 -3
  29. data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
  30. data/lib/action_view/helpers/capture_helper.rb +2 -2
  31. data/lib/action_view/helpers/date_helper.rb +27 -15
  32. data/lib/action_view/helpers/form_helper.rb +32 -11
  33. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  34. data/lib/action_view/helpers/form_tag_helper.rb +3 -3
  35. data/lib/action_view/helpers/number_helper.rb +6 -1
  36. data/lib/action_view/helpers/prototype_helper.rb +1 -1
  37. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +10 -2
  39. data/lib/action_view/helpers/tag_helper.rb +4 -4
  40. data/lib/action_view/helpers/translation_helper.rb +1 -1
  41. data/lib/action_view/helpers/url_helper.rb +3 -3
  42. data/lib/action_view/locale/en.yml +3 -0
  43. data/lib/action_view/partials.rb +1 -1
  44. data/lib/action_view/safe_buffer.rb +28 -0
  45. data/lib/action_view/template.rb +8 -2
  46. data/lib/action_view/test_case.rb +102 -27
  47. data/lib/actionpack.rb +1 -0
  48. data/test/controller/cookie_test.rb +7 -0
  49. data/test/controller/dom_assertions_test.rb +53 -0
  50. data/test/controller/filter_params_test.rb +1 -0
  51. data/test/controller/html-scanner/sanitizer_test.rb +1 -0
  52. data/test/controller/http_digest_authentication_test.rb +22 -0
  53. data/test/controller/integration_test.rb +38 -0
  54. data/test/controller/layout_test.rb +11 -0
  55. data/test/controller/polymorphic_routes_test.rb +4 -0
  56. data/test/controller/request_forgery_protection_test.rb +19 -1
  57. data/test/controller/routing_test.rb +23 -15
  58. data/test/controller/session/test_session_test.rb +2 -2
  59. data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
  60. data/test/fixtures/test/_from_helper.erb +1 -0
  61. data/test/template/active_record_helper_test.rb +1 -1
  62. data/test/template/asset_tag_helper_test.rb +12 -0
  63. data/test/template/benchmark_helper_test.rb +6 -6
  64. data/test/template/compiled_templates_test.rb +2 -1
  65. data/test/template/date_helper_i18n_test.rb +10 -9
  66. data/test/template/date_helper_test.rb +29 -13
  67. data/test/template/form_helper_test.rb +90 -10
  68. data/test/template/number_helper_test.rb +4 -0
  69. data/test/template/raw_output_helper_test.rb +21 -0
  70. data/test/template/sanitize_helper_test.rb +10 -1
  71. data/test/template/tag_helper_test.rb +1 -0
  72. data/test/view/safe_buffer_test.rb +36 -0
  73. data/test/view/test_case_test.rb +173 -5
  74. metadata +13 -4
@@ -105,6 +105,11 @@ module ActionController
105
105
  class TestCase < ActiveSupport::TestCase
106
106
  include TestProcess
107
107
 
108
+ def initialize(*args)
109
+ super
110
+ @controller = nil
111
+ end
112
+
108
113
  module Assertions
109
114
  %w(response selector tag dom routing model).each do |kind|
110
115
  include ActionController::Assertions.const_get("#{kind.camelize}Assertions")
@@ -195,7 +200,7 @@ module ActionController
195
200
  @controller.send(:initialize_current_url)
196
201
  end
197
202
  end
198
-
203
+
199
204
  # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
200
205
  def rescue_action_in_public!
201
206
  @request.remote_addr = '208.77.188.166' # example.com
@@ -91,7 +91,7 @@ module ActionController #:nodoc:
91
91
  @path || super()
92
92
  end
93
93
 
94
- def assign_parameters(controller_path, action, parameters)
94
+ def assign_parameters(controller_path, action, parameters = {})
95
95
  parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
96
96
  extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
97
97
  non_path_parameters = get? ? query_parameters : request_parameters
@@ -1,12 +1,12 @@
1
1
  module ActionController
2
2
  module Translation
3
3
  def translate(*args)
4
- I18n.translate *args
4
+ I18n.translate(*args)
5
5
  end
6
6
  alias :t :translate
7
7
 
8
8
  def localize(*args)
9
- I18n.localize *args
9
+ I18n.localize(*args)
10
10
  end
11
11
  alias :l :localize
12
12
  end
@@ -3,14 +3,14 @@ module ActionController
3
3
  def self.included(base)
4
4
  base.class_eval do
5
5
  attr_accessor :original_path, :content_type
6
- alias_method :local_path, :path
6
+ alias_method :local_path, :path if method_defined?(:path)
7
7
  end
8
8
  end
9
9
 
10
10
  def self.extended(object)
11
11
  object.class_eval do
12
12
  attr_accessor :original_path, :content_type
13
- alias_method :local_path, :path
13
+ alias_method :local_path, :path if method_defined?(:path)
14
14
  end
15
15
  end
16
16
 
@@ -162,7 +162,7 @@ module HTML #:nodoc:
162
162
  end
163
163
 
164
164
  closing = ( scanner.scan(/\//) ? :close : nil )
165
- return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
165
+ return Text.new(parent, line, pos, content) unless name = scanner.scan(/[-:\w\x00-\x09\x0b-\x0c\x0e-\x1f]+/)
166
166
  name.downcase!
167
167
 
168
168
  unless closing
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 2
4
4
  MINOR = 3
5
- TINY = 4
5
+ TINY = 5
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -49,10 +49,10 @@ module ActionView
49
49
  autoload :TemplateHandler, 'action_view/template_handler'
50
50
  autoload :TemplateHandlers, 'action_view/template_handlers'
51
51
  autoload :Helpers, 'action_view/helpers'
52
+ autoload :SafeBuffer, 'action_view/safe_buffer'
52
53
  end
53
54
 
54
- class ERB
55
- autoload :Util, 'action_view/erb/util'
56
- end
55
+ require 'action_view/erb/util'
56
+
57
57
 
58
58
  I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
@@ -187,13 +187,18 @@ module ActionView #:nodoc:
187
187
  @@cache_template_loading = nil
188
188
  cattr_accessor :cache_template_loading
189
189
 
190
+ # :nodoc:
191
+ def self.xss_safe?
192
+ false
193
+ end
194
+
190
195
  def self.cache_template_loading?
191
196
  ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
192
197
  end
193
198
 
194
199
  attr_internal :request
195
200
 
196
- delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
201
+ delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
197
202
  :flash, :logger, :action_name, :controller_name, :to => :controller
198
203
 
199
204
  module CompiledTemplates #:nodoc:
@@ -18,6 +18,12 @@ class ERB
18
18
  s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
19
19
  end
20
20
 
21
+ undef :h
22
+ alias h html_escape
23
+
24
+ module_function :html_escape
25
+ module_function :h
26
+
21
27
  # A utility method for escaping HTML entities in JSON strings.
22
28
  # This method is also aliased as <tt>j</tt>.
23
29
  #
@@ -14,6 +14,7 @@ module ActionView #:nodoc:
14
14
  autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper'
15
15
  autoload :NumberHelper, 'action_view/helpers/number_helper'
16
16
  autoload :PrototypeHelper, 'action_view/helpers/prototype_helper'
17
+ autoload :RawOutputHelper, 'action_view/helpers/raw_output_helper'
17
18
  autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper'
18
19
  autoload :RecordTagHelper, 'action_view/helpers/record_tag_helper'
19
20
  autoload :SanitizeHelper, 'action_view/helpers/sanitize_helper'
@@ -45,6 +46,7 @@ module ActionView #:nodoc:
45
46
  include JavaScriptHelper
46
47
  include NumberHelper
47
48
  include PrototypeHelper
49
+ include RawOutputHelper
48
50
  include RecordIdentificationHelper
49
51
  include RecordTagHelper
50
52
  include SanitizeHelper
@@ -3,7 +3,7 @@ require 'action_view/helpers/form_helper'
3
3
 
4
4
  module ActionView
5
5
  class Base
6
- @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }
6
+ @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe! }
7
7
  cattr_accessor :field_error_proc
8
8
  end
9
9
 
@@ -171,7 +171,7 @@ module ActionView
171
171
  options = params.extract_options!.symbolize_keys
172
172
 
173
173
  if object = options.delete(:object)
174
- objects = [object].flatten
174
+ objects = Array.wrap(object)
175
175
  else
176
176
  objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
177
177
  end
@@ -290,7 +290,7 @@ module ActionView
290
290
  end
291
291
 
292
292
  def error_wrapping(html_tag, has_error)
293
- has_error ? Base.field_error_proc.call(html_tag, self) : html_tag
293
+ has_error ? Base.field_error_proc.call(html_tag, self).html_safe! : html_tag
294
294
  end
295
295
 
296
296
  def error_message
@@ -285,7 +285,7 @@ module ActionView
285
285
  end
286
286
  javascript_src_tag(joined_javascript_name, options)
287
287
  else
288
- expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")
288
+ expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe!
289
289
  end
290
290
  end
291
291
 
@@ -434,7 +434,7 @@ module ActionView
434
434
  end
435
435
  stylesheet_tag(joined_stylesheet_name, options)
436
436
  else
437
- expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n")
437
+ expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe!
438
438
  end
439
439
  end
440
440
 
@@ -118,13 +118,13 @@ module ActionView
118
118
  def content_for(name, content = nil, &block)
119
119
  ivar = "@content_for_#{name}"
120
120
  content = capture(&block) if block_given?
121
- instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}")
121
+ instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}".html_safe!)
122
122
  nil
123
123
  end
124
124
 
125
125
  # Use an alternate output buffer for the duration of the block.
126
126
  # Defaults to a new empty string.
127
- def with_output_buffer(buf = '') #:nodoc:
127
+ def with_output_buffer(buf = "") #:nodoc:
128
128
  self.output_buffer, old_buffer = buf, output_buffer
129
129
  yield
130
130
  output_buffer
@@ -26,8 +26,10 @@ module ActionView
26
26
  # 47 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
27
27
  # 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month
28
28
  # 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
29
- # 1 yr <-> 2 yrs minus 1 secs # => about 1 year
30
- # 2 yrs <-> max time or date # => over [2..X] years
29
+ # 1 yr <-> 1 yr, 3 months # => about 1 year
30
+ # 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
31
+ # 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
32
+ # 2 yrs <-> max time or date # => (same rules as 1 yr)
31
33
  #
32
34
  # With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
33
35
  # 0-4 secs # => less than 5 seconds
@@ -43,17 +45,18 @@ module ActionView
43
45
  # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
44
46
  # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
45
47
  # distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds
46
- # distance_of_time_in_words(from_time, 3.years.from_now) # => over 3 years
48
+ # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
47
49
  # distance_of_time_in_words(from_time, from_time + 60.hours) # => about 3 days
48
50
  # distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute
49
51
  # distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
50
52
  # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
51
53
  # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
52
- # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => over 4 years
54
+ # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
55
+ # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
53
56
  #
54
57
  # to_time = Time.now + 6.years + 19.days
55
- # distance_of_time_in_words(from_time, to_time, true) # => over 6 years
56
- # distance_of_time_in_words(to_time, from_time, true) # => over 6 years
58
+ # distance_of_time_in_words(from_time, to_time, true) # => about 6 years
59
+ # distance_of_time_in_words(to_time, from_time, true) # => about 6 years
57
60
  # distance_of_time_in_words(Time.now, Time.now) # => less than a minute
58
61
  #
59
62
  def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
@@ -81,12 +84,21 @@ module ActionView
81
84
  when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
82
85
  when 45..89 then locale.t :about_x_hours, :count => 1
83
86
  when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
84
- when 1440..2879 then locale.t :x_days, :count => 1
85
- when 2880..43199 then locale.t :x_days, :count => (distance_in_minutes / 1440).round
87
+ when 1440..2529 then locale.t :x_days, :count => 1
88
+ when 2530..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
86
89
  when 43200..86399 then locale.t :about_x_months, :count => 1
87
- when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes / 43200).round
88
- when 525600..1051199 then locale.t :about_x_years, :count => 1
89
- else locale.t :over_x_years, :count => (distance_in_minutes / 525600).round
90
+ when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
91
+ else
92
+ distance_in_years = distance_in_minutes / 525600
93
+ minute_offset_for_leap_year = (distance_in_years / 4) * 1440
94
+ remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600)
95
+ if remainder < 131400
96
+ locale.t(:about_x_years, :count => distance_in_years)
97
+ elsif remainder < 394200
98
+ locale.t(:over_x_years, :count => distance_in_years)
99
+ else
100
+ locale.t(:almost_x_years, :count => distance_in_years + 1)
101
+ end
90
102
  end
91
103
  end
92
104
  end
@@ -904,15 +916,15 @@ module ActionView
904
916
 
905
917
  class InstanceTag #:nodoc:
906
918
  def to_date_select_tag(options = {}, html_options = {})
907
- datetime_selector(options, html_options).select_date
919
+ datetime_selector(options, html_options).select_date.html_safe!
908
920
  end
909
921
 
910
922
  def to_time_select_tag(options = {}, html_options = {})
911
- datetime_selector(options, html_options).select_time
923
+ datetime_selector(options, html_options).select_time.html_safe!
912
924
  end
913
925
 
914
926
  def to_datetime_select_tag(options = {}, html_options = {})
915
- datetime_selector(options, html_options).select_datetime
927
+ datetime_selector(options, html_options).select_datetime.html_safe!
916
928
  end
917
929
 
918
930
  private
@@ -923,7 +935,7 @@ module ActionView
923
935
  options[:field_name] = @method_name
924
936
  options[:include_position] = true
925
937
  options[:prefix] ||= @object_name
926
- options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
938
+ options[:index] = @auto_index if defined?(@auto_index) && @auto_index && !options.has_key?(:index)
927
939
  options[:datetime_separator] ||= ' &mdash; '
928
940
  options[:time_separator] ||= ' : '
929
941
 
@@ -280,7 +280,7 @@ module ActionView
280
280
 
281
281
  concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
282
282
  fields_for(object_name, *(args << options), &proc)
283
- concat('</form>')
283
+ concat('</form>'.html_safe!)
284
284
  end
285
285
 
286
286
  def apply_form_for_options!(object_or_array, options) #:nodoc:
@@ -445,6 +445,15 @@ module ActionView
445
445
  # <% end %>
446
446
  # <% end %>
447
447
  #
448
+ # Or a collection to be used:
449
+ #
450
+ # <% form_for @person, :url => { :action => "update" } do |person_form| %>
451
+ # ...
452
+ # <% person_form.fields_for :projects, @active_projects do |project_fields| %>
453
+ # Name: <%= project_fields.text_field :name %>
454
+ # <% end %>
455
+ # <% end %>
456
+ #
448
457
  # When projects is already an association on Person you can use
449
458
  # +accepts_nested_attributes_for+ to define the writer method for you:
450
459
  #
@@ -788,7 +797,7 @@ module ActionView
788
797
  add_default_name_and_id(options)
789
798
  hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
790
799
  checkbox = tag("input", options)
791
- hidden + checkbox
800
+ (hidden + checkbox).html_safe!
792
801
  end
793
802
 
794
803
  def to_boolean_select_tag(options = {})
@@ -930,7 +939,7 @@ module ActionView
930
939
  end
931
940
  end
932
941
 
933
- (field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
942
+ (field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector|
934
943
  src = <<-end_src
935
944
  def #{selector}(method, options = {}) # def text_field(method, options = {})
936
945
  @template.send( # @template.send(
@@ -989,6 +998,11 @@ module ActionView
989
998
  def radio_button(method, tag_value, options = {})
990
999
  @template.radio_button(@object_name, method, tag_value, objectify_options(options))
991
1000
  end
1001
+
1002
+ def hidden_field(method, options = {})
1003
+ @emitted_hidden_id = true if method == :id
1004
+ @template.hidden_field(@object_name, method, objectify_options(options))
1005
+ end
992
1006
 
993
1007
  def error_message_on(method, *args)
994
1008
  @template.error_message_on(@object, method, *args)
@@ -1002,6 +1016,10 @@ module ActionView
1002
1016
  @template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
1003
1017
  end
1004
1018
 
1019
+ def emitted_hidden_id?
1020
+ @emitted_hidden_id
1021
+ end
1022
+
1005
1023
  private
1006
1024
  def objectify_options(options)
1007
1025
  @default_options.merge(options.merge(:object => @object))
@@ -1013,18 +1031,21 @@ module ActionView
1013
1031
 
1014
1032
  def fields_for_with_nested_attributes(association_name, args, block)
1015
1033
  name = "#{object_name}[#{association_name}_attributes]"
1016
- association = @object.send(association_name)
1017
- explicit_object = args.first if args.first.respond_to?(:new_record?)
1034
+ association = args.first
1035
+
1036
+ if association.respond_to?(:new_record?)
1037
+ association = [association] if @object.send(association_name).is_a?(Array)
1038
+ elsif !association.is_a?(Array)
1039
+ association = @object.send(association_name)
1040
+ end
1018
1041
 
1019
1042
  if association.is_a?(Array)
1020
- children = explicit_object ? [explicit_object] : association
1021
1043
  explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash)
1022
-
1023
- children.map do |child|
1044
+ association.map do |child|
1024
1045
  fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block)
1025
1046
  end.join
1026
- else
1027
- fields_for_nested_model(name, explicit_object || association, args, block)
1047
+ elsif association
1048
+ fields_for_nested_model(name, association, args, block)
1028
1049
  end
1029
1050
  end
1030
1051
 
@@ -1033,8 +1054,8 @@ module ActionView
1033
1054
  @template.fields_for(name, object, *args, &block)
1034
1055
  else
1035
1056
  @template.fields_for(name, object, *args) do |builder|
1036
- @template.concat builder.hidden_field(:id)
1037
1057
  block.call(builder)
1058
+ @template.concat builder.hidden_field(:id) unless builder.emitted_hidden_id?
1038
1059
  end
1039
1060
  end
1040
1061
  end
@@ -296,7 +296,7 @@ module ActionView
296
296
  options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}>#{html_escape(text.to_s)}</option>)
297
297
  end
298
298
 
299
- options_for_select.join("\n")
299
+ options_for_select.join("\n").html_safe!
300
300
  end
301
301
 
302
302
  # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
@@ -432,7 +432,7 @@ module ActionView
432
432
  concat(tag(:fieldset, options, true))
433
433
  concat(content_tag(:legend, legend)) unless legend.blank?
434
434
  concat(content)
435
- concat("</fieldset>")
435
+ concat("</fieldset>".html_safe!)
436
436
  end
437
437
 
438
438
  private
@@ -459,14 +459,14 @@ module ActionView
459
459
 
460
460
  def form_tag_html(html_options)
461
461
  extra_tags = extra_tags_for_form(html_options)
462
- tag(:form, html_options, true) + extra_tags
462
+ (tag(:form, html_options, true) + extra_tags).html_safe!
463
463
  end
464
464
 
465
465
  def form_tag_in_block(html_options, &block)
466
466
  content = capture(&block)
467
467
  concat(form_tag_html(html_options))
468
468
  concat(content)
469
- concat("</form>")
469
+ concat("</form>".html_safe!)
470
470
  end
471
471
 
472
472
  def token_tag
@@ -246,6 +246,11 @@ module ActionView
246
246
  # number_to_human_size(483989, :precision => 0) # => 473 KB
247
247
  # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
248
248
  #
249
+ # Zeros after the decimal point are always stripped out, regardless of the
250
+ # specified precision:
251
+ # helper.number_to_human_size(1234567890123, :precision => 5) # => "1.12283 TB"
252
+ # helper.number_to_human_size(524288000, :precision=>5) # => "500 MB"
253
+ #
249
254
  # You can still use <tt>number_to_human_size</tt> with the old API that accepts the
250
255
  # +precision+ as its optional second parameter:
251
256
  # number_to_human_size(1234567, 2) # => 1.18 MB
@@ -291,7 +296,7 @@ module ActionView
291
296
  :precision => precision,
292
297
  :separator => separator,
293
298
  :delimiter => delimiter
294
- ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
299
+ ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
295
300
  storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
296
301
  rescue
297
302
  number