actionpack 3.0.0.beta3 → 3.0.0.beta4

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 (83) hide show
  1. data/CHANGELOG +19 -0
  2. data/lib/abstract_controller.rb +1 -1
  3. data/lib/abstract_controller/asset_paths.rb +9 -0
  4. data/lib/abstract_controller/base.rb +5 -13
  5. data/lib/abstract_controller/callbacks.rb +1 -1
  6. data/lib/abstract_controller/helpers.rb +0 -1
  7. data/lib/abstract_controller/layouts.rb +3 -3
  8. data/lib/abstract_controller/logger.rb +1 -1
  9. data/lib/abstract_controller/rendering.rb +1 -0
  10. data/lib/action_controller/base.rb +5 -1
  11. data/lib/action_controller/caching.rb +2 -3
  12. data/lib/action_controller/caching/actions.rb +1 -1
  13. data/lib/action_controller/caching/fragments.rb +1 -1
  14. data/lib/action_controller/caching/pages.rb +8 -8
  15. data/lib/action_controller/caching/sweeping.rb +1 -0
  16. data/lib/action_controller/deprecated/base.rb +10 -36
  17. data/lib/action_controller/metal.rb +45 -3
  18. data/lib/action_controller/metal/compatibility.rb +2 -2
  19. data/lib/action_controller/metal/helpers.rb +3 -3
  20. data/lib/action_controller/metal/http_authentication.rb +158 -0
  21. data/lib/action_controller/metal/instrumentation.rb +5 -5
  22. data/lib/action_controller/metal/rack_delegation.rb +4 -4
  23. data/lib/action_controller/metal/renderers.rb +3 -3
  24. data/lib/action_controller/metal/request_forgery_protection.rb +45 -74
  25. data/lib/action_controller/metal/responder.rb +1 -1
  26. data/lib/action_controller/metal/url_for.rb +8 -0
  27. data/lib/action_controller/railtie.rb +26 -39
  28. data/lib/action_controller/test_case.rb +147 -135
  29. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -0
  30. data/lib/action_dispatch.rb +0 -1
  31. data/lib/action_dispatch/http/parameters.rb +2 -1
  32. data/lib/action_dispatch/http/request.rb +19 -7
  33. data/lib/action_dispatch/http/response.rb +3 -33
  34. data/lib/action_dispatch/middleware/cookies.rb +44 -10
  35. data/lib/action_dispatch/middleware/flash.rb +11 -1
  36. data/lib/action_dispatch/middleware/params_parser.rb +3 -1
  37. data/lib/action_dispatch/middleware/session/abstract_store.rb +47 -83
  38. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -165
  39. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +2 -2
  40. data/lib/action_dispatch/middleware/show_exceptions.rb +18 -12
  41. data/lib/action_dispatch/middleware/stack.rb +17 -67
  42. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +1 -1
  43. data/lib/action_dispatch/railtie.rb +0 -2
  44. data/lib/action_dispatch/routing/deprecated_mapper.rb +1 -0
  45. data/lib/action_dispatch/routing/mapper.rb +89 -23
  46. data/lib/action_dispatch/routing/route_set.rb +22 -16
  47. data/lib/action_dispatch/routing/url_for.rb +1 -1
  48. data/lib/action_dispatch/testing/assertions/routing.rb +1 -0
  49. data/lib/action_dispatch/testing/assertions/selector.rb +11 -7
  50. data/lib/action_dispatch/testing/test_process.rb +3 -2
  51. data/lib/action_pack/version.rb +1 -1
  52. data/lib/action_view.rb +5 -1
  53. data/lib/action_view/base.rb +10 -4
  54. data/lib/action_view/helpers/active_model_helper.rb +1 -8
  55. data/lib/action_view/helpers/asset_tag_helper.rb +7 -4
  56. data/lib/action_view/helpers/cache_helper.rb +14 -14
  57. data/lib/action_view/helpers/capture_helper.rb +25 -6
  58. data/lib/action_view/helpers/date_helper.rb +33 -44
  59. data/lib/action_view/helpers/form_helper.rb +47 -27
  60. data/lib/action_view/helpers/form_options_helper.rb +26 -3
  61. data/lib/action_view/helpers/form_tag_helper.rb +8 -4
  62. data/lib/action_view/helpers/number_helper.rb +5 -2
  63. data/lib/action_view/helpers/prototype_helper.rb +1 -1
  64. data/lib/action_view/helpers/tag_helper.rb +1 -1
  65. data/lib/action_view/helpers/text_helper.rb +55 -46
  66. data/lib/action_view/helpers/translation_helper.rb +19 -8
  67. data/lib/action_view/helpers/url_helper.rb +2 -4
  68. data/lib/action_view/locale/en.yml +14 -14
  69. data/lib/action_view/lookup_context.rb +52 -22
  70. data/lib/action_view/paths.rb +1 -0
  71. data/lib/action_view/render/layouts.rb +3 -12
  72. data/lib/action_view/render/partials.rb +21 -10
  73. data/lib/action_view/render/rendering.rb +1 -1
  74. data/lib/action_view/template.rb +172 -26
  75. data/lib/action_view/template/error.rb +25 -27
  76. data/lib/action_view/template/handlers.rb +1 -1
  77. data/lib/action_view/template/handlers/erb.rb +92 -45
  78. data/lib/action_view/template/resolver.rb +4 -1
  79. data/lib/action_view/test_case.rb +105 -72
  80. data/lib/action_view/testing/resolvers.rb +43 -0
  81. metadata +62 -20
  82. data/lib/abstract_controller/assigns.rb +0 -21
  83. data/lib/action_dispatch/middleware/cascade.rb +0 -29
@@ -573,8 +573,19 @@ module ActionView
573
573
  # label(:post, :privacy, "Public Post", :value => "public")
574
574
  # # => <label for="post_privacy_public">Public Post</label>
575
575
  #
576
- def label(object_name, method, text = nil, options = {})
577
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options)
576
+ # label(:post, :terms) do
577
+ # 'Accept <a href="/terms">Terms</a>.'
578
+ # end
579
+ def label(object_name, method, content_or_options = nil, options = nil, &block)
580
+ if block_given?
581
+ options = content_or_options if content_or_options.is_a?(Hash)
582
+ text = nil
583
+ else
584
+ text = content_or_options
585
+ end
586
+
587
+ options ||= {}
588
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block)
578
589
  end
579
590
 
580
591
  # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -823,7 +834,7 @@ module ActionView
823
834
 
824
835
  module InstanceTagMethods #:nodoc:
825
836
  extend ActiveSupport::Concern
826
- include Helpers::TagHelper, Helpers::FormTagHelper
837
+ include Helpers::CaptureHelper, Context, Helpers::TagHelper, Helpers::FormTagHelper
827
838
 
828
839
  attr_reader :method_name, :object_name
829
840
 
@@ -844,28 +855,38 @@ module ActionView
844
855
  end
845
856
  end
846
857
 
847
- def to_label_tag(text = nil, options = {})
858
+ def to_label_tag(text = nil, options = {}, &block)
848
859
  options = options.stringify_keys
849
860
  tag_value = options.delete("value")
850
861
  name_and_id = options.dup
851
- name_and_id["id"] = name_and_id["for"]
862
+
863
+ if name_and_id["for"]
864
+ name_and_id["id"] = name_and_id["for"]
865
+ else
866
+ name_and_id.delete("id")
867
+ end
868
+
852
869
  add_default_name_and_id_for_value(tag_value, name_and_id)
853
870
  options.delete("index")
854
871
  options["for"] ||= name_and_id["id"]
855
872
 
856
- content = if text.blank?
857
- I18n.t("helpers.label.#{object_name}.#{method_name}", :default => "").presence
873
+ if block_given?
874
+ label_tag(name_and_id["id"], options, &block)
858
875
  else
859
- text.to_s
860
- end
876
+ content = if text.blank?
877
+ I18n.t("helpers.label.#{object_name}.#{method_name}", :default => "").presence
878
+ else
879
+ text.to_s
880
+ end
861
881
 
862
- content ||= if object && object.class.respond_to?(:human_attribute_name)
863
- object.class.human_attribute_name(method_name)
864
- end
882
+ content ||= if object && object.class.respond_to?(:human_attribute_name)
883
+ object.class.human_attribute_name(method_name)
884
+ end
865
885
 
866
- content ||= method_name.humanize
886
+ content ||= method_name.humanize
867
887
 
868
- label_tag(name_and_id["id"], content, options)
888
+ label_tag(name_and_id["id"], content, options)
889
+ end
869
890
  end
870
891
 
871
892
  def to_input_field_tag(field_type, options = {})
@@ -1012,7 +1033,7 @@ module ActionView
1012
1033
  pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
1013
1034
  specified_id = options["id"]
1014
1035
  add_default_name_and_id(options)
1015
- options["id"] += "_#{pretty_tag_value}" unless specified_id
1036
+ options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present?
1016
1037
  else
1017
1038
  add_default_name_and_id(options)
1018
1039
  end
@@ -1021,14 +1042,14 @@ module ActionView
1021
1042
  def add_default_name_and_id(options)
1022
1043
  if options.has_key?("index")
1023
1044
  options["name"] ||= tag_name_with_index(options["index"])
1024
- options["id"] ||= tag_id_with_index(options["index"])
1045
+ options["id"] = options.fetch("id", tag_id_with_index(options["index"]))
1025
1046
  options.delete("index")
1026
1047
  elsif defined?(@auto_index)
1027
1048
  options["name"] ||= tag_name_with_index(@auto_index)
1028
- options["id"] ||= tag_id_with_index(@auto_index)
1049
+ options["id"] = options.fetch("id", tag_id_with_index(@auto_index))
1029
1050
  else
1030
1051
  options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '')
1031
- options["id"] ||= tag_id
1052
+ options["id"] = options.fetch("id", tag_id)
1032
1053
  end
1033
1054
  end
1034
1055
 
@@ -1090,7 +1111,7 @@ module ActionView
1090
1111
  end
1091
1112
 
1092
1113
  (field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector|
1093
- src, line = <<-end_src, __LINE__ + 1
1114
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
1094
1115
  def #{selector}(method, options = {}) # def text_field(method, options = {})
1095
1116
  @template.send( # @template.send(
1096
1117
  #{selector.inspect}, # "text_field",
@@ -1098,8 +1119,7 @@ module ActionView
1098
1119
  method, # method,
1099
1120
  objectify_options(options)) # objectify_options(options))
1100
1121
  end # end
1101
- end_src
1102
- class_eval src, __FILE__, line
1122
+ RUBY_EVAL
1103
1123
  end
1104
1124
 
1105
1125
  def fields_for(record_or_name_or_array, *args, &block)
@@ -1137,8 +1157,8 @@ module ActionView
1137
1157
  @template.fields_for(name, *args, &block)
1138
1158
  end
1139
1159
 
1140
- def label(method, text = nil, options = {})
1141
- @template.label(@object_name, method, text, objectify_options(options))
1160
+ def label(method, text = nil, options = {}, &block)
1161
+ @template.label(@object_name, method, text, objectify_options(options), &block)
1142
1162
  end
1143
1163
 
1144
1164
  def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
@@ -1165,13 +1185,13 @@ module ActionView
1165
1185
  # submit button label, otherwise, it uses "Update Post".
1166
1186
  #
1167
1187
  # Those labels can be customized using I18n, under the helpers.submit key and accept
1168
- # the {{model}} as translation interpolation:
1188
+ # the %{model} as translation interpolation:
1169
1189
  #
1170
1190
  # en:
1171
1191
  # helpers:
1172
1192
  # submit:
1173
- # create: "Create a {{model}}"
1174
- # update: "Confirm changes to {{model}}"
1193
+ # create: "Create a %{model}"
1194
+ # update: "Confirm changes to %{model}"
1175
1195
  #
1176
1196
  # It also searches for a key specific for the given object:
1177
1197
  #
@@ -1179,7 +1199,7 @@ module ActionView
1179
1199
  # helpers:
1180
1200
  # submit:
1181
1201
  # post:
1182
- # create: "Add {{model}}"
1202
+ # create: "Add %{model}"
1183
1203
  #
1184
1204
  def submit(value=nil, options={})
1185
1205
  value, options = nil, value if value.is_a?(Hash)
@@ -270,6 +270,15 @@ module ActionView
270
270
  # options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
271
271
  # <option selected="selected">VISA</option>\n<option>MasterCard</option>\n<option selected="selected">Discover</option>
272
272
  #
273
+ # You can optionally provide html attributes as the last element of the array.
274
+ #
275
+ # Examples:
276
+ # options_for_select([ "Denmark", ["USA", {:class=>'bold'}], "Sweden" ], ["USA", "Sweden"])
277
+ # <option value="Denmark">Denmark</option>\n<option value="USA" class="bold" selected="selected">USA</option>\n<option value="Sweden" selected="selected">Sweden</option>
278
+ #
279
+ # options_for_select([["Dollar", "$", {:class=>"bold"}], ["Kroner", "DKK", {:onclick => "alert('HI');"}]])
280
+ # <option value="$" class="bold">Dollar</option>\n<option value="DKK" onclick="alert('HI');">Kroner</option>
281
+ #
273
282
  # If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
274
283
  # or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
275
284
  #
@@ -291,10 +300,11 @@ module ActionView
291
300
  selected, disabled = extract_selected_and_disabled(selected)
292
301
 
293
302
  options_for_select = container.inject([]) do |options, element|
303
+ html_attributes = option_html_attributes(element)
294
304
  text, value = option_text_and_value(element)
295
305
  selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
296
306
  disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
297
- options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}>#{html_escape(text.to_s)}</option>)
307
+ options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{html_escape(text.to_s)}</option>)
298
308
  end
299
309
 
300
310
  options_for_select.join("\n").html_safe
@@ -444,7 +454,7 @@ module ActionView
444
454
  body << content_tag(:optgroup, options_for_select(group[1], selected_key), :label => group[0])
445
455
  end
446
456
 
447
- body
457
+ body.html_safe
448
458
  end
449
459
 
450
460
  # Returns a string of option tags for pretty much any time zone in the
@@ -486,9 +496,22 @@ module ActionView
486
496
  end
487
497
 
488
498
  private
499
+ def option_html_attributes(element)
500
+ return "" unless Array === element
501
+ html_attributes = []
502
+ element.select { |e| Hash === e }.reduce({}, :merge).each do |k, v|
503
+ html_attributes << " #{k}=\"#{html_escape(v.to_s)}\""
504
+ end
505
+ html_attributes.join
506
+ end
507
+
489
508
  def option_text_and_value(option)
490
509
  # Options are [text, value] pairs or strings used for both.
491
- if !option.is_a?(String) and option.respond_to?(:first) and option.respond_to?(:last)
510
+ case
511
+ when Array === option
512
+ option = option.reject { |e| Hash === e }
513
+ [option.first, option.last]
514
+ when !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
492
515
  [option.first, option.last]
493
516
  else
494
517
  [option, option]
@@ -142,7 +142,7 @@ module ActionView
142
142
  tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
143
143
  end
144
144
 
145
- # Creates a label field
145
+ # Creates a label element. Accepts a block.
146
146
  #
147
147
  # ==== Options
148
148
  # * Creates standard HTML attributes for the tag.
@@ -156,8 +156,12 @@ module ActionView
156
156
  #
157
157
  # label_tag 'name', nil, :class => 'small_label'
158
158
  # # => <label for="name" class="small_label">Name</label>
159
- def label_tag(name, text = nil, options = {})
160
- content_tag :label, text || name.to_s.humanize, { "for" => sanitize_to_id(name) }.update(options.stringify_keys)
159
+ def label_tag(name = nil, content_or_options = nil, options = nil, &block)
160
+ options = content_or_options if block_given? && content_or_options.is_a?(Hash)
161
+ options ||= {}
162
+ options.stringify_keys!
163
+ options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
164
+ content_tag :label, content_or_options || name.to_s.humanize, options, &block
161
165
  end
162
166
 
163
167
  # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
@@ -289,7 +293,7 @@ module ActionView
289
293
  escape = options.key?("escape") ? options.delete("escape") : true
290
294
  content = html_escape(content) if escape
291
295
 
292
- content_tag :textarea, content.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
296
+ content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
293
297
  end
294
298
 
295
299
  # Creates a check box form input tag.
@@ -13,6 +13,9 @@ module ActionView
13
13
  # unchanged if can't be converted into a valid number.
14
14
  module NumberHelper
15
15
 
16
+ DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :unit => "$", :separator => ".", :delimiter => ",",
17
+ :precision => 2, :significant => false, :strip_insignificant_zeros => false }
18
+
16
19
  # Raised when argument +number+ param given to the helpers is invalid and
17
20
  # the option :raise is set to +true+.
18
21
  class InvalidNumberError < StandardError
@@ -104,9 +107,9 @@ module ActionView
104
107
 
105
108
  defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
106
109
  currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :default => {})
107
- defaults = defaults.merge(currency)
108
110
 
109
- options = options.reverse_merge(defaults)
111
+ defaults = DEFAULT_CURRENCY_VALUES.merge(defaults).merge!(currency)
112
+ options = defaults.merge!(options)
110
113
 
111
114
  unit = options.delete(:unit)
112
115
  format = options.delete(:format)
@@ -767,7 +767,7 @@ module ActionView
767
767
  end
768
768
 
769
769
  def grep(variable, pattern, &block)
770
- enumerate :grep, :variable => variable, :return => true, :method_args => [pattern], :yield_args => %w(value index), &block
770
+ enumerate :grep, :variable => variable, :return => true, :method_args => [::ActiveSupport::JSON::Variable.new(pattern.inspect)], :yield_args => %w(value index), &block
771
771
  end
772
772
 
773
773
  def in_groups_of(variable, number, fill_with = nil)
@@ -110,7 +110,7 @@ module ActionView
110
110
 
111
111
  def content_tag_string(name, content, options, escape = true)
112
112
  tag_options = tag_options(options, escape) if options
113
- "<#{name}#{tag_options}>#{ERB::Util.h(content)}</#{name}>".html_safe
113
+ "<#{name}#{tag_options}>#{escape ? ERB::Util.h(content) : content}</#{name}>".html_safe
114
114
  end
115
115
 
116
116
  def tag_options(options, escape = true)
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/object/blank'
2
+ require 'active_support/core_ext/string/filters'
2
3
  require 'action_view/helpers/tag_helper'
3
4
 
4
5
  module ActionView
@@ -42,28 +43,25 @@ module ActionView
42
43
  # ==== Examples
43
44
  #
44
45
  # truncate("Once upon a time in a world far far away")
45
- # # => Once upon a time in a world...
46
+ # # => "Once upon a time in a world..."
46
47
  #
47
- # truncate("Once upon a time in a world far far away", :separator => ' ')
48
- # # => Once upon a time in a world...
48
+ # truncate("Once upon a time in a world far far away", :length => 17)
49
+ # # => "Once upon a ti..."
49
50
  #
50
- # truncate("Once upon a time in a world far far away", :length => 14)
51
- # # => Once upon a...
51
+ # truncate("Once upon a time in a world far far away", :lenght => 17, :separator => ' ')
52
+ # # => "Once upon a..."
52
53
  #
53
- # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)")
54
- # # => And they found t(clipped)
55
- #
56
- # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25)
57
- # # => And they f... (continued)
54
+ # truncate("And they found that many people were sleeping better.", :length => 25, :omission => '... (continued)')
55
+ # # => "And they f... (continued)"
58
56
  #
59
57
  # You can still use <tt>truncate</tt> with the old API that accepts the
60
58
  # +length+ as its optional second and the +ellipsis+ as its
61
59
  # optional third parameter:
62
60
  # truncate("Once upon a time in a world far far away", 14)
63
- # # => Once upon a...
61
+ # # => "Once upon a..."
64
62
  #
65
63
  # truncate("And they found that many people were sleeping better.", 25, "... (continued)")
66
- # # => And they f... (continued)
64
+ # # => "And they f... (continued)"
67
65
  def truncate(text, *args)
68
66
  options = args.extract_options!
69
67
  unless args.empty?
@@ -73,14 +71,11 @@ module ActionView
73
71
  options[:length] = args[0] || 30
74
72
  options[:omission] = args[1] || "..."
75
73
  end
76
- options.reverse_merge!(:length => 30, :omission => "...")
77
74
 
78
- if text
79
- l = options[:length] - options[:omission].mb_chars.length
80
- chars = text.mb_chars
81
- stop = options[:separator] ? (chars.rindex(options[:separator].mb_chars, l) || l) : l
82
- (chars.length > options[:length] ? chars[0...stop] + options[:omission] : text).to_s
83
- end
75
+ options.reverse_merge!(:length => 30)
76
+
77
+ text = sanitize(text) unless text.html_safe? || options[:safe]
78
+ text.truncate(options.delete(:length), options) if text
84
79
  end
85
80
 
86
81
  # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
@@ -111,6 +106,7 @@ module ActionView
111
106
  end
112
107
  options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
113
108
 
109
+ text = sanitize(text) unless text.html_safe? || options[:safe]
114
110
  if text.blank? || phrases.blank?
115
111
  text
116
112
  else
@@ -250,13 +246,14 @@ module ActionView
250
246
  #
251
247
  def textilize(text, *options)
252
248
  options ||= [:hard_breaks]
249
+ text = sanitize(text) unless text.html_safe? || options.delete(:safe)
253
250
 
254
251
  if text.blank?
255
252
  ""
256
253
  else
257
254
  textilized = RedCloth.new(text, options)
258
255
  textilized.to_html
259
- end
256
+ end.html_safe
260
257
  end
261
258
 
262
259
  # Returns the text with all the Textile codes turned into HTML tags,
@@ -277,8 +274,8 @@ module ActionView
277
274
  #
278
275
  # textilize_without_paragraph("Visit the Rails website "here":http://www.rubyonrails.org/.)
279
276
  # # => "Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>."
280
- def textilize_without_paragraph(text)
281
- textiled = textilize(text)
277
+ def textilize_without_paragraph(text, *options)
278
+ textiled = textilize(text, *options)
282
279
  if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
283
280
  if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
284
281
  return textiled
@@ -301,8 +298,9 @@ module ActionView
301
298
  #
302
299
  # markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
303
300
  # # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
304
- def markdown(text)
305
- text.blank? ? "" : BlueCloth.new(text).to_html
301
+ def markdown(text, *options)
302
+ text = sanitize(text) unless text.html_safe? || options.delete(:safe)
303
+ (text.blank? ? "" : BlueCloth.new(text).to_html).html_safe
306
304
  end
307
305
 
308
306
  # Returns +text+ transformed into HTML using simple formatting rules.
@@ -326,14 +324,15 @@ module ActionView
326
324
  #
327
325
  # simple_format("Look ma! A class!", :class => 'description')
328
326
  # # => "<p class='description'>Look ma! A class!</p>"
329
- def simple_format(text, html_options={})
327
+ def simple_format(text, html_options={}, options={})
328
+ text = '' if text.nil?
330
329
  start_tag = tag('p', html_options, true)
331
- text = h(text)
330
+ text = sanitize(text) unless text.html_safe? || options[:safe]
332
331
  text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
333
332
  text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph
334
333
  text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
335
334
  text.insert 0, start_tag
336
- text.safe_concat("</p>")
335
+ text.html_safe.safe_concat("</p>")
337
336
  end
338
337
 
339
338
  # Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
@@ -374,7 +373,7 @@ module ActionView
374
373
  # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
375
374
  # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
376
375
  def auto_link(text, *args, &block)#link = :all, html = {}, &block)
377
- return '' if text.blank?
376
+ return ''.html_safe if text.blank?
378
377
 
379
378
  options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
380
379
  unless args.empty?
@@ -384,9 +383,9 @@ module ActionView
384
383
  options.reverse_merge!(:link => :all, :html => {})
385
384
 
386
385
  case options[:link].to_sym
387
- when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), options[:html], &block)
386
+ when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], options, &block), options[:html], &block)
388
387
  when :email_addresses then auto_link_email_addresses(text, options[:html], &block)
389
- when :urls then auto_link_urls(text, options[:html], &block)
388
+ when :urls then auto_link_urls(text, options[:html], options, &block)
390
389
  end
391
390
  end
392
391
 
@@ -537,22 +536,26 @@ module ActionView
537
536
  end
538
537
 
539
538
  AUTO_LINK_RE = %r{
540
- ( https?:// | www\. )
539
+ (?: ([\w+.:-]+:)// | www\. )
541
540
  [^\s<]+
542
- }x unless const_defined?(:AUTO_LINK_RE)
541
+ }x
542
+
543
+ # regexps for determining context, used high-volume
544
+ AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
545
+
546
+ AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/
543
547
 
544
548
  BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
545
549
 
546
550
  # Turns all urls into clickable links. If a block is given, each url
547
551
  # is yielded and the result is used as the link text.
548
- def auto_link_urls(text, html_options = {})
552
+ def auto_link_urls(text, html_options = {}, options = {})
549
553
  link_attributes = html_options.stringify_keys
550
554
  text.gsub(AUTO_LINK_RE) do
551
- href = $&
555
+ scheme, href = $1, $&
552
556
  punctuation = []
553
- left, right = $`, $'
554
- # detect already linked URLs and URLs in the middle of a tag
555
- if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
557
+
558
+ if auto_linked?($`, $')
556
559
  # do not change string; URL is already linked
557
560
  href
558
561
  else
@@ -566,28 +569,34 @@ module ActionView
566
569
  end
567
570
 
568
571
  link_text = block_given?? yield(href) : href
569
- href = 'http://' + href unless href =~ %r{^[a-z]+://}i
572
+ href = 'http://' + href unless scheme
570
573
 
571
- content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('')
574
+ content_tag(:a, link_text, link_attributes.merge('href' => href), !(options[:safe] || text.html_safe?)) + punctuation.reverse.join('')
572
575
  end
573
- end
576
+ end.html_safe
574
577
  end
575
578
 
576
579
  # Turns all email addresses into clickable links. If a block is given,
577
580
  # each email is yielded and the result is used as the link text.
578
- def auto_link_email_addresses(text, html_options = {})
579
- body = text.dup
580
- text.gsub(/([\w\.!#\$%\-+]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
581
- text = $1
581
+ def auto_link_email_addresses(text, html_options = {}, options = {})
582
+ text.gsub(AUTO_EMAIL_RE) do
583
+ text = $&
582
584
 
583
- if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
584
- text
585
+ if auto_linked?($`, $')
586
+ text.html_safe
585
587
  else
586
588
  display_text = (block_given?) ? yield(text) : text
589
+ display_text = sanitize(display_text) unless options[:safe]
587
590
  mail_to text, display_text, html_options
588
591
  end
589
592
  end
590
593
  end
594
+
595
+ # Detects already linked context or position in the middle of a tag
596
+ def auto_linked?(left, right)
597
+ (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
598
+ (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
599
+ end
591
600
  end
592
601
  end
593
602
  end