diff options
Diffstat (limited to 'actionpack')
22 files changed, 165 insertions, 83 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 5314dcc193..f4b6464bdc 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,33 @@  *Rails 3.2.0 (unreleased)* +* Generate hidden input before select with :multiple option set to true. +  This is useful when you rely on the fact that when no options is set, +  the state of select will be sent to rails application. Without hidden field +  nothing is sent according to HTML spec [Bogdan Gusiev] + +* Refactor ActionController::TestCase cookies [Andrew White] + +  Assigning cookies for test cases should now use cookies[], e.g: + +    cookies[:email] = 'user@example.com' +    get :index +    assert_equal 'user@example.com', cookies[:email] + +  To clear the cookies, use clear, e.g: + +    cookies.clear +    get :index +    assert_nil cookies[:email] + +  We now no longer write out HTTP_COOKIE and the cookie jar is +  persistent between requests so if you need to manipulate the environment +  for your test you need to do it before the cookie jar is created. + + +*Rails 3.1.0 (unreleased)* + +* json_escape will now return a SafeBuffer string if it receives SafeBuffer string [tenderlove] +  * Make sure escape_js returns SafeBuffer string if it receives SafeBuffer string [Prem Sichanugrist]  * Fix escape_js to work correctly with the new SafeBuffer restriction [Paul Gallagher] @@ -31,27 +59,6 @@    You can read more about this change in http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2e516e7acc96c4fb -* Refactor ActionController::TestCase cookies [Andrew White] - -  Assigning cookies for test cases should now use cookies[], e.g: - -    cookies[:email] = 'user@example.com' -    get :index -    assert_equal 'user@example.com', cookies[:email] - -  To clear the cookies, use clear, e.g: - -    cookies.clear -    get :index -    assert_nil cookies[:email] - -  We now no longer write out HTTP_COOKIE and the cookie jar is -  persistent between requests so if you need to manipulate the environment -  for your test you need to do it before the cookie jar is created. - - -*Rails 3.1.0 (unreleased)* -  * Added 'ActionView::Helpers::FormHelper.fields_for_with_index', similar to fields_for but allows to have access to the current iteration index [Jorge Bejar]  * Warn if we cannot verify CSRF token authenticity [José Valim] diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb index 1e43104f0a..505d5560b1 100644 --- a/actionpack/lib/action_dispatch/http/headers.rb +++ b/actionpack/lib/action_dispatch/http/headers.rb @@ -3,9 +3,10 @@ require 'active_support/memoizable'  module ActionDispatch    module Http      class Headers < ::Hash -      extend ActiveSupport::Memoizable +      @@env_cache = Hash.new { |h,k| h[k] = "HTTP_#{k.upcase.gsub(/-/, '_')}" }        def initialize(*args) +          if args.size == 1 && args[0].is_a?(Hash)            super()            update(args[0]) @@ -25,9 +26,8 @@ module ActionDispatch        private          # Converts a HTTP header name to an environment variable name.          def env_name(header_name) -          "HTTP_#{header_name.upcase.gsub(/-/, '_')}" +          @@env_cache[header_name]          end -        memoize :env_name      end    end  end diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb index 37effade4f..a15ad28f16 100644 --- a/actionpack/lib/action_dispatch/http/upload.rb +++ b/actionpack/lib/action_dispatch/http/upload.rb @@ -4,7 +4,7 @@ module ActionDispatch        attr_accessor :original_filename, :content_type, :tempfile, :headers        def initialize(hash) -        @original_filename = hash[:filename] +        @original_filename = encode_filename(hash[:filename])          @content_type      = hash[:type]          @headers           = hash[:head]          @tempfile          = hash[:tempfile] @@ -30,6 +30,16 @@ module ActionDispatch        def size          @tempfile.size        end +       +      private +      def encode_filename(filename) +        # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8 +        if "ruby".encoding_aware? && filename +          filename.force_encoding("UTF-8").encode! +        else +          filename +        end +      end      end      module Upload diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 74c090f260..1dcd83ceb5 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -161,7 +161,7 @@ module ActionDispatch    # Consider the following route, which you will find commented out at the    # bottom of your generated <tt>config/routes.rb</tt>:    # -  #   match ':controller(/:action(/:id(.:format)))' +  #   match ':controller(/:action(/:id))(.:format)'    #    # This route states that it expects requests to consist of a    # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 70ea419e81..65895590bf 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -87,7 +87,7 @@ module ActionDispatch                  raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"                end              end -        end +          end            # match "account/overview"            def using_match_shorthand?(path, options) diff --git a/actionpack/lib/action_view/helpers/controller_helper.rb b/actionpack/lib/action_view/helpers/controller_helper.rb index db59bca159..1a583e62ae 100644 --- a/actionpack/lib/action_view/helpers/controller_helper.rb +++ b/actionpack/lib/action_view/helpers/controller_helper.rb @@ -20,4 +20,4 @@ module ActionView        end      end    end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 56e3af683b..6a724749f4 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -621,7 +621,6 @@ module ActionView      end      class DateTimeSelector #:nodoc: -      extend ActiveSupport::Memoizable        include ActionView::Helpers::TagHelper        DEFAULT_PREFIX = 'date'.freeze @@ -786,11 +785,12 @@ module ActionView          # Returns translated month names, but also ensures that a custom month          # name array has a leading nil element.          def month_names -          month_names = @options[:use_month_names] || translated_month_names -          month_names.unshift(nil) if month_names.size < 13 -          month_names +          @month_names ||= begin +            month_names = @options[:use_month_names] || translated_month_names +            month_names.unshift(nil) if month_names.size < 13 +            month_names +          end          end -        memoize :month_names          # Returns translated month names.          #  => [nil, "January", "February", "March", @@ -825,9 +825,8 @@ module ActionView          end          def date_order -          @options[:order] || translated_date_order +          @date_order ||= @options[:order] || translated_date_order          end -        memoize :date_order          def translated_date_order            I18n.translate(:'date.order', :locale => @options[:locale]) || [] diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 6513edcf6e..3dc6d65432 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -128,6 +128,28 @@ module ActionView        # By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection        # or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option        # tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled. +      # +      # ==== Gotcha +      # +      # The HTML specification says when +multiple+ parameter passed to select and all options got deselected  +      # web browsers do not send any value to server. Unfortunately this introduces a gotcha: +      # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user +      # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So, +      # any mass-assignment idiom like +      # +      #   @user.update_attributes(params[:user]) +      # +      # wouldn't update roles. +      # +      # To prevent this the helper generates an auxiliary hidden field before +      # every multiple select. The hidden field has the same name as multiple select and blank value. +      # +      # This way, the client either sends only the hidden field (representing +      # the deselected multiple select box), or both fields. Since the HTML specification +      # says key/value pairs have to be sent in the same order they appear in the +      # form, and parameters extraction gets the last occurrence of any repeated +      # key in the query string, that works for ordinary forms. +      #        def select(object, method, choices, options = {}, html_options = {})          InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)        end @@ -552,43 +574,26 @@ module ActionView        include FormOptionsHelper        def to_select_tag(choices, options, html_options) -        html_options = html_options.stringify_keys -        add_default_name_and_id(html_options) -        value = value(object) -        selected_value = options.has_key?(:selected) ? options[:selected] : value -        disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil -        content_tag("select", add_options(options_for_select(choices, :selected => selected_value, :disabled => disabled_value), options, selected_value), html_options) +        selected_value = options.has_key?(:selected) ? options[:selected] : value(object) +        select_content_tag(options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]), options, html_options)        end        def to_collection_select_tag(collection, value_method, text_method, options, html_options) -        html_options = html_options.stringify_keys -        add_default_name_and_id(html_options) -        value = value(object) -        disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil -        selected_value = options.has_key?(:selected) ? options[:selected] : value -        content_tag( -          "select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options +        selected_value = options.has_key?(:selected) ? options[:selected] : value(object) +        select_content_tag( +          options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options          )        end        def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) -        html_options = html_options.stringify_keys -        add_default_name_and_id(html_options) -        value = value(object) -        content_tag( -          "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options +        select_content_tag( +          option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options          )        end        def to_time_zone_select_tag(priority_zones, options, html_options) -        html_options = html_options.stringify_keys -        add_default_name_and_id(html_options) -        value = value(object) -        content_tag("select", -          add_options( -            time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), -            options, value -          ), html_options +        select_content_tag( +            time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options          )        end @@ -603,6 +608,17 @@ module ActionView            end            option_tags.html_safe          end + +        def select_content_tag(option_tags, options, html_options) +          html_options = html_options.stringify_keys +          add_default_name_and_id(html_options) +          select = content_tag("select", add_options(option_tags, options, value(object)), html_options) +          if html_options["multiple"] +            tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select  +          else +            select +          end +        end      end      class FormBuilder diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index a91e86f4db..72bc4510b5 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -597,6 +597,12 @@ module ActionView          number_field_tag(name, value, options.stringify_keys.update("type" => "range"))        end +      # Creates the hidden UTF8 enforcer tag. Override this method in a helper +      # to customize the tag. +      def utf8_enforcer_tag +        tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe) +      end +        private          def html_options_for_form(url_for_options, options, *parameters_for_url)            options.stringify_keys.tap do |html_options| @@ -611,9 +617,6 @@ module ActionView          end          def extra_tags_for_form(html_options) -          snowman_tag = tag(:input, :type => "hidden", -                            :name => "utf8", :value => "✓".html_safe) -            authenticity_token = html_options.delete("authenticity_token")            method = html_options.delete("method").to_s @@ -629,7 +632,7 @@ module ActionView                tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)            end -          tags = snowman_tag << method_tag +          tags = utf8_enforcer_tag << method_tag            content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')          end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 472b996a5e..4be95d8f7e 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -255,7 +255,7 @@ module ActionView        #   simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false)        #   # => "<p><span>I'm allowed!</span> It's true.</p>"        def simple_format(text, html_options={}, options={}) -        text = ''.html_safe if text.nil? +        text = '' if text.nil?          start_tag = tag('p', html_options, true)          text = sanitize(text) unless options[:sanitize] == false          text = text.to_str @@ -263,7 +263,7 @@ module ActionView          text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}")  # 2+ newline  -> paragraph          text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline   -> br          text.insert 0, start_tag -        text = ActiveSupport::SafeBuffer.new(text).safe_concat("</p>") +        text.html_safe.safe_concat("</p>")        end        # Creates a Cycle object whose _to_s_ method cycles through elements of an diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index fd8fe417d0..26b6e8b599 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -5,7 +5,7 @@ module I18n    class ExceptionHandler      include Module.new {        def call(exception, locale, key, options) -        exception.is_a?(MissingTranslation) ? super.html_safe : super +        exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super        end      }    end diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 4b497d142d..38eb00ce01 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -54,12 +54,21 @@ module Sprockets          env = Sprockets::Environment.new(app.root.to_s)          env.static_root = File.join(app.root.join("public"), assets.prefix) -        env.paths.concat assets.paths + +        if env.respond_to?(:append_path) +          assets.paths.each { |path| env.append_path(path) } +        else +          env.paths.concat assets.paths +        end          env.logger = Rails.logger -        env.js_compressor  = expand_js_compressor(assets.js_compressor) -        env.css_compressor = expand_css_compressor(assets.css_compressor) +        if assets.compress +          # temporarily hardcode default JS compressor to uglify. Soon, it will work +          # the same as SCSS, where a default plugin sets the default. +          env.js_compressor  = expand_js_compressor(assets.js_compressor || :uglifier) +          env.css_compressor = expand_css_compressor(assets.css_compressor) +        end          env        end diff --git a/actionpack/test/abstract/abstract_controller_test.rb b/actionpack/test/abstract/abstract_controller_test.rb index 53712a60ec..5823e64637 100644 --- a/actionpack/test/abstract/abstract_controller_test.rb +++ b/actionpack/test/abstract/abstract_controller_test.rb @@ -181,7 +181,7 @@ module AbstractController      class TestLayouts < ActiveSupport::TestCase        test "layouts are included" do          controller = Me4.new -        result = controller.process(:index) +        controller.process(:index)          assert_equal "Me4 Enter : Hello from me4/index.erb : Exit", controller.response_body        end      end diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index f48b73b63a..6265e78030 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -612,7 +612,6 @@ XML            send(method, :test_remote_addr)            assert false, "expected RuntimeError, got nothing"          rescue RuntimeError => error -          assert true            assert_match(%r{@#{variable} is nil}, error.message)          rescue => error            assert false, "expected RuntimeError, got #{error.class}" diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 3f3d6dcc2f..484e996f31 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -293,8 +293,8 @@ module AbstractController          first_class.default_url_options[:host] = first_host          second_class.default_url_options[:host] = second_host -        assert_equal first_class.default_url_options[:host], first_host -        assert_equal second_class.default_url_options[:host], second_host +        assert_equal  first_host, first_class.default_url_options[:host] +        assert_equal second_host, second_class.default_url_options[:host]        end        def test_with_stringified_keys diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index 621fb79915..ae8588cbb0 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -30,7 +30,7 @@ class WebServiceTest < ActionDispatch::IntegrationTest    def test_check_parameters      with_test_route_set do        get "/" -      assert_blank @controller.response.body +      assert_equal '', @controller.response.body      end    end @@ -162,7 +162,7 @@ class WebServiceTest < ActionDispatch::IntegrationTest    def test_use_xml_ximple_with_empty_request      with_test_route_set do        assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} } -      assert_blank @controller.response.body +      assert_equal '', @controller.response.body      end    end diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb index e2a7f1bad7..7e4a1519fb 100644 --- a/actionpack/test/dispatch/uploaded_file_test.rb +++ b/actionpack/test/dispatch/uploaded_file_test.rb @@ -12,6 +12,13 @@ module ActionDispatch        uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)        assert_equal 'foo', uf.original_filename      end +     +    if "ruby".encoding_aware? +      def test_filename_should_be_in_utf_8 +        uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new) +        assert_equal "UTF-8", uf.original_filename.encoding.to_s +      end +    end      def test_content_type        uf = Http::UploadedFile.new(:type => 'foo', :tempfile => Object.new) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 5296556fe6..0507045ad2 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1890,7 +1890,7 @@ class FormHelperTest < ActionView::TestCase      assert_dom_equal expected, output_buffer    end -  def snowman(method = nil) +  def hidden_fields(method = nil)      txt =  %{<div style="margin:0;padding:0;display:inline">}      txt << %{<input name="utf8" type="hidden" value="✓" />}      if method && !method.to_s.in?(['get', 'post']) @@ -1918,7 +1918,7 @@ class FormHelperTest < ActionView::TestCase        method = options      end -    form_text(action, id, html_class, remote, multipart, method) + snowman(method) + contents + "</form>" +    form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>"    end    def test_default_form_builder diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index b92e1d9890..f3969895ae 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -457,6 +457,22 @@ class FormOptionsHelperTest < ActionView::TestCase      )    end +  def test_select_with_multiple_to_add_hidden_input +    output_buffer =  select(:post, :category, "", {}, :multiple => true) +    assert_dom_equal( +      "<input type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>", +      output_buffer +    ) +  end + +  def test_select_with_multiple_and_disabled_to_add_disabled_hidden_input +    output_buffer =  select(:post, :category, "", {}, :multiple => true, :disabled => true) +    assert_dom_equal( +      "<input disabled=\"disabled\"type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" disabled=\"disabled\" id=\"post_category\" name=\"post[category][]\"></select>", +      output_buffer +    ) +  end +    def test_select_with_blank      @post = Post.new      @post.category = "<mus>" @@ -649,11 +665,11 @@ class FormOptionsHelperTest < ActionView::TestCase      )    end -  def test_collection_select_with_multiple_option_appends_array_brackets +  def test_collection_select_with_multiple_option_appends_array_brackets_and_hidden_input      @post = Post.new      @post.author_name = "Babe" -    expected = "<select id=\"post_author_name\" name=\"post[author_name][]\" multiple=\"multiple\"><option value=\"\"></option>\n<option value=\"<Abe>\"><Abe></option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>" +    expected = "<input type=\"hidden\" name=\"post[author_name][]\" value=\"\"/><select id=\"post_author_name\" name=\"post[author_name][]\" multiple=\"multiple\"><option value=\"\"></option>\n<option value=\"<Abe>\"><Abe></option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>"      # Should suffix default name with [].      assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { :include_blank => true }, :multiple => true) diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index f95308b847..979251bfd1 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -9,7 +9,7 @@ class FormTagHelperTest < ActionView::TestCase      @controller = BasicController.new    end -  def snowman(options = {}) +  def hidden_fields(options = {})      method = options[:method]      txt =  %{<div style="margin:0;padding:0;display:inline">} @@ -34,7 +34,7 @@ class FormTagHelperTest < ActionView::TestCase    end    def whole_form(action = "http://www.example.com", options = {}) -    out = form_text(action, options) + snowman(options) +    out = form_text(action, options) + hidden_fields(options)      if block_given?        out << yield << "</form>" diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index 9b5c6d127c..cd9f54e04c 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -38,11 +38,19 @@ class TranslationHelperTest < ActiveSupport::TestCase    def test_returns_missing_translation_message_wrapped_into_span      expected = '<span class="translation_missing" title="translation missing: en.translations.missing">Missing</span>'      assert_equal expected, translate(:"translations.missing") +    assert_equal true, translate(:"translations.missing").html_safe?    end    def test_returns_missing_translation_message_using_nil_as_rescue_format      expected = 'translation missing: en.translations.missing'      assert_equal expected, translate(:"translations.missing", :rescue_format => nil) +    assert_equal false, translate(:"translations.missing", :rescue_format => nil).html_safe? +  end + +  def test_i18n_translate_defaults_to_nil_rescue_format +    expected = 'translation missing: en.translations.missing' +    assert_equal expected, I18n.translate(:"translations.missing") +    assert_equal false, I18n.translate(:"translations.missing").html_safe?    end    def test_translation_returning_an_array diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 4aa45c8bf0..1e54f0174c 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -15,6 +15,7 @@ class UrlHelperTest < ActiveSupport::TestCase    routes.draw do      match "/" => "foo#bar"      match "/other" => "foo#other" +    match "/article/:id" => "foo#article", :as => :article    end    include routes.url_helpers @@ -264,6 +265,13 @@ class UrlHelperTest < ActiveSupport::TestCase      assert_equal '<a href="/">Example site</a>', out    end +  def test_link_tag_with_html_safe_string +    assert_dom_equal( +      "<a href=\"/article/Gerd_M%C3%BCller\">Gerd Müller</a>", +      link_to("Gerd Müller", article_path("Gerd_Müller".html_safe)) +    ) +  end +    def test_link_to_unless      assert_equal "Showing", link_to_unless(true, "Showing", url_hash)  | 
