Fix for Issue #17547

ActiveModel built-in validators should use the custom message passed in as :message option
Re:  alternative implementation

This alternative is not able to handle all use cases. It will fail when options[:message] is a string (not a symbol).

For example, this test case in activemodel/test/cases/validations/exclusion_validation_test.rb will fail:

  def test_validates_exclusion_of_with_formatted_message
      Topic.validates_exclusion_of(:title, in: %w( abe monkey ), message: "option %{value} is restricted")

      assert Topic.new("title" => "something", "content" => "abc")

      t = Topic.new("title" => "monkey")
      assert t.invalid?
      assert t.errors[:title].any?
      assert_equal ["option monkey is restricted"], t.errors[:title]
    end

We are expecting "option monkey is restricted". However, "option %{value} is restricted" will be returned instead.

In order to use your alternative implementation, we have to make change to the rest of the method:

    def normalize_message(attribute, default_message, options)
      message = options[:message] || default_message

      case message
      when Symbol, String
        generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
      when Proc
        message.call
      else
        message
      end
    end