mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
For performance reasons, you can no longer call html_safe! on Strings. Instead, all Strings are always not html_safe?. Instead, you can get a SafeBuffer from a String by calling #html_safe, which will SafeBuffer.new(self).
* Additionally, instead of doing concat("</form>".html_safe), you can do safe_concat("</form>"), which will skip both the flag set, and the flag check. * For the first pass, I converted virtually all #html_safe!s to #html_safe, and the tests pass. A further optimization would be to try to use #safe_concat as much as possible, reducing the performance impact if we know up front that a String is safe.
This commit is contained in:
parent
1c83fd2eff
commit
4cbb9db0a5
31 changed files with 179 additions and 196 deletions
|
@ -56,7 +56,9 @@ module ActionView
|
|||
autoload :TestCase, 'action_view/test_case'
|
||||
end
|
||||
|
||||
require 'action_view/erb/util'
|
||||
require 'active_support/core_ext/string/output_safety'
|
||||
require 'action_view/base'
|
||||
|
||||
ActionView::SafeBuffer = ActiveSupport::SafeBuffer
|
||||
|
||||
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
|
||||
|
|
|
@ -284,7 +284,7 @@ module ActionView #:nodoc:
|
|||
@helpers = self.class.helpers || Module.new
|
||||
|
||||
@_controller = controller
|
||||
@_content_for = Hash.new {|h,k| h[k] = ActionView::SafeBuffer.new }
|
||||
@_content_for = Hash.new {|h,k| h[k] = ActiveSupport::SafeBuffer.new }
|
||||
@_virtual_path = nil
|
||||
self.view_paths = view_paths
|
||||
end
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
require 'erb'
|
||||
|
||||
class ERB
|
||||
module Util
|
||||
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' }
|
||||
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
|
||||
|
||||
# A utility method for escaping HTML tag characters.
|
||||
# This method is also aliased as <tt>h</tt>.
|
||||
#
|
||||
# In your ERb templates, use this method to escape any unsafe content. For example:
|
||||
# <%=h @person.name %>
|
||||
#
|
||||
# ==== Example:
|
||||
# puts html_escape("is a > 0 & a < 10?")
|
||||
# # => is a > 0 & a < 10?
|
||||
def html_escape(s)
|
||||
s = s.to_s
|
||||
if s.html_safe?
|
||||
s
|
||||
else
|
||||
s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe!
|
||||
end
|
||||
end
|
||||
|
||||
undef :h
|
||||
alias h html_escape
|
||||
|
||||
module_function :html_escape
|
||||
module_function :h
|
||||
|
||||
# A utility method for escaping HTML entities in JSON strings.
|
||||
# This method is also aliased as <tt>j</tt>.
|
||||
#
|
||||
# In your ERb templates, use this method to escape any HTML entities:
|
||||
# <%=j @person.to_json %>
|
||||
#
|
||||
# ==== Example:
|
||||
# puts json_escape("is a > 0 & a < 10?")
|
||||
# # => is a \u003E 0 \u0026 a \u003C 10?
|
||||
def json_escape(s)
|
||||
s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
|
||||
end
|
||||
|
||||
alias j json_escape
|
||||
module_function :j
|
||||
module_function :json_escape
|
||||
end
|
||||
end
|
|
@ -6,7 +6,7 @@ require 'active_support/core_ext/kernel/reporting'
|
|||
|
||||
module ActionView
|
||||
class Base
|
||||
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe! }
|
||||
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
|
||||
cattr_accessor :field_error_proc
|
||||
end
|
||||
|
||||
|
@ -86,12 +86,11 @@ module ActionView
|
|||
submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
|
||||
|
||||
contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
|
||||
contents << hidden_field(record_name, :id) unless record.new_record?
|
||||
contents << all_input_tags(record, record_name, options)
|
||||
contents.safe_concat hidden_field(record_name, :id) unless record.new_record?
|
||||
contents.safe_concat all_input_tags(record, record_name, options)
|
||||
yield contents if block_given?
|
||||
contents << submit_tag(submit_value)
|
||||
contents << '</form>'
|
||||
contents.html_safe!
|
||||
contents.safe_concat submit_tag(submit_value)
|
||||
contents.safe_concat('</form>')
|
||||
end
|
||||
|
||||
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
|
||||
|
|
|
@ -293,7 +293,7 @@ module ActionView
|
|||
else
|
||||
sources = expand_javascript_sources(sources, recursive)
|
||||
ensure_javascript_sources!(sources) if cache
|
||||
sources.collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe!
|
||||
sources.collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -444,7 +444,7 @@ module ActionView
|
|||
else
|
||||
sources = expand_stylesheet_sources(sources, recursive)
|
||||
ensure_stylesheet_sources!(sources) if cache
|
||||
sources.collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe!
|
||||
sources.collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -588,7 +588,7 @@ module ActionView
|
|||
|
||||
if sources.is_a?(Array)
|
||||
content_tag("video", options) do
|
||||
sources.map { |source| tag("source", :src => source) }.join.html_safe!
|
||||
sources.map { |source| tag("source", :src => source) }.join.html_safe
|
||||
end
|
||||
else
|
||||
options[:src] = path_to_video(sources)
|
||||
|
|
|
@ -616,7 +616,7 @@ module ActionView
|
|||
|
||||
build_selects_from_types(order)
|
||||
else
|
||||
"#{select_date}#{@options[:datetime_separator]}#{select_time}".html_safe!
|
||||
"#{select_date}#{@options[:datetime_separator]}#{select_time}".html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -835,7 +835,7 @@ module ActionView
|
|||
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
|
||||
select_html << select_options_as_html.to_s
|
||||
|
||||
(content_tag(:select, select_html, select_options) + "\n").html_safe!
|
||||
(content_tag(:select, select_html, select_options) + "\n").html_safe
|
||||
end
|
||||
|
||||
# Builds a prompt option tag with supplied options or from default options
|
||||
|
@ -865,7 +865,7 @@ module ActionView
|
|||
:id => input_id_from_type(type),
|
||||
:name => input_name_from_type(type),
|
||||
:value => value
|
||||
}) + "\n").html_safe!
|
||||
}) + "\n").html_safe
|
||||
end
|
||||
|
||||
# Returns the name attribute for the input tag
|
||||
|
@ -896,7 +896,7 @@ module ActionView
|
|||
separator = separator(type) unless type == order.first # don't add on last field
|
||||
select.insert(0, separator.to_s + send("select_#{type}").to_s)
|
||||
end
|
||||
select.html_safe!
|
||||
select.html_safe
|
||||
end
|
||||
|
||||
# Returns the separator for a given datetime component
|
||||
|
@ -916,15 +916,15 @@ module ActionView
|
|||
|
||||
class InstanceTag #:nodoc:
|
||||
def to_date_select_tag(options = {}, html_options = {})
|
||||
datetime_selector(options, html_options).select_date.html_safe!
|
||||
datetime_selector(options, html_options).select_date.html_safe
|
||||
end
|
||||
|
||||
def to_time_select_tag(options = {}, html_options = {})
|
||||
datetime_selector(options, html_options).select_time.html_safe!
|
||||
datetime_selector(options, html_options).select_time.html_safe
|
||||
end
|
||||
|
||||
def to_datetime_select_tag(options = {}, html_options = {})
|
||||
datetime_selector(options, html_options).select_datetime.html_safe!
|
||||
datetime_selector(options, html_options).select_datetime.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -27,10 +27,10 @@ module ActionView
|
|||
def debug(object)
|
||||
begin
|
||||
Marshal::dump(object)
|
||||
"<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", " ")}</pre>".html_safe!
|
||||
"<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", " ")}</pre>".html_safe
|
||||
rescue Exception => e # errors from Marshal or YAML
|
||||
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
|
||||
"<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe!
|
||||
"<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -311,7 +311,7 @@ module ActionView
|
|||
|
||||
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
|
||||
fields_for(object_name, *(args << options), &proc)
|
||||
concat('</form>'.html_safe!)
|
||||
safe_concat('</form>')
|
||||
end
|
||||
|
||||
def apply_form_for_options!(object_or_array, options) #:nodoc:
|
||||
|
@ -879,7 +879,7 @@ module ActionView
|
|||
end
|
||||
hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
|
||||
checkbox = tag("input", options)
|
||||
(hidden + checkbox).html_safe!
|
||||
(hidden + checkbox).html_safe
|
||||
end
|
||||
|
||||
def to_boolean_select_tag(options = {})
|
||||
|
|
|
@ -296,7 +296,7 @@ module ActionView
|
|||
options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}>#{html_escape(text.to_s)}</option>)
|
||||
end
|
||||
|
||||
options_for_select.join("\n").html_safe!
|
||||
options_for_select.join("\n").html_safe
|
||||
end
|
||||
|
||||
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
|
||||
|
|
|
@ -446,7 +446,7 @@ module ActionView
|
|||
concat(tag(:fieldset, options, true))
|
||||
concat(content_tag(:legend, legend)) unless legend.blank?
|
||||
concat(content)
|
||||
concat("</fieldset>".html_safe!)
|
||||
safe_concat("</fieldset>")
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -474,14 +474,14 @@ module ActionView
|
|||
|
||||
def form_tag_html(html_options)
|
||||
extra_tags = extra_tags_for_form(html_options)
|
||||
(tag(:form, html_options, true) + extra_tags).html_safe!
|
||||
(tag(:form, html_options, true) + extra_tags).html_safe
|
||||
end
|
||||
|
||||
def form_tag_in_block(html_options, &block)
|
||||
content = capture(&block)
|
||||
concat(form_tag_html(html_options))
|
||||
concat(content)
|
||||
concat("</form>".html_safe!)
|
||||
safe_concat("</form>")
|
||||
end
|
||||
|
||||
def token_tag
|
||||
|
|
|
@ -92,7 +92,7 @@ module ActionView
|
|||
:precision => precision,
|
||||
:delimiter => delimiter,
|
||||
:separator => separator)
|
||||
).gsub(/%u/, unit).html_safe!
|
||||
).gsub(/%u/, unit).html_safe
|
||||
rescue
|
||||
number
|
||||
end
|
||||
|
|
|
@ -610,7 +610,7 @@ module ActionView
|
|||
# page.hide 'spinner'
|
||||
# end
|
||||
def update_page(&block)
|
||||
JavaScriptGenerator.new(@template, &block).to_s.html_safe!
|
||||
JavaScriptGenerator.new(@template, &block).to_s.html_safe
|
||||
end
|
||||
|
||||
# Works like update_page but wraps the generated JavaScript in a <script>
|
||||
|
|
|
@ -2,7 +2,7 @@ module ActionView #:nodoc:
|
|||
module Helpers #:nodoc:
|
||||
module RawOutputHelper
|
||||
def raw(stringish)
|
||||
stringish.to_s.html_safe!
|
||||
stringish.to_s.html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,11 +50,7 @@ module ActionView
|
|||
# confuse browsers.
|
||||
#
|
||||
def sanitize(html, options = {})
|
||||
returning self.class.white_list_sanitizer.sanitize(html, options) do |sanitized|
|
||||
if sanitized
|
||||
sanitized.html_safe!
|
||||
end
|
||||
end
|
||||
self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe)
|
||||
end
|
||||
|
||||
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
|
||||
|
@ -77,11 +73,7 @@ module ActionView
|
|||
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
|
||||
# # => Welcome to my website!
|
||||
def strip_tags(html)
|
||||
returning self.class.full_sanitizer.sanitize(html) do |sanitized|
|
||||
if sanitized
|
||||
sanitized.html_safe!
|
||||
end
|
||||
end
|
||||
self.class.full_sanitizer.sanitize(html).try(:html_safe)
|
||||
end
|
||||
|
||||
# Strips all link tags from +text+ leaving just the link text.
|
||||
|
|
|
@ -41,7 +41,7 @@ module ActionView
|
|||
# tag("img", { :src => "open & shut.png" }, false, false)
|
||||
# # => <img src="open & shut.png" />
|
||||
def tag(name, options = nil, open = false, escape = true)
|
||||
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe!
|
||||
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
|
||||
end
|
||||
|
||||
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
|
||||
|
@ -94,7 +94,7 @@ module ActionView
|
|||
# cdata_section(File.read("hello_world.txt"))
|
||||
# # => <![CDATA[<hello from a text file]]>
|
||||
def cdata_section(content)
|
||||
"<![CDATA[#{content}]]>".html_safe!
|
||||
"<![CDATA[#{content}]]>".html_safe
|
||||
end
|
||||
|
||||
# Returns an escaped version of +html+ without affecting existing escaped entities.
|
||||
|
@ -128,7 +128,7 @@ module ActionView
|
|||
|
||||
def content_tag_string(name, content, options, escape = true)
|
||||
tag_options = tag_options(options, escape) if options
|
||||
"<#{name}#{tag_options}>#{content}</#{name}>".html_safe!
|
||||
"<#{name}#{tag_options}>#{content}</#{name}>".html_safe
|
||||
end
|
||||
|
||||
def tag_options(options, escape = true)
|
||||
|
@ -143,7 +143,7 @@ module ActionView
|
|||
attrs << %(#{key}="#{final_value}")
|
||||
end
|
||||
end
|
||||
" #{attrs.sort * ' '}".html_safe! unless attrs.empty?
|
||||
" #{attrs.sort * ' '}".html_safe unless attrs.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,14 +24,14 @@ module ActionView
|
|||
# end
|
||||
# # will either display "Logged in!" or a login link
|
||||
# %>
|
||||
def concat(string, unused_binding = nil)
|
||||
if unused_binding
|
||||
ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.", caller)
|
||||
end
|
||||
|
||||
def concat(string)
|
||||
output_buffer << string
|
||||
end
|
||||
|
||||
def safe_concat(string)
|
||||
output_buffer.safe_concat(string)
|
||||
end
|
||||
|
||||
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
|
||||
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
|
||||
# for a total length not exceeding <tt>:length</tt>.
|
||||
|
|
|
@ -12,7 +12,7 @@ module ActionView
|
|||
# prepend the key with a period, nothing is converted.
|
||||
def translate(key, options = {})
|
||||
options[:raise] = true
|
||||
I18n.translate(scope_key_by_partial(key), options).html_safe!
|
||||
I18n.translate(scope_key_by_partial(key), options).html_safe
|
||||
rescue I18n::MissingTranslationData => e
|
||||
keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
|
||||
content_tag('span', keys.join(', '), :class => 'translation_missing')
|
||||
|
|
|
@ -98,7 +98,7 @@ module ActionView
|
|||
polymorphic_path(options)
|
||||
end
|
||||
|
||||
escape ? escape_once(url).html_safe! : url
|
||||
escape ? escape_once(url).html_safe : url
|
||||
end
|
||||
|
||||
# Creates a link tag of the given +name+ using a URL created by the set
|
||||
|
@ -208,7 +208,7 @@ module ActionView
|
|||
if block_given?
|
||||
options = args.first || {}
|
||||
html_options = args.second
|
||||
concat(link_to(capture(&block), options, html_options).html_safe!)
|
||||
safe_concat(link_to(capture(&block), options, html_options))
|
||||
else
|
||||
name = args[0]
|
||||
options = args[1] || {}
|
||||
|
@ -226,7 +226,7 @@ module ActionView
|
|||
end
|
||||
|
||||
href_attr = "href=\"#{url}\"" unless href
|
||||
"<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe!
|
||||
"<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -312,7 +312,7 @@ module ActionView
|
|||
html_options.merge!("type" => "submit", "value" => name)
|
||||
|
||||
("<form method=\"#{form_method}\" action=\"#{escape_once url}\" #{"data-remote=\"true\"" if remote} class=\"button-to\"><div>" +
|
||||
method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe!
|
||||
method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -240,7 +240,7 @@ module ActionView
|
|||
end
|
||||
|
||||
result = @template ? collection_with_template : collection_without_template
|
||||
result.join(spacer).html_safe!
|
||||
result.join(spacer).html_safe
|
||||
end
|
||||
|
||||
def collection_with_template(template = @template)
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
module ActionView #:nodoc:
|
||||
class SafeBuffer < String
|
||||
def <<(value)
|
||||
if value.html_safe?
|
||||
super(value)
|
||||
else
|
||||
super(ERB::Util.h(value))
|
||||
end
|
||||
end
|
||||
|
||||
def concat(value)
|
||||
self << value
|
||||
end
|
||||
|
||||
def html_safe?
|
||||
true
|
||||
end
|
||||
|
||||
def html_safe!
|
||||
self
|
||||
end
|
||||
|
||||
def to_s
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
|
@ -53,7 +53,7 @@ module ActionView
|
|||
setup :setup_with_controller
|
||||
def setup_with_controller
|
||||
@controller = TestController.new
|
||||
@output_buffer = ActionView::SafeBuffer.new
|
||||
@output_buffer = ActiveSupport::SafeBuffer.new
|
||||
@rendered = ''
|
||||
|
||||
self.class.send(:include_helper_modules!)
|
||||
|
|
|
@ -611,7 +611,7 @@ class FragmentCachingTest < ActionController::TestCase
|
|||
@store.write('views/expensive', 'fragment content')
|
||||
fragment_computed = false
|
||||
|
||||
buffer = 'generated till now -> '
|
||||
buffer = 'generated till now -> '.html_safe
|
||||
@controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
|
||||
|
||||
assert fragment_computed
|
||||
|
@ -622,7 +622,7 @@ class FragmentCachingTest < ActionController::TestCase
|
|||
@store.write('views/expensive', 'fragment content')
|
||||
fragment_computed = false
|
||||
|
||||
buffer = 'generated till now -> '
|
||||
buffer = 'generated till now -> '.html_safe
|
||||
@controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
|
||||
|
||||
assert !fragment_computed
|
||||
|
|
|
@ -13,7 +13,7 @@ class OutputEscapingTest < ActiveSupport::TestCase
|
|||
test "escapeHTML shouldn't touch explicitly safe strings" do
|
||||
# TODO this seems easier to compose and reason about, but
|
||||
# this should be verified
|
||||
assert_equal "<", ERB::Util.h("<".html_safe!)
|
||||
assert_equal "<", ERB::Util.h("<".html_safe)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ class ErbUtilTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_html_escape_passes_html_escpe_unmodified
|
||||
escaped = h("<p>".html_safe!)
|
||||
escaped = h("<p>".html_safe)
|
||||
assert_equal "<p>", escaped
|
||||
assert escaped.html_safe?
|
||||
end
|
||||
|
|
|
@ -1170,7 +1170,7 @@ class FormHelperTest < ActionView::TestCase
|
|||
(field_helpers - %w(hidden_field)).each do |selector|
|
||||
src = <<-END_SRC
|
||||
def #{selector}(field, *args, &proc)
|
||||
("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe!
|
||||
("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe
|
||||
end
|
||||
END_SRC
|
||||
class_eval src, __FILE__, __LINE__
|
||||
|
|
|
@ -335,19 +335,19 @@ class FormTagHelperTest < ActionView::TestCase
|
|||
expected = %(<fieldset><legend>Your details</legend>Hello world!</fieldset>)
|
||||
assert_dom_equal expected, output_buffer
|
||||
|
||||
self.output_buffer = ''
|
||||
self.output_buffer = ''.html_safe
|
||||
field_set_tag { concat "Hello world!" }
|
||||
|
||||
expected = %(<fieldset>Hello world!</fieldset>)
|
||||
assert_dom_equal expected, output_buffer
|
||||
|
||||
self.output_buffer = ''
|
||||
self.output_buffer = ''.html_safe
|
||||
field_set_tag('') { concat "Hello world!" }
|
||||
|
||||
expected = %(<fieldset>Hello world!</fieldset>)
|
||||
assert_dom_equal expected, output_buffer
|
||||
|
||||
self.output_buffer = ''
|
||||
self.output_buffer = ''.html_safe
|
||||
field_set_tag('', :class => 'format') { concat "Hello world!" }
|
||||
|
||||
expected = %(<fieldset class="format">Hello world!</fieldset>)
|
||||
|
|
|
@ -16,7 +16,7 @@ class SafeBufferTest < ActionView::TestCase
|
|||
end
|
||||
|
||||
test "Should NOT escape a safe value passed to it" do
|
||||
@buffer << "<script>".html_safe!
|
||||
@buffer << "<script>".html_safe
|
||||
assert_equal "<script>", @buffer
|
||||
end
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ module ActionView
|
|||
class AssertionsTest < ActionView::TestCase
|
||||
def render_from_helper
|
||||
form_tag('/foo') do
|
||||
concat render(:text => '<ul><li>foo</li></ul>').html_safe!
|
||||
safe_concat render(:text => '<ul><li>foo</li></ul>')
|
||||
end
|
||||
end
|
||||
helper_method :render_from_helper
|
||||
|
|
|
@ -67,5 +67,6 @@ module ActiveSupport
|
|||
autoload :XmlMini
|
||||
end
|
||||
|
||||
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
|
||||
autoload :TestCase
|
||||
end
|
||||
|
|
|
@ -1,3 +1,53 @@
|
|||
require "erb"
|
||||
|
||||
class ERB
|
||||
module Util
|
||||
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' }
|
||||
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
|
||||
|
||||
# A utility method for escaping HTML tag characters.
|
||||
# This method is also aliased as <tt>h</tt>.
|
||||
#
|
||||
# In your ERb templates, use this method to escape any unsafe content. For example:
|
||||
# <%=h @person.name %>
|
||||
#
|
||||
# ==== Example:
|
||||
# puts html_escape("is a > 0 & a < 10?")
|
||||
# # => is a > 0 & a < 10?
|
||||
def html_escape(s)
|
||||
s = s.to_s
|
||||
if s.html_safe?
|
||||
s
|
||||
else
|
||||
s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
undef :h
|
||||
alias h html_escape
|
||||
|
||||
module_function :html_escape
|
||||
module_function :h
|
||||
|
||||
# A utility method for escaping HTML entities in JSON strings.
|
||||
# This method is also aliased as <tt>j</tt>.
|
||||
#
|
||||
# In your ERb templates, use this method to escape any HTML entities:
|
||||
# <%=j @person.to_json %>
|
||||
#
|
||||
# ==== Example:
|
||||
# puts json_escape("is a > 0 & a < 10?")
|
||||
# # => is a \u003E 0 \u0026 a \u003C 10?
|
||||
def json_escape(s)
|
||||
s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
|
||||
end
|
||||
|
||||
alias j json_escape
|
||||
module_function :j
|
||||
module_function :json_escape
|
||||
end
|
||||
end
|
||||
|
||||
class Object
|
||||
def html_safe?
|
||||
false
|
||||
|
@ -10,32 +60,46 @@ class Fixnum
|
|||
end
|
||||
end
|
||||
|
||||
class String
|
||||
attr_accessor :_rails_html_safe
|
||||
alias html_safe? _rails_html_safe
|
||||
module ActiveSupport #:nodoc:
|
||||
class SafeBuffer < String
|
||||
alias safe_concat concat
|
||||
|
||||
def concat(value)
|
||||
if value.html_safe?
|
||||
super(value)
|
||||
else
|
||||
super(ERB::Util.h(value))
|
||||
end
|
||||
end
|
||||
|
||||
def +(other)
|
||||
dup.concat(other)
|
||||
end
|
||||
|
||||
def <<(value)
|
||||
self.concat(value)
|
||||
end
|
||||
|
||||
def html_safe?
|
||||
true
|
||||
end
|
||||
|
||||
def html_safe
|
||||
self
|
||||
end
|
||||
|
||||
def to_s
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class String
|
||||
def html_safe!
|
||||
@_rails_html_safe = true
|
||||
self
|
||||
raise "You can't call html_safe! on a String"
|
||||
end
|
||||
|
||||
def html_safe
|
||||
dup.html_safe!
|
||||
ActiveSupport::SafeBuffer.new(self)
|
||||
end
|
||||
|
||||
alias original_plus +
|
||||
def +(other)
|
||||
result = original_plus(other)
|
||||
result._rails_html_safe = html_safe? && other.html_safe?
|
||||
result
|
||||
end
|
||||
|
||||
alias original_concat <<
|
||||
alias safe_concat <<
|
||||
def <<(other)
|
||||
@_rails_html_safe = false unless other.html_safe?
|
||||
result = original_concat(other)
|
||||
end
|
||||
|
||||
alias concat <<
|
||||
end
|
|
@ -342,12 +342,12 @@ class OutputSafetyTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
test "A string can be marked safe" do
|
||||
@string.html_safe!
|
||||
assert @string.html_safe?
|
||||
string = @string.html_safe
|
||||
assert string.html_safe?
|
||||
end
|
||||
|
||||
test "Marking a string safe returns the string" do
|
||||
assert_equal @string, @string.html_safe!
|
||||
assert_equal @string, @string.html_safe
|
||||
end
|
||||
|
||||
test "A fixnum is safe by default" do
|
||||
|
@ -361,7 +361,7 @@ class OutputSafetyTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
@string.html_safe!
|
||||
@string.html_safe
|
||||
@string << klass.new
|
||||
|
||||
assert_equal "helloother", @string
|
||||
|
@ -369,44 +369,44 @@ class OutputSafetyTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
test "Adding a safe string to another safe string returns a safe string" do
|
||||
@other_string = "other".html_safe!
|
||||
@string.html_safe!
|
||||
@combination = @other_string + @string
|
||||
@other_string = "other".html_safe
|
||||
string = @string.html_safe
|
||||
@combination = @other_string + string
|
||||
|
||||
assert_equal "otherhello", @combination
|
||||
assert @combination.html_safe?
|
||||
end
|
||||
|
||||
test "Adding an unsafe string to a safe string returns an unsafe string" do
|
||||
@other_string = "other".html_safe!
|
||||
@combination = @other_string + @string
|
||||
@other_combination = @string + @other_string
|
||||
test "Adding an unsafe string to a safe string escapes it and returns a safe string" do
|
||||
@other_string = "other".html_safe
|
||||
@combination = @other_string + "<foo>"
|
||||
@other_combination = @string + "<foo>"
|
||||
|
||||
assert_equal "otherhello", @combination
|
||||
assert_equal "helloother", @other_combination
|
||||
assert_equal "other<foo>", @combination
|
||||
assert_equal "hello<foo>", @other_combination
|
||||
|
||||
assert !@combination.html_safe?
|
||||
assert @combination.html_safe?
|
||||
assert !@other_combination.html_safe?
|
||||
end
|
||||
|
||||
test "Concatting safe onto unsafe yields unsafe" do
|
||||
@other_string = "other"
|
||||
@string.html_safe!
|
||||
@string.html_safe
|
||||
|
||||
@other_string.concat(@string)
|
||||
assert !@other_string.html_safe?
|
||||
end
|
||||
|
||||
test "Concatting unsafe onto safe yields unsafe" do
|
||||
@other_string = "other".html_safe!
|
||||
|
||||
@other_string.concat(@string)
|
||||
assert !@other_string.html_safe?
|
||||
test "Concatting unsafe onto safe yields escaped safe" do
|
||||
@other_string = "other".html_safe
|
||||
string = @other_string.concat("<foo>")
|
||||
assert_equal "other<foo>", string
|
||||
assert string.html_safe?
|
||||
end
|
||||
|
||||
test "Concatting safe onto safe yields safe" do
|
||||
@other_string = "other".html_safe!
|
||||
@string.html_safe!
|
||||
@other_string = "other".html_safe
|
||||
@string.html_safe
|
||||
|
||||
@other_string.concat(@string)
|
||||
assert @other_string.html_safe?
|
||||
|
@ -414,31 +414,32 @@ class OutputSafetyTest < ActiveSupport::TestCase
|
|||
|
||||
test "Concatting safe onto unsafe with << yields unsafe" do
|
||||
@other_string = "other"
|
||||
@string.html_safe!
|
||||
@string.html_safe
|
||||
|
||||
@other_string << @string
|
||||
assert !@other_string.html_safe?
|
||||
end
|
||||
|
||||
test "Concatting unsafe onto safe with << yields unsafe" do
|
||||
@other_string = "other".html_safe!
|
||||
|
||||
@other_string << @string
|
||||
assert !@other_string.html_safe?
|
||||
test "Concatting unsafe onto safe with << yields escaped safe" do
|
||||
@other_string = "other".html_safe
|
||||
string = @other_string << "<foo>"
|
||||
assert_equal "other<foo>", string
|
||||
assert string.html_safe?
|
||||
end
|
||||
|
||||
test "Concatting safe onto safe with << yields safe" do
|
||||
@other_string = "other".html_safe!
|
||||
@string.html_safe!
|
||||
@other_string = "other".html_safe
|
||||
@string.html_safe
|
||||
|
||||
@other_string << @string
|
||||
assert @other_string.html_safe?
|
||||
end
|
||||
|
||||
test "Concatting a fixnum to safe always yields safe" do
|
||||
@string.html_safe!
|
||||
@string.concat(13)
|
||||
assert @string.html_safe?
|
||||
string = @string.html_safe
|
||||
string = string.concat(13)
|
||||
assert_equal "hello".concat(13), string
|
||||
assert string.html_safe?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue