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.
- data/CHANGELOG +19 -0
- data/lib/abstract_controller.rb +1 -1
- data/lib/abstract_controller/asset_paths.rb +9 -0
- data/lib/abstract_controller/base.rb +5 -13
- data/lib/abstract_controller/callbacks.rb +1 -1
- data/lib/abstract_controller/helpers.rb +0 -1
- data/lib/abstract_controller/layouts.rb +3 -3
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/rendering.rb +1 -0
- data/lib/action_controller/base.rb +5 -1
- data/lib/action_controller/caching.rb +2 -3
- data/lib/action_controller/caching/actions.rb +1 -1
- data/lib/action_controller/caching/fragments.rb +1 -1
- data/lib/action_controller/caching/pages.rb +8 -8
- data/lib/action_controller/caching/sweeping.rb +1 -0
- data/lib/action_controller/deprecated/base.rb +10 -36
- data/lib/action_controller/metal.rb +45 -3
- data/lib/action_controller/metal/compatibility.rb +2 -2
- data/lib/action_controller/metal/helpers.rb +3 -3
- data/lib/action_controller/metal/http_authentication.rb +158 -0
- data/lib/action_controller/metal/instrumentation.rb +5 -5
- data/lib/action_controller/metal/rack_delegation.rb +4 -4
- data/lib/action_controller/metal/renderers.rb +3 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +45 -74
- data/lib/action_controller/metal/responder.rb +1 -1
- data/lib/action_controller/metal/url_for.rb +8 -0
- data/lib/action_controller/railtie.rb +26 -39
- data/lib/action_controller/test_case.rb +147 -135
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -0
- data/lib/action_dispatch.rb +0 -1
- data/lib/action_dispatch/http/parameters.rb +2 -1
- data/lib/action_dispatch/http/request.rb +19 -7
- data/lib/action_dispatch/http/response.rb +3 -33
- data/lib/action_dispatch/middleware/cookies.rb +44 -10
- data/lib/action_dispatch/middleware/flash.rb +11 -1
- data/lib/action_dispatch/middleware/params_parser.rb +3 -1
- data/lib/action_dispatch/middleware/session/abstract_store.rb +47 -83
- data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -165
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +2 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +18 -12
- data/lib/action_dispatch/middleware/stack.rb +17 -67
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +1 -1
- data/lib/action_dispatch/railtie.rb +0 -2
- data/lib/action_dispatch/routing/deprecated_mapper.rb +1 -0
- data/lib/action_dispatch/routing/mapper.rb +89 -23
- data/lib/action_dispatch/routing/route_set.rb +22 -16
- data/lib/action_dispatch/routing/url_for.rb +1 -1
- data/lib/action_dispatch/testing/assertions/routing.rb +1 -0
- data/lib/action_dispatch/testing/assertions/selector.rb +11 -7
- data/lib/action_dispatch/testing/test_process.rb +3 -2
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view.rb +5 -1
- data/lib/action_view/base.rb +10 -4
- data/lib/action_view/helpers/active_model_helper.rb +1 -8
- data/lib/action_view/helpers/asset_tag_helper.rb +7 -4
- data/lib/action_view/helpers/cache_helper.rb +14 -14
- data/lib/action_view/helpers/capture_helper.rb +25 -6
- data/lib/action_view/helpers/date_helper.rb +33 -44
- data/lib/action_view/helpers/form_helper.rb +47 -27
- data/lib/action_view/helpers/form_options_helper.rb +26 -3
- data/lib/action_view/helpers/form_tag_helper.rb +8 -4
- data/lib/action_view/helpers/number_helper.rb +5 -2
- data/lib/action_view/helpers/prototype_helper.rb +1 -1
- data/lib/action_view/helpers/tag_helper.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +55 -46
- data/lib/action_view/helpers/translation_helper.rb +19 -8
- data/lib/action_view/helpers/url_helper.rb +2 -4
- data/lib/action_view/locale/en.yml +14 -14
- data/lib/action_view/lookup_context.rb +52 -22
- data/lib/action_view/paths.rb +1 -0
- data/lib/action_view/render/layouts.rb +3 -12
- data/lib/action_view/render/partials.rb +21 -10
- data/lib/action_view/render/rendering.rb +1 -1
- data/lib/action_view/template.rb +172 -26
- data/lib/action_view/template/error.rb +25 -27
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +92 -45
- data/lib/action_view/template/resolver.rb +4 -1
- data/lib/action_view/test_case.rb +105 -72
- data/lib/action_view/testing/resolvers.rb +43 -0
- metadata +62 -20
- data/lib/abstract_controller/assigns.rb +0 -21
- 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
|
-
|
577
|
-
|
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
|
-
|
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
|
-
|
857
|
-
|
873
|
+
if block_given?
|
874
|
+
label_tag(name_and_id["id"], options, &block)
|
858
875
|
else
|
859
|
-
text.
|
860
|
-
|
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
|
-
|
863
|
-
|
864
|
-
|
882
|
+
content ||= if object && object.class.respond_to?(:human_attribute_name)
|
883
|
+
object.class.human_attribute_name(method_name)
|
884
|
+
end
|
865
885
|
|
866
|
-
|
886
|
+
content ||= method_name.humanize
|
867
887
|
|
868
|
-
|
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}"
|
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"]
|
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"]
|
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"]
|
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
|
-
|
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
|
-
|
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 {
|
1188
|
+
# the %{model} as translation interpolation:
|
1169
1189
|
#
|
1170
1190
|
# en:
|
1171
1191
|
# helpers:
|
1172
1192
|
# submit:
|
1173
|
-
# create: "Create a {
|
1174
|
-
# update: "Confirm changes to {
|
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 {
|
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
|
-
|
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
|
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,
|
160
|
-
|
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
|
-
|
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", :
|
48
|
-
# # => Once upon a
|
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", :
|
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,
|
54
|
-
# # => And they
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
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 =
|
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
|
-
(
|
539
|
+
(?: ([\w+.:-]+:)// | www\. )
|
541
540
|
[^\s<]+
|
542
|
-
}x
|
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
|
-
|
554
|
-
|
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
|
572
|
+
href = 'http://' + href unless scheme
|
570
573
|
|
571
|
-
content_tag(:a,
|
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
|
-
|
580
|
-
|
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
|
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
|